Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android IPC之Messenger淺談

Android IPC之Messenger淺談

編輯:關於Android編程

之前寫過一篇有關IPC之AIDL淺談的文章。今天再來介紹另一種IPC-Messenger。

一、概述。

首先看Messenger介紹,

 

 Reference to a Handler, which others can use to send messages to it.
This allows for the implementation of message-based communication across
  processes, by creating a Messenger pointing to a Handler in one process,
 and handing that Messenger to another process.
大概意思是說,通過Handler來發送消息。允許在進程中基於消息通信實現,在一個進程中創建一個信使指向一個Handler,並將該信使傳遞給另外一個進程。

 

換句話說,就是Messenger通過Handler來發送消息,可以在不同的進程間通信。

下面通過一張圖來說明,

\

該圖基本說明了Messenger的上層工作流程。小結如下:

1.Messenger的使用需要有服務端和客戶端;

2.客戶端需要綁定遠程連接,綁定成功後,就能獲取遠程的IBinder對象,通過該IBinder就可以得到遠程的Messenger對象;

3.向服務端發送消息時,需要使用服務端的Messenger對象,並且可以把客戶端的Messenger對象賦值給消息的‘replyTo’屬性,一並傳遞到服務端;

4.向客戶端發送消息,需要使用客戶端的Messenger對象,該對象是從客戶端發送的消息的‘replyTo’屬性中獲取的。

下面依舊通過一個實例來演示Messenger的整個流程。

二、實例。

需要有兩個進程,才能IPC,單進程談IPC是毫無意義的。可以在一個應用中使用Messenger實現IPC,該應用存在多個進程;當然也可以創建兩個應用,一個是服務端,一個客戶端。這兩種方式在本質上都是一樣的。本篇文章我們使用後者來實現IPC。

1.服務端應用實現。

創建一個Service,

 

/**
 * 服務端
 */
public class MessengerService extends Service {
    private static final String TAG = "MessengerService";
    private static final int WHAT = 0x101;
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case WHAT:
                    //接受從客戶端傳來的消息
                    Bundle bundle= (Bundle) msg.obj;
                    String str = (String) bundle.get("str");
                    Log.e(TAG, "服務端已接受到客戶端的消息");
                    Log.e(TAG, "消息是---->" + str);
                    //發送數據給客戶端
                    Message message = Message.obtain();
                    Bundle bundle1= new Bundle();
                    bundle1.putString("str","我已經知道了,加油,看好你!");
                    message.obj = bundle1;
                    message.what=0x102;
                    try {
                        //向客戶端發送消息
                        msg.replyTo.send(message);//首先獲取從客戶端傳遞的Messenger對象,然後調用該Messenger對象的send()方法
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    };
    private Messenger messenger = new Messenger(mHandler);

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }


}

 

實現該Service的onBind()方法,調用‘messenger.getBinder()’返回一個IBinder,返回的IBinder我們將會在客戶端接收並使用。創建一個Handler,用於接收客戶端傳遞的消息,當接收到消息後,然後向客戶端發送了一個消息。服務端就是這些內容!

一定要記得,發送消息傳遞數據時不能把數據直接扔給消息,否則會報錯,如下所示:

 

java.lang.RuntimeException: Can't marshal non-Parcelable objects across processes.
因為Binder事務傳遞的數據被稱為包裹(Parcel),必須實現Parcelable接口,否則無法在兩個應用之間進行通信。所以,如果需要在消息中攜帶數據,請使用Bundle,因為Bundle類以及實現了Parcelable接口。
還要記得注冊該服務。

 

 

... 
 
            
                
                
            
        
...
其中android:exported的作用是“是否支持其它應用調用當前組件”,true允許被啟動;false不允許被啟動。並且設置了“action”屬性,便於我們隱式調用該服務。

 

一切Ok的話,就可以運行該程序了!

 

PS:

IBinder是一個接口。IBinder是遠程對象的基本接口,是為高性能而設計的輕量級遠程調用機制的核心部分。但它不僅用於遠程調用,也用於進程內調用。這個接口定義了與遠程對象交互的協議。不要直接實現這個接口,而應該從Binder派生。

IBinder的主要API是transact(),與它對應另一方法是Binder.onTransact()。第一個方法使你可以向遠端的IBinder對象發送發出調用,第二個方法使你自己的遠程對象能夠響應接收到的調用。IBinder的API都是同步執行的,比如transact()直到對方的Binder.onTransact()方法調用完成後才返回。調用發生在進程內時無疑是這樣的,而在進程間時,在IPC的幫助下,也是同樣的效果。

2.客戶端應用實現。

 

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int WHAT = 0x102;
    private boolean isConn;
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case WHAT:
                    //接受從服務端發送的消息
                    Bundle bundle= (Bundle) msg.obj;
                    String str = (String) bundle.get("str");
                    Log.e(TAG, "客戶端服已接受到務端的消息");
                    Log.e(TAG, "消息是---->" + str);
                    break;
            }
        }
    };

    private Messenger mClientMessenger = new Messenger(mHandler);
    private Messenger mServiceMessenger;
    
    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "連接Service成功---->");
            mServiceMessenger = new Messenger(service);
            isConn = true;
            Message message = Message.obtain();
            Bundle bundle=new Bundle();
            bundle.putString("str","你好!我是程序猿!");
            message.obj=bundle;
            message.what = 0x101;
            message.replyTo = mClientMessenger;
            try {
                mServiceMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "連接Service---->disconnected");
            mServiceMessenger = null;
            isConn = false;
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_start_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService();
            }
        });
    }

    /**
     * 綁定遠程連接服務
     */
    private void bindService() {
        Intent intent = new Intent();
        intent.setAction("cn.xinxing.messengerservice");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isConn) {
            unbindService(serviceConnection);
        }
    }
}

 

屏幕中有一個按鈕,點擊按鈕時,綁定遠程連接服務;綁定成功後,發送消息;Handler用於接收消息;當Activity銷毀時,解除綁定。

整個實現還是比較簡單的。

運行客戶端後,點擊按鈕,看Log輸出,

\
遠程連接服務綁定成功,接著服務端輸出Log,

\

最後客戶端Log輸出,

\

小結:整個流程是,點擊按鈕時,綁定遠程連接服務,綁定成功後,並且會拿到遠程服務的Messenger對象,然後使用Messenger對象發送消息,並且將客戶端的Messenger對象也發送到了服務端,當服務端收到消息後,會輸出客戶端發送的消息內容,並且拿到客戶端的Messenger對象,然後使用該Messenger對象向客戶端發送消息,客戶端收到消息後,會輸出Log。

整體上,使用Messenger實現IPC,相比AIDL來說,還是比較簡單的。下面我們通過Messenger的源碼來分析,它的內部實現。

三、源碼解析。

先看Messenger的構造方法,

 

  private final IMessenger mTarget;
  
     public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

 

構造方法傳遞一個Handler,然後將HandlerIMessenger對象傳遞給mTarget對象(MessengerImpl)。下面是Handler的getIMessenger()具體實現,

 

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

 

實例化一個MessengerImpl對象,然後返回了一個IMessenger對象。其中MessengerImpl的源碼如下,

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }
看到這裡IMessenger.Stub,貌似很熟悉啊!這不就是AIDL的實現嗎!bingo!是的!Messenger的底層其實就AIDL!只不過,Messenger已經為我們封裝了好了,不需要我們自己關心AIDL的具體細節。

IMessenger的源碼位於‘\base\core\java\android\os\IMessenger.aidl’,可以看到它是一個aidl文件,下面是它的源碼,
 

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}
內部只有一個方法,用於發送消息。這裡更加證實了,Messenger的底層其實就AIDL!

 

當發送消息時,調用Messengersend()方法,

 

    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
調用了mTarget的send()方法,而mTarget是MessengerImpl。通過上文可以得知MessengerImpl類實現了send()方法,該方法是發送一個消息,可以看到它其實還是通過Handler發送消息。

服務端有一個onBind()方法,會返回一個IBinder對象,該對象來自於Messenger,

 

    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

 

繼續跟下去,

    public IBinder getBinder() {
        return mTarget.asBinder();
    }
    
返回mTarget對象的asBinder(),而“mTarget.asBinder()”到底是什麼呢?
我們先看看使用AIDL時,AAPT自動生成的AIDL類中的asBinder()方法,
@Override public android.os.IBinder asBinder()
{
return this;
}
該方法返回的是自己,即xxxx.Stub類。那麼我們此時再看看mTarget對象,mTarget是MessengerImpl,而MessengerImpl是繼承自IMessenger.Stub,那麼mTarget.asBinder()返回的就是Messenger.Stub.asBinder(),而Messenger.Stub.asBinder()返回的又是Messenger.Stub,而MessengerImpl是繼承自IMessenger.Stub,所以繞了一圈,“mTarget.asBinder()”返回的就是MessengerImpl對象自己。也就是說“mTarget.asBinder()”返回的是當前Messenger中的MessengerImpl對象。

當在服務端時,“mTarget.asBinder()”返回的是服務端的MessengerImpl對象,使用它來向服務端發送消息;當在客戶端時,“mTarget.asBinder()”返回的是客戶端的MessengerImpl對象,使用它來向客戶端發送消息。

客戶端綁定遠程連接成功後,會獲取到一個遠程IBinder對象,通過該IBinder對象,就可以獲取到服務端的Messenger對象,即調用下面的方法,

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

這不是和AIDL的寫法一模一樣嗎!下面是AIDL的代碼,

 

 //遠程連接
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            calculateAidl = CalculateAidl.Stub.asInterface(service);
            isBindSuccess=true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            calculateAidl = null;
            isBindSuccess=false;
        }
    };

 

好了,有關Messenger的源碼分析就這麼多!

四、小結。

1.Messenger的底層還是使用的AIDL;

2.服務端和客戶端通信,向服務端發現消息,使用的是服務端的Messenger(該對象來自於遠程連接成功通過IBinder獲取),向客戶端發送消息,使用的是客戶端的Messenger(該對象是向服務端發送的消息中攜帶的-replyTo屬性攜帶的參數);

3.在IPC中,要傳遞參數,需要實現Parcelable接口,建議使用Bundle封裝參數。

4.Messenger會將所有Service請求入隊列,所以它不支持多線程通信。如果你要支持多線程,那麼請使用AIDL。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved