官方文檔中國版: https://developer.android.google.cn/guide/components/bound-services.html
谷歌2016年底為中國開發者提供中國版,友善通路!
綁定服務簡介
Android服務與用戶端互相調用(傳遞消息),必須建立綁定服務bindService()提供IBinder接口()!
服務與用戶端互動方式(傳遞消息)有三種:
1.使用Binder類(用戶端與服務在同一程序)
如果服務與用戶端在同一程序中運作,通過擴充Binder類并從onBind()傳回它的一個執行個體來建立接口。
用戶端收到Binder後,可利用它直接通路Binder實作中乃至Service中可用的公共方法!
2.使用Messenger(用戶端與服務在不同程序,服務隻能是單線程)
不同程序工作,可用Messenger為服務建立接口。Message對象内含Handler。
随後可與用戶端分享一個IBinder,進而讓用戶端能利用 Message 對象向服務發送指令。
此外,用戶端還可定義自有 Messenger,以便服務回傳消息!
這是程序間通信(IPC)的最簡單方法(AIDL的簡化),因為Messenger會在服務單一線程建立所有請求隊列。
3.使用AIDL(用戶端與服務在不同程序,服務可以是多線程)
AIDL(Android 接口定義語言)執行所有将對象分解成原語的工作,
作業系統可以識别這些原語并将它們編組到各程序中以執行IPC。
之前的Messenger實際是以AIDL作為其底層結構。如果服務同時處理多個請求(多線程),應該用AIDL。
注:大多數應用“都不會”使用AIDL來建立綁定服務,因為它可能要求具備多線程處理能力,
并可能導緻實作的複雜性增加。是以,AIDL并不适合大多數應用!
4.此外,還可用廣播這個萬金油元件進行傳遞消息,無論是不同程序,還是不同APP應用,都可用廣播傳遞消息!
但是需要注意的是, 廣播有可能洩露資料、惡意程式發送廣播等安全性問題,
是以應該限制廣播隻在本應用内傳播:
1.設定permission或Intent.setPackage,不把廣播發送到應用外!
2.使用LocalBroadcastManager或設定android:exported="false"不接收應用外廣播!
一.使用Binder類(用戶端與服務在同一程序)
// 1.本地服務————————————————————————————————————
public class LocalService extends Service {
private final IBinder mLocalBinder = new LocalBinder();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mLocalBinder;
}
public int getRandomNumber() {
return new Random().nextInt();
}
}
// 2.用戶端————————————————————————————————————
public class BindingActivity extends Activity {
boolean mBound = false;
@Override
protected void onStart() {
super.onStart();
// 綁定本地服務(同一程序)
bindService(new Intent(this, LocalService.class),
mConnection,
Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBound = true;
// 擷取本地服務的執行個體,并調用方法,擷取随機數
LocalService localService = ((LocalBinder) service).getService();
int num = localService.getRandomNumber();
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
@Override
protected void onStop() {
super.onStop();
if (mBound) {
// 解綁本地服務
unbindService(mConnection);
mBound = false;
}
}
}
二.使用Messenger(用戶端與服務在不同程序,服務隻能是單線程)
// 1.注冊遠端服務————————————————————————————————————
<service
android:name=".MessengerService"
android:process=":remote"> (process指定服務在另一個程序,名叫remote)
</service>
// 2.遠端服務————————————————————————————————————
public class MessengerService extends Service {
final Messenger mServiceMessenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
// 接收用戶端消息
switch (msg.what) {
case :
Toast.makeText(getApplicationContext(), "service hello!", Toast.LENGTH_SHORT).show();
Thread.sleep();
// 向用戶端發消息
Messenger messenger = msg.replyTo;
messenger.send(Message.obtain(null, , , ))
break;
}
}
});
@Override
public IBinder onBind(Intent intent) {
return mServiceMessenger.getBinder();
}
}
// 3.用戶端————————————————————————————————————
public class ActivityMessenger extends Activity {
boolean mBound = false;
private Messenger mActivityMessenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg){
// 接收遠端服務消息
switch (msg.what){
case :
Toast.makeText(ActivityMessenger.this, "activity hello!", Toast.LENGTH_SHORT).show();
break;
}
}
});
private ServiceConnection mConnection = new ServiceConnection(){
public void onServiceConnected(ComponentName className, IBinder service) {
mBound = true;
// 向遠端服務發消息
Messenger messenger = new Messenger(service);
Message msg = Message.obtain(null, , , );
msg.replyTo = mActivityMessenger
messenger.send(msg);
}
public void onServiceDisconnected(ComponentName className) {
mBound = false;
}
};
@Override
protected void onStart() {
super.onStart();
// 綁定遠端服務(不同程序)
bindService(new Intent(this, MessengerService.class),
mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// 解綁遠端服務
if (mBound) {
unbindService(mConnection);
}
}
}
三.使用AIDL(用戶端與服務在不同程序,服務可以是多線程)
1.建立AIDL接口
預設情況下AIDL支援下列資料類型
int、long、char、boolean、String、CharSequence、List、Map
List中元素都必須是以上類型、其他AIDL接口、可打包類型(即實作Parcelable接口).
Map中元素都必須是以上類型、其他AIDL接口、可打包類型(即實作了Parcelable接口)
不支援通用Map(如Map<String,Integer>形式的Map)
// IRemoteService.aidl檔案
interface IRemoteService {
// 注冊回調
void registerCallback(IRemoteServiceCallBack cb);
void unregisterCallback(IRemoteServiceCallBack cb);
}
// IRemoteServiceCallBack.aidl檔案
interface IRemoteServiceCallBack{
void valueChanged(int value);
}
2.在遠端服務中實作AIDL接口
public class RemoteService extends Service {
// 一個服務會綁定多個用戶端, 需要集合來存放用戶端的回調接口
private RemoteCallbackList<IRemoteServiceCallBack> mCallbackList
= new RemoteCallbackList<IRemoteServiceCallBack>();
// 實作IRemoteService.AIDL接口
private IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public void registerCallback(IRemoteServiceCallBack cb)throws RemoteException {
if (cb != null) {
mCallbackList.register(cb);
}
}
@Override
public void unRegisterCallback(IRemoteServiceCallBack cb)throws RemoteException {
if (cb != null) {
mCallbackList.unregister(cb);
}
}
};
// 通知所有用戶端的回調接口
public void sendMsg(){
int N = mCallbackList.beginBroadcast();
for(int i=;i<N;i++){
try {
// 回調通知用戶端
mCallbackList.getBroadcastItem(i).valueChanged(mValue);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallbackList.finishBroadcast(); // 通知完成
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
3.用戶端
public class BindActivity extends Activity {
IRemoteService mService = null;
private boolean mIsBound = false;
// 實作IRemoteServiceCallback.AIDL接口
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* 來自遠端服務的回調通知,
* 此方法不在UI線程中, 更新UI需小心!
*/
public void valueChanged(int value) {
...
}
};
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder service){
mIsBound = true;
mService = IRemoteService.Stub.asInterface(service);
// 注冊回調接口
mService.registerCallback(mCallback);
}
public void onServiceDisconnected(ComponentName className) {
mIsBound = false;
mService = null;
}
};
@Override
protected void onStart() {
super.onStart();
// 綁定遠端服務
bindService(new Intent(BindActivity.this, RemoteService.class),
mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (mIsBound){
// 取消回調接口
mService.unregisterCallback(mCallback);
// 解綁遠端服務
unbindService(mConnection);
}
}
}
總結:
綜上比較, AIDL實作用戶端與遠端服務通信太繁瑣, 互相通知調用至少需要兩個AIDL接口檔案
在遠端服務中通知用戶端, 需要循環通知, 相當繁瑣!
除非迫不得以(如服務可以是多線程運作), 實在不建議使用AIDL!
用戶端與服務在不同程序時, 建議服務單線程運作,
使用Messenger通信, 這也是Android官方推薦的!
簡書: http://www.jianshu.com/p/aec29a98bc1e
CSDN部落格: http://blog.csdn.net/qq_32115439/article/details/72760479
GitHub部落格:http://lioil.win/2017/05/25/Android_bindService.html
Coding部落格:http://c.lioil.win/2017/05/25/Android_bindService.html