1. 程式人生 > >Android程序間通訊(3)-Messenger實現

Android程序間通訊(3)-Messenger實現

前面兩篇文章中已經介紹了兩種實現程序間通訊的方式,那是不是隻有這兩種方式實現程序間通訊呢?當然不是,還有更好的實現方式,那就是Messenger。本篇文章將帶領大家一起來學習下Messenger.

沒有看我寫的上面兩篇文章的同學,建議先去看下上面兩篇文章,以更好的理解。

什麼是基於訊息的程序通訊?
這裡寫圖片描述
該圖片是引用自弘揚大神的部落格

其實基於訊息的程序通訊就是客戶端通過Handler傳送一條Message,服務端收到這條Message然後獲取到客戶端的值,服務端進行處理,再將結果封裝成Message傳遞給客戶端。通過這種方式實現aidl有什麼好處呢?

  • 不用再寫aidl檔案了(很多同學一提到aidl就頭疼)
  • 基於Message,這個大家已經很熟悉了

接下來我們通過程式碼詳細的介紹下。

1,服務端,我們定義一個MessengerService.java的Service,程式碼如下:

package com.wms.github.aidl.server;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import
android.os.Messenger; import android.os.RemoteException; import android.util.Log; /** * Created by 王夢思 on 2017/5/25. */ public class MessengerService extends Service { private static final String TAG = "MessengerService"; private Messenger messenger = new Messenger(new Handler() { @Override
public void handleMessage(Message clientMsg) { if (clientMsg.what == 0x001) { //注意這個裡clientMsg是客戶端傳送過來的訊息 Bundle bundle = (Bundle) clientMsg.obj; String str = bundle.getString("str"); str = str.toUpperCase(); //服務端處理完邏輯後,將資料通過Messeage的方式傳遞給客戶端 Message message = Message.obtain(); Bundle sendBundle = new Bundle(); sendBundle.putString("str", str); message.obj = sendBundle; message.what = 0x001; try { //將訊息傳送到客戶端 clientMsg.replyTo.send(message); } catch (RemoteException e) { e.printStackTrace(); } } super.handleMessage(clientMsg); } }); @Override public IBinder onBind(Intent intent) { Log.e(TAG, "onBind..."); //這裡不能返回null,必須要返回我們建立的Binder物件 return messenger.getBinder(); } @Override public boolean onUnbind(Intent intent) { Log.e(TAG, "onUnbind..."); return super.onUnbind(intent); } @Override public void onStart(Intent intent, int startId) { Log.e(TAG, "onStart..."); super.onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG, "onStartCommand..."); return super.onStartCommand(intent, flags, startId); } @Override public void onCreate() { Log.e(TAG, "onCreate..."); super.onCreate(); } @Override public void onDestroy() { Log.e(TAG, "onDestroy..."); super.onDestroy(); } }

服務端就一個Service,可以看到程式碼相當的簡單,只需要去宣告一個Messenger物件,然後onBind方法返回messenger.getBinder();
然後坐等客戶端將訊息傳送到handleMessage想法,根據message.what去判斷進行什麼操作,然後做對應的操作,最終將結果通過 clientMsg.replyTo.send(),這裡注意一定要呼叫clientMsg.replyTo.send()去傳送訊息,不然客戶端接收不到。

註冊MessengerService

<service
            android:name="com.wms.github.aidl.server.MessengerService"
            android:exported="true">
            <intent-filter>
                <action android:name="com.wms.github.aidl.server.MessengerService"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

2,客戶端。在客戶端我新建一個MessengerActivity.java,程式碼如下:

package com.wms.github.aidl.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.EditText;

/**
 * Created by 王夢思 on 2017/5/25.
 */

public class MessengerActivity extends MainActivity {
    private EditText mEditText;
    private Messenger mService;

    private Messenger clientMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {

            if (msg.what == 0x001) {
                Bundle bundle = (Bundle) msg.obj;
                mEditText.setText(bundle.getString("str"));
            }

            super.handleMessage(msg);
        }
    });

    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //當繫結成功後呼叫
            Log.e("MainActivity", "onServiceConnected...");
            mService = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("MainActivity", "onServiceDisconnected...");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mEditText = (EditText) findViewById(R.id.id_edittext);

        findViewById(R.id.bind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService();
            }
        });

        findViewById(R.id.unbind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unBindService();
            }
        });

        findViewById(R.id.invokeServer).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                invokeServer();
            }
        });
    }

    /**
     * 繫結服務
     */
    public void bindService() {
        Intent intent = new Intent();
        intent.setAction("com.wms.github.aidl.server.MessengerService");
        //Android 5.0以上必須要加這句程式碼,不然報錯
        intent.setPackage("com.wms.github.aidl.server");
        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
    }

    public void unBindService() {
        unbindService(mServiceConn);
    }

    public void invokeServer() {
        String inputStr = mEditText.getText().toString().trim();
        try {
            Message message = Message.obtain();
            message.what = 0x001;
            Bundle bundle = new Bundle();
            bundle.putString("str",inputStr);
            message.obj = bundle;

            //將訊息的迴應設定為clientMessenger,這樣客戶端傳送訊息就能在客戶端收到了。
            message.replyTo = clientMessenger;
            mService.send(message);
        } catch (RemoteException e) {
            //這裡會丟擲遠端異常
            e.printStackTrace();
        }
    }
}

佈局檔案和上篇文章中一樣。

MessengerActivity.java中程式碼也很簡單,就是繫結一個服務,然後再onServiceConnect中例項化一個Messenger,這個Messenger和MessengerActivity中屬性clientMessenger不一樣,因為這個Messenger例項化的時候把Binder驅動傳遞進來了。所以當客戶端呼叫mService.send(message); 後,服務端將會收到客戶端傳遞過來的資料,服務端處理完之後,返回到Client端的clientMessenger中的Handler的handleMessage方法中,這樣就完成了客戶端和服務端的通訊。

效果圖:
這裡寫圖片描述

當我們點選呼叫服務端轉換後,服務端就把大寫字母回傳給客戶端。以上就是簡單的Messenger的使用,肯定很多人有疑問,為什麼這樣可以實現程序間的通訊呢?下面我將帶領大家一起來從原始碼的角度來看看。

1,先分析服務端,服務端中onBind()方法裡面我們返回了messenger.getBinder(); 很簡單,就一行程式碼,我們進入Messenger的內部看下getBinder內部到底做了什麼事情

/**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     * 
     * @return Returns the IBinder backing this Messenger.
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

getBinder方法也很簡單,也就一行程式碼。這裡mTarget是什麼呢?檢視下原始碼,private final IMessenger mTarget; 可以看出mTarget是一個IMessenger物件,mTarget賦值是在我們例項化clientMessenger物件的時候Messenger構造方法裡面。我們再來看看Messenger的構造方法

/**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

繼續跟進到Handler內部的getImessenger方法

final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

恍然大悟,原來mTarget其實是一個MessengerImpl物件。再回到Messenger中的getBinder方法,其實就是呼叫了MessengerImpl內部的asBinder方法,我們進入到MessengerImpl中的asBinder中看看其內部做了什麼事情。

MessengerImpl原來是繼承自IMessenger.Stub,是不是很熟悉?這不又回到了我們前面的文章中通過aidl實現程序通訊的了麼?原來Android內部已經幫我們實現了一個IMessenger.aidl檔案,這個檔案位於framework中的platform_frameworks_base/core/java/android/os/IMessenger.aidl,大家可以下載framework原始碼去檢視下。由於aidl程式碼較少,我下面貼出IMessenger.aidl內部程式碼如下:

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

哇,好簡答,就一個send方法,send方法裡面傳遞的是一個Message物件。所以這一切又回到了我們前面所說的aidl進行程序通訊的內容了。如果不明白的可以看我之前寫過的一篇文章 基於aidl實現的程序通訊

2,接下來我們分析下客戶端原始碼
當客戶端呼叫繫結服務的時候,會呼叫onServiceConnect方法

private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //當繫結成功後呼叫
            Log.e("MainActivity", "onServiceConnected...");
            mService = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("MainActivity", "onServiceDisconnected...");
        }
    };

在onServiceConnected中我們例項化了一個Messenger,並且傳遞了一個IBinder物件,這個IBinder物件就是一直說的Binder驅動。我們進入Messenger這個建構函式中看看:

/**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     * 
     * @param target The IBinder this Messenger should communicate with.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

是不是又很熟悉?這不就是我們在 基於aidl實現的程序通訊 中onSeviceConnect方法實現的一樣麼?

綜上分析客戶端和服務端的原始碼,其實和我們寫aidl完全一樣,沒有任何區別,Messenger底層其實就是基於aidl來實現的程序通訊,只是Android內部已經給我們寫好了一個IMessnger.aidl檔案,不需要我們手動實現了。

到此,我們就已經分析完了原始碼了,如果還是不明白的同學建議去翻看下Messenger的原始碼,並不複雜,到此Android中實現程序通訊的常用方式已經介紹完了。

程式碼傳送門 : http:github.com/wms1993/blog_aidl_demo