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实现数据跨进程传输,客户端-->服务端,然后服务端->客户端。


       这篇文章就分享到这里啦,有疑问可以留言,亦可纠错,亦可补充,互相学习...^_^


      

      






       

       



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值