Android 跨进程通信大总结
轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/111553746
本文出自【趙彥軍的博客】
文章目錄
- 1、Android進程
- 2、修改Android默認進程
- 3、指定activity、Service進程
- 4、activity進程間通信
- 5、AIDL 簡介
- 6、activity、service 進程間通信 AIDL
- 7、AIDL 傳遞復雜對象
- 8、SharedPreferences 跨進程
- 9、廣播跨進程
- 10、ContentProvider跨進程
- 什么是ContentProvider ?
- ContentProvider什么作用?
- ContentProvider原理
- ContentProvider優秀文章鏈接
- 11、Socket跨進程通信
- 12、文件共享跨進程
- 13、Bundle跨進程
- 14、MMKV
1、Android進程
android {compileSdkVersion 29buildToolsVersion "29.0.3"defaultConfig {applicationId "com.zhaoyanjun"minSdkVersion 16targetSdkVersion 29versionCode 1versionName "1.0"} }applicationId 為:com.zhaoyanjun,那項目運行起來,所有的 activity 、service 默認運行在 com.zhaoyanjun 進程.
工具類:
package com.zhaoyanjun;import java.io.FileInputStream; import java.io.IOException;/*** @author yanjun.zhao* @time 2020/12/16 11:01 AM* @desc*/ public class Process {/*** 獲取當前進程名* @return*/public static String getCurrentProcessName() {FileInputStream in = null;try {String fn = "/proc/self/cmdline";in = new FileInputStream(fn);byte[] buffer = new byte[256];int len = 0;int b;while ((b = in.read()) > 0 && len < buffer.length) {buffer[len++] = (byte) b;}if (len > 0) {String s = new String(buffer, 0, len, "UTF-8");return s;}} catch (Throwable e) {e.printStackTrace();} finally {if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}}return null;} }2、修改Android默認進程
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.zhaoyanjun"><applicationandroid:process=":appProcess"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.RecyclerViewDemo"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application> </manifest>在 application 標簽下添加 android:process 屬性可以修改Android默認的進程名字。進程的名字為:com.zhaoyanjun:appProcess , 這個修改是全局的,所有 activity 都默認運行在這個進程中。
3、指定activity、Service進程
activity 指定進程
<activityandroid:process=":remote"android:name=".MainActivity2"> </activity>
service 指定進程
4、activity進程間通信
MainActivity有一個 button 按鈕,點擊跳轉到 MainActivity2
//MainActivity代碼 findViewById<Button>(R.id.bt1).setOnClickListener {var intent = Intent(this, MainActivity2::class.java)intent.putExtra("key", "value1")startActivity(intent) }MainActivity2 指定進程
<activityandroid:process=":remote"android:name=".MainActivity2"> </activity>MainActivity2 邏輯
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main2)var value = intent.getStringExtra("key")Log.d("zhaoyanjun", "MainActivity2 進程: ${Process.getCurrentProcessName()} 獲取值: $value") }日志:
D/zhaoyanjun: MainActivity2 進程: com.zhaoyanjun:remote 獲取值: value15、AIDL 簡介
AIDL的語法十分簡單,與Java語言基本保持一致,需要記住的規則有以下幾點:
1、AIDL文件以 .aidl 為后綴名
2、 AIDL支持的數據類型分為如下幾種:
- 八種基本數據類型:byte、char、short、int、long、float、double、boolean
String,CharSequence - 實現了Parcelable接口的數據類型
- List 類型。List承載的數據必須是AIDL支持的類型,或者是其它聲明的AIDL對象
- Map類型。Map承載的數據必須是AIDL支持的類型,或者是其它聲明的AIDL對象
3、AIDL文件可以分為兩類。一類用來聲明實現了Parcelable接口的數據類型,以供其他AIDL文件使用那些非默認支持的數據類型。還有一類是用來定義接口方法,聲明要暴露哪些接口給客戶端調用,定向Tag就是用來標注這些方法的參數值。
3、定向Tag。定向Tag表示在跨進程通信中數據的流向,用于標注方法的參數值,分為 in、out、inout 三種。
- 其中 in 表示數據只能由客戶端流向服務端,
- out 表示數據只能由服務端流向客戶端,而 inout 則表示數據可在服務端與客戶端之間雙向流通。此外,如果AIDL方法接口的參數值類型是:基本數據類型、String、CharSequence或者其他AIDL文件定義的方法接口,那么這些參數值的定向 Tag 默認是且只能是 in,所以除了這些類型外,其他參數值都需要明確標注使用哪種定向Tag。定向Tag具體的使用差別后邊會有介紹
4、明確導包。在AIDL文件中需要明確標明引用到的數據類型所在的包名,即使兩個文件處在同個包名下
6、activity、service 進程間通信 AIDL
創建 IBookAidlInterface.aidl文件
創建 IBookAidlInterface 完成后,會在 main 目錄下,創建 aidl 文件夾,并且會創建同名的包名,如下:
我們把默認的 basicTypes 方法刪除,修改如下
下面創建 Service , 并指定進程名字:
<serviceandroid:name=".MyService"android:enabled="true"android:exported="true"android:process=":remote" />MyService 如下:
class MyService : Service() {private var binder: Binder = object : IBookAidlInterface.Stub() {override fun getTitle(): String {return "aidl->title"}override fun setTitle(title: String?) {Log.d("aidl", "service-setTitle:$title")}}override fun onBind(intent: Intent): IBinder {return binder}override fun onCreate() {super.onCreate()} }MainActivity 綁定Service
class MainActivity : AppCompatActivity() {private var iBookAidlInterface: IBookAidlInterface? = nullprivate var serviceConnection: ServiceConnection = object : ServiceConnection {override fun onServiceDisconnected(name: ComponentName?) {}override fun onServiceConnected(name: ComponentName?, service: IBinder?) {iBookAidlInterface = IBookAidlInterface.Stub.asInterface(service)var name = iBookAidlInterface?.titleLog.d("aidl", "getTitle: $name")iBookAidlInterface?.title = "我是一個title"}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)bindService()}/*** 綁定服務*/private fun bindService() {var intent = Intent(this, MyService::class.java)bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)}override fun onDestroy() {super.onDestroy()unbindService(serviceConnection)} }運行項目,日志如下:
可以看到,子進程 Service 和 主進程的 Activity 已經可以通信了。
github 地址:https://github.com/zyj1609wz/AndroidMultiProgress
7、AIDL 傳遞復雜對象
首先創建 Book 類,并且實現 Parcelable 接口。這里要注意,一定要實現 Parcelable 接口,因為只有實現 Parcelable 的復雜對象才能在 AIDL 中傳遞。
public class Book implements Parcelable {public String title;Book() {}protected Book(Parcel in) {title = in.readString();}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(title);}@Overridepublic int describeContents() {return 0;}public static final Creator<Book> CREATOR = new Creator<Book>() {@Overridepublic Book createFromParcel(Parcel in) {return new Book(in);}@Overridepublic Book[] newArray(int size) {return new Book[size];}}; }同時在 aidl 目錄中定義 Book.aidl 文件,并且聲明 parcelable 屬性。Book.aidl文件如下:
package com.yanjun.progress;parcelable Book;下面我們修改 IBookAidlInterface.aidl 的方法,如下:
interface IBookAidlInterface {String getTitle();void setTitle(String title);Book getBook();void setBook(in Book book); }需要注意的是 setBook方法,一定要聲明 Book 對象是 in屬性,表明是 book 對象是從客戶端流向服務端。
最后在 Service 里面實現接口就行了,如下:
class MyService : Service() {private var binder: Binder = object : IBookAidlInterface.Stub() {override fun getTitle(): String {return "aidl->title"}override fun setTitle(title: String?) {Log.d("aidl", "service-setTitle:$title")}override fun getBook(): Book {var book = Book()book.title = "書的名字"return book}override fun setBook(book: Book?) {}}override fun onBind(intent: Intent): IBinder ?{return binder}override fun onCreate() {super.onCreate()} }demo github 地址:https://github.com/zyj1609wz/AndroidMultiProgress
8、SharedPreferences 跨進程
SharedPreferences要實現跨進程通信,要遵循文件名字相同的規則。而且是同一個應用,兩個進程間。
SharedPreferences之所以能實現跨進程,是因為SharedPreferences 數據存在SD卡的磁盤中,兩個進程共用一個文件。
注意事項:
- 雖然 SharedPreferences 可以實現跨進程,但是 Google 官方不建議使用,因為 Google 認為多個進程讀同一個文件都是不安全的,Android 不保證該模式總是能正確的工作,建議使用 ContentProvider 替代多進程之間文件的共享。
額外話題 SharedPreferences 優化:
- commit 是把內容同步提交到硬盤的,返回 boolean 表明修改是否提交成功
- apply先立即把修改提交到內存,然后開啟一個異步的線程提交到硬盤,并且如果提交失敗,你不會收到任何通知,apply沒有返回值 。
- 使用了apply方式異步寫sp的時候,每次apply()調用都會新增一個finisher。在有些系統生命周期事件發生的時候都會去檢查已經提交的apply寫操作是否完成,如果沒有完成則阻塞主線程,造成 ANR
- 當一個文件較大時,首次讀取可能會較慢,每次寫入也會較慢
- 在不同模塊使用多個文件存儲的情況下,那么未被使用到的模塊,不會被讀取進內存
- 當存儲的value為json或者html形式時,由于特殊符號較多,會占用更多的字符
如何解決 ANR :
- 如果處理 SP的時候,使用commit方法替代apply,在保證調用時線程正確處理,并保證同一文件不使用多個線程寫入的情況下,不會出現該ANR。
- 自查是否有一下子修改多個key-value且apply多次的情況,如有,可合并為put多次但apply一次
9、廣播跨進程
Context.sendBroadcast()
發送的是普通廣播,所有訂閱者都有機會獲得并進行處理
10、ContentProvider跨進程
什么是ContentProvider ?
即 內容提供者,是 Android 四大組件之一
ContentProvider什么作用?
進程間 進行數據交互 & 共享,即跨進程通信
ContentProvider原理
ContentProvider的底層原理 = Android中的Binder機制
ContentProvider優秀文章鏈接
本來是想總結一下的,但是我發現了一篇寫的很好的文章:
建議把文章和評論都看看,很有收獲。
Android:關于ContentProvider的知識都在這里了!
Android中使用Contentprovider導致進程被殺死
11、Socket跨進程通信
優點:
- 功能強大,可通過網絡傳輸字節流,支持一對多實時并發通信
缺點:
- 實現細節步驟稍繁瑣,不支持直接的RPC
適用場景:
- 網絡間的數據交換
12、文件共享跨進程
優點
- 簡單易用
缺點:
- 不適用高并發場景,并且無法做到進程間即時通信
適用場景:
- 適用于無關發的情況下,交換簡單的數據,對實時性要求不高的場景。
13、Bundle跨進程
優點
- 簡單易用
缺點:
- 只能傳輸Bundle支持的數據類型
適用場景:
- 四大組件間的進程間通信
14、MMKV
MMKV是國內微信團隊開源的高性能 key-value組件,良好的支持Android跨進程通信,具體使用方法就自己看文檔吧,這里不贅述了。
github: https://github.com/Tencent/MMKV/
中文文檔:MMKV中文文檔
總結
以上是生活随笔為你收集整理的Android 跨进程通信大总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android RecyclerView
- 下一篇: Android 如何做一次内存泄漏大排查