IPC 机制 -- 重读《Android 开发艺术探索》

623 阅读4分钟

重读《Android 开发艺术探索》,本篇是书中的第二章内容的总结。

一、 IPC 简介

IPC 是 Inter-Process Communication 的缩写,含义是进程间通信或者夸进程通信,是两个进程之间进行数据互换的过程。

进程通常指应用,一个进程可以包括多个线程。

Android 中的多进程场景:一个应用采用多进程模式实现;两个应用之间的数据共享。

二、开启多进程模式

通过给四大组件在 AndroidManifest.xml 中指定 android:process 属性,就可以开启多进程模式。 未指定 process 属性,则运行在默认进程中,默认进程名为包名。 代码如下:

<activity
        android:name=".ThirdActivity"
        // 设置属性值为com.hyh.okhttpdemo.remote进程名为com.hyh.okhttpdemo.remote该进程为全局进程其他应用可以通过 ShareUID 方式和它跑在同一个进程中
        android:process="com.hyh.okhttpdemo.remote" 
        android:exported="false" >
</activity>
<activity
        android:name=".ChildActivity"
        // 设置属性值为:remote进程名为com.hyh.okhttpdemo:remote该进程属于当前应用的私有进程其他应用的组建不可以和它跑在同一个进程中
        android:process=":remote"
        android:exported="false" />
<activity
        android:name=".MainActivity"
        android:configChanges="orientation|screenSize"
        android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

process 命名方式的格式及区别

image.png

查看进程信息:adb shell ps | grep 实际包名

获取当前进程名

private fun Context.currentProcessName(pid: Int): String {
    val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
    activityManager.runningAppProcesses?.forEach { info ->
        if (info.pid == pid) {
            return info.processName
        }
    }
    return ""
}

// 调用
val processName = currentProcessName(Process.myPid())

本章接下来的内容以思维导图的方式进行了汇总。

image.png

接下来是不同进程间通信方式的 Demo,已提交至 GitHub

一个简单的使用 Messenager 进行进程间通信的例子

服务端

class MessengerService : Service() {

    companion object {
        const val TAG = "MessengerService"
    }

    class MessengerHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_FROM_CLIENT -> {
                    // 接收到客户端消息
                    Log.d(TAG, "receive msg from Client: ${msg.data.getString("msg")}")
                    // 服务器回复客户端消息
                    val client = msg.replyTo
                    val replayMsg = Message.obtain(null, MSG_FROM_SERVICE)
                    val bundle = Bundle()
                    bundle.putString("reply", "already receive, replay soon.")
                    replayMsg.data = bundle
                    try {
                        client.send(replayMsg)
                    } catch (e: RemoteException) {
                        e.printStackTrace()
                    }
                }
                else -> {
                    super.handleMessage(msg)
                }
            }
        }
    }

    private val mMessenger = Messenger(MessengerHandler(Looper.myLooper()!!))

    override fun onBind(intent: Intent): IBinder {
        return mMessenger.binder
    }
}

客户端

class MessengerActivity : AppCompatActivity() {

    companion object {
        const val TAG = "MessengerActivity"
    }

    // 接收消息
    private val mReceiveMessenger = Messenger(MessengerHandler(Looper.myLooper()!!))

    class MessengerHandler(looper: Looper) : Handler(looper) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                Constants.MSG_FROM_SERVICE -> {
                    // 接收服务端消息
                    Log.d(TAG, "receive msg from service: ${msg.data.getString("reply")}")
                }
                else -> {
                    super.handleMessage(msg)
                }
            }
        }
    }

    // 发送消息
    private lateinit var mService: Messenger

    private val mConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mService = Messenger(service)
            val msg = Message.obtain(null, Constants.MSG_FROM_CLIENT)
            val data = Bundle()
            data.putString("msg", "hello, this is client.")
            msg.data = data
            // 将接收服务器消息的对象传递给服务端
            msg.replyTo = mReceiveMessenger
            try {
                mService.send(msg)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }

        override fun onServiceDisconnected(name: ComponentName?) {
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_messenger)
        // 绑定服务端 service
        val intent = Intent(this, MessengerService::class.java)
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
    }

    override fun onDestroy() {
        unbindService(mConnection)
        super.onDestroy()
    }
}

声明

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

<activity
    android:name=".messenger.MessengerActivity"
    android:configChanges="orientation|screenSize">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

一个简单的使用 AIDL 通信的例子

Book.aidl 文件

package com.hyh.ipc.aidl;

parcelable Book;

IBookManager.aidl 文件

package com.hyh.ipc.aidl;

import com.hyh.ipc.aidl.Book;
import com.hyh.ipc.aidl.IOnNewBookArriveListener;

interface IBookManager {

    List<Book> getBookList();

    void addBook(in Book book);

    void registerListener(IOnNewBookArriveListener listener);

    void unregisterListener(IOnNewBookArriveListener listener);
}

IOnNewBookArriveListener.aidl 文件

package com.hyh.ipc.aidl;
import com.hyh.ipc.aidl.Book;

interface IOnNewBookArriveListener {
    void onNewBookArrived(in Book newBook);
}

Book.kt 文件

package com.hyh.ipc.aidl

import android.os.Parcel
import android.os.Parcelable

class Book() : Parcelable {

    var bookId: Int = 0
    var bookName: String = ""

    constructor(id: Int, name: String) : this() {
        this.bookId = id
        this.bookName = name
    }

    constructor(parcel: Parcel) : this() {
        bookId = parcel.readInt()
        bookName = parcel.readString() ?: ""
    }

    override fun describeContents(): Int {
        return 0
    }

    override fun writeToParcel(dest: Parcel?, flags: Int) {
        dest?.apply {
            writeInt(bookId)
            writeString(bookName)
        }

    }

    companion object CREATOR : Parcelable.Creator<Book> {
        override fun createFromParcel(parcel: Parcel): Book {
            return Book(parcel)
        }

        override fun newArray(size: Int): Array<Book?> {
            return arrayOfNulls(size)
        }
    }

    override fun toString(): String {
        return "[bookId: $bookId, bookName: $bookName]"
    }
}

客户端

package com.hyh.ipc.aidl

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.*
import androidx.appcompat.app.AppCompatActivity
import android.util.Log
import com.hyh.ipc.R

class BookManagerActivity : AppCompatActivity() {

    companion object {
        const val TAG = "BookManagerActivity"
        const val MESSAGE_NEW_BOOK_ARRIVED = 1
    }

    private var mRemoteBookManager: IBookManager? = null

    private val mHandler = object : Handler(Looper.myLooper()!!) {
        override fun handleMessage(msg: Message) {
            when(msg.what) {
                MESSAGE_NEW_BOOK_ARRIVED -> {
                    Log.d(TAG, "receive new book : ${msg.obj}")
                }
                else -> {
                    super.handleMessage(msg)
                }
            }
        }
    }


    private val mConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val bookManager = IBookManager.Stub.asInterface(service)
            try {
                // 赋值
                mRemoteBookManager = bookManager
                val list = bookManager.bookList
                Log.d(TAG, "list type : ${list.javaClass.canonicalName}")
                Log.d(TAG, "query book list: $list")
                bookManager.addBook(Book(3, "C"))
                val newList = bookManager.bookList
                Log.d(TAG, "query book newList: $newList")
                // 注册事件
                bookManager.registerListener(mIOnNewBookArriveListener)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mRemoteBookManager = null
        }
    }

    private val mIOnNewBookArriveListener = object : IOnNewBookArriveListener.Stub() {
        override fun onNewBookArrived(newBook: Book?) {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget()
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_book_manager)
        val intent = Intent(this, BookManagerService::class.java)
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
    }

    override fun onDestroy() {
        Log.d(TAG, "BookManagerActivity onDestroy()")
        mRemoteBookManager?.takeIf {
            it.asBinder().isBinderAlive
        }?.let {
            try {
                Log.d(TAG, "unregister listener $mIOnNewBookArriveListener")
                // 解绑事件
                it.unregisterListener(mIOnNewBookArriveListener)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
        unbindService(mConnection)
        super.onDestroy()
    }
}

服务端

package com.hyh.ipc.aidl

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.RemoteCallbackList
import android.os.RemoteException
import android.util.Log
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean

class BookManagerService : Service() {

    companion object {
        const val TAG = "BMS"
    }

    private var mIsServiceDestroyed = AtomicBoolean(false)

    val mBookList = CopyOnWriteArrayList<Book>()

    private val mListenerList = RemoteCallbackList<IOnNewBookArriveListener>()

    private val mBinder = object : IBookManager.Stub() {
        override fun getBookList(): MutableList<Book> {
            return mBookList
        }

        override fun addBook(book: Book?) {
            mBookList.add(book)
        }

        override fun registerListener(listener: IOnNewBookArriveListener?) {
            mListenerList.register(listener)
            val n = mListenerList.beginBroadcast()
            Log.d(TAG, "registerListener  $n")
            mListenerList.finishBroadcast()
        }

        override fun unregisterListener(listener: IOnNewBookArriveListener?) {
            val success = mListenerList.unregister(listener)
            if (success) {
                Log.d(TAG, "unregister success.")
            } else {
                Log.d(TAG, "not found, can not unregister.")
            }
            // beginBroadcast() 和 finishBroadcast() 需要配对使用
            val N = mListenerList.beginBroadcast()
            mListenerList.finishBroadcast()
            Log.d(TAG, "unregisterListener, current size:$N")
        }
    }

    override fun onCreate() {
        super.onCreate()
        mBookList.add(Book(1, "A"))
        mBookList.add(Book(1, "B"))
        Thread(ServiceWorker()).start()
    }

    override fun onDestroy() {
        super.onDestroy()
        mIsServiceDestroyed.set(true)
    }


    override fun onBind(intent: Intent?): IBinder? {
        return mBinder
    }

    private fun onNewBookArrived(book: Book) {
        mBookList.add(book)
        val N = mListenerList.beginBroadcast()
        for (i in 0 until N) {
            val l = mListenerList.getBroadcastItem(i)
            l?.let {
                l.onNewBookArrived(book)
            }
        }
        mListenerList.finishBroadcast()
    }

    inner class ServiceWorker : Runnable {

        override fun run() {
            while (!mIsServiceDestroyed.get()) {
                try {
                    Thread.sleep(5000)
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
                val bookId = mBookList.size + 1
                val newBook = Book(bookId, "new book#$bookId")
                try {
                    onNewBookArrived(newBook)
                } catch (e: RemoteException) {
                    e.printStackTrace()
                }
            }
        }

    }
}

组建声明部分

<activity
    android:name=".aidl.BookManagerActivity"
    android:exported="false" />
<service
    android:name=".aidl.BookManagerService"
    android:process=":remote" />

一个简单的使用 ContentProvider 通信的例子

该例子中将数据存储到 SQLite 中,所以使用到了 DbOpenHelper

class DbOpenHelper @JvmOverloads constructor(
    val context: Context,
    name: String = DB_NAME,
    factory: SQLiteDatabase.CursorFactory? = null,
    version: Int = DB_VERSION,
    errorHandler: DatabaseErrorHandler? = null,
) : SQLiteOpenHelper(context, name, factory, version, errorHandler) {

    companion object {
        const val DB_NAME = "book_provider.db"
        const val BOOK_TABLE_NAME = "book"
        const val USER_TABLE_NAME = "user"
        const val DB_VERSION = 1

        const val CREATE_BOOK_TABLE =
            "CREATE TABLE IF NOT EXISTS $BOOK_TABLE_NAME (_id INTEGER PRIMARY KEY, name TEXT)"

        const val CREATE_USER_TABLE =
            "CREATE TABLE IF NOT EXISTS $USER_TABLE_NAME (_id INTEGER PRIMARY KEY, name TEXT)"
    }

    override fun onCreate(db: SQLiteDatabase?) {
        db?.execSQL(CREATE_BOOK_TABLE)
        db?.execSQL(CREATE_USER_TABLE)
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {

    }
}

Provider 部分

class BookProvider : ContentProvider() {

    companion object {
        const val TAG = "BookProvider"

        const val AUTHORITY = "com.hyh.book.provider"

        val BOOK_CONTENT_URI = Uri.parse("content://$AUTHORITY/book")
        val USER_CONTENT_URI = Uri.parse("content://$AUTHORITY/user")

        const val BOOK_URI_CODE = 0
        const val USER_URI_CODE = 1

        val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH)
    }

    init {
        // 关联 code 和 uri
        sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE)
        sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE)
    }


    private lateinit var mDb: SQLiteDatabase

    fun getTableName(uri: Uri): String {
        return when (sUriMatcher.match(uri)) {
            BOOK_URI_CODE -> {
                DbOpenHelper.BOOK_TABLE_NAME
            }
            USER_URI_CODE -> {
                DbOpenHelper.USER_TABLE_NAME
            }
            else -> {
                ""
            }
        }
    }

    override fun onCreate(): Boolean {
        Log.d(TAG, "onCreate current Thread ${Thread.currentThread().name}")
        context?.let {
            mDb = DbOpenHelper(it).writableDatabase
            return true
        }
        return false
    }

    override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?,
    ): Cursor? {
        Log.d(TAG, "query current Thread ${Thread.currentThread().name}")
        val table = getTableName(uri)
        table?.let {
            return mDb.query(table, projection, selection, selectionArgs, null, null, sortOrder, null)
        }
        return null
    }

    override fun getType(uri: Uri): String? {
        Log.d(TAG, "getType current Thread ${Thread.currentThread().name}")
        return null
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        Log.d(TAG, "insert current Thread ${Thread.currentThread().name}")
        val table =  getTableName(uri)
        mDb.insert(table, null, values)
        // 通知更新
        context?.contentResolver?.notifyChange(uri, null)
        return null
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
        Log.d(TAG, "delete current Thread ${Thread.currentThread().name}")
        getTableName(uri).let { 
            val count = mDb.delete(it, selection, selectionArgs)
            if (count > 0) {
                context?.contentResolver?.notifyChange(uri, null)
            }
            return count
        }
    }

    override fun update(
        uri: Uri,
        values: ContentValues?,
        selection: String?,
        selectionArgs: Array<out String>?,
    ): Int {
        Log.d(TAG, "update current Thread ${Thread.currentThread().name}")
        getTableName(uri).let { 
            val row = mDb.update(it, values, selection, selectionArgs)
            if (row > 0) {
                context?.contentResolver?.notifyChange(uri, null)
            }
            return row
        }
    }
}

类调用部分

class ProviderActivity : AppCompatActivity() {

    companion object {
        const val TAG = "ProviderActivity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_provider)

        // 值为 authorities 的值
        val bookUri = BookProvider.BOOK_CONTENT_URI
        val values = ContentValues()
        values.put("name", "AAA-BB")
        contentResolver.insert(bookUri, values)
        contentResolver.query(bookUri, arrayOf("_id", "name"), null, null, null)?.let { cursor ->
            while (cursor.moveToNext()) {
                val book = Book(cursor.getInt(0), cursor.getString(1))
                Log.d(TAG, "book = $book")
            }
            cursor.close()
        }
    }
}

组建声明部分

<activity
    android:name=".contentprovider.ProviderActivity"
    android:exported="false" />
<!-- authorities 唯一标识-->
<provider
    android:name=".contentprovider.BookProvider"
    android:authorities="com.hyh.book.provider"
    android:permission="com.hyh.PROVIDER"
    android:process=":provider" />

一个简单实用 Socket 进行通信的例子 客户端部分

界面需要:

  • 一个 TextView: 展示消息
  • 一个 EditText:输入想要发送的消息
  • 一个 Button:点击后发送消息
class TCPClientActivity : AppCompatActivity() {

    companion object {
        const val TAG = "TCPClientActivity"
        const val MSG_RECEIVE_NEW_MSG = 1
        const val MSG_SOCKET_CONNECTION = 2
    }

    private lateinit var mPrintWriter: PrintWriter
    private lateinit var mClientSocket: Socket

    private lateinit var mEtInput: EditText
    private lateinit var mTvShow: TextView
    private lateinit var mBtnSend: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_tcpclient)

        mEtInput = findViewById(R.id.msg)
        mTvShow = findViewById(R.id.msg_container)

        // 启动服务
        val intent = Intent(this, TCPServerService::class.java)
        startService(intent)
        
        GlobalScope.launch(Dispatchers.IO) {
            // 延时一会,否则会出现首次链接失败问题
            Thread.sleep(1000)
            connectTCPServer()
        }

        mBtnSend = findViewById(R.id.send)

        mBtnSend.setOnClickListener {
            GlobalScope.launch(Dispatchers.IO) {
                val msg = mEtInput.text.toString().trim()
                mPrintWriter.println(msg)
                withContext(Dispatchers.Main) {
                    mTvShow.text = "${mTvShow.text}\n client: $msg"
                }
                mEtInput.setText("")
            }
        }

    }

    private suspend fun connectTCPServer() {
        var socket: Socket? = null
        while (null == socket) {
            try {
                socket = Socket("127.0.0.1", 8688)
                // 该写法也可以
//                socket = Socket("localhost", 8688)
                mClientSocket = socket
                mPrintWriter = PrintWriter(BufferedWriter(OutputStreamWriter(mClientSocket.getOutputStream())),
                        true)
                withContext(Dispatchers.Main) {
                    mBtnSend.isEnabled = true
                }
                Log.d(TAG, "connected")
            } catch (e: Exception) {
                Thread.sleep(1000)
                e.printStackTrace()
            }
        }

        try {
            val br = BufferedReader(InputStreamReader(socket.getInputStream()))
            while (!isFinishing) {
                val msg = br.readLine()
                Log.d(TAG, "receive $msg")
                withContext(Dispatchers.Main) {
                    mTvShow.text = "${mTvShow.text}\nserver: $msg"
                }
            }
            Log.d(TAG, "quit...")
            mPrintWriter.close()
            br.close()
            socket.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun onDestroy() {
        if (!mClientSocket.isClosed) {
            mClientSocket.shutdownInput()
            mClientSocket.close()
        }
        super.onDestroy()
    }
}

服务端部分

class TCPServerService : Service() {

    companion object {
        const val TAG = "TCPServerService"
    }

    private var mIsServiceDestroy = false

    private val mDefinedMessage =
        arrayListOf("hello", "What's your name?", "hi", "nice to meet you")

    override fun onCreate() {
        Thread(TcpServer()).start()
        super.onCreate()
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onDestroy() {
        mIsServiceDestroy = true
        super.onDestroy()
    }

    inner class TcpServer : Runnable {
        override fun run() {
            var serverSocket: ServerSocket? = null
            try {
                // 监听 8688 端口
                serverSocket = ServerSocket(8688)
                Log.d(TAG, "power")
            } catch (e: IOException) {
                Log.d(TAG, "establish tcp server failed, port: 8688")
                e.printStackTrace()
                return
            }

            while (!mIsServiceDestroy) {
                try {
                    // 接收客户端请求
                    val client = serverSocket.accept()
                    Log.d(TAG, "accept")
                    Thread {
                        responseClient(client)
                    }.start()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }
    }

    private fun responseClient(client: Socket) {
        // 接收客户端信息
        val ir = BufferedReader(InputStreamReader(client.getInputStream()))
        // 给客户端发消息
        val out = PrintWriter(BufferedWriter(OutputStreamWriter(client.getOutputStream())), true)
        out.println("welcome~")
        while (!mIsServiceDestroy) {
            val str = ir.readLine()
            Log.d(TAG, "msg from client $str")
            if (null == str) {
                // 断开客户端连接
                break
            }
            val i = Random.nextInt(0, mDefinedMessage.size - 1)
            val msg = mDefinedMessage[i]
            out.println(msg)
            Log.d(TAG, "msg send to client $msg")
        }
        Log.d(TAG, "client quit")
        out.close()
        ir.close()
        client.close()
    }
}

组建声明

<activity
    android:name=".socket.TCPClientActivity"
    android:exported="false" />
<service
    android:name=".socket.TCPServerService"
    android:process=":socket" />