Android基础只是 IPC相关

点点圈 提交于 2019-12-02 09:17:11

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

1、什么情况下使用多进程模式
	分担主进程的内存压力
	
2、如何开启多进程
	四大组件,在Manifest中 指定 android:process 属性。

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

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

2、Android 要采用 Binder 作为 IPC 机制
	https://blog.csdn.net/lanye11/article/details/77962825
	
3、 Binder死亡监听
	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;
                        /* 此处回调到了Server端的主线程中,如果这里重新连接服务就是在主线程连接服务
						 * 可以放到子线程重新请求连接Service
						 */
                    }
                }, 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
	
	支持的数据类型:
		1)基本数据类型
		2)String、CharSequence
		3)List:只支持 ArrayList,里面的每个元素必须能够被AIDL支持
		4)Map: 只支持 HashMap,里面每个元素必须能够被AIDL支持,包括key和value
		5)Parcelable:所有实现了Parcelable接口的对象
		6)AIDL:所有AIDL接口本身也可以再AIDL文件中使用(最终生成的代码里,会是一个Binder对象,Binder有实现Parcelable接口)

5、 使用ContentProvider
	android中用于不同应用间进行数据共享的方式。
	底层也是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的工具。

支持的数据类型:
	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 来管理,不能用普通的容器

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的onBinder中做权限验证
	@Nullable
	@Override
	public IBinder onBind(Intent intent) {
		int check = 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接口类型的对象】。
	如果Client端与Server端位于同一进程,那么此方法返回 Server端的 Stub对象本身。
	如果Client端与Server端不同进程,返回的是系统封装后的 Stub.Proxy对象。

3、 asBinder
	此方法用于返回当前 Binder 对象
	
4、 onTransact

	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)调用 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 原理剖析 https://www.jianshu.com/p/429a1ff3560c
如果需要深入了解,推荐GitYuan大大的 Binder 系列文章 http://gityuan.com/2015/10/31/binder-prepare/

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!