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