Android中的进程间通讯

一、简介

进程间通讯(InterProcess Communication)

指在不同进程之间传播或交换信息,Android是基于Linux 系统的,在Linux 中进程间是不能直接通讯的,IPC就是为了解决这一问题

每个操作系统都有相应的IPC机制,Android中一个进程会对应一个虚拟机实例,不同的虚拟机在内存分配上有不同的地址空间

只有在多进程的环境下才会使用IPC进行通讯

二、使用场景

  • 多模块应用:由于Android应用受到系统的最大内存限制,为了获得更多的内存空间将不同的模块放在不同的线程中,此时需要用到跨进程通信
  • 其它应用:获取其它应用里的数据,比如:获取通讯录和短信

三、实现多进程

Android默认是运行在包名的进程中,可以通过修改AndroidManifest文件,在 application 标签下添加 android:process 属性可以修改Android默认的进程名字

给四大组件(Activity Service Broadcast ContentProvider)设置android:process属性来实现多进程

四、造成的问题

  • 多进程会造成Application的多次创建:当一个组件需要运行在新的进程中时,实际的创建过程就相当于又重新启动了一次应用,就会创建Application。而运行在不同进程中的组件,不仅属于不同的虚拟机,而且其Application也是不同的。
  • 多进程会导致静态成员和单例完全无效:由于不同进程都被分配了独立且不同的虚拟机,其在内存分配上有这不同的地址空间。这就会导致在着一个类的多个副本,各自修改互不影响。
  • 多进程模式下,线程的同步机制也会失效:因为不同的进程,其线程不所属同一内存,那么无论是对象锁还是类锁,锁本身都不是同一个了。
  • 多进程模式下SharedPreferences风险会增大:SharedPreferences底层是通过文件读写实现的,并发操作可能会造成问题。

在不同进程中的组件,如果使用内存来通讯,都会存在隐患或者直接失败。这就是多进程带来的最主要的影响。

五、Android中的跨进程通讯(IPC)

利用Bundle

        在Android开发中,

Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle实现了 Parcelable 接口,以键值对的方式保存数据,可以在不同的进程间进行传输。 可以将其视为一个容器,其支持基本数据类型(String、int、boolean、byte、float、long、double)以及它们对应的数据。当需要传递对象或对象数组时,被传递的对象必须实现Serialiable或Parcelable接口。

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("msg", "message");
intent.putExtras(bundle);
startActivity(intent);

使用文件共享

        通过共享文件,不同的进程可以使用读写文件的方式交换数据

        不要使用SharedPreferences去做跨进程通讯,原则上它不支持多进程。虽然它本质上也是一个文件,但是由于它在应用运行时会在内存中加载缓存,然而进程间是不能内存共享的,每个进程操作的SharedPreferences都会是一个单独的实例,这就会导致安全性问题,这个问题只能通过多进程间其它的通信方式或者是在确保不会同时操作SharedPreferences数据的前提下使用SharedPreferences来解决。

使用ContentProvider

        ContentProvider提供一种应用管理其自身和其他应用所存储数据的能力,并提供与其他应用共享这些数据的方法。它会封装数据,并提供用于定义数据安全性的机制。无论你是否需要和其他应用分享数据,你都可以使用ContentProvider去访问这些数据,虽然当无需和其他应用共享数据时没必要使用它。系统预置了许多ContentProvider,比如通话记录,通讯录,信息等,只需要通过ContentProvider就可以拿到这些信息。ContentProvider以一个或多个表的形式将数据呈现给外部应用,这些表与关系型数据库中的表类似。行表示提供程序收集的某种类型数据的实例,行中的每一列表示为一个实例所收集的单个数据。

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

使用场景:

  • 通过实现代码访问其他应用中的现有的ContentProvider用来获得数据;
  • 创建新的ContentProvider,与其他应用共享数据。

使用Messenger

       Messenger是一种轻量级的 IPC 方案,可以在不同进程中传递 Message 对象,它一次只处理一个请求。Messenger通过Handler将Message发送到另一个进程,实现了进程间通信,底层依然是使用了Binder机制,其本质上也是基于AIDL实现的。
        Messenger的缺点:

  • 服务端是以串行的方式在处理消息,不太适合用来出来大量的并发请求。
  • 主要作用是传递消息,不太适合用来跨进程调用方法。

使用方式:构建一个运行在独立进程中的服务端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"/>

客户端:

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()
    }
}

AIDL

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

支持数据类型:

  • 八种基本数据类型:byte、char、short、int、long、float、double、boolean
  • String,CharSequence
  • 实现了Parcelable接口的数据类型
  • List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
  • Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象

aidl跨进程通信的步骤:

  1. 创建aidl文件
  2. build后生成aidl对应的java文件
  3. 创建服务端Service
  4. 创建客户端Activity,bindService(),获取IBinder的proxy 

Socket

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值