Android IPC 跨进程通讯的几种方式

Android中的多进程通信方式

AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现 RPC (远程过程调用)。
Messenger:支持一对多的串行实时通信, AIDL 的简化版本。
Bundle:四大组件的进程通信方式,只能传输 Bundle 支持的数据类型。
ContentProvider:强大的数据源访问支持,主要支持 CRUD 操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据。
BroadcastReceiver:即广播,但只能单向通信,接收者只能被动的接收消息。
文件共享:在非高并发情况下共享简单的数据。
Socket:通过网络传输数据。

IPC适合的场景及优缺点

  • Intent

Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle实现了 Parcelable 接口,可以在不同的进程间进行传输。
在一个进程中启动了另一个进程的 Activity,Service 和 Receiver ,可以在Bundle 中附加要传递的数据通过 Intent 发送出去。

Intent intent = new Intent(MainActivity.this, LoginActivity.class);
Bundle bundle = new Bundle();
bundle.putString("msg", "This is a message");
intent.putExtras(bundle);
startActivity(intent);
  • 文件共享

Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同)
SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences

读写共享文件:

public class FileShare {

    public static final String SHARE_FILE = "/sharefile";

    /**
     * 将对象写入文件
     */
    public void writeObject() {
        NoteBook noteBook = new NoteBook();
        noteBook.setName("cr");
        noteBook.setContent("Empty");

        File file = new File(Environment.getExternalStorageDirectory() + SHARE_FILE);
        ObjectOutputStream outputStream = null;
        try{
            outputStream = new ObjectOutputStream(new FileOutputStream(file));
            outputStream.writeObject(noteBook);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 从文件读取对象
     */
    public void readObject() {
        File file = new File(Environment.getExternalStorageDirectory() + SHARE_FILE);
        ObjectInputStream objectInputStream = null;

        try {
            objectInputStream = new ObjectInputStream(new FileInputStream(file));
            NoteBook noteBook = (NoteBook) objectInputStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                objectInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

class NoteBook implements Serializable {
    private String mName;
    private String mContent;

    public void setName(String name) {
        mName = name;
    }

    public void setContent(String content) {
        mContent = content;
    }
}
  • Messenger

Messenger是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。

构建一个运行在独立进程中的服务端Service,创建Messenger,将Messenger的binder返回给Service的onBind方法。然后接收客户端Messege需要用Handler接收。

public class MessengerService extends Service {

    /**
     * 处理客户端消息,并用于构建Messenger
     */
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 100:
                    System.out.println(msg.getData().getString("data"));

                    sendToClient(msg);
                    break;
            }
        }
    }

    /**
     * 给客户度发送消息
     */
    private static void sendToClient(Message message) {
        Messenger client = message.replyTo;  //通过 message拿到客户传递过来的 Messenger,通过该对象发送message
        //当然,回传消息还是要通过message
        Message msg = Message.obtain(null, 100);
        Bundle bundle = new Bundle();
        bundle.putString("data", "客户端, 我收到你的消息了");
        msg.setData(bundle);
        try {
            client.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    /**
     * 构建Messenger对象
     */
    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //将Messenger对象的Binder返回给客户端
        return mMessenger.getBinder();
    }
}

注册service,当然要设置在独立的进程

        <service android:name=".messenger.MessengerService"
            android:process=":messengerprocess"/>

然后客户端是通过绑定服务端返回的binder来创建Messenger对象,并通过这个Messenger对象来向服务端发送Message消息

class MainActivity : ComponentActivity() {

    private var mService: Messenger?= null
    private lateinit var btnMsg: Button
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnMsg = findViewById(R.id.btn_msg)

        //绑定service
        bindService(Intent(this, MessengerService::class.java),
            connection, Context.BIND_AUTO_CREATE)

        btnMsg.setOnClickListener {
            mService?.let {
                //创建消息,通过Bundle传递数据
                var message = Message.obtain(null, 100)
                message.data = Bundle().apply {
                    putString("data", "服务端,我给你发送消息了")
                }

                //将客户端的Messenger对象传递给服务端,才能让双方互相通信
                message.replyTo = mClientMessenger

                //像服务端进程发送消息
                it.send(message)
            }
        }
    }

    /**
     * 客户端Messenger对象
     */
    private val mClientMessenger: Messenger = Messenger(MessengerHandler())

    /**
     * 用于构建客户端的Messenger对象,并处理服务端的消息
     */
    private class MessengerHandler : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                100 -> {
                    println(msg.data.getString("data"))
                }
            }
        }
    }

    private val connection: ServiceConnection = object: ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, ibinder: IBinder?) {
            mService = Messenger(ibinder)
        }

        override fun onServiceDisconnected(name: ComponentName?) {

        }
    }

    override fun onDestroy() {
        //解绑service
        unbindService(connection)

        super.onDestroy()
    }
}

打印结果:


Messenger作为服务端、客户端两个进程通信的桥梁,底层是运用的Binder通信机制,媒介是Message,用messenger.send方法发送消息,上层通过handler接收消息。

总结:
  • 使用Messager来传递Message,Message中能使用的字段只有what、arg1、arg2、Bundle和replyTo,自定义的Parcelable对象无法通过object字段来传输
  • Message中的Bundle支持多种数据类型,replyTo字段用于传输Messager对象,以便进程间相互通信
  • Messager以串行的方式处理客户端发来的消息,不适合有大量并发的请求
  • Messager方法只能传递消息,不能跨进程调用方法

Messenger参考:
https://juejin.cn/post/6844903665795334158

  • AIDL

AIDL是Android中IPC(Inter-Process Communication)方式中的一种,AIDL是Android Interface definition language的缩写,AIDL的作用是让你可以在自己的APP里绑定一个其他APP的service,这样你的APP可以和其他APP交互。

AIDL底层也是通过Binder实现的。
Messenger 是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端只能一个一个处理,所以大量并发请求就不适合用 Messenger ,而且Messenger 只适合传递消息,不能跨进程调用服务端的方法。AIDL 可以解决并发和跨进程调用方法的问题,要知道 Messenger 本质上也是 AIDL ,只不过系统做了封装方便上层的调用而已。

详情见另一篇文章:AIDL简单例子

  • ContentProvider

ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。


  • Socket

Socket也称作“套接字“,是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。
分为流式套接字和数据包套接字,分别对应网络传输控制层的TCP和UDP协议。

参考:
https://cloud.tencent.com/developer/article/2158294
//www.greatytc.com/p/84a12977dc26

Github代码地址:

https://github.com/running-libo/CrossProcess

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容