【Android - IPC】之Messenger简介

参考资料:

1、《Android开发艺术探索》第二章2.4.3

2、【Messenger完全解析】

 

1、Messenger概述

  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相同。

 

posted on 2017-04-05 14:46  ITGungnir  阅读(493)  评论(0编辑  收藏  举报

导航