iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android Messenger实现进程间通信及其原理
  • 321
分享到

Android Messenger实现进程间通信及其原理

2024-04-02 19:04:59 321人浏览 泡泡鱼
摘要

前言 之前分析Android消息机制的源码时,曾遇到过replyTo、IMessenger等属性字段,当时只是说这些字段用于进程间通信,并未作深入分析。今天这篇文字就来演示一下使用M

前言

之前分析Android消息机制的源码时,曾遇到过replyTo、IMessenger等属性字段,当时只是说这些字段用于进程间通信,并未作深入分析。今天这篇文字就来演示一下使用Messenger如何进行进程间通信并分析其源码实现。

Messenger进程间通信的流程

Messenger顾名思义,即信使,那么它的作用就是满足不同进程两边的通信需要了。通常我们会写aiDL来实现进程间通信,其实简单的IPC可以用Messenger来实现,需要知道的是Messenger也是基于AIDL的,只不过Messenger帮我们做了封装而已,其进程间通信框架是这样的:

如上图,假设两个进程分别为Client Process和Server Process,首先Server端需要将自己这边的Messenger引用传给Client,然后Client使用Server端传过来的Messenger来发消息给Server端,这样就实现了一个单向通信。同理,如果想要实现双向通信,则需要Client端也发送一个自己的Messenger到Server端,那么Server端也就可以利用该Messenger向Client发消息了。虽然Messenger是基于AIDL的,但它们最底层都是基于Binder的。

Messenger进程间双向通信示例

创建一个Service模拟Server进程

一般的进程间通信多是在两个App之间,但一个App中也可以有多进程,这个很常见,如应用中的推送服务一般位于单独的进程。当然我们可以把这个Service创建到另一个App中,但为了方便测试,这里只是将该Service注册为另一个进程,但还是在同一个应用中。

该Service的实现很简单,如下:


public class RemoteService extends Service {
    private WorkThread mWorkThread = new WorkThread();
    private Messenger mMessenger;

    @Override
    public void onCreate() {
        super.onCreate();
        mWorkThread.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mWorkThread.quit();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

    private void prepareMessenger() {
        mMessenger = new Messenger(mWorkThread.mHandler);
    }

    private class WorkThread extends Thread {
        Handler mHandler;

        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what) {
                        case MessageConstant.CLIENT_TO_SERVER:
                            Toast.makeText(RemoteService.this, "Hello Server:" + msg.arg1 + "," + msg.arg2, Toast.LENGTH_SHORT).show();
                            if (msg.replyTo != null) {
                                try {
                                    msg.replyTo.send(Message.obtain(null, MessageConstant.SERVER_TO_CLIENT, 0, msg.arg1 + msg.arg2));
                                } catch (RemoteException e) {
                                    e.printStackTrace();
                                }
                            }
                            break;
                        default:
                            break;
                    }

                }
            };
            prepareMessenger();
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }

上述代码虽然简单,但有几点需要注意:

1、为什么Service中要开一个工作线程?因为Service作为四大组件之一,它是运行在主线程的,所以不能执行耗时操作,一旦进程间交互是耗时操作,那么Service所在进程就会阻塞,而Client端进程则不会阻塞。
2、该Service中创建了一个Messenger对象,并在onBind中返回了IBinder对象,这里是进程间通信的关键,在后面会详细分析。
3、该Service的子线程中创建了一个Handler,并关联给Messenger,用于进程间通信的消息处理。Handler消息处理跟我们平时用的一样,但有一点提一下,子线程是没有默认Looper的,因此需要自己创建并启动,否则子线程的Handler无法收到Message。
4、Server端收到消息后,Toast一下“hello server”并显示Cient传过来的两个整数值。如果Client端也将自己的Messenger传过来了,则向Client端回复消息,将两个整数之和返回。

另外该Service在AndroidManifest.xml中的注册如下:


<service
    android:name=".messenger.RemoteService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
    <intent-filter>
        <action android:name="com.aspook.remote.ACTION_BIND" />
        <cateGory android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

核心一句为android:process=":remote",将该Service置于另一个进程之中,从而可以在同一个App中模拟进程间通信。

创建一个Activity模拟Client进程

该Activity默认就是该App所在进程了,具体实现如下:



public class MessengerActivity extends AppCompatActivity {

    private Button btn_start;
    private Button btn_bind;
    private Button btn_send;
    private boolean mBound = false;
    private Messenger mRemoteMessenger = null;

    private ServiceConnection mRemoteConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteMessenger = new Messenger(service);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteMessenger = null;
            mBound = false;
        }
    };

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

        findViews();
        setListeners();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mRemoteConnection);
    }

    public void findViews() {
        btn_start = (Button) findViewById(R.id.btn_start);
        btn_bind = (Button) findViewById(R.id.btn_bind);
        btn_send = (Button) findViewById(R.id.btn_send);
    }

    public void setListeners() {
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // start Remote Service first
                Intent intent = new Intent(MessengerActivity.this, RemoteService.class);
                startService(intent);
                btn_start.setEnabled(false);
            }
        });

        btn_bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // bind the Remote Service, if the Remote service run in another App, you should run the App and start the service first
                try {
                    bindRemoteService();
                    btn_bind.setEnabled(false);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        btn_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mBound) {
                    Handler mClientHandler = new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            switch (msg.what) {
                                case MessageConstant.SERVER_TO_CLIENT:
                                    Toast.makeText(MessengerActivity.this, "Hello Client:" + msg.arg2, Toast.LENGTH_SHORT).show();

                                    break;
                                default:
                                    break;
                            }
                        }
                    };

                    try {
                        Message msg = Message.obtain(null, MessageConstant.CLIENT_TO_SERVER, 66, 88);
                        // Messenger of client sended to server is used for sending message to client
                        msg.replyTo = new Messenger(mClientHandler);
                        mRemoteMessenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(MessengerActivity.this, "Service not bind", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    
    public void bindRemoteService() {
        // Method one
        Intent intent = new Intent("com.aspook.remote.ACTION_BIND");// 5.0+ need explicit intent
        intent.setPackage("com.aspook.androidnotes"); // the package name of Remote Service
        bindService(intent, mRemoteConnection, BIND_AUTO_CREATE);
    }
}

代码逻辑也很简单,界面有3个按钮,操作如下:

1、先启动Server端的Service,暂且叫做启动远程Service
2、绑定远程Service
3、Client向Servcie端发送消息,并接收返回的消息

需要注意的有如下几点:

1、绑定远程Service后,Client端才拿到了Server端的Messenger引用。
2、Client端的Messenger需要关联自己的Handler,用来处理从Server端收到的消息。这里也需要注意,理论上如果Server端与Client端交互也是耗时的话,也需要开子线程,这个例子中由于只是显示下消息,直接放在UI线程了。
3、如果需要双向通信,Client端需要通过Message的replyTo参数将自己的Messenger发到Server端。
4、Android 5.0+要求绑定Service时必须使用显式Intent,可以通过设置包名的方式来解决,注意我是在同一个App中开的两个进程,因此包名相同,但如果远程Service位于另一个App,则应该填写其所在App的包名。
5、Client端收到回复消息后,Toast“Hello client”及两个整数之和。

示例效果演示

以上示例的进程间通信效果演示如下:

Messenger进程间通信原理分析

关于Service的启动、绑定不必多说,先从Client端通过绑定远程Service获取Server端的Messenger入手,代码如下:


private ServiceConnection mRemoteConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mRemoteMessenger = new Messenger(service);
        mBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mRemoteMessenger = null;
        mBound = false;
    }
};

接着来看mRemoteMessenger = new Messenger(service);的源码实现:



public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

注意到该构造方法的参数IBinder,就是远程Service中onBind返回的,具体代码如下:


@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mMessenger.getBinder();
}

再来看这一句代码:


mTarget = IMessenger.Stub.asInterface(target);

mTarget是IMessenger对象,看起来越来越像AIDL的写法了,其实不能说像,本来就是AIDL。于是猜想源码必定中有一个名为IMessenger.aidl的文件,它应该定义了发送消息的相关接口。果然在源码目录 “/frameworks/base/core/java/android/os/”下找到了IMessenger.aidl文件,其内容如下:


package android.os;
import android.os.Message;


oneway interface IMessenger {
    void send(in Message msg);
}

因此可知Messenger只是帮我们省去了写AIDL的工作而已,底层还是AIDL。

再来看Messenger是如何发送消息的,即Messenger的send方法:



public void send(Message message) throws RemoteException {
    mTarget.send(message);
}

通过注释可知,Messenger会将消息发送到其关联的Handler,且Handler不存在时会报异常,这就是我们无论是创建客户端还是服务端Messenger时都为其创建了一个Handler的原因。

另外上述示例中为了简便,只是在进程间传递了基本类型的值,其实类似单进程的消息机制,也可以传递Bundle数据,但注意需要序列化,具体说明可参考Message源码的基本字段支撑。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: Android Messenger实现进程间通信及其原理

本文链接: https://www.lsjlt.com/news/126505.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android Messenger实现进程间通信及其原理
    前言 之前分析Android消息机制的源码时,曾遇到过replyTo、IMessenger等属性字段,当时只是说这些字段用于进程间通信,并未作深入分析。今天这篇文字就来演示一下使用M...
    99+
    2024-04-02
  • Android Messenger实现进程间双向通信
    简介 Messenger是安卓进程间通信 (IPC) 最为简单的方式,可以实现进程间双向通信。详见官网介绍 代码实现 服务端应用实现 MessengerService接收客户端发送的...
    99+
    2024-04-02
  • Android中怎么实现进程间通信
    这篇文章给大家介绍Android中怎么实现进程间通信,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。由于android系统中应用程序之间不能共享内存。因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些。进程间通...
    99+
    2023-06-04
  • Electron进程间通信的实现
    目录主进程与渲染进程之间通信ipc模块 + window.webContentsremote模块渲染进程之间通信使用Electron开发出来的桌面应用都是多进程的,其中包含了一个主进...
    99+
    2024-04-02
  • Android进程间使用Intent进行通信
    安卓使用Intent来封装程序的“调用意图”,使用Intent可以让程序看起来更规范,更易于维护。 除此之外,使用Intent还有一个好处:有些时候我们只是想...
    99+
    2023-02-28
    Android Intent通信 Android进程通信
  • 怎么在Android中使用AIDL实现进程间通信
    怎么在Android中使用AIDL实现进程间通信?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、概述AIDL 意思即 Android Interface Definiti...
    99+
    2023-05-30
    android aidl
  • golang进程间通信怎么实现
    在Go语言中,有多种方式可以实现进程间通信。以下是一些常见的方法: 使用管道(Pipe):管道是进程间通信的一种简单而有效的方式...
    99+
    2023-10-25
    golang
  • Electron进程间通信如何实现
    今天小编给大家分享一下Electron进程间通信如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。使用Electron开...
    99+
    2023-06-30
  • Linux进程间通信怎么实现
    这篇文章主要讲解了“Linux进程间通信怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux进程间通信怎么实现”吧!共享内存共享内存可以说是最有用的进程间通信方式,也是最快的IP...
    99+
    2023-07-05
  • Android 通过Messager与Service实现进程间双向通信案例详解
    目录Messenger使用步骤Service代码客户端代码分析结果注意事项Android中的Service和其调用者既可以在同一个App中,也可以在不同的App。如果Service在...
    99+
    2024-04-02
  • Android 图文详解Binder进程通信底层原理
    目录🔥 什么是进程间通信🔥 什么是 Binder🔥 Android 中 IPC 的方式🔥 Binder 优势€...
    99+
    2024-04-02
  • Android进程间如何使用Intent进行通信
    这篇文章主要介绍“Android进程间如何使用Intent进行通信”,在日常操作中,相信很多人在Android进程间如何使用Intent进行通信问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android进程...
    99+
    2023-07-05
  • Python通过队列实现进程间通信详情
    目录一、前言二、队列简介三、多进程队列的使用四、使用队列在进程间通信一、前言 在多进程中,每个进程之间是什么关系呢?其实每个进程都有自己的地址空间、内存、数据栈以及其他记录其运行状态...
    99+
    2024-04-02
  • Python如何通过队列实现进程间通信
    本篇内容主要讲解“Python如何通过队列实现进程间通信”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python如何通过队列实现进程间通信”吧!一、前言在多进程中,每个进程之间是什么关系呢?其...
    99+
    2023-07-02
  • Node中的进程间通信怎么实现
    这篇“Node中的进程间通信怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Node...
    99+
    2024-04-02
  • Android图片三级缓存的原理及其实现
    为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流...
    99+
    2023-05-30
    android 图片 三级缓存
  • Android进程间通信的方式有哪些
    Android进程间通信的方式有以下几种:1. Intent:通过Intent对象进行进程间的通信。可以使用隐式Intent在不同的...
    99+
    2023-10-08
    Android
  • vue与electron实现进程间的通信详情
    目录一、配置内容1.进程间的通信第一种方式引入ipcRenderer第二种方式引入ipcRenderer2.渲染进程常用配置3.将ipcMain封装到一个js中统一处理三、总结前言:...
    99+
    2024-04-02
  • Python multiprocessing进程间通信方式如何实现
    这篇文章主要介绍“Python multiprocessing进程间通信方式如何实现”,在日常操作中,相信很多人在Python multiprocessing进程间通信方式如何实现问题上存在疑惑,小编查阅了各式资料,整理...
    99+
    2023-07-05
  • Android AIDL实现跨进程通信的示例代码
    AIDL是Android接口定义语言,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。实现步骤例:用 A程序去访问 B程序的MyService.java服务 在B...
    99+
    2023-05-30
    android 跨进程通信 aidl
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作