Android基础知识 IPC相关

一、在Android中什么样的情况下会使用多进程模式,如何开启多进程

1、什么情况下使用多进程模式

分担主进程的内存压力

2、如何开启多进程

四大组件,在 Manifest中 指定 android:process 属性

二、Android为什么采用Binder做为IPC机制

1、 Binder

Binder 是 Android中一种跨进程方式。

2、Android 要采用 Binder 作为 IPC 机制

Linux 进程间IPC方式:
1、管道:在创建时分配一个page大小的内存,缓存区大小比较有限;

2、消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;

3、共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;
但进程间的同步问题操作 系统无法实现,必须各进程利用同步工具解决;

4、套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;

5、信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6、信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

7、Binder
1)性能上:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,
但共享内存方式一次内存拷贝都不需要;
从性能角度看,Binder性能仅次于共享内存

2)稳定性
Binder是基于C/S架构的,Server端与Client端相对独立,稳定性较好
而共享内存实现方式复杂,没有客户与服务端之别,
需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;
从这稳定性角度看,Binder架构优越于共享内存。
3)安全上
传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;
Binder 基于C/S架构,系统中对外只暴露Client端,Client端将任务发送给Server端,
Server端会对Client的请求做校验,判断UID/PID是否满足访问权限,Binder的安全性更高

3、 Binder死亡监听 DeathRecipient
public void onServiceConnected(ComponentName name, IBinder service) {
   if (service != null) {
         mBinderManager = IBinderManager.Stub.asInterface(service);
         try {
             mBinderManager.asBinder().linkToDeath(new IBinder.DeathRecipient() {
                 @Override
                 public void binderDied() {//子进程的主线程中监听binder的死亡通知
                     mBinderManager.asBinder().unlinkToDeath(this, 0);
                     mBinderManager = null;
                 }
             }, 0);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
    }           
}

三、IPC进程间数据传输

使用Bundle、使用文件共享、使用Messenger、使用AIDL、使用ContentProvider、使用Socket

1、 使用Bundle

Bundle实现了Parcelable接口,可以传递【基本类型数据和实现了Parcelable接口的对象】

2、 使用文件共享

常规的,就是一个进程把数据写入文件,另一个进程再从同一个文件中去读取数据

3、 使用Messenger

Messenger 对 AIDL做了封装,一次处理一个请求,因此再Server端不用考虑线程同步的问题。
Server端、Client端,分别创建要给Handler对象用于处理消息。
消息传到一端后,都是塞入Handler 的 MessageQueue 中实现串行处理消息,一次只能处理一个消息。

4、 使用AIDL

AIDL文件中不能使用中文注释,否则会生成代码为空的java文件

AIDL支持的数据类型:

  • 1)基本数据类型
  • 2)String、CharSequence
  • 3)List:只支持 ArrayList,里面的每个元素必须能够被AIDL支持
    aidl使用List类型时的使用
  • 4)Map: 只支持 HashMap,里面每个元素必须能够被AIDL支持,包括key和value
  • 5)Parcelable:所有实现了Parcelable接口的对象
  • 6)AIDL:所有AIDL接口本身也可以再AIDL文件中使用
    (最终生成的代码里,会是一个Binder对象,Binder有实现Parcelable接口
5、 使用ContentProvider

用于不同应用间进行数据共享,底层也是Binder 实现

  • 1)Server端app。
    创建自定义 MyContentProvider 类,继承 ContentProvider 类,并实现相关方法

  • 2)Server端app。在Manifest中注册

    <provider 
      	android:name=".provider.MyContentProvider"
      	android:authorities="com.xxx.common.provider"
      	android:permission="com.xxx.PROVIDER"
      	android:process=":provider">
    </provider>
    
  • 3)Client端app。访问MyContentProvider

    Uri uri = Uri.parse("content://com.xxx.common.provider");
    Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
    ...
    
6、 使用Socket
  • 1)权限

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    
  • 2)具体Socket服务端、客户端代码
    // TODO …

四、AIDL的语义

AIDL文件,是系统为提供的一种快速实现Binder的工具

AIDL支持的数据类型:

  • 1)基本数据类型
  • 2)String、CharSequence
  • 3)List:只支持 ArrayList,里面的每个元素必须能够被AIDL支持
  • 4)Map: 只支持 HashMap,里面每个元素必须能够被AIDL支持,包括key和value
  • 5)Parcelable:所有实现了Parcelable接口的对象
  • 6)AIDL:所有AIDL接口本身也可以再AIDL文件中使用
    (最终生成的代码里,会是一个Binder对象,Binder有实现Parcelable接口

// IWebAidlInterface.aidl

package com.xxx.common.webview;
// Declare any non-default types here with import statements
import com.xxx.common.webview.IWebBinderCallback;
interface IWebBinder {
	 /**
	  * methodName: 方法名   jsonParams: 方法参数    callback跨进程回调函数
	  */
	  void handleJsFunction(String actionName, String jsonParams, IWebBinderCallback callback);
	 /**
	  * type:消息类型   data:数据
	  */
	  void sendEventBus(int type, boolean success, String data);
}

五、RemoteCallbackList

RemoteCallbackList 是系统提供的,用于删除跨进程 listener 的类

  • Client端请求Server端,需要回调时,是通过AIDL定义的接口回调的。

    AIDL定义的接口,实际上转为Binder对象传到Server端的

  • Binder 会把客户端传递过来的对象重新转化并生成一个【新的对象
    虽然我们在注册和解注册过程中使用的是同一个客户端,但是通过 Binder 传递到服务端后,却会产生全新的对象。
    因为对象的跨进程传输,本质上都是反序列化的过程。

  • 如果Server端,用普通的容器管理Client端传的listener的化,
    客户端每次调用,Server端都会反序列化创建新的listener,导致容器中出现多个listener
    在反注册listener的时候,找不到匹配的listener

    所以Server端管理listener,要用 RemoteCallbackList 来管理,不能用普通的容器

  • 原理
    1、RemoteCallbackList,存入其中的所有 IBinder 类型跨进程接口,都会 linkToDeath(),接口Binder销毁时,会从 RemoteCallbackList 中删除该 接口
    2、RemoteCallbackList,用 ArrayMap<IBinder, Callback> 存储传入的回调函数
    客户端进程传入的 Callback,到服务端进程、反序列化后是一个新对象,但是,
    Callback 底层的 Binder对象 是同一个,RemoteCallbackList 中使用key-value方式存储回调函数,key 是 Binder,可以根据Binder,找到对应的回调函数

  • Demo:Service中

    private final RemoteCallbackList<IPersonArrivedListener> mListenerList = new RemoteCallbackList<>();
      
      	// AIDL中定义的注册接口的方法,在Service中的具体实现
      	 @Override
          public void registerListener(IOnNewPersonArrivedListener listener) throws RemoteException {
              mListenerList.register(listener);
          }
    
      	// AIDL中定义的反注册接口的方法,在Service中的具体实现
          @SuppressLint("NewApi")
          @Override
          public void unregisterListener(IOnNewPersonArrivedListener listener) throws RemoteException {
              mListenerList.unregister(listener);
          }
      	
      	//遍历 mListenerList 并 回调到 Client 端
      	private void onNewPersonArrived(Person person) throws RemoteException {
      		synchronized (mListenerList) {
      			int n = mListenerList.beginBroadcast();
      			try {
      				for (int i = 0; i < n; i++) {
      					IOnNewPersonArrivedListener listener = mListenerList.getBroadcastItem(i);
      					if (listener != null) {
      						listener.onNewPersonArrived(person);
      					}
      				}
      			} catch (RemoteException e) {
      				e.printStackTrace();
      			}
      			mListenerList.finishBroadcast();
      		}
    }
    

六、 AIDL 中使用权限验证功能

1、 AndroidMenifest 中定义 Service所需的权限
 <permission
	android:name="com.wuc.aidlservice.permission.ACCESS_SERVICE"
	android:protectionLevel="normal"/>
2、 IRemoteService的 onBind() 中调用
mContext.checkCallingOrSelfPermission
(…) 验证权限
@Nullable
@Override
public IBinder onBind(Intent intent) {
	int check = mContext.checkCallingOrSelfPermission("com.wuc.aidlservice.permission.ACCESS_SERVICE");
	if (check == PackageManager.PERMISSION_DENIED) {
		return null;
	}
	return mIBinder;
}
3、 AndroidMenifest 中声明权限
<uses-permission android:name="com.wuc.aidlservice.permission.ACCESS_SERVICE"/>

七、AIDL生成Java文件详细分析

1、 DESCRIPTOR

Binder 的唯一标识,一般用当前Binder 的类名表示。

2、asInterface(android.os.IBinder obj)

将服务端的 Binder对象转换成【客户端所需的AIDL接口类型的对象】
如果C-S 同进程,返回 Server端的 Stub对象本身
如果C-S 不同进程,返回 系统封装后的 Stub.Proxy对象。

3、 asBinder

返回当前 Binder 对象

4、Service # onTransact (int code, Parcel data, Parcel reply, int flags)
  • 1)该方法 运行在 Server端 的 Binder线程池
    客户端发起的跨进程请求,会通过系统底层封装后交由此方法处理

    public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
    
  • 2)Server端通过code,确定Client端所请求的目标方法

  • 3)如果目标方法有 参数,会从data中 取出目标方法所需参数

  • 4)执行目标方法

  • 5)如果有 返回值,方法执行完毕后,向reply中写入返回值

  • 6)如果 onTransact 方法 返回 false, 客户端的请求会失败

5、 Proxy#sendEventBus

AIDL中定义的方法,运行在客户端

客户端远程调用此方法时

  • 1)创建该方法需要的输入型Parcel对象_data、输出型Parcel对象_reply
    如果有返回值,还会创建返回值对象
    如果有参数,把该方法的参数写入_data 中

  • 2)客户端调用 IBinder # transact(…) 方法,发起RPC (远程调用)请求,同时 将当前线程挂起

  • 3)服务端的 onTransact() 方法会被调用
    直到 RPC过程返回后,Client端挂起的线程继续执行
    并从_reply 中取出 RPC 过程返回的结果

  • 4)如果有返回值,最后还会返回_reply中的数据

  • 由于Client端发起请求时线程会挂起,所以如果请求的方法很耗时,则不能放到UI线程发起请求

  • 由于 Server端的Binder 方法,运行在 Binder的线程池中,所以 Binder 方法应该同步的方式去实现,应该它已经运行在一个线程中了。

八、Binder连接池,方便业务解耦

public class BinderPool {
	public static final int BINDER_SPEAK = 0;
	public static final int BINDER_CALCULATE = 1;
 
	private Context mContext;
	private IBinderPool mBinderPool;
	private static volatile BinderPool sInstance;
	private CountDownLatch mConnectBinderPoolCountDownLatch;
 
	private BinderPool(Context context) {
		mContext = context.getApplicationContext();
		connectBinderPoolService();
	}
 
	public static BinderPool getInstance(Context context) {
		if (sInstance == null) {
			synchronized (BinderPool.class) {
				if (sInstance == null) {
					sInstance = new BinderPool(context);
				}
			}
		}
		return sInstance;
	}
 
	private synchronized void connectBinderPoolService() {
		mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
		Intent service = new Intent(mContext, BinderPoolService.class);
		mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
		try {
			mConnectBinderPoolCountDownLatch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public IBinder queryBinder(int binderCode) {
		IBinder binder = null;
		try {
			if (mBinderPool != null) {
				binder = mBinderPool.queryBinder(binderCode);
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return binder;
	}
 
	private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
 
		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
 
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mBinderPool = IBinderPool.Stub.asInterface(service);
			try {
				mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
			mConnectBinderPoolCountDownLatch.countDown();
		}
	};
 
	private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {    // 6
		@Override
		public void binderDied() {
			mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
			mBinderPool = null;
			connectBinderPoolService();
		}
	};
 
	public static class BinderPoolImpl extends IBinderPool.Stub {
 
		public BinderPoolImpl() {
			super();
		}
 
		@Override
		public IBinder queryBinder(int binderCode) throws RemoteException {
			IBinder binder = null;
			switch (binderCode) {
				case BINDER_SPEAK: {
					binder = new Speak();
					break;
				}
				case BINDER_CALCULATE: {
					binder = new Calculate();
					break;
				}
				default:
					break;
			}
 
			return binder;
		}
	}
}

推荐阅读:
《Android开发艺术探索》 第二章 IPC机制
《深如理解LINUX内核 第三版涵盖2.6版》 第三章 进程 3.2节进程描述符
写给 Android 应用工程师的 Binder 原理剖析
如果需要深入了解,推荐GitYuan大大的 Binder 系列文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值