Android Service Messenger & AIDL 的跨進程通信例子


很久沒用跨進程通信了,然後等自己又要用的時候,網上一搜,千遍一律,還很渣,吐血~

工程有兩份`MessengerService`&`MessengerActivity`,前個提供服務,後個訪問服務。


跨進程通信

就多個apk互相通信,當然廣播也是不錯的。不過這裏用的是Messenger和AIDL,並且都弄成雙向的了。


Messenger

信使,底層說是也用的AIDL。用起來挺方便的。


MessengerService

1)AndroidManifest.xml聲明權限

  1. <permission 
  2.     android:name="org.join.permission.SERVICE" 
  3.     android:protectionLevel="normal" > 
  4. </permission> 
  5.  
  6. <uses-permission android:name="org.join.permission.SERVICE" /> 

爲什麼弄個帶權限的呢?因爲討厭任何警告==。

2)註冊MessengerService

  1. <service 
  2.     android:name="org.join.messenger.MessengerService" 
  3.     android:exported="true" 
  4.     android:permission="org.join.permission.SERVICE" 
  5.     android:process=":remote" > 
  6.     <intent-filter> 
  7.         <action android:name="org.join.service.MESSENGER" /> 
  8.     </intent-filter> 
  9. </service> 

這裏把權限加上了,不然最新ADT是會有個警告的。就一嘀咕的老頭,討人厭。

3)MessengerService核心代碼

  1. public class MessengerService extends Service implements MessageCode { 
  2.  
  3.     static final String TAG = "MessengerService"
  4.  
  5.     public static final String ACTION = "org.join.service.MESSENGER"
  6.  
  7.     // 第一步:新建 
  8.     final Messenger mMessenger = new Messenger(new Handler() { 
  9.  
  10.         // 第二步:處理Message 
  11.         @Override 
  12.         public void dispatchMessage(Message msg) { 
  13.             switch (msg.what) { 
  14.             case CLNT_REQ_HELLO: 
  15.                 toast("Hello!"); 
  16.                 Bundle data = new Bundle(); 
  17.                 data.putString("msg""Who am I?"); 
  18.                 // 這個`msg.replyTo`是客戶端的信使 
  19.                 respClnt(msg.replyTo, SERV_RES_HAHA, data); 
  20.                 break
  21.             case CLNT_REQ_SURPRISE: 
  22.                 String s = msg.getData().getString("msg"); 
  23.                 toast("Surprise! " + s); 
  24.                 break
  25.             } 
  26.         } 
  27.  
  28.         // 第三步:回覆用,注意`msg.replyTo`。 
  29.         private void respClnt(Messenger clnt, int msgCode, Bundle data) { 
  30.             if (clnt == null) { 
  31.                 return
  32.             } 
  33.             try { 
  34.                 Message msg = Message.obtain(null, msgCode); 
  35.                 msg.replyTo = mMessenger; // 綁定服務端信使 
  36.                 if (data != null) { 
  37.                     msg.setData(data); 
  38.                 } 
  39.                 clnt.send(msg); // 用客戶端信使發消息 
  40.             } catch (RemoteException e) { 
  41.                 e.printStackTrace(); 
  42.             } 
  43.         } 
  44.     }); 
  45.  
  46.     @Override 
  47.     public IBinder onBind(Intent intent) { 
  48.         Log.e(TAG, "onBind"); 
  49.         return mMessenger.getBinder(); 
  50.     } 
  51.  
  52.     // ...... 
  53.  


MessengerActivity

1)AndroidManifest.xml聲明權限

  1. <uses-permission android:name="org.join.permission.SERVICE" /> 

2)MessengerActivity核心代碼

1. 創建Messenger

  1. // 服務端的信使,後面會綁定 
  2. private Messenger mService; 
  3.  
  4. // 第一步:新建 
  5. final Messenger mMessenger = new Messenger(new Handler() { 
  6.  
  7.     // 第二步:處理Message 
  8.     @Override 
  9.     public void dispatchMessage(Message msg) { 
  10.         switch (msg.what) { 
  11.         case SERV_RES_HAHA: 
  12.             String s = msg.getData().getString("msg"); 
  13.             toast("HAHA! " + s); 
  14.             Bundle data = new Bundle(); 
  15.             data.putString("msg""I am you."); 
  16.             sendMsg(CLNT_REQ_SURPRISE, data); 
  17.             break
  18.         } 
  19.     } 
  20. }); 
  21.  
  22. // 第三步:回覆用,同樣注意`msg.replyTo`。 
  23. private void sendMsg(int msgCode, Bundle data) { 
  24.     if (mService == null) { 
  25.         toast("Service is disconnected!"); 
  26.         return
  27.     } 
  28.     try { 
  29.         Message msg = Message.obtain(null, msgCode); 
  30.         msg.replyTo = mMessenger; // 綁定客戶端信使 
  31.         if (data != null) { 
  32.             msg.setData(data); 
  33.         } 
  34.         mService.send(msg); // 用服務端信使發消息 
  35.     } catch (RemoteException e) { 
  36.         e.printStackTrace(); 
  37.     } 

2. 綁定Service

  1. // 和一般綁定Service一樣 
  2. private ServiceConnection servConnection = new ServiceConnection() { 
  3.  
  4.     @Override 
  5.     public void onServiceConnected(ComponentName name, IBinder service) { 
  6.         mService = new Messenger(service); // 不過,這裏改爲Messenger 
  7.     } 
  8.  
  9.     @Override 
  10.     public void onServiceDisconnected(ComponentName name) { 
  11.         mService = null
  12.     } 
  13. }; 
  14.  
  15. // 綁定方法 
  16. protected void doBindService() { 
  17.     bindService(new Intent("org.join.service.MESSENGER"), servConnection, BIND_AUTO_CREATE); 
  18.     isBound = true
  19.  
  20. // 解綁方法 
  21. protected void doUnbindService() { 
  22.     if (isBound) { 
  23.         unbindService(servConnection); 
  24.         isBound = false
  25.     } 

好了,客戶端要發送消息,調用sendMsg()就可以了。


AIDL

也挺簡單的,恩,繼續...


RemoteService

1)AndroidManifest.xml聲明權限,就之前的,ok了。

2)註冊RemoteService

  1. <service 
  2.     android:name="org.join.aidl.RemoteService" 
  3.     android:exported="true" 
  4.     android:permission="org.join.permission.SERVICE" 
  5.     android:process=":remote" > 
  6.     <intent-filter> 
  7.         <action android:name="org.join.service.AIDL" /> 
  8.     </intent-filter> 
  9. </service> 

3)搞個parcelable對象

不能就用基本類型是不,所以要搞個對象^^。

恩,Event.java就是了。然後要爲它寫個aidl。

Event.aidl

  1. package org.join.aidl; 
  2.  
  3. parcelable Event; 

簡單吧。

4)然後弄個Callback

不能就單向通信是不,所以要用來回調客戶端。

IRemoteCallback.aidl

  1. package org.join.aidl; 
  2.  
  3. import org.join.aidl.Event; 
  4.  
  5. /** 
  6.  * @brief 遠程回調 
  7.  * @author join 
  8.  */ 
  9. interface IRemoteCallback { 
  10.  
  11.     void onEvent(in Event event); 
  12.  

4)服務的AIDL了

IRemoteService.aidl

  1. package org.join.aidl; 
  2.  
  3. import org.join.aidl.IRemoteCallback; 
  4. import org.join.aidl.Event; 
  5.  
  6. /** 
  7.  * @brief 遠程服務 
  8.  * @author join 
  9.  */ 
  10. interface IRemoteService { 
  11.  
  12.     void registerCallback(in IRemoteCallback cb); 
  13.  
  14.     void unregisterCallback(in IRemoteCallback cb); 
  15.  
  16.     Event requestEvent(); 
  17.  

5)哦,總算到RemoteService了

RemoteService.java,恩,直接參考了ApiDemo,捂臉~

  1. public class RemoteService extends Service { 
  2.  
  3.     static final String TAG = "RemoteService"
  4.  
  5.     public static final String ACTION = "org.join.service.AIDL"
  6.  
  7.     final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<IRemoteCallback>(); 
  8.  
  9.     private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { 
  10.  
  11.         @Override 
  12.         public void registerCallback(IRemoteCallback cb) throws RemoteException { 
  13.             if (cb != null
  14.                 mCallbacks.register(cb); 
  15.         } 
  16.  
  17.         @Override 
  18.         public void unregisterCallback(IRemoteCallback cb) throws RemoteException { 
  19.             if (cb != null
  20.                 mCallbacks.unregister(cb); 
  21.         } 
  22.  
  23.         @Override 
  24.         public Event requestEvent() throws RemoteException { 
  25.             Event event = new Event(); 
  26.             event.setId(Integer.MAX_VALUE); 
  27.             event.setName("Are you?"); 
  28.             return event; 
  29.         } 
  30.     }; 
  31.  
  32.     private static final int REPORT_EVENT = 1
  33.  
  34.     private final Handler mHandler = new Handler() { 
  35.  
  36.         private Event event = new Event(); 
  37.         private int count = 0
  38.  
  39.         @Override 
  40.         public void dispatchMessage(Message msg) { 
  41.             switch (msg.what) { 
  42.             case REPORT_EVENT: { 
  43.                 event.setId(count); 
  44.                 event.setName("name" + count); 
  45.                 count++; 
  46.  
  47.                 // Broadcast to all clients the new value. 
  48.                 final int N = mCallbacks.beginBroadcast(); 
  49.                 for (int i = 0; i < N; i++) { 
  50.                     try { 
  51.                         mCallbacks.getBroadcastItem(i).onEvent(event); 
  52.                     } catch (RemoteException e) { 
  53.                         // The RemoteCallbackList will take care of removing the dead object for us. 
  54.                     } 
  55.                 } 
  56.                 mCallbacks.finishBroadcast(); 
  57.  
  58.                 // Repeat every 1 second. 
  59.                 sendMessageDelayed(obtainMessage(REPORT_EVENT), 1 * 1000); 
  60.             } 
  61.                 break
  62.             default
  63.                 super.handleMessage(msg); 
  64.             } 
  65.         } 
  66.     }; 
  67.  
  68.     @Override 
  69.     public IBinder onBind(Intent intent) { 
  70.         Log.e(TAG, "onBind"); 
  71.         return mBinder; 
  72.     } 
  73.  
  74.     @Override 
  75.     public void onCreate() { 
  76.         Log.e(TAG, "onCreate"); 
  77.         super.onCreate(); 
  78.         mHandler.sendEmptyMessage(REPORT_EVENT); 
  79.     } 
  80.  
  81.     @Override 
  82.     public void onDestroy() { 
  83.         Log.e(TAG, "onDestroy"); 
  84.         super.onDestroy(); 
  85.         mHandler.removeMessages(REPORT_EVENT); 
  86.     } 
  87.  
  88.     // ...... 
  89.  

只要這個服務開啓,每1秒回調下onEvent()。

至於RemoteCallbackList,是方便我們管理的,不然在RemoteException時,我們要親自remote才行。


MessengerActivity

1)AndroidManifest.xml聲明權限,也是之前的,ok了。

2)然後,之前辛苦工作的獎勵時間^^

Event.javaEvent.aidlIRemoteCallback.aidlIRemoteService.aidl拷貝過來,開心吧。

3)還是MessengerActivity內綁定

1. 回調和請求

  1. // 遠程服務,後面會綁定 
  2. private IRemoteService mRemoteService = null
  3.  
  4. // 回調的實現,注意了吧,要用handler,之前也是。 
  5. private IRemoteCallback mCallback = new IRemoteCallback.Stub() { 
  6.     @Override 
  7.     public void onEvent(Event event) throws RemoteException { 
  8.         mHandler.sendMessage(mHandler.obtainMessage(BUMP_EVENT, event)); 
  9.     } 
  10. }; 
  11.  
  12. private static final int BUMP_EVENT = 1
  13.  
  14. private Handler mHandler = new Handler() { 
  15.     @Override 
  16.     public void handleMessage(Message msg) { 
  17.         switch (msg.what) { 
  18.         case BUMP_EVENT: 
  19.             Event event = (Event) msg.obj; 
  20.             tvEvent.setText(event.toString()); 
  21.             break
  22.         default
  23.             super.handleMessage(msg); 
  24.         } 
  25.     } 
  26.  
  27. }; 
  28.  
  29. // 直接請求事件 
  30. public void reqEvent(View v) { 
  31.     Event event = null
  32.     try { 
  33.         if (mRemoteService != null) { 
  34.             event = mRemoteService.requestEvent(); 
  35.         } 
  36.     } catch (RemoteException e) { 
  37.     } finally { 
  38.         toast(event == null ? "Remote service is disconnected!" : event.toString()); 
  39.     } 

2. 綁定Service

  1. // 同樣是綁定Service的過程 
  2. private ServiceConnection servRemoteConnection = new ServiceConnection() { 
  3.     public void onServiceConnected(ComponentName className, IBinder service) { 
  4.         mRemoteService = IRemoteService.Stub.asInterface(service); // 這裏,綁定的遠程服務 
  5.         tvEvent.setText("Attached."); 
  6.         try { 
  7.             mRemoteService.registerCallback(mCallback); // 調用服務方法註冊回調 
  8.         } catch (RemoteException e) { 
  9.         } 
  10.     } 
  11.  
  12.     public void onServiceDisconnected(ComponentName className) { 
  13.         mRemoteService = null
  14.         btnServAidl.setChecked(false); 
  15.         tvEvent.setText("Disconnected."); 
  16.     } 
  17. }; 
  18.  
  19. protected void doBindRemoteService() { 
  20.     bindService(new Intent("org.join.service.AIDL"), servRemoteConnection, BIND_AUTO_CREATE); 
  21.     isRemoteBound = true
  22.     tvEvent.setText("Binding."); 
  23.  
  24. protected void doUnbindRemoteService() { 
  25.     if (isRemoteBound) { 
  26.         unbindService(servRemoteConnection); 
  27.         isRemoteBound = false
  28.     } 

哦也,all done。


禮物

附贈兩個小工程

`AndroidFragment`

  • Fragment組件的例子。
  • 主要註釋,一些方法說明&注意點。

`AndroidJUnit`

  • Run as -> Android JUnit test。
  • 主要代碼,src/test目錄下。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章