作者:蒼王
時間:2018.6.1
以下是我這個系列的相關文章,有興趣可以參考一下,可以給個喜歡或者關注我的文章。
[Android]如何做一個崩潰率少於千分之三噶應用app–章節列表
元件化群1已經滿員,進來的可以加群2 763094035
近來看到愛奇藝釋出了多程式的架構框架Andromeda。研究一下其多程式的通訊方式。
具體github地址
通過此框架的初步分析
1.通過grade外掛完善AndroidManifest.xml配置檔案
2.通過ContentProvider傳輸binder物件
3.活動binder.stub物件
4.動態繫結程式service以及binder物件
5.多程式binder管理
其在利用自定義Gradle外掛來插入DispatchService和DipatchProvider。
自定義的Gradle外掛只會有app module上執行。
其會讀取Manifest裡面的內容並進行修改。
1.查詢遍歷Manifest檔案
void injectStubServiceToManifest(Project project) {
println "injectStubServiceToManifest"
//主目錄
rootDirPath = project.rootDir.absolutePath
def android = project.extensions.getByType(AppExtension)
this.dispatcher = project.extensions.getByType(DispatcherExtension)
project.afterEvaluate {
android.applicationVariants.all { variant ->
if (pkgName == null) {
//獲取包名
pkgName = getPackageName(variant)
println "pkgName:" + pkgName
}
//查詢遍歷Mainfest檔案
variant.outputs.each { output ->
output.processManifest.doLast {
println "manifestOutputDirectory:" + output.processManifest.manifestOutputDirectory.absolutePath
//output.getProcessManifest().manifestOutputDirectory
output.processManifest.outputs.files.each { File file ->
//在gradle plugin 3.0.0之前,file是檔案,且檔名為AndroidManifest.xml
//在gradle plugin 3.0.0之後,file是目錄,且不包含AndroidManifest.xml,需要自己拼接
//除了目錄和AndroidManifest.xml之外,還可能會包含manifest-merger-debug-report.txt等不相干的檔案,過濾它
if ((file.name.equalsIgnoreCase("AndroidManifest.xml") && !file.isDirectory()) || file.isDirectory()) {
if (file.isDirectory()) {
//3.0.0之後,自己拼接AndroidManifest.xml
injectManifestFile(new File(file, "AndroidManifest.xml"))
} else {
//3.0.0之前,直接使用
injectManifestFile(file)
}
}
}
}
}
}
}
}
複製程式碼
其內會寫入三種檔案
def static final STUB_SERVICE = `org.qiyi.video.svg.stub.CommuStubService$CommuStubService`
xml.application {
int index = 0
//每個程式都宣告使用這個CommuStubService檔案
customProcessNames.each {
//加上序號,$CommuStuService會替換成數字
String serviceName = "${STUB_SERVICE}" + index.toString()
service("${NAME}": serviceName,
"${ENABLED}": "${TRUE}",
"${EXPORTED}": "${FALSE}",
"${PROCESS}": it
)
if (matchedServices == null) {
matchedServices = new HashMap<>()
}
matchedServices.put(it, serviceName)
++index
}
複製程式碼
這裡DipatcherService和DipatchProvider是binder的中轉管理控制元件,預設配置到主程式。
//配置程式地址
this.dispatcher = project.extensions.getByType(DispatcherExtension)
//之後,寫入DispatcherService和DispatcherProvider
def dispatcherProcess = dispatcher.process
println "dispatcher.process:" + dispatcher.process
if (dispatcherProcess != null && dispatcherProcess.length() > 0) {
service("${NAME}": DISPATCHER_SERVICE,
"${ENABLED}": "${TRUE}",
"${EXPORTED}": "${FALSE}",
"${PROCESS}": dispatcherProcess
)
provider(
"${AUTHORITIES}": getAuthority(),
"${EXPORTED}": "${FALSE}",
"${NAME}": DISPTACHER_PROVIDER,
"${ENABLED}": "${TRUE}",
"${PROCESS}": dispatcherProcess
)
} else {
service("${NAME}": DISPATCHER_SERVICE,
"${ENABLED}": "${TRUE}",
"${EXPORTED}": "${FALSE}"
)
provider(
"${AUTHORITIES}": getAuthority(),
"${EXPORTED}": "${FALSE}",
"${NAME}": DISPTACHER_PROVIDER,
"${ENABLED}": "${TRUE}"
)
}
複製程式碼
正如通訊所涉及的必要的幾個步驟
1.註冊
2.發其通訊
3.binder間的相互繫結
4.binder間傳輸資料
首先註冊,通過公共的Andromeda入口註冊提供遠端溝通的服務
Andromeda.getInstance().registerRemoteService(IBuyApple.class, BuyAppleImpl.getInstance());
//RemoteTransfer
public static <T extends IBinder> void registerRemoteService(Class serviceClass, T stubBinder) {
if (null == serviceClass || null == stubBinder) {
return;
}
RemoteTransfer.getInstance().registerStubService(serviceClass.getCanonicalName(), stubBinder);
}
//RemoteServiceTransfer
public void registerStubServiceLocked(String serviceCanonicalName, IBinder stubBinder,
Context context, IDispatcher dispatcherProxy, IRemoteTransfer.Stub stub) {
stubBinderCache.put(serviceCanonicalName, stubBinder);
if (dispatcherProxy == null) {
BinderWrapper wrapper = new BinderWrapper(stub.asBinder());
Intent intent = new Intent(context, DispatcherService.class);
intent.setAction(Constants.DISPATCH_REGISTER_SERVICE_ACTION);
intent.putExtra(Constants.KEY_REMOTE_TRANSFER_WRAPPER, wrapper);
intent.putExtra(Constants.KEY_BUSINESS_BINDER_WRAPPER, new BinderWrapper(stubBinder));
intent.putExtra(Constants.KEY_SERVICE_NAME, serviceCanonicalName);
setProcessInfo(intent, context);
ServiceUtils.startServiceSafely(context, intent);
} else {
try {
dispatcherProxy.registerRemoteService(serviceCanonicalName,
ProcessUtils.getProcessName(context), stubBinder);
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
複製程式碼
啟動Service註冊到DispatcherService當中
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
return super.onStartCommand(intent, flags, startId);
}
Logger.d("DispatcherService-->onStartCommand,action:" + intent.getAction());
if (Constants.DISPATCH_REGISTER_SERVICE_ACTION.equals(intent.getAction())) {
//註冊遠端服務
registerRemoteService(intent);
} else if (Constants.DISPATCH_UNREGISTER_SERVICE_ACTION.equals(intent.getAction())) {
//登出遠端服務
unregisterRemoteService(intent);
} else if (Constants.DISPATCH_EVENT_ACTION.equals(intent.getAction())) {
//傳遞資料
publishEvent(intent);
}
return super.onStartCommand(intent, flags, startId);
}
private void registerRemoteService(Intent intent) {
BinderWrapper wrapper = intent.getParcelableExtra(Constants.KEY_REMOTE_TRANSFER_WRAPPER);
BinderWrapper businessWrapper = intent.getParcelableExtra(Constants.KEY_BUSINESS_BINDER_WRAPPER);
String serviceCanonicalName = intent.getStringExtra(Constants.KEY_SERVICE_NAME);
int pid = intent.getIntExtra(Constants.KEY_PID, -1);
String processName = intent.getStringExtra(Constants.KEY_PROCESS_NAME);
try {
if (TextUtils.isEmpty(serviceCanonicalName)) {
//注意:RemoteTransfer.sendRegisterInfo()時,serviceCanonicalName為null,這是正常的,此時主要目的是reigsterAndReverseRegister()
Logger.e("service canonical name is null");
} else {
//註冊到分發器上管理
Dispatcher.getInstance().registerRemoteService(serviceCanonicalName,
processName, businessWrapper.getBinder());
}
} catch (RemoteException ex) {
ex.printStackTrace();
} finally {
if (wrapper != null) {
registerAndReverseRegister(pid, wrapper.getBinder());
}
}
}
複製程式碼
ServiceDipatcher負責管理服務資訊
@Override
public void registerRemoteServiceLocked(final String serviceCanonicalName, String processName,
IBinder binder) throws RemoteException {
Log.d(TAG, "ServiceDispatcher-->registerStubServiceLocked,serviceCanonicalName:" + serviceCanonicalName + ",pid:" + android.os.Process.myPid() + ",thread:" + Thread.currentThread().getName());
if (binder != null) {
binder.linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Logger.d("ServiceDispatcher-->binderDied,serviceCanonicalName:" + serviceCanonicalName);
BinderBean bean = remoteBinderCache.remove(serviceCanonicalName);
//實際上這裡是還沒實現執行緒同步,但是並不會影響執行結果,所以其實下面這句就沒有同步的必要。
if (bean != null) {
emergencyHandler.handleBinderDied(Andromeda.getAppContext(), bean.getProcessName());
}
}
}, 0);
remoteBinderCache.put(serviceCanonicalName, new BinderBean(binder, processName));
Logger.d("ServiceDispatcher-->registerRemoteServiceLocked(),binder is not null");
} else {
Log.d(TAG, "ServiceDispatcher-->registerRemoteServiceLocked(),binder is null");
}
}
複製程式碼
最後需要完成反向繫結,意思是Dispatcher註冊可以傳輸的binder物件,傳輸中心也需要繫結Dispatcher物件
/**
* 註冊和反向註冊
*
* @param pid
* @param transterBinder
*/
private void registerAndReverseRegister(int pid, IBinder transterBinder) {
Logger.d("DispatcherService-->registerAndReverseRegister,pid=" + pid + ",processName:" + ProcessUtils.getProcessName(pid));
IRemoteTransfer remoteTransfer = IRemoteTransfer.Stub.asInterface(transterBinder);
Dispatcher.getInstance().registerRemoteTransfer(pid, transterBinder);
if (remoteTransfer != null) {
Logger.d("now register to RemoteTransfer");
try {
remoteTransfer.registerDispatcher(Dispatcher.getInstance().asBinder());
} catch (RemoteException ex) {
ex.printStackTrace();
}
} else {
Logger.d("IdspatcherRegister IBinder is null");
}
}
複製程式碼
在呼叫遠端的時候getRemoteSevice獲取目標的binder物件
IBuyApple buyApple = IBuyApple.Stub.asInterface(Andromeda.with(this).getRemoteService(IBuyApple.class));
複製程式碼
遠端管理RemoteManager
@Override
public IBinder getRemoteService(Class<?> serviceClass) {
if (null == serviceClass) {
return null;
}
return getRemoteService(serviceClass.getCanonicalName());
}
@Override
public synchronized IBinder getRemoteService(String serviceCanonicalName) {
Logger.d(this.toString() + "-->getRemoteService,serviceName:" + serviceCanonicalName);
if (TextUtils.isEmpty(serviceCanonicalName)) {
return null;
}
BinderBean binderBean = RemoteTransfer.getInstance().getRemoteServiceBean(serviceCanonicalName);
String commuStubServiceName = ConnectionManager.getInstance().bindAction(appContext, binderBean.getProcessName());
commuStubServiceNames.add(commuStubServiceName);
return binderBean.getBinder();
}
複製程式碼
RemoteDispatcher會呼叫DipatchProvider來獲取遠端的binder物件
@Override
public synchronized BinderBean getRemoteServiceBean(String serviceCanonicalName) {
Logger.d("RemoteTransfer-->getRemoteServiceBean,pid=" + android.os.Process.myPid() + ",thread:" + Thread.currentThread().getName());
//獲取binder包裝的物件
BinderBean cacheBinderBean = serviceTransfer.getIBinderFromCache(context, serviceCanonicalName);
if (cacheBinderBean != null) {
return cacheBinderBean;
}
if (null == dispatcherProxy) {
IBinder dispatcherBinder = getIBinderFromProvider();
if (null != dispatcherBinder) {
Logger.d("the binder from provider is not null");
dispatcherProxy = IDispatcher.Stub.asInterface(dispatcherBinder);
registerCurrentTransfer();
}
}
if (null == dispatcherProxy) {
sendRegisterInfo();
try {
wait(MAX_WAIT_TIME);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
if (serviceTransfer == null || dispatcherProxy == null) {
return null;
}
return serviceTransfer.getAndSaveIBinder(serviceCanonicalName, dispatcherProxy);
}
private IBinder getIBinderFromProvider() {
Logger.d("RemoteTransfer-->getIBinderFromProvider()");
Cursor cursor = null;
try {
//查詢出cursor物件
cursor = context.getContentResolver().query(getDispatcherProviderUri(), DispatcherProvider.PROJECTION_MAIN,
null, null, null);
if (cursor == null) {
return null;
}
//使用DispatcherCursor解包
return DispatcherCursor.stripBinder(cursor);
} finally {
IOUtils.closeQuietly(cursor);
}
}
複製程式碼
DispatcherProvider使用的查詢返回
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Logger.d("DispatcherProvider-->query,uri:" + uri.getAuthority());
//DispatcherCursor封裝binder
return DispatcherCursor.generateCursor(Dispatcher.getInstance().asBinder());
}
//通過
public static DispatcherCursor generateCursor(IBinder binder) {
try {
DispatcherCursor cursor;
cursor = cursorCache.get(binder.getInterfaceDescriptor());
if (cursor != null) {
return cursor;
}
cursor = new DispatcherCursor(DEFAULT_COLUMNS, binder);
cursorCache.put(binder.getInterfaceDescriptor(), cursor);
return cursor;
} catch (RemoteException ex) {
return null;
}
}
//通過BinderWrapper來封裝binder
public DispatcherCursor(String[] columnNames, IBinder binder) {
super(columnNames);
binderExtras.putParcelable(KEY_BINDER_WRAPPER, new BinderWrapper(binder));
}
複製程式碼
@Override
public synchronized IBinder getRemoteService(String serviceCanonicalName) {
Logger.d(this.toString() + "-->getRemoteService,serviceName:" + serviceCanonicalName);
if (TextUtils.isEmpty(serviceCanonicalName)) {
return null;
}
//獲取binder資訊
BinderBean binderBean = RemoteTransfer.getInstance().getRemoteServiceBean(serviceCanonicalName);
//在binder繫結啟動的程式service
String commuStubServiceName = ConnectionManager.getInstance().bindAction(appContext, binderBean.getProcessName());
commuStubServiceNames.add(commuStubServiceName);
return binderBean.getBinder();
}
複製程式碼
一開始介紹插入了CommuStubService物件,這裡通過StubServiceMatcher通過程式名來找到啟動的程式的service,然後將binder和service繫結到一起
//這裡不能按照serviceCanonicalName來區分,而是要按照target service來劃分,如果targetService一樣,那就沒必要再繫結
public synchronized String bindAction(Context context, String serverProcessName) {
Logger.d("ConnectionManager-->bindAction,serverProcessName:" + serverProcessName);
//匹配對應的binder對應的Service(CommuStubService$0)
Intent intent = StubServiceMatcher.matchIntent(context, serverProcessName);
if (null == intent) {
Logger.d("match intent is null");
return null;
}
String commuStubServiceName = getCommuStubServiceName(intent);
ConnectionBean bean = connectionCache.get(commuStubServiceName);
if (null == bean) {
//服務連線
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Logger.d("onServiceConnected,name:" + name.getShortClassName());
}
@Override
public void onServiceDisconnected(ComponentName name) {
Logger.d("onServiceDisconnected,name:" + name.getShortClassName());
}
};
bean = new ConnectionBean(connection);
connectionCache.put(commuStubServiceName, bean);
Logger.d("really start to bind");
//啟動服務
context.bindService(intent, connection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
} else {
bean.increaseRef();
}
return commuStubServiceName;
}
複製程式碼
1.通過gradle外掛來動態新增配置的四大元件資料
2.通過分發DispatchService服務來管理binder物件
3.通過Parcelable序列化傳輸binder
4.活用IBinder.Stub物件