IPC第二篇:Messenger+Handler

Messenger是通过Message和Handler的方式实现的进程间的通信。它的实现步骤大体上可以分为两个部分:

1、继承Service实现自己的服务,并通过Handler处理来自客户端的数据

2、客户端实现ServiceConnection接口,获取服务器实例,并发送数据

其实Messenger本质上是AIDL的高度封装,它的底层实现还是基于Binder的。为什么这么说呢,我们可以找一下Messenger的源码:

public final class Messenger implements Parcelable {
    private final IMessenger mTarget;

    /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    
    /**
     * Send a Message to this Messenger's Handler.
     * 
     * @param message The Message to send.  Usually retrieved through
     * {@link Message#obtain() Message.obtain()}.
     * 
     * @throws RemoteException Throws DeadObjectException if the target
     * Handler no longer exists.
     */
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
    
    /**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     * 
     * @return Returns the IBinder backing this Messenger.
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }
    
    ......
    
    /**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     * 
     * @param target The IBinder this Messenger should communicate with.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
}

我们摘取了部分Messenger的源码,可以看到它有两个构造函数,一个传递Handler另一个传递IBinder,我们先看一下Handler的那个构造方法,我们发现它调用了Handler里面的getIMessenger()这个函数,我们跟到Handler类里面发现这个函数返回的是一个IMessenger的实例,而这个实例的本质就是AIDL:

@UnsupportedAppUsage
    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

另一个传递IBinder的构造函数我们也发现了AIDL的影子。所以说Messenger是AIDL的高度封装。

接下来我们来基于Messenger实现夸进程的通信。

1、实现Service

首先我们定义一个YangService继承自Service,然后实现onBind方法,这个方法要求我们返回一个IBinder的实例。Messenger源码中有一个getBinder()的方法,可以为我们返回一个IBinder实例,我们通过构造一个Messenger对象,然后再调用getBinder()就可以搞定了。Service部分代码:

@Override
    public IBinder onBind(Intent intent) {
        Messenger messenger = new Messenger(mHandler);

        return messenger.getBinder();
    }

为了构建一个Messenger实例,我们需要一个Handler对象,因此需要在Service中创建一个YangHandler继承自Handler,然后重写handlerMessage()方法,在这个方法里,我会可以拿到客户端发送过来的消息,进行解析。

private static class YangHandler extends Handler{

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            //获取客户端传递的消息
            Bundle clientBundle = msg.getData();
            String clientMsg = clientBundle.getString("client");
            Log.d(TAG,"客户端消息:"+clientMsg);


            //收到消息之后,给客户端回复
            Message serviceMsg = Message.obtain();
            Bundle serviceBundle = new Bundle();
            serviceBundle.putString("service","你好客户端,谢谢你的祝福,我会一直稳定运行下去!");
            serviceMsg.setData(serviceBundle);
            try {
                //调用客户端的Messenger发送消息给客户端
                msg.replyTo.send(serviceMsg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

如果想让服务器相应客户端的消息,可以在发送消息的时候附加一个replyTo对象,这个是一个Messenger对象,我们是通过调用Messenger的send()方法发送一个Message的。它的本质是调用了Handler的Handler的sendMessage()方法。

Messenger的send方法,可以看到它调用了Handler的send方法

/**
     * Send a Message to this Messenger's Handler.
     * 
     * @param message The Message to send.  Usually retrieved through
     * {@link Message#obtain() Message.obtain()}.
     * 
     * @throws RemoteException Throws DeadObjectException if the target
     * Handler no longer exists.
     */
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

进入Handler源码可以看到,最后调用的是Handler的sendMessage()方法

private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

服务器代码写完之后,记得去AndroidMainfest.xml文件中声明service标签,这里我多写了一个action,表示这个服务既可以显式调用,也可以隐式调用。

<service
            android:name=".YangService"
            android:enabled="true"
            android:exported="true"
            android:process=":yzy">
            <intent-filter>
                <action android:name="YANGZHENYU.MSG.SERVIEC"/>
            </intent-filter>
        </service>

接下来我们开始写客户端的代码

2、实现客户端

客户端的实现相对来说比较简单,首先我们需要实现一个ServiceConnection的实例,然后在onServiceConnected()方法中获取服务器的Messenger的实例。

private ServiceConnection mConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

接下来我们需要定义一个Messenger,这一步和服务器端一样,也是通过调用Handler参数的构造方法创建的。因此我们还需要创建一个Handler,客户端的Handler的相关代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private ClientHandler mHandler = new ClientHandler();
    private Messenger mClient = new Messenger(mHandler);


    ...

    private static class ClientHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            Bundle bundle = msg.getData();
            String serviceMsg = bundle.getString("service");
            Log.d(TAG,"服务器消息:"+serviceMsg);
        }
    }
}

接下来我们就可以通过Intent和服务器建立连接了,这里我才用的是显式意图,如果采用隐式意图,需要注意在Android5.0之后需要设置隐式意图的包名,否则会报错。

Intent intent = new Intent(this,YangService.class);
bindService(intent,mConn,BIND_AUTO_CREATE);

发送按钮要做的事情也比较简单,在之前的ServiceConnection的onServiceConnected()方法中我们已经拿到了服务器端的Messenger并保存了起来,当我们按下发送问候的时候,就是通过这个实例向服务器发送的消息。为了能够让服务器相应我们的问候,在消息中我们通过Message的replyTo变量将客户端的mClient(Messenger对象)传递给了服务器:

Message clientMsg = Message.obtain();
                Bundle clientBundle = new Bundle();
                clientBundle.putString("client","你好服务器,我是客户端,希望你运行稳定!");
                clientMsg.setData(clientBundle);
                clientMsg.replyTo = mClient;

                try {
                    mService.send(clientMsg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

为了方便测试我这里有两个按钮,一个是绑定服务,另一个是发送问候。

接下来我们就简单的测试一下,首先点击绑定服务按钮和服务器建立连接,当我们绑定服务之后,会发现目前有两个进程,一个是主进程,另一个是yzy的子进程。(这个名字可以随意起)

然后我们点击发送问候,看一下控制台的输出内容:

//服务器打印的消息
com.yangzhenyu.ipcmessenger:yzy D/yang: 客户端消息:你好服务器,我是客户端,希望你运行稳定!


//客户端打印的消息

com.yangzhenyu.ipcmessenger D/yang: 服务器消息:你好客户端,谢谢你的祝福,我会一直稳定运行下去!

我们可以看到服务器收到了来自客户端的问候,然后紧接着客户端收到了来自服务器的回复信息。至此整个基于Messenger的进程通信流程执行完毕。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值