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>
时间: 2024-10-10 12:59:37

Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger的相关文章

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

前面两片文章讲解了通过AIDL和Messenger两种方式实现Android IPC.而本文所讲的并不是第三种IPC方式,而是对前面两种方式进行封装,这样我们就不用直接把Aidl文件,java文件拷贝到客户端了,而是为客户端提供一个aar(Anroid Archive)包.通过这个aar包对AIDL或者Messenger进行封装,最终客户端就可以像调用一般的java类一样,而不用处理通信方面的内容.(实际上书中说是要打包成jar包,但是在新建的Java Library Module中,我没能成功

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

服务端: 最终项目结构: 这个项目中,我们将用到自定义类CustomData作为服务端与客户端传递的数据. Step 1:创建CustomData类 package com.ldb.android.example.aidl; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import java.util.ArrayList; import java.util.Date; impor

Android Programming: Pushing the Limits -- Chapter 4: Android User Experience and Interface Design

User Stories Android UI Design 附加资源 User Stories: @.通过写故事来设计应用. @.每个故事只关注一件事. @.不同的故事可能使用相同的组件,因此尽早地对故事进行分类. @.把目标用户构想到故事里,描述他们的基本特征,会在什么时候.什么地点使用该应用等信息,因此来确定故事的优先级. Android UI Design: @.构思应用需要展示的界面及内容,不需要详细的界面设计. @.确定各界面的跳转关系. @.用户界面原型设计,可通过工具进行,比如A

Android Programming: Pushing the Limits -- Chapter 5: Android User Interface Operations

多屏幕 自定义View 多屏幕 @.Android 4.2 开始支持多屏幕. @.举例: public class SecondDisplayDemo extends Activity { private Presentation mPresentation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.lay

Android Programming: Pushing the Limits -- Chapter 3: Components, Manifests, and Resources

Android Components Manifest文件 Resource and Assets v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} Normal 0 false 7.8 磅 0 2 false false false EN-US ZH-CN X-N

Android Programming: Pushing the Limits -- Chapter 2: Efficient Java Code for Android

Android's Dalvik Java 与 Java SE 进行比较 Java代码优化 内存管理与分配 Android的多线程操作 Android’s Dalvik Java 与 Java SE 进行比较: @.Dalvik虚拟机是register-based machine:Java SE虚拟机是stack machine. @.从Android 2.2 版本开始引进JIT(Just In Time)编译器,之前是纯解释器. @.Android SDK 使用dx这个工具把Java SE s

Android Programming: Pushing the Limits -- Chapter 1: Fine-Tuning Your Development Environment

ADB命令 Application Exerciser Monkey Gradle ProGuard 代码重用 版本控制 静态代码分析 代码重构 开发者模式   ADB命令: @.adb help:查看adb命令. @.adb devices:列出所有连接的安卓设备和模拟器. @.adb push <local> <remote> 把计算机里的文件拷贝到设备中. adb push e:\test.xml /sdcard/files.ldb/ @.adb pull <remot

Android Programming: Pushing the Limits -- Chapter 6: Services and Background Tasks

什么时候使用Service 服务类型 开启服务 后台运行 服务通信 附加资源 什么时候使用Service: @.任何与用户界面无关的操作,可移到后台线程,然后由一个Service来控制这个线程. 服务类型: @.First is the one that performs work for the application independent of the user’s input. 如:后台执行的音乐播放器. @.The other type of Service is one that’s

[iOS翻译]《iOS 7 Programming Pushing the Limits》系列:你可能不知道的Objective-C技巧

简介: 如果你阅读这本书,你可能已经牢牢掌握iOS开发的基础,但这里有一些小特点和实践是许多开发者并不熟悉的,甚至有数年经验的开发者也是.在这一章里,你会学到一些很重要的开发技巧,但这仍远远不够,你还需要积累更多的实践来让你的代码更强力. /* 本文翻译自<iOS 7 Programming Pushing the Limits>一书的第三章“You May Not Know”,想体会原文精髓的朋友请支持原书正版. ——————(博客园.新浪微博)葛布林大帝 */ 目录: 一. 最好的命名实践