很久沒用跨進程通信了,然後等自己又要用的時候,網上一搜,千遍一律,還很渣,吐血~
工程有兩份`MessengerService`&`MessengerActivity`,前個提供服務,後個訪問服務。
跨進程通信
就多個apk互相通信,當然廣播也是不錯的。不過這裏用的是Messenger和AIDL,並且都弄成雙向的了。
Messenger
信使,底層說是也用的AIDL。用起來挺方便的。
MessengerService
1)AndroidManifest.xml聲明權限
- <permission
- android:name="org.join.permission.SERVICE"
- android:protectionLevel="normal" >
- </permission>
- <uses-permission android:name="org.join.permission.SERVICE" />
爲什麼弄個帶權限的呢?因爲討厭任何警告==。
2)註冊MessengerService
- <service
- android:name="org.join.messenger.MessengerService"
- android:exported="true"
- android:permission="org.join.permission.SERVICE"
- android:process=":remote" >
- <intent-filter>
- <action android:name="org.join.service.MESSENGER" />
- </intent-filter>
- </service>
這裏把權限加上了,不然最新ADT是會有個警告的。就一嘀咕的老頭,討人厭。
3)MessengerService核心代碼
- public class MessengerService extends Service implements MessageCode {
- static final String TAG = "MessengerService";
- public static final String ACTION = "org.join.service.MESSENGER";
- // 第一步:新建
- final Messenger mMessenger = new Messenger(new Handler() {
- // 第二步:處理Message
- @Override
- public void dispatchMessage(Message msg) {
- switch (msg.what) {
- case CLNT_REQ_HELLO:
- toast("Hello!");
- Bundle data = new Bundle();
- data.putString("msg", "Who am I?");
- // 這個`msg.replyTo`是客戶端的信使
- respClnt(msg.replyTo, SERV_RES_HAHA, data);
- break;
- case CLNT_REQ_SURPRISE:
- String s = msg.getData().getString("msg");
- toast("Surprise! " + s);
- break;
- }
- }
- // 第三步:回覆用,注意`msg.replyTo`。
- private void respClnt(Messenger clnt, int msgCode, Bundle data) {
- if (clnt == null) {
- return;
- }
- try {
- Message msg = Message.obtain(null, msgCode);
- msg.replyTo = mMessenger; // 綁定服務端信使
- if (data != null) {
- msg.setData(data);
- }
- clnt.send(msg); // 用客戶端信使發消息
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- });
- @Override
- public IBinder onBind(Intent intent) {
- Log.e(TAG, "onBind");
- return mMessenger.getBinder();
- }
- // ......
- }
MessengerActivity
1)AndroidManifest.xml聲明權限
- <uses-permission android:name="org.join.permission.SERVICE" />
2)MessengerActivity核心代碼
1. 創建Messenger
- // 服務端的信使,後面會綁定
- private Messenger mService;
- // 第一步:新建
- final Messenger mMessenger = new Messenger(new Handler() {
- // 第二步:處理Message
- @Override
- public void dispatchMessage(Message msg) {
- switch (msg.what) {
- case SERV_RES_HAHA:
- String s = msg.getData().getString("msg");
- toast("HAHA! " + s);
- Bundle data = new Bundle();
- data.putString("msg", "I am you.");
- sendMsg(CLNT_REQ_SURPRISE, data);
- break;
- }
- }
- });
- // 第三步:回覆用,同樣注意`msg.replyTo`。
- private void sendMsg(int msgCode, Bundle data) {
- if (mService == null) {
- toast("Service is disconnected!");
- return;
- }
- try {
- Message msg = Message.obtain(null, msgCode);
- msg.replyTo = mMessenger; // 綁定客戶端信使
- if (data != null) {
- msg.setData(data);
- }
- mService.send(msg); // 用服務端信使發消息
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
2. 綁定Service
- // 和一般綁定Service一樣
- private ServiceConnection servConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mService = new Messenger(service); // 不過,這裏改爲Messenger
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
- }
- };
- // 綁定方法
- protected void doBindService() {
- bindService(new Intent("org.join.service.MESSENGER"), servConnection, BIND_AUTO_CREATE);
- isBound = true;
- }
- // 解綁方法
- protected void doUnbindService() {
- if (isBound) {
- unbindService(servConnection);
- isBound = false;
- }
- }
好了,客戶端要發送消息,調用sendMsg()就可以了。
AIDL
也挺簡單的,恩,繼續...
RemoteService
1)AndroidManifest.xml聲明權限,就之前的,ok了。
2)註冊RemoteService
- <service
- android:name="org.join.aidl.RemoteService"
- android:exported="true"
- android:permission="org.join.permission.SERVICE"
- android:process=":remote" >
- <intent-filter>
- <action android:name="org.join.service.AIDL" />
- </intent-filter>
- </service>
3)搞個parcelable對象
不能就用基本類型是不,所以要搞個對象^^。
恩,Event.java就是了。然後要爲它寫個aidl。
Event.aidl
- package org.join.aidl;
- parcelable Event;
簡單吧。
4)然後弄個Callback
不能就單向通信是不,所以要用來回調客戶端。
IRemoteCallback.aidl
- package org.join.aidl;
- import org.join.aidl.Event;
- /**
- * @brief 遠程回調
- * @author join
- */
- interface IRemoteCallback {
- void onEvent(in Event event);
- }
4)服務的AIDL了
IRemoteService.aidl
- package org.join.aidl;
- import org.join.aidl.IRemoteCallback;
- import org.join.aidl.Event;
- /**
- * @brief 遠程服務
- * @author join
- */
- interface IRemoteService {
- void registerCallback(in IRemoteCallback cb);
- void unregisterCallback(in IRemoteCallback cb);
- Event requestEvent();
- }
5)哦,總算到RemoteService了
RemoteService.java,恩,直接參考了ApiDemo,捂臉~
- public class RemoteService extends Service {
- static final String TAG = "RemoteService";
- public static final String ACTION = "org.join.service.AIDL";
- final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<IRemoteCallback>();
- private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
- @Override
- public void registerCallback(IRemoteCallback cb) throws RemoteException {
- if (cb != null)
- mCallbacks.register(cb);
- }
- @Override
- public void unregisterCallback(IRemoteCallback cb) throws RemoteException {
- if (cb != null)
- mCallbacks.unregister(cb);
- }
- @Override
- public Event requestEvent() throws RemoteException {
- Event event = new Event();
- event.setId(Integer.MAX_VALUE);
- event.setName("Are you?");
- return event;
- }
- };
- private static final int REPORT_EVENT = 1;
- private final Handler mHandler = new Handler() {
- private Event event = new Event();
- private int count = 0;
- @Override
- public void dispatchMessage(Message msg) {
- switch (msg.what) {
- case REPORT_EVENT: {
- event.setId(count);
- event.setName("name" + count);
- count++;
- // Broadcast to all clients the new value.
- final int N = mCallbacks.beginBroadcast();
- for (int i = 0; i < N; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onEvent(event);
- } catch (RemoteException e) {
- // The RemoteCallbackList will take care of removing the dead object for us.
- }
- }
- mCallbacks.finishBroadcast();
- // Repeat every 1 second.
- sendMessageDelayed(obtainMessage(REPORT_EVENT), 1 * 1000);
- }
- break;
- default:
- super.handleMessage(msg);
- }
- }
- };
- @Override
- public IBinder onBind(Intent intent) {
- Log.e(TAG, "onBind");
- return mBinder;
- }
- @Override
- public void onCreate() {
- Log.e(TAG, "onCreate");
- super.onCreate();
- mHandler.sendEmptyMessage(REPORT_EVENT);
- }
- @Override
- public void onDestroy() {
- Log.e(TAG, "onDestroy");
- super.onDestroy();
- mHandler.removeMessages(REPORT_EVENT);
- }
- // ......
- }
只要這個服務開啓,每1秒回調下onEvent()。
至於RemoteCallbackList,是方便我們管理的,不然在RemoteException時,我們要親自remote才行。
MessengerActivity
1)AndroidManifest.xml聲明權限,也是之前的,ok了。
2)然後,之前辛苦工作的獎勵時間^^
把Event.java、Event.aidl、IRemoteCallback.aidl、IRemoteService.aidl拷貝過來,開心吧。
3)還是MessengerActivity內綁定
1. 回調和請求
- // 遠程服務,後面會綁定
- private IRemoteService mRemoteService = null;
- // 回調的實現,注意了吧,要用handler,之前也是。
- private IRemoteCallback mCallback = new IRemoteCallback.Stub() {
- @Override
- public void onEvent(Event event) throws RemoteException {
- mHandler.sendMessage(mHandler.obtainMessage(BUMP_EVENT, event));
- }
- };
- private static final int BUMP_EVENT = 1;
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case BUMP_EVENT:
- Event event = (Event) msg.obj;
- tvEvent.setText(event.toString());
- break;
- default:
- super.handleMessage(msg);
- }
- }
- };
- // 直接請求事件
- public void reqEvent(View v) {
- Event event = null;
- try {
- if (mRemoteService != null) {
- event = mRemoteService.requestEvent();
- }
- } catch (RemoteException e) {
- } finally {
- toast(event == null ? "Remote service is disconnected!" : event.toString());
- }
- }
2. 綁定Service
- // 同樣是綁定Service的過程
- private ServiceConnection servRemoteConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- mRemoteService = IRemoteService.Stub.asInterface(service); // 這裏,綁定的遠程服務
- tvEvent.setText("Attached.");
- try {
- mRemoteService.registerCallback(mCallback); // 調用服務方法註冊回調
- } catch (RemoteException e) {
- }
- }
- public void onServiceDisconnected(ComponentName className) {
- mRemoteService = null;
- btnServAidl.setChecked(false);
- tvEvent.setText("Disconnected.");
- }
- };
- protected void doBindRemoteService() {
- bindService(new Intent("org.join.service.AIDL"), servRemoteConnection, BIND_AUTO_CREATE);
- isRemoteBound = true;
- tvEvent.setText("Binding.");
- }
- protected void doUnbindRemoteService() {
- if (isRemoteBound) {
- unbindService(servRemoteConnection);
- isRemoteBound = false;
- }
- }
哦也,all done。
禮物
附贈兩個小工程
`AndroidFragment`
- Fragment組件的例子。
- 主要註釋,一些方法說明&注意點。
`AndroidJUnit`
- Run as -> Android JUnit test。
- 主要代碼,src/test目錄下。