Android中的IPC進程通訊方式第一篇

ps:本文系轉載文章,閱讀原文可獲取源碼,文章末尾有原文連接java

ps:本文的 demo 是用 Kotlin 語言寫的linux

只要是操做系統,無論是 linux 仍是 Windows 系統,都會有 IPC 進程通訊機制;每一個進程之間是相互獨立的,它們之間的數據是不共享的,只有同一個進程間的數據才共享的;雖然每個進程之間不能夠共享數據,可是能夠進行進程之間的通訊;在 Android 中 IPC 跨進程通訊離不開 Serializable 接口、Parcelable 接口以及 Binder 這3個東西,Serializable和 Parcelable接口能夠完成對象的序列化過程,Binder 實現了 IBinder 接口,Binder 是 Android 中的一種跨進程通訊角色;Android IPC 通訊的有使用 Bundle、使用文件共享、使用 Messenger、使用 AIDL、使用 ContentProvider 和使用 Socket 這幾種方式,這一篇是寫使用 Bundle 和 使用 Messenger 這2種方式。android

一、使用 Bundle編程

Bundle 實現了 Parcelable 接口,它能夠在不一樣的進程間通訊,Activity、Service 和 Receiver 這3個角色之間是能夠用 Bundle 傳輸數據的;若是咱們啓動了另外一個進程的 Activity、Service 和 Receiver 這3者中的一個,能夠經過 Bundle 中附加數據用 Intent 傳送給另外一個進程的 Activity、Service 和 Receiver 這3者中的一個;Bundle 支持傳送的數據類型有 基本數據類型、實現了 Serializable 接口的對象、實現了 Parcelable 接口的對象和可以支持 Android 序列化的特殊對象。緩存

下面咱們舉個例子:多線程

(1)新建一個 kt 文件 MainActivity,它的包名爲 com.xe.demo.ipcdemo1.ipcdemo1併發

class MainActivity: AppCompatActivity() {app

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

fun onClick(v: View) {
    if (v.id == R.id.btn_1) {
        var s: String = "使用Bundle進行IPC跨進程通訊"
        var intent: Intent = Intent(this, BundleActivity::class.java)
        var bundle: Bundle = Bundle()
        bundle.putCharSequence("key",s)
        intent.putExtra("bundle",bundle)
        startActivity(intent)
    }
}

}ide

(2)新建一個 xml 文件 activity_main:高併發

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xe.demo.ipcdemo1.ipcdemo1.MainActivity">

<Button
    android:id="@+id/btn_1"
    android:layout_width="match_parent"
    android:text="使用Bundle進行IPC跨進程通訊"
    android:textAllCaps="false"
    android:onClick="onClick"
    android:layout_height="wrap_content" />

</LinearLayout>

(3)新建一個 kt 文件 BundleActivity,它的包名爲 com.xe.demo.ipcdemo1.ipc

class BundleActivity : AppCompatActivity() {

var mTv: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_bundle)
    mTv = findViewById(R.id.tv)
    var bundle: Bundle = intent.getBundleExtra("bundle")
    var s: CharSequence = bundle.getCharSequence("key")
    mTv!!.setText(s)
}

}

(4)新建一個 xml 文件 activity_bundle:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xe.demo.ipcdemo1.ipc.BundleActivity">
<TextView
    android:id="@+id/tv"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</LinearLayout>

(5)在 AndroidManifest.xml 配置一下信息,目的是爲了開啓多進程:

<activity android:name="com.xe.demo.ipcdemo1.ipc.BundleActivity"

android:process="com.xe.demo.ipcdemo1.ipc_bundle"></activity>

程序一開始運行的界面以下所示:

圖片

點擊「使用Bundle進行IPC跨進程通訊」按鈕,跳轉到以下界面:

圖片
這個界面顯示的數據,是從 MainActivity 中的 Bundle 附加過來的,當咱們打開控制檯的地方,查看以下圖圈出來的地方:

圖片

就會看到有2個進程,說明多進程開啓成功,一個進程是 com.xe.demo.ipcdemo1.ipcdemo1,是以包名來命名的,一個進程是 com.xe.demo.ipcdemo1.ipc_bundle,MainActivity 沒有指定進程,因此運行在 com.xe.demo.ipcdemo1.ipcdemo1,BundleActivity 指定了進程名 com.xe.demo.ipcdemo1.ipc_bundle,因此運行在 com.xe.demo.ipcdemo1.ipc_bundle 進程中。

二、使用文件共享

在 Linux 中,2個進程併發讀/寫文件是沒有限制的,進程之間用文件交換數據,能夠從一個進程存儲數據到文件中而後從另外一個進程中恢復這些數據,可是若是多進程之間開啓多線程併發讀/寫文件,可能會出問題,就是數據不能同步;使用文件共享這種方式來跨進程通訊對文件格式是沒有具體要求的,咱們使用的 SharedPreferences 有點特別,它是使用鍵值對的方式來存儲數據,系統對它的讀/寫有必定的緩存策略,即在內存中會有一份 SharedPreferences 文件的緩存,在使用多進程的時候,面對高併發的讀/寫訪問,頗有可能丟失數據,所以不推薦使用 SharedPreferences。

下面用多進程之間使用單線程讀/寫文件舉個例子:

(1)在案例「使用 Bundle」的基礎上對 MainActivity 文件作以下修改:

class MainActivity: AppCompatActivity() {

var filePath: String = "/filePath"
var myHandler: MyHandler? = null
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    myHandler = MyHandler()
}

fun onClick(v: View) {
    if (v.id == R.id.btn_1) {
        var s: String = "使用Bundle進行IPC跨進程通訊"
        var intent: Intent = Intent(this, BundleActivity::class.java)
        var bundle: Bundle = Bundle()
        bundle.putCharSequence("key",s)
        intent.putExtra("bundle",bundle)
        startActivity(intent)
    } else if (v.id == R.id.btn_2) {
        saveData()
    }
}

fun saveData() {
    Thread(Runnable {
        var myObject: MyObject = MyObject("公衆號小二玩編程",21)
        val dir = File(getExternalStorageDirectory().getPath() + filePath)
        if (!dir.exists()) {
            dir.mkdirs()
        }
        val cachedFile = File(Environment.getExternalStorageDirectory().getPath() + filePath + "/my_cache")
        var objectOutputStream: ObjectOutputStream? = null
        try {
            if (!cachedFile.exists()) {
                cachedFile.createNewFile()
            }
            objectOutputStream = ObjectOutputStream(FileOutputStream(cachedFile))
            objectOutputStream!!.writeObject(myObject)
            myHandler!!.sendEmptyMessage(0)
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            try {
                if (objectOutputStream != null) {
                    objectOutputStream!!.close()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

        }
    }).start()
}

inner class MyHandler: Handler() {
    override fun handleMessage(msg: Message?) {
        super.handleMessage(msg)
        this@MainActivity.startSharedActivity()
    }
}

fun startSharedActivity() {
    var intent: Intent = Intent(this@MainActivity, SharedActivity::class.java)
    startActivity(intent)
}

}

(2)在案例「使用 Bundle」的基礎上對 activity_main 文件作以下修改:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xe.demo.ipcdemo1.ipcdemo1.MainActivity">

<Button
    android:id="@+id/btn_1"
    android:layout_width="match_parent"
    android:text="使用Bundle進行IPC跨進程通訊"
    android:textAllCaps="false"
    android:onClick="onClick"
    android:layout_height="wrap_content" />
<Button
    android:id="@+id/btn_2"
    android:layout_width="match_parent"
    android:text="使用文件共享"
    android:textAllCaps="false"
    android:onClick="onClick"
    android:layout_height="wrap_content" />

</LinearLayout>

(3)新建一個 kt 文件 MyObject:

class MyObject: Serializable{

var name: String
var age: Int

constructor(name: String,age: Int) {

this.name = name
   this.age = age

}

}

(4)新建一個 kt 文件 SharedActivity:

class SharedActivity : AppCompatActivity() {

var myThread: MyThread? = null
var myH: MyH? = null
var mTv: TextView? = null
var filePath: String = "/filePath"
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_shared)
    mTv = findViewById(R.id.tv)
    myThread = MyThread()
    myThread!!.start()
    myH = MyH()
}

override fun onDestroy() {
    super.onDestroy()
    if (myH != null) {
        myH!!.removeCallbacksAndMessages(null)
    }
    if (myThread != null) {
        myThread = null
    }
}

inner class MyH: Handler() {
    override fun handleMessage(msg: Message?) {
        super.handleMessage(msg)
        var myObject: MyObject = msg!!.obj as MyObject
        var name: String = myObject.name
        var age: Int = myObject.age
        mTv!!.setText("個人名字叫:" + name + ",年齡是:" + age)
    }
}

inner class MyThread: Thread() {
    override fun run() {
        super.run()
        var cachedFile: File = File(Environment.getExternalStorageDirectory().getPath()+filePath + "/my_cache");
        if (cachedFile.exists()) {
            var objectInputStream: ObjectInputStream? = null;
            try {
                objectInputStream = ObjectInputStream(FileInputStream(cachedFile));
                var any: Any = objectInputStream!!.readObject();
                var message: Message = Message.obtain()
                message.obj = any
                myH!!.sendMessage(message)
            } catch (e: IOException) {
                e.printStackTrace();
            } catch (e: ClassNotFoundException) {
                e.printStackTrace();
            } catch (e: RemoteException) {
                e.printStackTrace();
            } finally {
                try {
                    if (objectInputStream != null) {
                        objectInputStream!!.close();
                    }
                } catch (e: Exception) {
                    e.printStackTrace();
                }
            }
        }
    }
}

}

(5)新建一個 xml 文件 activity_shared:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.xe.demo.ipcdemo1.ipc.SharedActivity">
<TextView
    android:id="@+id/tv"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</LinearLayout>

(6)AndroidManifest.xml 文件的配置以下所示:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<activity android:name="com.xe.demo.ipcdemo1.ipc.SharedActivity"

android:process="com.xe.demo.ipcdemo1.ipc_bundle"></activity>

程序一開始運行的界面以下所示(一些手機要手動打開 APP 的讀寫權限):

圖片

點擊「使用文件共享」按鈕,跳轉到以下界面:

圖片

從這個界面顯示的文字能夠看出,咱們使用文件共享的方式進行 IPC 進程通訊成功,使用文件共享方式最好是在單線程的方式進行;在案例「使用 Bundle」裏已描述,查看進程的方式這裏再也不重複描述。

相關文章
相關標籤/搜索