CC跨进程调用的实现

939 阅读2分钟

跨进程调用的实现

ContentProvider

​ CC通过自定义ContentProvider实现跨进程通信,每个应用在AndroidManifest.xml中定义好provider。

  • exported为true,表明可跨进程调用。
  • applicationId在build.gradle中配置,一般是应用的包名
  • aithroities是包名+provider本身的名称
<provider
	android:authorities="${applicationId}.com.billy.cc.core.remote"
 	android:name=".remote.RemoteProvider"
 	android:exported="true"
 	/>

RemoteProvider

​ RemoteProvider继承了ContentProvider,重写了query()方法,返回一个RemoteCursor对象

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() {
        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()) {
            //获取当前ContentProvider所在进程中的RemoteCursor单例对象
            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;
    }
}

RemoteCursor

​ RemoteCursor继承了MatrixCursor,是用于跨进程通信的游标,通过bundle跨进程传递IBinder对象。

public class RemoteCursor extends MatrixCursor {
    private static final String KEY_BINDER_WRAPPER = "BinderWrapper";

    static final String[] DEFAULT_COLUMNS = {"cc"};

    //-------------------------单例模式 start --------------
    /** 单例模式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));
    }
    /** 获取CCCursor在当前进程中的单例对象 */
    public static RemoteCursor getInstance() {
        return RemoteCursor.CCCursorHolder.INSTANCE;
    }
    //-------------------------单例模式 end --------------

    private Bundle binderExtras = new Bundle();

    @Override
    public Bundle getExtras() {
        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;
    }
}

BinderWrapper

​ BinderWrapper用于封装IBinder,实现Parcelable接口,使得IBinder对象可以被序列化和反序列化,并可以跨进程传递。

跨进程CC调用的流程

  1. 开启跨进程功能,CC查询本机支持CC组件的app(通过线程池启动任务,每隔50ms进行监听)

    (1)监听本机应用的安装和卸载等事件,并更新

    private void listenComponentApps() {
        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);
    }
    

    (2)查询所有安装的应用中支持CC组件的应用,并通过线程池定时查询该应用的 可供调用的Binder对象

    class ConnectTask implements Runnable {
        String packageName;
    
        ConnectTask(String packageName) {
            this.packageName = packageName;
        }
    
        @Override
        public void run() {
            IRemoteCCService service = getMultiProcessService(packageName);
            if (service != null) {
                REMOTE_CONNECTIONS.put(packageName, service);
            }
        }
    }
    
  2. 通过ContentResolver查询对应应用的RemoteProvider

    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);
                }
            }
        }
    }
    
  3. 如果获取到Cursor,拿到该Cursor的bundle,并序列化为BinderWrapper,得到IBinder对象后,转换成IRemoteCCService接口对象。

    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;
    }
    
  4. 通过IRemoteCCService对象就可以执行跨进程调用对应应用上的组件

    interface IRemoteCCService {
    
        void call(in RemoteCC remoteCC, in IRemoteCallback callback);
    
        void cancel(String callId);
    
        void timeout(String callId);
    
        String getComponentProcessName(String componentName);
    }