Implementation of cross-process invocation
ContentProvider
CC implements cross-process communication through custom ContentProviders, which are defined for each application in androidmanifest.xml.
- Exported is true to indicate that it can be invoked across processes.
- ApplicationId is configured in build.gradle and is usually the package name of the application
- Aithroities is the package name + the name of the provider itself
<provider
android:authorities="${applicationId}.com.billy.cc.core.remote"
android:name=".remote.RemoteProvider"
android:exported="true"
/>
Copy the code
RemoteProvider
RemoteProvider inherits ContentProvider and overrides the Query () method to return a RemoteCursor object
public class RemoteProvider extends ContentProvider {
public static final String[] PROJECTION_MAIN = {"cc"};
public static final String URI_SUFFIX = "com.billy.cc.core.remote";
@Override
public boolean onCreate(a) {
CC.log("RemoteProvider onCreated! class:%s".this.getClass().getName());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if (CC.isRemoteCCEnabled() || getCallingUid() == Process.myUid()) {
// Get the RemoteCursor singleton from the current ContentProvider process
return RemoteCursor.getInstance();
}
return null;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0; }}Copy the code
RemoteCursor
RemoteCursor inherits the MatrixCursor and is a cursor used for cross-process communication, passing IBinder objects across processes through bundles.
public class RemoteCursor extends MatrixCursor {
private static final String KEY_BINDER_WRAPPER = "BinderWrapper";
static final String[] DEFAULT_COLUMNS = {"cc"};
/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the singleton pattern start -- -- -- -- -- -- -- -- -- -- -- -- -- --
/** singleton Holder */
private static class CCCursorHolder {
private static final RemoteCursor INSTANCE = new RemoteCursor(DEFAULT_COLUMNS, RemoteCCService.getInstance());
}
private RemoteCursor(String[] columnNames, IBinder binder) {
super(columnNames);
binderExtras.putParcelable(KEY_BINDER_WRAPPER, new BinderWrapper(binder));
}
/** Gets the CCCursor singleton in the current process */
public static RemoteCursor getInstance(a) {
return RemoteCursor.CCCursorHolder.INSTANCE;
}
/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the singleton pattern end -- -- -- -- -- -- -- -- -- -- -- -- -- --
private Bundle binderExtras = new Bundle();
@Override
public Bundle getExtras(a) {
return binderExtras;
}
public static IRemoteCCService getRemoteCCService(Cursor cursor) {
if (null == cursor) {
return null;
}
Bundle bundle = cursor.getExtras();
bundle.setClassLoader(BinderWrapper.class.getClassLoader());
BinderWrapper binderWrapper = bundle.getParcelable(KEY_BINDER_WRAPPER);
if(binderWrapper ! =null) {
IBinder binder = binderWrapper.getBinder();
return IRemoteCCService.Stub.asInterface(binder);
}
return null; }}Copy the code
BinderWrapper
BinderWrapper is used to encapsulate the IBinder and implement the Parcelable interface so that IBinder objects can be serialized and deserialized and can be passed across processes.
The process for cross-process CC calls
-
Enable cross-process function, CC query native APP that supports CC components (start task through thread pool, listen every 50ms)
(1) Monitor the installation and uninstallation of the local application and update it
private void listenComponentApps(a) { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addAction(Intent.ACTION_MY_PACKAGE_REPLACED); intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); intentFilter.addDataScheme(INTENT_FILTER_SCHEME); CC.getApplication().registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String packageName = intent.getDataString(); if (TextUtils.isEmpty(packageName)) { return; } if (packageName.startsWith(INTENT_FILTER_SCHEME)) { packageName = packageName.replace(INTENT_FILTER_SCHEME + ":".""); } String action = intent.getAction(); CC.log("onReceived..... pkg=" + packageName + ", action=" + action); if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { REMOTE_CONNECTIONS.remove(packageName); } else { CC.log("start to wakeup remote app:%s", packageName); if (RemoteConnection.tryWakeup(packageName)) { ComponentManager.threadPool(new ConnectTask(packageName)); } } } }, intentFilter); } Copy the code
(2) Query all installed applications that support CC components, and periodically query the Binder objects available for the application through the thread pool
class ConnectTask implements Runnable { String packageName; ConnectTask(String packageName) { this.packageName = packageName; } @Override public void run(a) { IRemoteCCService service = getMultiProcessService(packageName); if(service ! =null) { REMOTE_CONNECTIONS.put(packageName, service); }}}Copy the code
-
Query the RemoteProvider of the corresponding application by using the ContentResolver
private static IRemoteCCService getService(String processNameTo) { Cursor cursor = null; try { cursor = CC.getApplication().getContentResolver() .query(getDispatcherProviderUri(processNameTo) , RemoteProvider.PROJECTION_MAIN, null , null.null ); if (cursor == null) { return null; } return RemoteCursor.getRemoteCCService(cursor); } finally { if(cursor ! =null) { try { cursor.close(); } catch(Exception e) { CCUtil.printStackTrace(e); }}}}Copy the code
-
If you get a Cursor, get the Cursor’s bundle and serialize it into BinderWrapper. After you get the IBinder object, convert it into an IRemoteCCService interface object.
public static IRemoteCCService getRemoteCCService(Cursor cursor) { if (null == cursor) { return null; } Bundle bundle = cursor.getExtras(); bundle.setClassLoader(BinderWrapper.class.getClassLoader()); BinderWrapper binderWrapper = bundle.getParcelable(KEY_BINDER_WRAPPER); if(binderWrapper ! =null) { IBinder binder = binderWrapper.getBinder(); return IRemoteCCService.Stub.asInterface(binder); } return null; } Copy the code
-
The IRemoteCCService object makes cross-process calls to components on the corresponding application
interface IRemoteCCService { void call(in RemoteCC remoteCC, in IRemoteCallback callback); void cancel(String callId); void timeout(String callId); String getComponentProcessName(String componentName); } Copy the code