1. 程式人生 > >【朝花夕拾】跨程序通訊,你只知道AIDL,就OUT了

【朝花夕拾】跨程序通訊,你只知道AIDL,就OUT了

一、前言

      提起跨程序通訊,大多數人首先會想到AIDL。我們知道,用AIDL來實現跨程序通訊,需要在客戶端和服務端都新增上aidl檔案,並在服務端的Service中實現aidl對應的介面。如果還需要服務端給客戶端傳送資訊,還需要再添加回調相關的aidl檔案,以及使用RemoteCallbackList來輔助實現該功能。在我的另外一篇文章【朝花夕拾】Android效能篇之(七)Android跨程序通訊篇中,就專門介紹過AIDL來實現客戶端和服務端互相通訊的方式,不清楚的可以看看這篇文章的介紹。本文將介紹一下另外一種更簡單的方式——Messenger,來實現客戶端和服務端跨程序互相通訊。

 

二、Messenger簡介

       Messenger翻譯為信使,顧名思義,就是用於傳遞資訊的,通過它可以在不同程序中傳遞Message物件。在Message中放入我們需要傳遞的資訊,然後通過Messenger將Message傳遞給對方,就可以輕輕鬆鬆實現跨程序資料傳遞。實際上Messenger是一種輕量級的IPC(跨程序通訊)方式,它的底層仍然是實現的AIDL。它是一種基於訊息的程序通訊,就像子執行緒和UI執行緒傳送訊息那樣,Demo中服務端和客戶端使用的Handler,正好說明了這一點。

 

三、示例程式碼演示

       話不多說,咱們這裡看看一個完整的Demo,來直觀感受一下Messenger的使用。本Demo演示的功能很簡單,客戶端傳送訊息給服務端,服務端收到訊息後再發送訊息給客戶端作為響應。

  1、服務端程式碼實現

 1 public class MessengerService extends Service {
 2     private static final String TAG = "Messenger-Demo";
 3     private static final int MSG_CLIENT = 0x001;
 4     private static final int MSG_SERVER = 0X002;
 5     private static final String KEY_CLIENT = "key_client";
 6     private static final String KEY_SERVER = "key_server";
 7 
 8     private final Messenger mMessenger = new Messenger(new MessageHandler());
 9 
10     @Nullable
11     @Override
12     public IBinder onBind(Intent intent) {
13         return mMessenger.getBinder();
14     }
15 
16     private static class MessageHandler extends Handler {
17         @Override
18         public void handleMessage(Message msg) {
19             switch (msg.what) {
20                 case MSG_CLIENT:
21                     Log.d(TAG, "receive msg from Client:" + msg.getData().getString(KEY_CLIENT));
22                     Messenger messenger = msg.replyTo;
23                     Message serverMsg = Message.obtain();
24                     serverMsg.what = MSG_SERVER;
25                     Bundle bundle = new Bundle();
26                     bundle.putString(KEY_SERVER, "Hello Client! I am fine, thank you");
27                     serverMsg.setData(bundle);
28                     try {
29                         messenger.send(serverMsg);
30                     } catch (RemoteException e) {
31                         e.printStackTrace();
32                     }
33                     break;
34                 default:
35                     super.handleMessage(msg);
36             }
37         }
38     }
39 }
View Code

       對應清單檔案中的註冊

1 <service
2     android:name=".MessengerService "
3     android:exported="true"/>

這裡需要注意的是第三行,該Service需要提供給其它應用呼叫,需要將該屬性值設定為true。

  2、客戶端程式碼實現

 1 public class MessengerClientActivity extends AppCompatActivity {
 2 
 3     private static final String TAG = "Messenger-Demo";
 4     private static final int MSG_CLIENT = 0x001;
 5     private static final int MSG_SERVER = 0X002;
 6     private static final String KEY_CLIENT = "key_client";
 7     private static final String KEY_SERVER = "key_server";
 8     private static final String SERVER_PKGNAME = "com.example.messageserver";
 9     private static final String SERVICE_PATH = "com.example.messageserver.MessengerService";
10     private Messenger mRemoteMessenger;
11     private Messenger mLocalMessenger = new Messenger(new MessengerClientHandler());
12 
13     @Override
14     protected void onCreate(Bundle savedInstanceState) {
15         super.onCreate(savedInstanceState);
16         setContentView(R.layout.activity_main);
17         Log.d(TAG,"onCreate");
18         bindService();
19     }
20 
21     private void bindService() {
22         Intent intent = new Intent();
23         ComponentName componentName = new ComponentName(SERVER_PKGNAME, SERVICE_PATH);
24         intent.setComponent(componentName);
25         bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
26     }
27 
28     private static class MessengerClientHandler extends Handler {
29         @Override
30         public void handleMessage(Message msg) {
31             switch (msg.what) {
32                 case MSG_SERVER:
33                     Log.d(TAG, "receive msg from Server:" + msg.getData().getString(KEY_SERVER));
34                     break;
35                 default:
36                     break;
37             }
38             super.handleMessage(msg);
39         }
40     }
41 
42     private ServiceConnection mServiceConnection = new ServiceConnection() {
43         @Override
44         public void onServiceConnected(ComponentName name, IBinder service) {
45             mRemoteMessenger = new Messenger(service);
46             Message clientMsg = Message.obtain();
47             clientMsg.what = MSG_CLIENT;
48             Bundle bundle = new Bundle();
49             bundle.putString(KEY_CLIENT, "Hello,Server! How are you ?");
50             clientMsg.setData(bundle);
51             clientMsg.replyTo = mLocalMessenger;
52             try {
53                 mRemoteMessenger.send(clientMsg);
54             } catch (RemoteException e) {
55                 e.printStackTrace();
56             }
57         }
58 
59         @Override
60         public void onServiceDisconnected(ComponentName name) {
61 
62         }
63     };
64 
65     @Override
66     protected void onDestroy() {
67         super.onDestroy();
68         unbindService(mServiceConnection);
69     }
70 }
View Code

  3、執行

       執行時先啟動服務端,再啟動客戶端,可以看到如下log資訊:

1 15185-15185/com.example.messageserver D/Messenger-Demo: receive msg from Client:Hello,Server! How are you ?
2 14269-14269/com.example.messageclient D/Messenger-Demo: receive msg from Server:Hello Client! I am fine, thank you

 這樣客戶端和服務端就完成了一次互相通訊。從程式碼上來看,就能感受到,相比於直接使用AIDL方式,Messenger簡潔方便了很多。

 

四、Messenger的使用步驟

       通過前面的Demo直觀感受了Messenger的使用,其互動流程大致為一下六步:

 

 

       對照Demo和上圖,應該能夠輕鬆理解Messenger的互動流程了。這裡需要注意的是,實際上給Server端的Handler傳送訊息的Messenger,是結合服務端返回的IBinder例項來生成的服務端遠端代理;給客戶端Handler傳送訊息的Messenger也是第4步中傳送給服務端的客戶端本地Messenger, 可以理解為是自己的Messenger給自己的Handler在傳送訊息。

 

五、Messenger和AIDL的聯絡與區別

       前面我們說過Messager的底層還是實現的AIDL,這是它們的聯絡。它們的區別是:

    (1)Messenger使用起來比AIDL簡潔方便。

    (2)AIDL的客戶端介面會同時向服務端傳送多個請求,服務端需要應用多執行緒處理。而Messenger會將所有請求排入佇列(Handler對應的MessageQueue),讓伺服器一次處理一個呼叫,不用處理多執行緒問題。大多數情況下,服務端不需要執行多執行緒處理此時選擇Messenger方式更合適,而如果客戶端的請求要求服務端執行多執行緒處理,就應該使用AIDL來實現,選擇哪一種,還是需要根據實際情況來選擇。

 

結語

       由於筆者水平有限,文章中如果有描述不準確或者不妥當的地方,還請讀者不吝賜教,非常感