Android跨進(jìn)程(跨APP)調(diào)度有多種方式,四大組件都是支持跨進(jìn)程角度。
IPC方式
Activity
我們可以通過(guò)startActivity打開(kāi)其他進(jìn)程的頁(yè)面,并且?guī)弦恍﹨?shù),數(shù)據(jù)大小限制在1MB(1020KB)
BroadcastReceiver
廣播不僅可以在APP內(nèi)部實(shí)現(xiàn)通信,也可以實(shí)現(xiàn)跨跨進(jìn)程調(diào)度,系統(tǒng)提供了很多基于廣播的接口,比如實(shí)現(xiàn)電話監(jiān)聽(tīng)、WIFI狀態(tài)等。
Service
可以通過(guò)Service和AIDL實(shí)現(xiàn)IPC,這種方式是APP內(nèi)部跨進(jìn)程最常用的方式。
ContentProvider
ContentProvider 也是很常見(jiàn)的IPC方式,比如系統(tǒng)提供的手機(jī)通訊錄就是基于ContentProvider實(shí)現(xiàn)的。ContentProvider是基于數(shù)據(jù)庫(kù)增刪改查的思路來(lái)提供服務(wù)的。
Socket
通過(guò)四大組件可以實(shí)現(xiàn)IPC,另辟途徑,我們還可以利用網(wǎng)絡(luò)的特性,使用Socket實(shí)現(xiàn)IPC。
數(shù)據(jù)傳輸方式
通常實(shí)現(xiàn)IPC主要的功能就是消息傳遞和數(shù)據(jù)傳輸,實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)姆绞揭彩怯卸喾N。
Bundle
Activity、Service、Receiver都支持Intent中傳遞Bundle數(shù)據(jù),由于Bundle實(shí)現(xiàn)了Parcelable接口,所以它可以方便的在不同的進(jìn)程間傳輸,可以傳遞基本類型、基本類型數(shù)組、序列化對(duì)象,但是大小是有限的,不能傳遞過(guò)大的文件,比如大圖片。
如果要傳遞Bitmap,記得要對(duì)圖片進(jìn)行壓縮,并且不要傳PNG圖片。
文件共享
也可以通過(guò)把數(shù)據(jù)寫(xiě)入文件中,比如SharedPreferences,然后通知其他進(jìn)程讀取數(shù)據(jù),這種方式可以實(shí)現(xiàn)任意圖片大小的傳遞,非常適合APP內(nèi)部的多進(jìn)程調(diào)度,如果是跨APP調(diào)度就要考慮到sdcard的存儲(chǔ)權(quán)限問(wèn)題了,所以這種方式的適用性不廣。
Messenger
messenger來(lái)實(shí)現(xiàn)跨app通信也就是在兩個(gè)不同的app中實(shí)現(xiàn)雙向通信。其實(shí)Messenger底層也是使用aidl的方式來(lái)實(shí)現(xiàn)的,只不過(guò)其使用handler來(lái)處理消息。
教程
class MessengerService : Service() {
class MessengerHandler : Handler() {
override fun handleMessage(msg: Message?) {
// 來(lái)自客戶端的消息
if (msg != null) {
Log.i("MessengerService", msg.data.getString("msg"))
}
}
}
private val messenger = Messenger(MessengerHandler())
override fun onBind(intent: Intent?): IBinder? {
return messenger.binder
}
}
在AndroidManifest.xml添加注冊(cè)
<application>
<service android:name=".MessengerService" android:process=":messenger"/>
</application>
在Activity實(shí)現(xiàn)綁定服務(wù)
class MainActivity : AppCompatActivity() {
private val conn = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val messenger = Messenger(service)
// 發(fā)送消息
val message = Message()
message.data = Bundle()
message.data.putString("msg", "Wiki")
messenger.send(message)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindService(Intent(this, MessengerService::class.java), conn, Context.BIND_AUTO_CREATE)
}
}
AIDL+匿名共享內(nèi)存(Ashmem)
如果我們需要通過(guò)IPC實(shí)現(xiàn)大文件傳遞,除了可以通過(guò)寫(xiě)入文件讀取文件的方式實(shí)現(xiàn),也可以同匿名內(nèi)存的方式,這種方式可以實(shí)現(xiàn)不同的App之間傳遞大文件內(nèi)容。
創(chuàng)建一個(gè)IMemoryAidlInterface.aidl文件,然后make Project 就會(huì)生成一個(gè)IMemoryAidlInterface.java文件
// IMemoryAidlInterface.aidl
package com.taoweiji.camerax.androidipcexample;
import android.os.ParcelFileDescriptor;
interface IMemoryAidlInterface {
ParcelFileDescriptor getParcelFileDescriptor();
}
創(chuàng)建服務(wù)
class MemoryFetchService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return MemoryFetchStub()
}
class MemoryFetchStub : IMemoryAidlInterface.Stub() {
override fun getParcelFileDescriptor(): ParcelFileDescriptor {
val data = "這里匿名共享內(nèi)存".toByteArray()
val memoryFile = MemoryFile("test", data.size)
memoryFile.outputStream.write(data)
val method = memoryFile.javaClass.getDeclaredMethod("getFileDescriptor")
val fileDescriptor = method.invoke(memoryFile) as FileDescriptor
return ParcelFileDescriptor.dup(fileDescriptor)
}
}
}
Activity 通過(guò)AIDL讀取MemoryFetchService寫(xiě)入匿名共享內(nèi)存的信息
class MemoryExampleActivity : AppCompatActivity() {
private val conn = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val memoryAidlInterface = IMemoryAidlInterface.Stub.asInterface(service)
val fileDescriptor = memoryAidlInterface!!.parcelFileDescriptor.fileDescriptor
val fis = FileInputStream(fileDescriptor)
Log.i("匿名共享內(nèi)存信息", String(fis.readBytes()))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindService(Intent(this, MemoryFetchService::class.java), conn, Context.BIND_AUTO_CREATE)
}
}
如果需要不不同的App之間使用AIDL,必須要復(fù)制生成后的.java文件到Activity所在的App,包名也要保持一致。