什么是进程
在Android中进程是资源拥有的基本单位。每一个进程间的数据是不互通,正常情况下是无法直接调用的不同进程保存的资源的,因此为了能可够能调用其他进程的资源而产生了ipc(进程间通信)。
进程间通信
- Android常见的进程通信有:bundle、文件共享、Messenger、aidl、contentProvider、Socket;
Bundle
Android四大组件中有三个都可以通过Intent启动的,Intent中的Bundle可以传递一些基本的数据类型以及实现了序列化的对象, 但是需注意在Intent中传递的数据总大小不能大于1MB。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val test = intent.getFloatExtra(TEST, 0f)
}
companion object {
const val TEST = "test"
fun newInstance(activity: BaseActivity, bundle: Bundle) {
var intent = Intent(activity, SecondActivity::class.java)
bundle.putFloat(TEST, 0.1f)
intent.putExtras(bundle)
activity.startActivity(intent)
}
}
文件共享
android中两个进程可以通过对同一个文件进行读写来进行数据交换,例如:A进程中将数据写入约定好的文件中,B进程从这约定好的文件中读取数据。 注意:多个进程对同一个文件执行并发操作时,会产生数据错乱问题,因此在使用文件共享时避免并发操作。
//A进程执行写入数据方法
fun writeToData() {
Thread(Runnable {
var user = User(1, "hello world", false) //实现了序列化的类
var dir = File("/test.txt")
if (!dir.exists()) {
dir.mkdir()
}
var outputStream: ObjectOutputStream? = null
try {
outputStream = ObjectOutputStream(FileOutputStream(dir))
outputStream.writeObject(outputStream)
} catch (e: Exception) {
e.printStackTrace()
} finally {
outputStream?.close()
}
}).start()
}
//B进程执行读取数据的方法
fun readToData() {
Thread(Runnable {
var user : User //实现了序列化的类
var dir = File("/test.txt")
if (!dir.exists()) {
dir.mkdir()
}
var inputStream: ObjectInputStream? = null
try {
inputStream = ObjectInputStream(FileInputStream(dir))
user=inputStream.readObject() as User
} catch (e: Exception) {
e.printStackTrace()
} finally {
inputStream?.close()
}
}).start()
}
Messenger
Messenger 通过它可以在不同的进程中传递Message对象,在Message中传入我们需要传递的数据, 就可以轻松的实现进程间数据的传递。Messenger只能进行串行的数据处理,当多个请求同时来时会一个一个处理,并不会并行的处理。
//服务端
class MessengerService : Service() {
val MSG=0
val MSG_TWO=0
private val mHandler=Handler(Handler.Callback {
when(it.what){
MSG->{}
MSG_TWO->{}
else->{}
}
true
})
private val messenger=Messenger(mHandler)
override fun onBind(intent: Intent?): IBinder? {
return messenger.binder
}
}
//客户端
private lateinit var serviceMessenger: Messenger
private val mConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
serviceMessenger = Messenger(service)
val message = Message.obtain(null,MSG)
val bundle = Bundle()
bundle.putFloat("float", 1527f)
message.data = bundle
serviceMessenger.send(message)
}
}
private fun bindService(){
val intent=Intent(this,MessengerService::class.java)
bindService(intent,mConnection,Context.BIND_AUTO_CREATE)
}
若想接收服务端返回的消息,则客户端在发送消息前将Message的replyTo参数赋值,然后在服务端调用Message的replyTo恢复消息即可。
AIDL
AIDL:Android Interface Definition Language,即Android接口定义语言。其也是Messenger的底层实现。
-
服务端
首先创建一个Service来监听客户端的连接请求,然后创建一个aidl文件,将暴露给客户端的接口在这个AIDL中声明,最后在Service中实现aidl.stub接口,通过onBind返回给客户端调用。
- 注意:
- 在aidl中使用自定义对象需要实现序列化;
- 自定义对象需要创建一个同名的aidl文件进行声明;
- 在aidl接口中调用自定义对象时需要手动import 类的全路径;
- 引用其他aidl文件时也需要手动import 全路径
- RemoteCallbackList可以提供远程删除功能,使用前必须先调用beginBroadcast,使用结束后也必须调用finshBroadcast
-
客户端
首先和服务端一样创建相同路径下的aidl文件,然后在在绑定远程服务端时,通过aidl的stub.asInterface将服务端的IBinder转换为aidl接口对象,之后就可以通过此接口对象调用服务端方法。
ContentProvider
contentProvider是四大组件之一,用来不同应用间的数据共享,因此其也可以用来实现进程间的数据的增删改查。
在android系统中预置了许多ContentProvider,例如:通讯录,日程信息表等; 我们可以通过ContentResolver来对ContentProvider提供的数据进行增删改查。
Socket
Socket属于网络架构的传输层,在Socket进行客户端与服务端连接时,两端都会产生一个实例,操作这个实例就可以完成所需的通信。
- 服务端
private val mIsServiceDestoryed = false
private fun run() {
Thread(Runnable {
var serverSocket: ServerSocket? = null
try {
serverSocket = ServerSocket(8800)
} catch (e: IOException) {
e.printStackTrace()
}
while (!mIsServiceDestoryed) {
try {
val accept = serverSocket?.accept()
responseClient(accept)
} catch (e: IOException) {
e.printStackTrace()
}
}
}).start()
}
private fun responseClient(client: Socket?){
//接收客户端的消息
var reader=BufferedReader(InputStreamReader(client?.getInputStream()))
//用于向客户端发送消息
var writer=PrintWriter(BufferedWriter(OutputStreamWriter(client?.getOutputStream())),true)
while(!mIsServiceDestoryed){
val readLine = reader.readLine() ?: break
var sendMessageSever="服务端发送的消息"
writer.println(sendMessageSever)
}
reader.close()
writer.close()
client?.close()
}
- 客户端
private var clientIsDestory = false
private fun startConnetTcp() {
Thread(Runnable {
var socket: Socket? = null
var printWriter: PrintWriter? = null
while (socket == null) {
socket = Socket("localhist", 8800)
//用于对服务器发送消息
printWriter =
PrintWriter(BufferedWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream()))), true)
printWriter.println("客户端发送的消息")
}
//接收服务端发送来的消息
var reader = BufferedReader(InputStreamReader(socket.getInputStream()))
while (!clientIsDestory) {
val readLine = reader.readLine()
println("服务端发送来的消息:$readLine")
}
printWriter?.close()
reader.close()
socket.close()
})
}
以上方法的优缺点对比
| 名称 | 优点 | 缺点 | 使用场景 |
|---|---|---|---|
| Bundle | 简单易用 | 只能传输Bundle支持的数据 | 四大组件间的进程通信 |
| 文件共享 | 简单易用 | 不能高并发,且不能实时的通信 | 并发低的情况,且数据实时性要求不高 |
| messenger | 调用简单,支持一对多串行通信,及实时通信 | 不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的类型 | 低并发的一对多即时通信,无RPC需求 |
| aidl | 功能多,支持一对多串行通信,支持实时通信 | 使用较复杂,需注意线程同步问题 | 一对多且有RPC需求 |
| contentProvider | 数据访问方面功能强大,支持一对多并发数据共享,可通过call方法扩展其他操作 | 主要提供数据源的CRUD | 提供其他应用数据查询情况 |
| socket | 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 | 实现较为繁琐,不支持直接RPC | 进行网络间进程数据交换 |