【Android 1、Messenger概述 2、Messenger通信实例 3、 总结
参考资料:
1、《Android开发艺术探索》第二章2.4.3
Messenger,译为“信使”,是Android中一种基于Binder机制的IPC(Inter-Process Communication,进程间通信)方式。通过Messenger可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松的实现进程间数据的传递了。即:Messenger允许实现基于消息的进程间通信的方式。
Messenger是一种轻量级的IPC方案,其实现底层是AIDL。
Messenger一次只能处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端不存在并发执行的情形。
使用Messenger进行进程间通信的具体流程是:客户端发送一个Message给服务端,在服务端的Handler中接收到客户端的消息,然后进行对应的处理,处理完成后再将结果等数据封装成Message对象,发送给客户端,客户端的Handler中会接收到服务端传过来的数据并进行处理。
2、Messenger通信实例
在这个例子中,客户端的界面上有一个按钮,通过点击这个按钮,向服务端发送两个参数,服务端将这两个参数相加后回传给客户端,客户端收到答案之后显示到界面上。
2.1、服务端
服务端的需要新建一个Service,命名为MessengerService,代码如下:
package my.itgungnir.server; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.support.annotation.Nullable; /** * Messenger 服务端Service * Created by ITGungnir on 2017/4/5. */ public class MessengerServer extends Service { private static final int MSG_GETSUM = 0x001; // Messenger对象,其中的Handler用来接收从客户端传过来的Message,通过可以创建Message对象回传给客户端 Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgFromClient) { // 要回传给客户端的Message对象 Message msgToClient = Message.obtain(msgFromClient); switch (msgFromClient.what) { case MSG_GETSUM: msgToClient.what = MSG_GETSUM; try { Thread.sleep(2000); // 模拟耗时 msgToClient.arg2 = msgFromClient.arg1 + msgFromClient.arg2; // Message的replyTo也是一个Messenger对象 msgFromClient.replyTo.send(msgToClient); } catch (Exception e) { e.printStackTrace(); } break; } super.handleMessage(msgFromClient); } }); @Nullable @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); // 获取Messenger中的IBinder对象 } }
可以看到,服务端要做的工作是手动创建一个Messenger,然后在onBind()方法中返回这个Messenger对象的getBinder()对象。
在创建的Messenger中,参数是一个Handler,即从客户端向服务端发送消息时,就是将消息发送到了这个Handler对象中。因此,我们需要在这个Handler对象中处理客户端传过来的消息,当然也可以创建一个新的消息,返回给客户端。
因为服务端使用了Android四大组件中的Service,因此需要在Menifest文件中进行注册,代码如下:
<service android:name=".MessengerServer"> <intent-filter> <action android:name="my.itgungnir.messenger.getsum" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
2.2、客户端
在客户端中需要做的事情比服务端多。首先,我们需要先绑定到服务端的服务上,然后才可以向服务端请求服务。下面是客户端首页MainActivity.java类中的代码:
package my.itgungnir.client; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final int MSG_GETSUM = 0x001; private LinearLayout container; // 盛放所有算术题的TextView的容器(LinearLayout) private TextView state; // 显示服务器连接状态的TextView private Button require; // 向服务端请求算术题答案的按钮 private Messenger mService; // 客户端的Messenger对象 private boolean isConnected; // 指示是否连接到服务端 private int mArg1 = 0; // 算术题的被加数,同时也是TextView的id @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); bindToServer(); } // 初始化界面中的布局,并绑定相应的事件 private void initViews() { container = (LinearLayout) findViewById(R.id.client_ly_container); state = (TextView) findViewById(R.id.client_tv_connection); require = (Button) findViewById(R.id.client_btn_require); require.setOnClickListener(this); } // 获取服务端绑定状态的ServiceConnection对象 private ServiceConnection connection = new ServiceConnection() { @Override // 绑定服务端成功时回调的方法 public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); isConnected = true; state.setText("Server Connected!"); } @Override // 绑定服务端失败时回调的方法 public void onServiceDisconnected(ComponentName name) { mService = null; isConnected = false; state.setText("Server Not Connected!"); } }; // 绑定到服务端 private void bindToServer() { Intent intent = new Intent(); intent.setAction("my.itgungnir.messenger.getsum"); // Android 5.0 及以上的设备需要为intent设置package,否则会报错:Service Intent must be explicit intent.setPackage("my.itgungnir.server"); bindService(intent, connection, Context.BIND_AUTO_CREATE); } // 客户端的Messenger对象 private Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgToClient) { switch (msgToClient.what) { case MSG_GETSUM: // 接收服务端回传的数据并在Handler中更新UI TextView tv = (TextView) findViewById(msgToClient.arg1); tv.setText(tv.getText() + " ==> " + msgToClient.arg2); break; } super.handleMessage(msgToClient); } }); // 每点击一次按钮,就向服务端发送一条消息 @Override public void onClick(View v) { try { int arg1 = ++mArg1; int arg2 = (int) (Math.random() * 90 + 10); TextView tv = new TextView(MainActivity.this); tv.setTextSize(18f); tv.setText(arg1 + " + " + arg2 + " = Calculating... "); tv.setId(arg1); container.addView(tv); // 要发送到服务端的Message对象 Message msgFromClient = Message.obtain(null, MSG_GETSUM, arg1, arg2); msgFromClient.replyTo = mMessenger; if (isConnected) { // 将消息发送给服务端Messenger的Handler对象 mService.send(msgFromClient); } } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); } }
从代码中可以看到,调用bindService()方法之后,在ServiceConnection对象的回调方法中的onServiceConnected()方法(连接服务端成功的回调方法)中,会携带一个IBinder类型的参数,这个参数就是服务端的服务对象,我们可以通过 new Messenger(service) 方法获取到服务端的Messenger对象,然后就可以通过这个Messenger对象,向服务端的Handler发送消息了。
布局文件activity_main.xml文件中的代码如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/client_ly_container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="15.0dip"> <TextView android:id="@+id/client_tv_connection" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/black" android:textSize="20.0sp" android:textStyle="bold" /> <Button android:id="@+id/client_btn_require" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="15.0dip" android:layout_marginTop="15.0dip" android:text="Send Message To Server" /> </LinearLayout>
2.3、运行结果
先运行服务端,再运行客户端,如果连接服务端成功,客户端的状态TextView中会显示 connected 字样;此时我们点击客户端中的按钮,向服务端发送算术题,然后等待服务端计算后将结果返回给客户端。
运行结果如下图所示:
3、 总结
从上面的例子中,我们大致可以看出Messenger的工作机制:
主要是客户端向服务端发送消息请求服务,服务端接收到客户端的请求后提供相应的服务,然后将服务结果封装成消息对象回传给客户端,客户端收到服务端的回传结果对其进行处理使用(如更新UI界面)。
这里需要注意的有三点:
- Messenger中的send()方法:这个方法的作用是将一个Message对象发送给调用这个方法的Messenger的Handler对象(就是new Messenger的时候参数中传入的Handler对象);
- Message对象有一个Messenger类型的replyTo属性,这个属性指定了这条消息的回传对象。在上面的例子中,我们在客户端指定了发送给服务端的Message的replyTo属性是客户端的Messenger,这样在服务端才更容易的拿到客户端的具体的Messenger对象并进行消息的回传;
- Messenger的底层实现是AIDL,其机制也和AIDL相同。