首页 > 代码库 > Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger

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>

 

Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger