• Android进程间通讯之messenger


    这两天在看binder,无意间在文档看到messenger这么个东西,感觉这个东西还挺有意思的,给大家分享一下。

    平时一说进程间通讯,大家都会想到AIDL,其实messenger和AIDL作用一样,都可以进行进程间通讯。它是基于消息的进程间通信,就像子线程和UI线程发送消息那样,是不是很简单,还不用去写AIDL文件,是不是有点小爽。哈哈。

    此外,还支持记录客户端对象的Messenger,然后可以实现一对多的通信;甚至作为一个转接处,任意两个进程都能通过服务端进行通信。

    与 AIDL 比较:

      当您需要执行 IPC 时,为您的接口使用 Messenger 要比使用 AIDL 实现更加简单,因为 Messenger 会将所有服务调用排入队列,而纯粹的 AIDL 接口会同时向服务发送多个请求,服务随后必须应对多线程处理。

      对于大多数应用,服务不需要执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果您的服务必须执行多线程处理,则应使用 AIDL 来定义接口。

    接下来看下怎么用:

    服务端:

    1.创建一个handler对象,并实现hanlemessage方法,用于接收来自客户端的消息,并作处理

    2.创建一个messenger(送信人),封装handler 

    3.用messenger的getBinder()方法获取一个IBinder对象,通过onBind返回给客户端

    客户端:

    1.在activity中绑定服务

    2.创建ServiceConnection并在其中使用 IBinder 将 Messenger实例化 

    3.使用Messenger向服务端发送消息

    4.解绑服务

    5.服务端中在 handleMessage() 方法中接收每个 Message

    这样,客户端并没有调用服务的“方法”。而客户端传递的“消息”(Message 对象)是服务在其 Handler 中接收的。

    上面实现的仅仅是单向通信,即客户端给服务端发送消息,如果我需要服务端给客户端发送消息又该怎样做呢?

    其实,这也是很容易实现的,下面就让我们接着上面的步骤来实现双向通信吧

    1.在客户端中创建一个Handler对象,用于处理服务端发过来的消息

    2.创建一个客户端自己的messenger对象,并封装handler。

    3.将客户端的Messenger对象赋给待发送的Message对象的replyTo字段

    4.在服务端的Handler处理Message时将客户端的Messenger解析出来,并使用客户端的Messenger对象给客户端发送消息

    这样就实现了客户端和服务端的双向通信了。

    注意:注:Service在声明时必须对外开放,即android:exported="true"

    是不是看的头晕,忘掉吧,直接看下面。

    看一个简单的例子

     1 package com.zixue.god.myapplication;
     2 
     3 import android.app.Service;
     4 import android.content.Intent;
     5 import android.os.Handler;
     6 import android.os.IBinder;
     7 import android.os.Message;
     8 import android.os.Messenger;
     9 import android.os.RemoteException;
    10 import android.widget.Toast;
    11 
    12 //服务端service
    13 public class MyService extends Service {
    14     private static final int CODE = 1;
    15     public MyService() {
    16     }
    17     @Override
    18     public IBinder onBind(Intent intent) {
    19         return mMessenger.getBinder();
    20     }
    21 
    22     //创建一个送信人,封装handler
    23     private Messenger mMessenger = new Messenger(new Handler() {
    24         @Override
    25         public void handleMessage(Message msg) {
    26             Message toClient = Message.obtain();
    27             switch (msg.what) {
    28                 case CODE:
    29                     //接收来自客户端的消息,并作处理
    30                     int arg = msg.arg1;
    31                     Toast.makeText(getApplicationContext(),arg+"" , Toast.LENGTH_SHORT).show();
    32                     toClient.arg1 = 1111111111;
    33                     try {
    34                         //回复客户端消息
    35                         msg.replyTo.send(toClient);
    36                     } catch (RemoteException e) {
    37                         e.printStackTrace();
    38                     }
    39             }
    40             super.handleMessage(msg);
    41         }
    42     });
    43 }

    //客户端

     1 package com.zixue.god.fuck;
     2 
     3 import android.content.ComponentName;
     4 import android.content.Intent;
     5 import android.content.ServiceConnection;
     6 import android.os.Bundle;
     7 import android.os.Handler;
     8 import android.os.IBinder;
     9 import android.os.Message;
    10 import android.os.Messenger;
    11 import android.os.RemoteException;
    12 import android.support.v7.app.AppCompatActivity;
    13 import android.util.Log;
    14 import android.view.View;
    15 import android.widget.Button;
    16 import android.widget.Toast;
    17 
    18 public class MainActivity extends AppCompatActivity {
    19     private boolean mBond;
    20     private Messenger serverMessenger;
    21     private MyConn conn;
    22 
    23     @Override
    24     protected void onCreate(Bundle savedInstanceState) {
    25         super.onCreate(savedInstanceState);
    26         setContentView(R.layout.activity_main);
    27         //绑定服务
    28         Intent intent = new Intent();
    29         intent.setAction("com.zixue.god.myapplication.server");
    30         conn = new MyConn();
    31         bindService(intent, conn, BIND_AUTO_CREATE);
    32         Button button = (Button) findViewById(R.id.bt);
    33         button.setOnClickListener(new View.OnClickListener() {
    34             @Override
    35             public void onClick(View v) {
    36                 Message clientMessage = Message.obtain();
    37                 clientMessage.what = 1;
    38                 clientMessage.arg1 = 12345;
    39                 try {
    40                     clientMessage.replyTo = mMessenger;
    41                     serverMessenger.send(clientMessage);
    42                 } catch (RemoteException e) {
    43                     e.printStackTrace();
    44                 }
    45             }
    46         });
    47     }
    48 
    49     private class MyConn implements ServiceConnection {
    50 
    51         @Override
    52         public void onServiceConnected(ComponentName name, IBinder service) {
    53             //连接成功
    54             serverMessenger = new Messenger(service);
    55             Log.i("Main", "服务连接成功");
    56             mBond = true;
    57         }
    58 
    59         @Override
    60         public void onServiceDisconnected(ComponentName name) {
    61             serverMessenger = null;
    62             mBond = false;
    63         }
    64     }
    65     private Messenger mMessenger = new Messenger(new Handler(){
    66         @Override
    67         public void handleMessage(Message msg) {
    68             Toast.makeText(getApplicationContext(),msg.arg1+"",Toast.LENGTH_SHORT).show();
    69             super.handleMessage(msg);
    70         }
    71     });
    72     @Override
    73     protected void onDestroy() {
    74         if (mBond) {
    75             unbindService(conn);
    76         }
    77         super.onDestroy();
    78     }
    79 
    80 }

    这样就实现了客户端和服务端双向通信,是不是很简单呢。

       其实messenger底层也是AIDL。客户端和服务端通讯,就是普通的AIDL,客户端实例化stub之后,通过stub的send方法把消息发到服务端。服务端和客户端通讯:服务端通过解析message的replyto,获得客户端的stub,然后通过send方法发送到客户端。有精力的可以去翻一下源码。

  • 相关阅读:
    使用vagrant一键部署本地php开发环境(一)
    产品化机器学习的一些思考
    突破、进化,腾讯云数据库2018全年盘点
    WebGL 纹理颜色原理
    如何定制Linux外围文件系统?
    一文了解腾讯云数据库SaaS服务
    如何正确的选择云数据库?
    Node.js 进程平滑离场剖析
    Git合并不同url的项目
    mariadb 内存占用优化
  • 原文地址:https://www.cnblogs.com/makaruila/p/4869912.html
Copyright © 2020-2023  润新知