Android IPC機制之Messenger的使用

一,寫在前面

        在Android中實現IPC機制的方式有多種,例如:AIDL,ContentProvider,Messenger等。AIDL特點是提供AIDL接口的方法,ContentProvider特點是暴露數據庫,Messenger特點是進程間“數據”通信,數據指對象。Messenger實現進程間數據通信是建立在綁定服務基礎上,需要創建一個服務,稱之爲服務端;需要一個客戶端,去綁定服務端。

二,瞭解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();
    }

//...
/**
     * 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);
    }


參數爲Handler的構造方法,target.getIMessenger()返回的MessengerImpl對象,查看Handler$MessengerImpl源碼:

private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            Handler.this.sendMessage(msg);
        }
    }

         IMessenger.Stub這個不就是在AIDL中服務端返回的Binder對象麼;參數爲IBinder的構造方法,mTarget不就是AIDL中客戶端中返回的代理對象麼。因此Messenger內部是對AIDL進行了封裝,我們不需要使用AIDL接口,直接調用系統提供給我們的方法就可以了。上面簡單分析了下Messenger內部就是封裝了AIDL技術,下面將介紹如何使用Messenger。

    三,服務端實現

       首先是服務端,

       1,創建一個Service的子類,並重寫了onBinder(intent)方法;

       2,創建一個Handler子類實例,並重寫handleMessage(msg)方法;

       3,創建一個關聯handler的Messenger對象:Messenger mMessenger = new Messenger(mHandler);mHandler爲第二步中的handler;

       4,在onBinder(intent)方法中,調用mMessenger.getBinder()返回binder對象給客戶端;

    

       服務端的代碼:

public class MyService extends Service {

	private static final int MSG_FROM_CLIENT = 0;
	private static final int MSG_FROM_SERVICE = 1;
	
	private Handler mHandler = new Handler(){
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MSG_FROM_CLIENT:
				//計算值之和
				int sum = msg.arg1 + msg.arg2;
				//獲取消息中信使
				Messenger cs_messenger = msg.replyTo;
				
				//創建新的消息對象
				Message m = Message.obtain();
				m.what = MSG_FROM_SERVICE;
				Bundle b = new Bundle();
				b.putInt("sum", sum);
				m.obj = b;
				
				//發送消息給client
				try {
					cs_messenger.send(m);
				} catch (Exception e) {
					e.printStackTrace();
				}
				break;

			default:
				break;
			}
		};
	};
	
	private Messenger mMessenger = new Messenger(mHandler);
	@Override
	public IBinder onBind(Intent intent) {
		return mMessenger.getBinder();
	}

}


        服務端的代碼比較簡單,粗分的話只有兩步,上面1,4爲一步,2,3爲一步。handleMessage(msg)可以處理客戶端過來發送的消息,然後向客戶端發送消息,後面講到客戶端就比較好理解了。大家記得給Service在mainfest中進行配置,添加一個process屬性,這樣Service就運行在獨立的進程中,比較好模擬進程間通信。

四,客戶端實現

       然後是客戶端:

      1,調用bindService方法綁定服務端的Service,在客戶端與Service成功建立連接後,

           onServiceConnected方法被回調,可以獲取到Binder對象;

      2,創建一個客戶端與服務端通信的信使,Messenger對象,Messenger cs_messenger = new 

           Messenger(mHandler);mHandler同服務端一樣創建;

      3,在onServiceConnected方法中創建Messenger對象,執行:Messenger messenger = new 

           Messenger(binder);

      4,在onServiceConnected方法中創建Message對象,設置Message對象中必要字段what,可選字段

           obj,arg1,arg2;以及replyTo(第2步中信使cs_messenger );

      5,在onServiceConnected方法中調用:messenger.send(msg)發送消息;


      客戶端代碼:

public class MainActivity extends Activity {

	private MyServiceConnection conn;
	private static final int MSG_FROM_CLIENT = 0;
	private static final int MSG_FROM_SERVICE = 1;
	
	private Handler mHandler = new Handler(){
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MSG_FROM_SERVICE:
				//處理service發送的消息
				Bundle b = (Bundle) msg.obj;
				int sum = b.getInt("sum");
				Log.e("MainActivity", "sum:" + sum);
				
				break;

			default:
				break;
			}
		};
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Intent intent = new Intent();
		intent.setAction("com.example.messengerdemo.wang");
		conn = new MyServiceConnection();
		bindService(intent, conn, Context.BIND_AUTO_CREATE);
		
	}
	
	private Messenger cs_messenger = new Messenger(mHandler);
	
	private class MyServiceConnection implements ServiceConnection {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
		    try {
		    	Log.e("MainActivity", "true");
		    	//創建binder關聯的信使對象
		    	Messenger messenger = new Messenger(service);
		    	//創建一個消息對象
		    	Message msg = Message.obtain(null, MSG_FROM_CLIENT, 10, 20);
		    	msg.replyTo = cs_messenger;//replyTo爲handler關聯的信使
				messenger.send(msg);//信使發送消息
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
	}
	
	@Override
	protected void onDestroy() {
		if (conn != null) {
			unbindService(conn);
		}
		super.onDestroy();
	}
	
}

       第1,3步與使用AIDL使用相同,在第3步中創建了構造函數參數爲binder的Messenger對象,實際上就是執行:mTarget = IMessenger.Stub.asInterface(target)。第5步調用了Messenger的send方法發送消息,查看Messenger$send源碼:

public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }


       由上面分析可知:mTarget = IMessenger.Stub.asInterface(target);因此mTarget實際上是一個用於訪問AIDL接口的代理類,在使用AIDL時經常有用到。調用mTarget.send(message)後,會調用遠程服務的send方法,由Binder機制實現(遠程服務與本篇文章提到服務端不是一個)。於是我們需要找到extends IMessenger.Stub的類,這個不就是分析服務端時提到的MessengerImpl麼,查看它的send方法的實現,調用Handler.this.sendMessage(msg)。


      因此,我們可以得出這樣一個結論:創建一個關聯Binder的Messenger對象,並調用send(msg)方法,其實就是封裝 了AIDL技術,Handler+Message。在客戶端發送消息後,消息是由服務端的handleMessage(msg)處理,這裏完成了一個數據由客戶端-->到服務端的過程。


     那麼,服務端獲取了消息中數據,想要給客戶端傳遞數據,那應該怎麼做呢?在服務端中handleMessage()中發送消息給客戶端實現,分這樣幾步:

     1,取出Message中的存儲數據的字段obj/arg1/arg2,以及信使replyTo;

     2,創建一個新的Message對象,設置字段what,obj/arg1/arg2;

     3,使用第1步中的replyTo中存儲信使,調用cs_messenger.send(m),將第2步中消息對象發送給客戶端;


     值得一提的是:第三步中發送消息的信使只能是replyTo字段存儲的對象,不能是服務端創建的關聯了handler對象的Messenger。

     五,另外

     本篇文章提供的服務端的代碼中,在服務端給客戶端傳遞數據時,消息中存儲的數據放在字段obj裏,大家應該注意到Integer數據先經過Bundle對象的封裝,然後才賦值給msg.obj。由於是進程間傳遞數據,對象只能是Parcelable對象,並且賦值給字段obj的對象只能是系統的Parcelable對象,我們自定義的Parcelable對象無法通過obj傳遞。除了字段obj傳遞對象,還可以調用Message$setData(bundle)存放對象。另外,Message,Messenger都實現了Parcelable接口,因此可以進行跨進程的傳輸。

   

      前面服務端向客戶端通過發送消息傳遞了數據,客戶端處理消息是在handleMessage(msg)中,見代碼吧,沒什麼可分析的了,這樣就完成了一個數據由服務端-->到客戶端的過程。上面介紹瞭如何使用Messenger實現數據跨進程傳輸,客戶端-->服務端,然後服務端->客戶端。


       這篇文章就分享到這裏啦,有疑問可以留言,亦可糾錯,亦可補充,互相學習...^_^


      

      






       

       



發佈了47 篇原創文章 · 獲贊 28 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章