Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger
Messenger类实际是对Aidl方式的一层封装。本文只是对如何在Service中使用Messenger类实现与客户端的通信进行讲解,对Messenger的底层不做说明。阅读Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL了解如何使用Aidl的方式实现服务端与客户端的通信。
在Service中使用Messenger,大部分代码还是跟Android的消息机制打交道,具体一点就是跟Handler,Message打交道。阅读Android -- Looper、Handler、MessageQueue等类之间关系的序列图了解Android的消息机制。
服务端:
Step 1:定义两个变量
private Handler mMessageHandler; private Messenger mMessenger;
Step 2:对两个变量进行赋值
@Override public void onCreate() { super.onCreate(); HandlerThread handlerThread = new HandlerThread("MessengerService"); handlerThread.start(); mMessageHandler = new Handler(handlerThread.getLooper(), new MyHandlerCallback()); mMessenger = new Messenger(mMessageHandler); }
这里我们使用到了HandlerThread进行辅助。而MyHandlerCallback实现了Handler.Callback接口,实现对消息的处理,完成具体操作。
Step 3:实现Handler.Callback接口
private class MyHandlerCallback implements Handler.Callback{ @Override public boolean handleMessage(Message msg) { boolean delivered = false; switch (msg.what){ case MessageApi.SEND_TEXT_MSG: Bundle bundle = msg.getData(); delivered = sendTextMessage(bundle.getString(MessageApi.MSG_TEXT_KEY)); break; case MessageApi.SEND_PHOTO_MSG: delivered = sendPhotoMessage((Bitmap) msg.obj); break; } Message reply = Message.obtain(); reply.what = MessageApi.MESSAGE_DELIVERED_MSG; Bundle bundle = new Bundle(); bundle.putBoolean(MessageApi.MSG_DELIVERED_KEY, delivered); reply.setData(bundle); try { // Send message back via Message.replyto msg.replyTo.send(reply); } catch (RemoteException e) { Log.e(TAG, "Error sending message reply!", e); } return true; } }
这里与一般的handleMessage没多大区别,主要就是在给客户端回消息时使用到了Message.replyto。因此,可想而知,在客户端发送消息时,如果要接收服务端的消息就必须对消息指定replyto。而replyto实际也是一个Messenger实例。而服务端与客户端使用的消息代码要保持一致,因此这里我们单独用了一个类MessageApi进行存放:
public class MessageApi { public static final int SEND_TEXT_MSG = 10; public static final int SEND_PHOTO_MSG = 20; public static final int MESSAGE_DELIVERED_MSG = 30; public static final String MSG_TEXT_KEY = "text_key"; public static final String MSG_DELIVERED_KEY = "delivered_key"; }
Step 4:返回Binder实例对象到客户端。
@Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind"); return mMessenger.getBinder(); }
以上就是服务端的基本步骤了。
服务端示例完整代码:
package com.ldb.android.example.messengerservice.service; import android.app.Service; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.annotation.Nullable; import android.util.Log; import com.ldb.android.example.messengerservice.api.MessageApi; /** * Created by lsp on 2016/9/2. */ public class MessengerService extends Service { private static final String TAG = "MessengerService"; private Handler mMessageHandler; private Messenger mMessenger; @Override public void onCreate() { super.onCreate(); HandlerThread handlerThread = new HandlerThread("MessengerService"); handlerThread.start(); mMessageHandler = new Handler(handlerThread.getLooper(), new MyHandlerCallback()); mMessenger = new Messenger(mMessageHandler); } @Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind"); return mMessenger.getBinder(); } @Override public void onDestroy() { super.onDestroy(); mMessageHandler.getLooper().quit(); } private class MyHandlerCallback implements Handler.Callback{ @Override public boolean handleMessage(Message msg) { boolean delivered = false; switch (msg.what){ case MessageApi.SEND_TEXT_MSG: Bundle bundle = msg.getData(); delivered = sendTextMessage(bundle.getString(MessageApi.MSG_TEXT_KEY)); break; case MessageApi.SEND_PHOTO_MSG: delivered = sendPhotoMessage((Bitmap) msg.obj); break; } Message reply = Message.obtain(); reply.what = MessageApi.MESSAGE_DELIVERED_MSG; Bundle bundle = new Bundle(); bundle.putBoolean(MessageApi.MSG_DELIVERED_KEY, delivered); reply.setData(bundle); try { // Send message back via Message.replyto msg.replyTo.send(reply); } catch (RemoteException e) { Log.e(TAG, "Error sending message reply!", e); } return true; } } // Return true when delivered private boolean sendPhotoMessage(Bitmap photo) { // Implementation left out for brevity Log.d(TAG, "sendPhotoMessage"); return true; } // Return true when delivered private boolean sendTextMessage(String textMessage) { // Implementation left out for brevity Log.d(TAG, "sendTextMessage: " + textMessage); return true; } }
客户端:
Step 1:将服务端的MessageApi类拷贝到客户端。
Step 2:定义三个变量
private Messenger mRemoteMessenger; private Messenger mReplyMessenger; private Handler mReplyHandler;
Step 3:为mReplyHandler与mReplyMessenger赋值
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSendButton = (Button) findViewById(R.id.send_button); mSendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendText(); } }); HandlerThread handlerThread = new HandlerThread("MessageClient"); handlerThread.start(); mReplyHandler = new Handler(handlerThread.getLooper(), new ReplyHandlerCallback()); mReplyMessenger = new Messenger(mReplyHandler); }
与服务端类似。ReplyHandlerCallback实现了Handler.Callback接口.
Step 4:实行Handler.Callback,处理服务端返回的消息。
private class ReplyHandlerCallback implements Handler.Callback{ @Override public boolean handleMessage(Message msg) { switch (msg.what){ case MessageApi.MESSAGE_DELIVERED_MSG: Bundle bundle = msg.getData(); boolean delivered = (boolean) bundle.getBoolean(MessageApi.MSG_DELIVERED_KEY); Log.d(TAG, "delivered: " + delivered); break; } return true; } }
Step 5:实现ServiceConnection接口,在onServiceConnected()方法中,我们获取到了服务端的代理Messenger -- mRemoteMessenger, 客户端与服务端的通信都是通过此代理来完成的。
@Override public void onServiceConnected(ComponentName name, IBinder service) { mRemoteMessenger = new Messenger(service); } @Override public void onServiceDisconnected(ComponentName name) { mRemoteMessenger = null; }
Step 6:bindService,从Android 5.0开始,bindService需要通过Explicit Intent。
@Override protected void onResume() { super.onResume(); // Since Android 5.0(Lollipop), bindService should use explicit intent. Intent intent = new Intent("com.ldb.android.example.messengerservice.MessengerService"); bindService( createExplicitFromImplicitIntent(this, intent), this, BIND_AUTO_CREATE); }
Step 7:记得unbindService。
Step 8:发送消息,使用Bundle作为消息内容的载体,不要使用Message.obj,书中是直接使用Message.obj,但是实际操作却报错了,网上建议使用Bundle。如果希望服务端返回消息,则需要指定replyto,实际就是我们在onCreate中实例化的mReplyMessenger,因为它与mReplyHandler相关联,因此服务端通过它返回的消息最终都由mReplyHandler进行处理。
public void sendText(){ String textMessage = ((EditText) findViewById(R.id.message_edit_text)).getText().toString(); Message message = Message.obtain(); message.what = MessageApi.SEND_TEXT_MSG; // Can't use Message.obj // message.obj = textMessage; // Use Bundle to load the message content. Bundle bundle = new Bundle(); bundle.putString(MessageApi.MSG_TEXT_KEY, textMessage); message.setData(bundle); // Service would use replyto to send message back. message.replyTo = mReplyMessenger; try { mRemoteMessenger.send(message); } catch (RemoteException e) { Log.e(TAG, "Error sendText: " + message, e); } }
以上就是客户端的基本步骤了。
客户端完整代码:
package com.ldb.android.example.messengerclient; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Handler; import android.os.HandlerThread; 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.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.ldb.android.example.messengerclient.api.MessageApi; import java.util.List; public class MainActivity extends AppCompatActivity implements ServiceConnection{ private static final String TAG = "MainActivity"; private Messenger mRemoteMessenger; private Messenger mReplyMessenger; private Handler mReplyHandler; private Button mSendButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSendButton = (Button) findViewById(R.id.send_button); mSendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendText(); } }); HandlerThread handlerThread = new HandlerThread("MessageClient"); handlerThread.start(); mReplyHandler = new Handler(handlerThread.getLooper(), new ReplyHandlerCallback()); mReplyMessenger = new Messenger(mReplyHandler); } @Override protected void onResume() { super.onResume(); // Since Android 5.0(Lollipop), bindService should use explicit intent. Intent intent = new Intent("com.ldb.android.example.messengerservice.MessengerService"); bindService( createExplicitFromImplicitIntent(this, intent), this, BIND_AUTO_CREATE); } @Override protected void onPause() { super.onPause(); unbindService(this); } @Override protected void onDestroy() { super.onDestroy(); mReplyHandler.getLooper().quit(); } @Override public void onServiceConnected(ComponentName name, IBinder service) { mRemoteMessenger = new Messenger(service); } @Override public void onServiceDisconnected(ComponentName name) { mRemoteMessenger = null; } public void sendText(){ String textMessage = ((EditText) findViewById(R.id.message_edit_text)).getText().toString(); Message message = Message.obtain(); message.what = MessageApi.SEND_TEXT_MSG; // Can't use Message.obj // message.obj = textMessage; // Use Bundle to load the message content. Bundle bundle = new Bundle(); bundle.putString(MessageApi.MSG_TEXT_KEY, textMessage); message.setData(bundle); // Service would use replyto to send message back. message.replyTo = mReplyMessenger; try { mRemoteMessenger.send(message); } catch (RemoteException e) { Log.e(TAG, "Error sendText: " + message, e); } } public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) { // Retrieve all services that can match the given intent PackageManager pm = context.getPackageManager(); List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0); // Make sure only one match was found if (resolveInfo == null || resolveInfo.size() != 1) { return null; } // Get component info and create ComponentName ResolveInfo serviceInfo = resolveInfo.get(0); String packageName = serviceInfo.serviceInfo.packageName; String className = serviceInfo.serviceInfo.name; ComponentName component = new ComponentName(packageName, className); // Create a new intent. Use the old one for extras and such reuse Intent explicitIntent = new Intent(implicitIntent); // Set the component to be explicit explicitIntent.setComponent(component); return explicitIntent; } private class ReplyHandlerCallback implements Handler.Callback{ @Override public boolean handleMessage(Message msg) { switch (msg.what){ case MessageApi.MESSAGE_DELIVERED_MSG: Bundle bundle = msg.getData(); boolean delivered = (boolean) bundle.getBoolean(MessageApi.MSG_DELIVERED_KEY); Log.d(TAG, "delivered: " + delivered); break; } return true; } } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/message_edit_text" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/send_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Send"/> </LinearLayout>