Android中的IPC进程通信方式第一篇

小二玩编程

ps:本文系转载文章,阅读原文可获取源码,文章末尾有原文链接

ps:本文的 demo 是用 Kotlin 语言写的

只要是操作系统,不管是 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种方式。

1、使用 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() {

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)
    }
}

}

(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 进程中。

2、使用文件共享

在 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”里已描述,查看进程的方式这里不再重复描述。

阅读 400

活到老,学到老。

1 声望
1 粉丝
0 条评论
你知道吗?

活到老,学到老。

1 声望
1 粉丝
宣传栏