Android 进程间通信各种方法

286 阅读5分钟

什么是进程

在Android中进程是资源拥有的基本单位。每一个进程间的数据是不互通,正常情况下是无法直接调用的不同进程保存的资源的,因此为了能可够能调用其他进程的资源而产生了ipc(进程间通信)。

进程间通信

  • Android常见的进程通信有:bundle文件共享MessengeraidlcontentProviderSocket;

Bundle

Android四大组件中有三个都可以通过Intent启动的,Intent中的Bundle可以传递一些基本的数据类型以及实现了序列化的对象, 但是需注意在Intent中传递的数据总大小不能大于1MB。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val test = intent.getFloatExtra(TEST, 0f)
    }

    companion object {
        const val TEST = "test"
        fun newInstance(activity: BaseActivity, bundle: Bundle) {
            var intent = Intent(activity, SecondActivity::class.java)
            bundle.putFloat(TEST, 0.1f)
            intent.putExtras(bundle)
            activity.startActivity(intent)
        }
    }

文件共享

android中两个进程可以通过对同一个文件进行读写来进行数据交换,例如:A进程中将数据写入约定好的文件中,B进程从这约定好的文件中读取数据。 注意:多个进程对同一个文件执行并发操作时,会产生数据错乱问题,因此在使用文件共享时避免并发操作。

//A进程执行写入数据方法
 fun writeToData() {
        Thread(Runnable {
            var user = User(1, "hello world", false) //实现了序列化的类
            var dir = File("/test.txt")
            if (!dir.exists()) {
                dir.mkdir()
            }

            var outputStream: ObjectOutputStream? = null

            try {
                outputStream = ObjectOutputStream(FileOutputStream(dir))
                outputStream.writeObject(outputStream)
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                outputStream?.close()
            }

        }).start()
    }
    //B进程执行读取数据的方法
    fun readToData() {
        Thread(Runnable {
            var user : User //实现了序列化的类
            var dir = File("/test.txt")
            if (!dir.exists()) {
                dir.mkdir()
            }

            var inputStream: ObjectInputStream? = null

            try {
                inputStream = ObjectInputStream(FileInputStream(dir))
                user=inputStream.readObject() as User
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                inputStream?.close()
            }

        }).start()
    }

Messenger

Messenger 通过它可以在不同的进程中传递Message对象,在Message中传入我们需要传递的数据, 就可以轻松的实现进程间数据的传递。Messenger只能进行串行的数据处理,当多个请求同时来时会一个一个处理,并不会并行的处理。

    //服务端
    class MessengerService : Service() {

        val MSG=0
        val MSG_TWO=0

        private val mHandler=Handler(Handler.Callback {
            when(it.what){
                MSG->{}
                MSG_TWO->{}
                else->{}
            }
            true
        })

        private val messenger=Messenger(mHandler)
    
        override fun onBind(intent: Intent?): IBinder? {
            return messenger.binder
        }
    }
    
    //客户端
    private lateinit var serviceMessenger: Messenger

    private val mConnection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            serviceMessenger = Messenger(service)
            val message = Message.obtain(null,MSG)
            val bundle = Bundle()
            bundle.putFloat("float", 1527f)
            message.data = bundle
            serviceMessenger.send(message)
        }

    }

    private fun bindService(){
        val intent=Intent(this,MessengerService::class.java)
        bindService(intent,mConnection,Context.BIND_AUTO_CREATE)
    }

若想接收服务端返回的消息,则客户端在发送消息前将Message的replyTo参数赋值,然后在服务端调用Message的replyTo恢复消息即可。

AIDL

AIDL:Android Interface Definition Language,即Android接口定义语言。其也是Messenger的底层实现。

  • 服务端

首先创建一个Service来监听客户端的连接请求,然后创建一个aidl文件,将暴露给客户端的接口在这个AIDL中声明,最后在Service中实现aidl.stub接口,通过onBind返回给客户端调用。

  • 注意:
    • 在aidl中使用自定义对象需要实现序列化;
    • 自定义对象需要创建一个同名的aidl文件进行声明;
    • 在aidl接口中调用自定义对象时需要手动import 类的全路径;
    • 引用其他aidl文件时也需要手动import 全路径
    • RemoteCallbackList可以提供远程删除功能,使用前必须先调用beginBroadcast,使用结束后也必须调用finshBroadcast
  • 客户端

首先和服务端一样创建相同路径下的aidl文件,然后在在绑定远程服务端时,通过aidl的stub.asInterface将服务端的IBinder转换为aidl接口对象,之后就可以通过此接口对象调用服务端方法。

ContentProvider

contentProvider是四大组件之一,用来不同应用间的数据共享,因此其也可以用来实现进程间的数据的增删改查。

在android系统中预置了许多ContentProvider,例如:通讯录,日程信息表等; 我们可以通过ContentResolver来对ContentProvider提供的数据进行增删改查。

Socket

Socket属于网络架构的传输层,在Socket进行客户端与服务端连接时,两端都会产生一个实例,操作这个实例就可以完成所需的通信。

  • 服务端
    private val mIsServiceDestoryed = false
    private fun run() {
        Thread(Runnable {
            var serverSocket: ServerSocket? = null
            try {
                serverSocket = ServerSocket(8800)
            } catch (e: IOException) {
                e.printStackTrace()
            }
            while (!mIsServiceDestoryed) {
                try {
                    val accept = serverSocket?.accept()
                    responseClient(accept)
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }).start()
    }

    private fun responseClient(client: Socket?){
        //接收客户端的消息
        var reader=BufferedReader(InputStreamReader(client?.getInputStream()))

        //用于向客户端发送消息
        var writer=PrintWriter(BufferedWriter(OutputStreamWriter(client?.getOutputStream())),true)

        while(!mIsServiceDestoryed){
            val readLine = reader.readLine() ?: break
            
            var sendMessageSever="服务端发送的消息"
            writer.println(sendMessageSever)
            
        }
        reader.close()
        writer.close()
        client?.close()
    }
  • 客户端
private var clientIsDestory = false
    private fun startConnetTcp() {
        Thread(Runnable {
            var socket: Socket? = null
            var printWriter: PrintWriter? = null
            while (socket == null) {
                socket = Socket("localhist", 8800)
                //用于对服务器发送消息
                printWriter =
                        PrintWriter(BufferedWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream()))), true)

                printWriter.println("客户端发送的消息")
            }

            //接收服务端发送来的消息
            var reader = BufferedReader(InputStreamReader(socket.getInputStream()))
            while (!clientIsDestory) {
                val readLine = reader.readLine()
                println("服务端发送来的消息:$readLine")
            }

            printWriter?.close()
            reader.close()
            socket.close()

        })
    }

以上方法的优缺点对比

名称优点缺点使用场景
Bundle简单易用只能传输Bundle支持的数据四大组件间的进程通信
文件共享简单易用不能高并发,且不能实时的通信并发低的情况,且数据实时性要求不高
messenger调用简单,支持一对多串行通信,及实时通信不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的类型低并发的一对多即时通信,无RPC需求
aidl功能多,支持一对多串行通信,支持实时通信使用较复杂,需注意线程同步问题一对多且有RPC需求
contentProvider数据访问方面功能强大,支持一对多并发数据共享,可通过call方法扩展其他操作主要提供数据源的CRUD提供其他应用数据查询情况
socket功能强大,可以通过网络传输字节流,支持一对多并发实时通信实现较为繁琐,不支持直接RPC进行网络间进程数据交换