关于Android 进程间通信的基本知识以及Android中进程间通信的方式
IPC简介与基础概念
基础
- android 开启多进程的方式
- 指定四大组件的 android:process 属性
- 通过Jni在native层 fork一个进程
- "anroid:process" 进程命名的含义
- ":"开头表示当前进程名前面要加上包名
- ":" 表示是当前应用的私有进程,其它应用的组件不可以和它跑在同一个进程中
- 没有 ":" 表示是全局进程,其它应用的组件在ShareUID并且签名相同的情况下也可以跑在这个进程
- 多进程会带来的问题
- 静态成员和单例模式失效
- Application 会创建多次
- SharedPreference 变得不可靠
- 线程同步机制失效
- Serializable , Parcelable
-
Serializable
/** * 父类没有实现Serializable,子类实现了Serializable,父类的属性不会参与序列化 */ open class Parent(var parentName: String) { constructor() : this("parent") } /** * 定义[readObject] 和[writeObject] 可以对序列化进行一些自定义的操作 */ data class Child( var childName: String = "Bob", //有Transient标识的 不参与序列化 @Transient var childAge: Int = 5, var password: String = "password" ) : Serializable, Parent() { companion object { /** * 1.在反序列化的时候,只有这个值相同的情况下才有可能反序列化成功 * 2.可以不指定这个值,系统会有默认值 * 3.在默认值的情况下,如果增加和删除了成员变量,反序列化不能成功,默认值改变了 * 4.如果类名发生改变或成员的类型发生改变,不管这个值是相同,反序列化都不能成功 * */ private const val serialVersionUID = 2L } /** * 反序列化操作 * 默认会调用 [java.io.ObjectInputStream.defaultReadObject] * * */ private fun readObject(ins: ObjectInputStream) { try { val fields = ins.readFields() password = fields.get("password", "").toString() } catch (e: Exception) { Log.d("SerialTest", e.message ?: "UnKnowError") } } /** * 序列化操作 * 默认会调用 [java.io.ObjectOutputStream.defaultWriteObject] * * */ private fun writeObject(out: ObjectOutputStream) { try { val fields = out.putFields() fields.put("password", password + "encryption") // fields.put("childName",childName) out.writeFields() } catch (e: Exception) { Log.d("SerialTest", e.message ?: "UnKnowError") } } }
-
Parcelable
/** * 必须与相应的AIDL在同个包下 */ data class Info(var data: String, var times: Int) : Parcelable { companion object CREATOR : Parcelable.Creator<Info> { /** * 从序列化的对象中创建原始对象 * */ override fun createFromParcel(source: Parcel): Info = Info(source) /** * 创建指定长度的原始对象数组 * */ override fun newArray(size: Int): Array<Info?> = arrayOfNulls(size) } constructor(parcel: Parcel) : this( parcel.readString() ?: "", parcel.readInt()) /** * 将当前对象写入序列化对象中 * @param flags 1表示当前对象需要最为返回值返回,不能立即释放,几乎所有情况都为0 * [android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE] * */ override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.apply { writeString(data) writeInt(times) } } /** * @retrun 如果有文件描述符返回1, 否则返回0(几乎所有情况都是0) * [android.os.Parcelable.CONTENTS_FILE_DESCRIPTOR] * */ override fun describeContents() = 0 }
Binder
NOTE:
1. 客户端在RPC(远程请求调用)的时候,会挂起当前线程直到RPC返回后才会重新唤醒,所以客户端启动的RPC是耗时任务时务必要新开一个线程
2. 服务端相关的方法已经运行在Binder的线程池中,就算是耗时任务也不需要新开一个线程
3. 同理在服务端发起RPC时也会挂起服务端的当前线程,此时调用到的客户端的方法运行在客户端的Binder线程池中,涉及到UI操作时要做切换线程处理(可以使用Handler);如果是耗时操作,服务端务必不要在主线程中发起此RPC
- 通过AIDL了解Binder
package com.open.aqrlei.ipc.aidl;
public interface IChangeListener extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.open.aqrlei.ipc.aidl.IChangeListener {
/**
* Binder的唯一标识
*/
private static final java.lang.String DESCRIPTOR = "com.open.aqrlei.ipc.aidl.IChangeListener";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* @return
* 将服务端的Binder对象转换成客户端所需的AIDL接口对象。
* 如果是同一进程中,此方法返回的是服务端的Stub对象本生
* 如果是不同进程,返回的是系统封装后的Stub.proxy对象
*/
public static com.open.aqrlei.ipc.aidl.IChangeListener asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.open.aqrlei.ipc.aidl.IChangeListener))) {
return ((com.open.aqrlei.ipc.aidl.IChangeListener) iin);
}
return new com.open.aqrlei.ipc.aidl.IChangeListener.Stub.Proxy(obj);
}
/**
* @return 返回当前的 Binder对象
*/
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求通过系统底层封装后交由此方法处理
* {@link android.os.Binder}
* @param code 服务端通过code可以确定所请求的方法是什么
* @param data 从data中取出目标方法所需要的参数(如果需要的话),然后执行目标方法
* @param reply 在reply中写入返回值(如果需要的话)
* @return 返回 false ,客户端的请求就会失败
* */
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_msgChange: {
data.enforceInterface(descriptor);
com.open.aqrlei.ipc.Info _arg0;
if ((0 != data.readInt())) {
_arg0 = com.open.aqrlei.ipc.Info.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.msgChange(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.open.aqrlei.ipc.aidl.IChangeListener {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* 此方法运行在客户端。首先会创建两个对象,输入型Parcel:"_data",输出型Parcel:"_reply",如果有返回值
* 会创建一个返回值对象。其次,将方法需要的参数写入_data(如果有参数的话);接着调用transact
* 发起RPC(远程过程调用)请求同时当前线程挂起,服务端的onTransact会被调用,直到RPC过程返回后,当前线程
* 继续执行,并从_reply中取出结果,如果有返回值的话,赋值给返回值对象。然后_data,_reply调用recycle
* 最后如果有返回值的话,返回返回值
*
*/
@Override
public void msgChange(com.open.aqrlei.ipc.Info info) throws android.os.RemoteException {
//输入型Parcel
android.os.Parcel _data = android.os.Parcel.obtain();
//输出型Parcel
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((info != null)) {
_data.writeInt(1);
info.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_msgChange, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_msgChange = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void msgChange(com.open.aqrlei.ipc.Info info) throws android.os.RemoteException;
}
-
Binder工作机制
Binder工作机制
-
Binder死亡代理(linkToDeath(),unLinkToDeath())
private val mServiceConn = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { mBinder = IBinderPool.Stub.asInterface(service) //设置死亡监听 service?.linkToDeath(mDeathRecipient,0) } override fun onServiceDisconnected(name: ComponentName?) { } } private val mDeathRecipient = object : IBinder.DeathRecipient { /** * 远程服务端由于某种原因终止,此时Binder"死亡",当Binder死亡时, * 会回调此方法,在此处调用unlinkToDeath且将Binder置为NULL, * 并重新绑定服务 * */ override fun binderDied() { mBinder?.asBinder()?.unlinkToDeath(this, 0) mBinder = null //TODO bindService() 重新绑定远程服务 } }
IPC方式
Bundle
Bundle 结合 Intent一起使用,一般用于四大组件(Activity、Service、Broadcast)的进程间的通信
- 源码分析
NOTE:- Bundle继承BaseBundle类,实现了序列化Parcelable接口
- 在putXxx和getXxx的时候,第一步都要先调用unparcel方法,主要是对Parcel mParcelledData进行操作,然后再从map中获取数据
- Bundle中数据都存储在ArrayMap<String, Object> mMap中,如果mParcelledData不为null的时候, mMap就是null,数据存储为包含Bundle的Parcel。当数据unparcelled,mParcelledData置为null, map中存储数据
public void putInt(@Nullable String key, int value) { unparcel(); mMap.put(key, value); } void unparcel() { synchronized (this) { final Parcel source = mParcelledData; // 如果不为null,说明数据都存在一个包含Bundle的Parcel即mParcelledData中,此时map为null // 就要根据mParcelledData的情况创建(如果mMap为null的话)或更新ArrayMap,并根据mParcelledData写入内容 if (source != null) { initializeFromParcelLocked(source, /*recycleParcel=*/ true); } else { if (DEBUG) { Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + ": no parcelled data"); } } } } public int getInt(String key) { unparcel(); return getInt(key, 0); } public int getInt(String key, int defaultValue) { unparcel(); Object o = mMap.get(key); if (o == null) { return defaultValue; } try { return (Integer) o; } catch (ClassCastException e) { typeWarning(key, o, "Integer", defaultValue, e); return defaultValue; } }
文件共享
NOTE:
- 通过读/写磁盘中的同一个文件实现进程间的数据交换
- 由于Linux系统并发读写文件可以没有限制的进行,不适合并发的情景
- SharedPreference虽然数据也是存在磁盘中的文件里,但是系统利用Map<String, Object> mMap在内存中进行了缓存,这一点会导致问题
object FileStreamUtil {
fun getObjectFile(context: Context) = getCacheFile(context, "object_test")
fun getCacheFile(context: Context, uniqueName: String = "ipc_test.text"): File? {
val cachePath =
if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState() || !Environment.isExternalStorageRemovable())
context.externalCacheDir?.path
else
context.cacheDir.path
return File("$cachePath${File.separator}$uniqueName")
}
/**
* @param file 写入内容的文件
* @param content 要写入的字符串
* */
fun writeChar(file: File, content: String = "Hello IPC File"): Boolean {
return try {
BufferedWriter(FileWriter(file, true)).use { bWriter ->
bWriter.write((content))
bWriter.flush()
true
}
} catch (e: Exception) {
Log.d("IOTest", e.message ?: "UnKnowError")
false
}
}
/**
* @param user 要写入的可序列化对象
* */
fun writeObject(file: File?, user: User): Boolean {
return try {
ObjectOutputStream(FileOutputStream(file)).use {
it.writeObject(user)
it.flush()
true
}
} catch (e: Exception) {
false
}
}
/**
* 读取写入文件的序列化对象
* */
fun readObject(file: File?, action: (User?) -> Unit) {
if (file == null) {
return
}
try {
ObjectInputStream(FileInputStream(file)).use {
val user = it.readObject() as? User
action(user)
}
} catch (e: Exception) {
action(null)
}
}
/**
* 读取写入文件的字符
* */
fun readChar(file: File, action: (String) -> Unit) {
try {
BufferedReader(FileReader(file)).use { bReader ->
val str = StringBuffer()
var buffer = -1
while (buffer.let {
buffer = bReader.read()
buffer
} != -1) {
str.append(buffer.toChar().toString())
}
action(str.toString())
}
} catch (e: Exception) {
action(e.message ?: "UnKnowError")
}
}
}
Messenger
NOTE:
1. Messenger是基于Binder的,底层实现也是AIDL
2. Messenger同样实现了Parcelable接口
- 使用
-
客户端
// 在ServiceConnection#onServiceConnected里面获取到Binder并创建Messenger clientMessengerHandler.service = Messenger(it.queryBinder(IPCService.MESSENGER_BINDER_CODE)) class ClientMessengerHandler(private val activity: WeakReference<IPCActivity>) : Handler(Looper.getMainLooper()) { var service: Messenger? = null override fun handleMessage(msg: Message?) { when (msg?.what) { RECEIVE_FROM_SERVICE_CODE_INIT -> { //将客户端的Messenger通过服务端的Messenger传送给服务端 service?.send( Message.obtain(null, IPCService.RECEIVE_FROM_CLIENT_CODE_INIT).apply { replyTo = Messenger(clientMessengerHandler) }) } ... } super.handleMessage(msg) } }
-
服务端
MESSENGER_BINDER_CODE -> { // Binder 连接池,选用Messenger // 运行在Binder线程池中 // 此处必须加上Looper.getMainLooper() serviceMessengerHandler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message?) { when (msg?.what) { RECEIVE_FROM_CLIENT_CODE_INIT -> { //初次信息传递 if (client != msg.replyTo) {// 获取客户端返回的Messenger,用于之后两端通信 client = msg.replyTo } //通知客户端,首次消息接收成功 client?.send(Message.obtain(null, IPCActivity.RECEIVE_FROM_SERVICE_CODE_INIT)) } ... else -> { super.handleMessage(msg) } } } } //返回binder给客户端 Messenger(serviceMessengerHandler).binder }
-
- 源码
Messenger 中 public Messenger(Handler target) { mTarget = target.getIMessenger(); } public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); } public void send(Message message) throws RemoteException { mTarget.send(message); } Handler中 final IMessenger getIMessenger() { synchronized (mQueue) { if (mMessenger != null) { return mMessenger; } mMessenger = new MessengerImpl(); return mMessenger; } } private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) { msg.sendingUid = Binder.getCallingUid(); Handler.this.sendMessage(msg); } } IMessenger.aidl中 package android.os; import android.os.Message; /** @hide */ oneway interface IMessenger { void send(in Message msg); }
AIDL
-
支持的数据类型
类型 说明 基本数据类型 int, char,boolean,double,float等 String和CharSequence List 只支持ArrayList传输,但是如果List是返回类型,return了一个非ArrayList的数据,Binder会根据List的规范生成一个ArrayList返回 Map 只支持HashMap Parcelable 支持所有实现了Parcelable接口的类,自定义的Parcelable详情(下文) AIDL 所用AIDL接口,自定义的AIDL详情(下文) -
实现
-
使用自定义Parcelable作为传递的数据类型
- 用于AIDL的Parcelable实现类必须与对应的AIDL文件在同一个包下(Parcelable实现见基础部分)
- 使用自定义的Parcelable,要据此创建一个AIDL
//import 是必须要有的 Info.aidl package com.open.aqrlei.ipc; import com.open.aqrlei.ipc.Info; parcelable Info; IChangeListener.aidl package com.open.aqrlei.ipc; import com.open.aqrlei.ipc.Info; interface IChangeListener { void msgChange(in Info info); }
-
使用自定义的AIDL作为传递的数据类型,import必须要有
package com.open.aqrlei.ipc; import com.open.aqrlei.ipc.IChangeListener; interface IListenerManager { void registerChangeListener(in IChangeListener listener); void unregisterChangeListener(in IChangeListener listener); }
-
RemoteCallbackList(使用,源码分析)
NOTE:- 用与对回调方法的管理,由于是跨进程通信,经过反序列化后,回调方法的对象与序列化前的对象实际不是同一个,这样就造成不能够简单的注册和注销回调方法。而使用RemoteCallbackList能解决这个问题。
- 内部实现了线程同步
- Binder死亡后,能自动解除Binder所对应的回调
- 使用
// 注册和注销 private val mListenerManager = object : IListenerManager.Stub() { override fun registerChangeListener(listener: IChangeListener?) { listenerList.register(listener) sendMsgChange(Info("receive from service", 0)) } override fun unregisterChangeListener(listener: IChangeListener?) { listenerList.unregister(listener) } } // 回调 //必须先调用这个方法 val n = listenerList.beginBroadcast() for (i in 0 until n) { listenerList.getBroadcastItem(0).msgChange(info) }
- 源码
NOTE:- 注册的时候以底层不变的binder对象作为key保存到ArrayMap中,注销的时候同样用binder作为key从ArrayMap中移除。
- 使用的时候将ArrayMap中的数据添加到数组中,然后再从数组中获取对象进行回调的执行。
- 内部使用synchronized关键字实现了线程同步
- 在注册时候使用了linkToDeath() 关联了死亡代理 IBinder.DeathRecipient,在触发死亡代理的时候,移除相关的回调对象。在注销的时候unlinkToDeath()
注册和注销 public boolean register(E callback, Object cookie) { synchronized (mCallbacks) {//同步代码块 if (mKilled) { return false; } logExcessiveCallbacks(); IBinder binder = callback.asBinder(); try { Callback cb = new Callback(callback, cookie); // 关联到IBinder.DeathRecipient接口,Binder死亡后,移除此回调 binder.linkToDeath(cb, 0); //序列化和反序列化后,底层binder对象是同一个,以它作为key mCallbacks.put(binder, cb); return true; } catch (RemoteException e) { return false; } } } public boolean unregister(E callback) { synchronized (mCallbacks) { Callback cb = mCallbacks.remove(callback.asBinder()); if (cb != null) { cb.mCallback.asBinder().unlinkToDeath(cb, 0); return true; } return false; } } 广播 public int beginBroadcast() { synchronized (mCallbacks) { if (mBroadcastCount > 0) { throw new IllegalStateException( "beginBroadcast() called while already in a broadcast"); } final int N = mBroadcastCount = mCallbacks.size(); if (N <= 0) { return 0; } Object[] active = mActiveBroadcast; if (active == null || active.length < N) { mActiveBroadcast = active = new Object[N]; } for (int i=0; i<N; i++) { //将ArrayMap中的回调添加到数组active(mActiveBroadcast中) active[i] = mCallbacks.valueAt(i); } return N; } } public E getBroadcastItem(int index) { //从数据mACtiveBroadcast中获取注册的回调 return ((Callback)mActiveBroadcast[index]).mCallback; } public void finishBroadcast() { synchronized (mCallbacks) { if (mBroadcastCount < 0) { throw new IllegalStateException( "finishBroadcast() called outside of a broadcast"); } Object[] active = mActiveBroadcast; if (active != null) { final int N = mBroadcastCount; for (int i=0; i<N; i++) { //将active(mActiveBroadcast)中的数据置为null active[i] = null; } } mBroadcastCount = -1; } }
-
连接权限验证 (表格中的方式和地方无强制关联)
验证权限的方式 验证权限的地方 使用自定义Permission Service#onBind方法中,不通过则返回null 使用包名等(通过Uid获取包名) 实现AIDL的服务端调用的onTransact ,不通过则返回false //自定义Permission,位于AndroidManifest.xml <permission android:name="com.aqrlei.permission.IPC_AIDL" /> //示例 override fun onBind(intent: Intent?): IBinder? { val check = checkCallingOrSelfPermission("com.aqrlei.permission.PROVIDER") return if (check == PackageManager.PERMISSION_DENIED) { null } else { mIBinderPool } }
- 使用示例
//服务端 private val mListener = object:IChangeListener.Stub(){ override fun msgChange(info: Info?) { //method code } } override fun onBind(intent: Intent?): IBinder? { // 在onBind中返回 return mListener } //客户端 private var mListener:IChangeListener?= null //在ServiceConnction#onServiceConnected mListener = IChangeListener.Stub.asInterface(service) ... //接下来就可以通过mListener通信了
-
ContentProvider
NOTE:
1. ContentProvider是基于Binder实现的
2. 客户端的是ContentProviderNative#ContentProviderProxy,服务端的是ContentProvider#Transport
-
自定义ContentProvider (Sqlite为例)
class OrderProvider : ContentProvider() { companion object { //必须和AndroidManifest.xml中配置的一样,这是ContentProvider的唯一标识 private const val AUTHORITY = "aqrlei.OrderProvider" val ORDER_URL = Uri.parse("content://$AUTHORITY/order") private const val ORDER_CODE = 0X01 private val mMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { addURI(AUTHORITY, "order", ORDER_CODE) } } private lateinit var dataBaseOperator: DatabaseOperator override fun insert(uri: Uri, values: ContentValues?): Uri? { dataBaseOperator.insert(false) return uri } override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? { return dataBaseOperator.query() } override fun onCreate(): Boolean { DatabaseOperator.init(context!!) dataBaseOperator = DatabaseOperator.getInstance() return true } override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int { return dataBaseOperator.update(false) } override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int { return dataBaseOperator.delete(false) } override fun getType(uri: Uri): String? { return null } }
- 继承ContentProvider,实现方法
方法 说明 onCreate 可以作数据库的初始化操作 insert 插入数据,返回Uri对象 update 更新数据,返回影响的行数 delete 删除数据,返回影响的行数 query 返回Cursor对象 getType 返回MIME type,单条数据以vnd.android.cursor.item 开头 ,多条数据以 vnd.android.cursor.dir/ 开头.; 或者是null call 实现自定义的数据访问操作 - 在AndroidManifest.xml中配置
<provider android:name=".contentprovider.OrderProvider" //类名 android:authorities="aqrlei.OrderProvider" //ContentProvider 的唯一标识 android:process=":remote" // 指定进程名 android:permission="com.aqrlei.permission.PROVIDER" // 自定义使用权限(读写权限,也可以分别指定读写权限) />
- 使用
// ORDER_URL中包含authorities,根据这个ContentProvider唯一标识找到对应的ContentProvider contentResolver.query(OrderProvider.ORDER_URL, null, null, null, null)
-
分析
NOTE(可以根据以下所列查看源码):- 获取contentResolver, ContextImpl#ApplicationContentResolver
- 获取ContentProvider的顺序为 ApplicationContentResolver#acquireProvider -> ActivityThread#acquireProvider -> ContentProviderHolder#provider
// ContentProviderHolder private ContentProviderHolder(Parcel source) { info = ProviderInfo.CREATOR.createFromParcel(source); provider = ContentProviderNative.asInterface( source.readStrongBinder()); connection = source.readStrongBinder(); noReleaseNeeded = source.readInt() != 0; } //ContentProviderNative static public IContentProvider asInterface(IBinder obj){ if (obj == null) { return null; } IContentProvider in = (IContentProvider)obj.queryLocalInterface(descriptor); if (in != null) { return in; } return new ContentProviderProxy(obj); }
- ContentProviderNative#ContentProviderProxy是客户端通过ApplicationContentResolver获取的并运行在客户端的
- ContentProvider#Transport是运行在服务端的
Socket
NOTE: 使用Socket需要使用网络相关权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- 服务端
private inner class TcpServer : Runnable { private var client: Socket? = null override fun run() { val serverSocket: ServerSocket try { //监听 本地端口 serverSocket = ServerSocket(9999) } catch (e: IOException) { e.printStackTrace() return } while (!serviceDestroyed) {//服务未停止 try { Log.d("Socket", "TcpServer Run") client = serverSocket.accept() thread { try { //客户端连接上了 responseClient(client!!) } catch (e: IOException) { e.printStackTrace() Log.d("Socket", "TcpServer Thread Run onError ${e.message}") } } } catch (e: IOException) { Log.d("Socket", "TcpServer Run OnError ${e.message}") e.printStackTrace() } } client?.close() Log.d("Socket", "TcpServer Client close $serviceDestroyed") } @Throws(IOException::class) private fun responseClient(client: Socket) { val inReader: BufferedReader? = BufferedReader(InputStreamReader(client.getInputStream())) val outWriter: PrintWriter? = PrintWriter(BufferedWriter(OutputStreamWriter(client.getOutputStream())), true) Log.d("Socket", "TcpServer response $serviceDestroyed") while (!serviceDestroyed) { Log.d("Socket", "TcpServer response before $serviceDestroyed") //读取客户端发来的信息 val str = inReader?.readLine() if (str != null) { Log.d("Socket", "TcpServer response $str") val time = SimpleDateFormat("hh:mm:ss.SSS", Locale.ROOT).format(System.currentTimeMillis()) //给客户端写信息 outWriter?.println("通过Socket回传:$str-$time") sendMsgChange(Info("AIDL回传:$str-$time", -1)) /*outWriter?.close() inReader?.close()*/ } } Log.d("Socket", "TcpServer response after $serviceDestroyed") } }
- 客户端
private var mClientSocket: Socket? = null private var mPrintWriter: PrintWriter? = null private fun connectTcpServer() { var socket: Socket? = null while (socket == null) { // 连接失败的话,每隔1秒重连一次 try { //连接到本地端口 socket = Socket("localhost", 9999) mClientSocket = socket // 用于向服务端写入数据 mPrintWriter = PrintWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream())), true) //通过Handler通知Socket连接成功 clientMessengerHandler.sendEmptyMessage(LOCAL_SOCKET_CONNECTED) } catch (e: IOException) { SystemClock.sleep(1000) } } try { //获取服务端发送的数据 BufferedReader(InputStreamReader(socket.getInputStream())).use { while (!this.isFinishing) { var msg = it.readLine() if (!msg.isNullOrEmpty()) { msg = "$msg-$time" //通过Handler将数据发送到UI线程处理 clientMessengerHandler.obtainMessage(LOCAL_SOCKET_SEND_MESSAGE, msg).sendToTarget() } } } } catch (e: IOException) { e.printStackTrace() } } //向服务端发送消息 mPrintWriter?.println("Hello Socket:$time") // onDestroy的时候处理socket相关 override fun onDestroy() { if (mClientSocket != null) { try { mPrintWriter?.close() mClientSocket?.shutdownInput() mClientSocket?.close() } catch (e: IOException) { e.printStackTrace() } } super.onDestroy() }
Binder连接池
NOTE:AIDL的实现方式,主要原理是:
1. 客户端发送对应的Code;
2. 服务端根据Code返回对应的IBinder;
3. 客户端再根据IBinder将之转换成对应的AIDL接口.
IBinderPool.aidl
package com.open.aqrlei.ipc;
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
//#服务端
private val mIBinderPool = object : IBinderPool.Stub() {
@Throws(RemoteException::class)
override fun queryBinder(binderCode: Int): IBinder {
return when (binderCode) {
// method code
}
}
}
// #客户端-ServiceConnection#onServiceConnected
mBinder = IBinderPool.Stub.asInterface(service)
// 获取到Messenger的IBinder
messenger = Messenger(mBinder.queryBinder(USE_MESSENGER_CODE))
IPC方式适用场景(源于Android开发艺术探索)
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Bundle | 简单易用 | 只能传输Bundle支持的数据 | 四大组件间的进程间通信 |
文件共享 | 简单易用 | 不适合高并发,无法做到进程间实时通信 | 无并发,交换简单的数据,实时性不高 |
Messenger | 功能一般,一对多串行通信,实时通信 | 不能很好的处理高并发, 不支持RPC,数据通过Message传输,支持的类型有局限性 | 低并发的一对多通信,无RPC请求(或无需返回值的RPC) |
AIDL | 功能强大,一对多并发,实时通信 | 使用稍微复杂,需要处理好线程同步 | 一对多通信且有RPC需求 |
ContentProvider | 数据访问方面功能强大,一对多并发数据共享,Call方法扩展其它操作 | 主要提供数据源的CRUD | 一对多进程间数据共享 |
Socket | 功能强大,通过网络传输字节流,一对多并发实时通信 | 实现稍微繁琐,不支持RPC | 网络数据交换 |
网友评论