Android跨进程通信(三):Messenger使用教程

文章目录

Android跨进程通信(一):AIDL使用教程1
Android跨进程通信(二):AIDL使用教程2
Android跨进程通信(三):Messenger使用教程
Android跨进程通信(四):AIDL源码解析
Android跨进程通信(五):Messenger源码解析

前面的文章讲述了如何通过AIDL来实现Android的跨进程通信,本篇文章来讲解如何使用Messenger来实现同样的功能。

介绍

Messenger是AIDL的封装,进程可以通过Messenger来实现通信。直接编写AIDL相当复杂,而Messenger大大简化了编写流程。

下面先介绍Messenger的基本使用方法。

准备工作

创建Messenger对象需要传入一个Handler,在这个Handler里对接收到的数据进行处理。

Handler handler = new Handler(){
	@Override
        public void handleMessage(@NonNull Message msg) {
        	...
        }
}
Messenger messenger = new Messenger();

或者传入IBinder,这种方式是在客户端中实现的。

private class SensorConnection implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mSensorMessenger = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
}

发送数据方式如下。这里有两个地方容易弄混。
首先是message.replyTo = mMessenger,意思是在信封里说明谁是发送者。
其次是mRecvMessenger.send(message),A 要发送数据给 B,A用的是B的Messenger来发送。

Message message = Message.obtain();
//Bundle实现了序列化
Bundle bundle = new Bundle();
bundle.putString("name","PYJTLK");
message.setData(bundle);
message.replyTo = mMessenger;
mRecvMessenger.send(message);

而下面这种方式是错误的,因为是跨进程通信,发送的数据必须实现序列化。

Message message = Message.obtain();
//错误,因为String类没有实现序列化,应该把数据放入Bundle中,Bundle实现了序列化。
message.obj = new String("hello");
message.replyTo = mMessenger;
mRecvMessenger.send(message);

了解了Messenger的基本使用后,接下来就通过一个例子来讲述Messenger跨进程通信的完整流程。其中服务端提供温度传感器(模拟场景)服务,客户端绑定该服务,并通过Messenger来获取温度值。

服务端代码

首先创建一个传感器管理类,因为传感器在设备中是单例,所以这里使用单例模式。

public class SensorManager {
    private static final int CMD_GET_TEMPERATURE = 0;
    private static final int CMD_OPEN_SENSOR = 1;
    private static final int CMD_SHUT_SENSOR = 2;

    private static final String KEY_GET_TEMPERATURE_RESULT = "get result";
    private static final String KEY_OPEN_SENSOR_RESULT = "open result";
    private static final String KEY_SHUT_SENSOR_RESULT = "shut result";

    private static SensorManager sSensorManager;

    private SensorHandler mHandler;
    private Messenger mMessenger;

    private class SensorHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            try {
                Message message = Message.obtain();
                //信封内说明谁是发送者
                Messenger clientMessenger = msg.replyTo;
                Bundle bundle = new Bundle();
                message.setData(bundle);
                String result;

                switch (msg.what){
                    case CMD_GET_TEMPERATURE:
                        message.what = CMD_GET_TEMPERATURE;
                        //message.obj = new Double(...) 这种方法是不行的,因为Double类没有实现序列化,跨进程传输的类对象必须实现了序列化,比如Bundle
                        bundle.putDouble(KEY_GET_TEMPERATURE_RESULT,getTemperature());
                        message.replyTo = mMessenger;
                        //服务端发送数据,使用客户端的Messenger来发送
                        clientMessenger.send(message);
                        break;

                    case CMD_OPEN_SENSOR:
                        message.what = CMD_OPEN_SENSOR;
                        result = setTemperatureSensor(true) ? "open successfully" : "open failed";
                        //message.obj = new String("...") 这种方法是不行的,因为String类没有实现序列化,跨进程传输的类对象必须实现了序列化,比如Bundle
                        bundle.putString(KEY_OPEN_SENSOR_RESULT,result);
                        message.replyTo = mMessenger;
                        //服务端发送数据,使用客户端的Messenger来发送
                        clientMessenger.send(message);
                        break;

                    case CMD_SHUT_SENSOR:
                        message.what = CMD_SHUT_SENSOR;
                        result = setTemperatureSensor(true) ? "shut successfully" : "shut failed";
                        //message.obj = new String("...") 这种方法是不行的,因为String类没有实现序列化,跨进程传输的类对象必须实现了序列化,比如Bundle
                        bundle.putString(KEY_SHUT_SENSOR_RESULT,result);
                        message.replyTo = mMessenger;
                        //服务端发送数据,使用客户端的Messenger来发送
                        clientMessenger.send(message);
                        break;
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    private SensorManager(){
        mHandler = new SensorHandler();
        mMessenger = new Messenger(mHandler);
    }

    //手机上的传感器属于单例,因此使用单例模式
    public static SensorManager getInstance(){
        synchronized (SensorManager.class){
            if(sSensorManager == null){
                sSensorManager = new SensorManager();
            }
        }
        return sSensorManager;
    }

    public double getTemperature(){
        try {
            //假设获取温度信息是很耗时的任务
            Thread.sleep(100);
            double temperature = Math.random() * 100;
            return temperature;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return -1;
        }
    }

    public boolean setTemperatureSensor(boolean isOpen){
        try {
            //假设开关温度传感器是很耗时的操作
            Thread.sleep(100);
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }

    public Messenger getMessenger(){
        return mMessenger;
    }
}

接下来创建一个SensorService类。注意,因为是跨进程通信,这个服务需要对外开放。

public class SensorService extends Service {
    private static final String TAG = "SensorService";

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "bind to sensor service");
        return SensorManager.getInstance()
                .getMessenger()
                .getBinder();
    }
}

在AndroidManifest添加此服务。

<service
            android:name=".SensorService"
            android:enabled="true"
            android:exported="true"></service>

最后在服务端应用的MainActivity上启动此服务。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this,SensorService.class);
        startService(intent);
    }
}

至此,服务端的编写就完成了。下面来看看客户端的代码。

客户端代码

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private Button mGetButton;
    private Button mOpenButton;
    private Button mShutButton;
    private TextView mTextView;

    private SensorConnection mSensorConnection;
    private ClientHandler mClientHandler;
    private Messenger mClientMessenger;
    private Messenger mSensorMessenger;

    private class ClientHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            Bundle bundle = msg.getData();

            //如下是服务端的相关参数
            //private static final int CMD_GET_TEMPERATURE = 0;
            //private static final int CMD_OPEN_SENSOR = 1;
            //private static final int CMD_SHUT_SENSOR = 2;
            //private static final String KEY_GET_TEMPERATURE_RESULT = "get result";
            //private static final String KEY_OPEN_SENSOR_RESULT = "open result";
            //private static final String KEY_SHUT_SENSOR_RESULT = "shut result";

            switch (msg.what){
                //private static final int CMD_GET_TEMPERATURE = 0;
                case 0:
                    double temperature = bundle.getDouble("get result");
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mTextView.setText(temperature + "℃");
                        }
                    });
                    break;

                //private static final int CMD_OPEN_SENSOR = 1;
                case 1:
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "open successfully", Toast.LENGTH_SHORT).show();
                        }
                    });
                    break;

                //private static final int CMD_SHUT_SENSOR = 2;
                case 2:
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            MainActivity.this.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Toast.makeText(MainActivity.this, "shut successfully", Toast.LENGTH_SHORT).show();
                                }
                            });
                        }
                    });
                    break;
            }
        }
    }

    private class SensorConnection implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mSensorMessenger = new Messenger(service);
            Log.d(TAG, "connect successfully");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView = findViewById(R.id.text);
        mGetButton = findViewById(R.id.button_get);
        mOpenButton = findViewById(R.id.button_open);
        mShutButton = findViewById(R.id.button_shut);

        mGetButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Message message = Message.obtain();
                    //信封内告诉收件人谁是发送者
                    message.replyTo = mClientMessenger;
                    message.what = 0;
                    mSensorMessenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        mOpenButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Message message = Message.obtain();
                    //信封内告诉收件人谁是发送者
                    message.replyTo = mClientMessenger;
                    message.what = 1;
                    mSensorMessenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        mShutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Message message = Message.obtain();
                    //信封内告诉收件人谁是发送者
                    message.replyTo = mClientMessenger;
                    message.what = 2;
                    //客户端发送数据,使用服务端的Messenger来完成
                    mSensorMessenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        bindSensorService();
    }

    private void bindSensorService(){
        mSensorConnection = new SensorConnection();
        mClientHandler = new ClientHandler();
        mClientMessenger = new Messenger(mClientHandler);

		//Intent intent = new Intent("com.pyjtlk.sensorserver.SensorService"); //这是隐式绑定Service,Android5.0开始就不支持了
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.pyjtlk.sensorserver","com.pyjtlk.sensorserver.SensorService"));
        bindService(intent,mSensorConnection,BIND_AUTO_CREATE);
    }
}

比较与思考

Messenger和AIDL都可以实现跨进程通信,那么它们的区别在哪呢?

首先,从上面的例子我们知道Messenger的数据是放在Bundle里的,而Bundle只适合少量基本类型数据的传输。相反,AIDL可以传输序列化的对象。

其次,Messenger的数据收发是异步的,而AIDL是同步的。

因为是跨进程\跨应用通信,我们并不能保证本方在发送数据时对方还活着。因此,需要引入一些机制来确定双方是否已经断开了,比如超时等待机制。

最后

服务端和客户端都放在Gitee代码仓库里了。下一篇文章我们将通过源码来分析AIDL的原理。

对Messenger源码感兴趣的朋友可以看这篇文章

参考文章

《Android Messenger使用及分析》
《android Messenger》
《关于Android Messenger的总结》

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值