重读《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 命名方式的格式及区别
查看进程信息: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())
本章接下来的内容以思维导图的方式进行了汇总。
接下来是不同进程间通信方式的 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" />