android 组件化框架CC源码简要分析

最近项目中引入一个组件化的框架CC,框架很新。我们抱着学习的态度欣赏这个框架的源码。

这只是一部分供我们使用的代码,他有部分源码是操纵字节码,将用户静态的组件,初始化加入自己的static静态代码段。
在这里插入图片描述

我们先从简单的开始分析,这个一个缓存的对象池。用到ConcurrentLinkedQueue并发队列做他的底层容器。两个接口Resetable,Initable分别对应,放入池中被reset和用所给的参数从池中取出一个初始化。get方法中,如果池中有就直接取出,否则在用户实现的newInstance方法中创建一个。

public abstract class ObjPool<T, R> {

	protected ConcurrentLinkedQueue<T> list;

	/**
	 * 创建一个对象池,不限制缓存数量
	 */
	public ObjPool() {
		list = new ConcurrentLinkedQueue<>();
	}

	/**
	 * 从对象池中获取一个实例
	 * 优先从缓存中获取,如果缓存中没有实例,则创建一个实例并返回
	 * @param r 创建一个新实例需要的参数
	 * @return 获取的实例
	 */
	public T get(R r) {
		T t = list.poll();
		if (t == null) {
		    t = newInstance(r);
		}
		if (t != null && t instanceof Initable) {
			((Initable<R>) t).init(r);
		}
		return t;
	}


	/**
	 * 接收一个实例放到对象池中
	 * @param t 要放入对象池的实例
	 */
	public void put(T t) {
		if (t != null) {
			if (t instanceof Resetable) {
			    ((Resetable) t).reset();
			}
			list.offer(t);
		}
	}

	/**
	 * 清空对象池
	 * 推荐在确定不再需要此对象池的时候调用此方法来清空缓存的实例
	 */
	public void clear() {
		list.clear();
	}

	/**
	 * 创建新的实例
	 * @param r 创建对象所需的参数
	 * @return 新的实例
	 */
	protected abstract T newInstance(R r);

	public interface Resetable {
		void reset();
	}
	public interface Initable<R> {
		void init(R r);
	}
}

在项目中看到是aidl生成的java 文件,特地把源文件找出来,作者介绍是利用contentprovider做跨进程通信。一般都是把aidl文件放在client service两个项目中,通过bindService,获取binder对象,client用 xxx.Stub.Proxy对应的方法跨进程通信。这个项目唯一的区别可能是他这个binder是通过contentProvider(其实也是binder通信)跨进程传递,传递过来的仍然是原binder对象,这个是内部的Parcelable实现决定的。下次要分析下框架层这块的机理。

package com.billy.cc.core.component.remote;

// Declare any non-default types here with import statements
import com.billy.cc.core.component.remote.RemoteCCResult;

interface IRemoteCallback {

    void callback(in RemoteCCResult remoteCCResult);

}


// com.billy.core.component.IProcessCrossCCService.aidl
package com.billy.cc.core.component.remote;

// Declare any non-default types here with import statements
import com.billy.cc.core.component.remote.RemoteCC;
import com.billy.cc.core.component.remote.RemoteCCResult;
import com.billy.cc.core.component.remote.IRemoteCallback;

interface IRemoteCCService {

    void call(in RemoteCC remoteCC, in IRemoteCallback callback);

    void cancel(String callId);

    void timeout(String callId);

    String getComponentProcessName(String componentName);
}

IRemoteCCService 这个是根据上面的aidl文件由系统生成,作者发布的时候格式化了代码,让我们看的比较清晰。IRemoteCCService继承于IInterface这个接口。像aidl文件声明的那样拥有四个业务接口。内部多了一个静态抽象类Stub,和一个内部静态类Stub.Proxy. Stub是相对于服务器端,一般我们都是继承它,实现具体的业务,在client-bindService中以onBind返回。而client拿到它时候在跨进程的情况下,用到Proxy作为自己的代理,借助binder,相当于远程调用Stub的实现类。一个应用可以创建多个进程,所以跨进程间远程调用,并返回结果,这个场景很常见。IRemoteCallback代码与IRemoteCCService没什么太多技术区别,我们就不贴了。下面我们着重分析框架自身功能的实现。即在服务端IRemoteCCService.Stub的具体现实。

public interface IRemoteCCService extends IInterface {
    void call(RemoteCC var1, IRemoteCallback var2) throws RemoteException;

    void cancel(String var1) throws RemoteException;

    void timeout(String var1) throws RemoteException;

    String getComponentProcessName(String var1) throws RemoteException;

    public abstract static class Stub extends Binder implements IRemoteCCService {
        private static final String DESCRIPTOR = "com.billy.cc.core.component.remote.IRemoteCCService";
        static final int TRANSACTION_call = 1;
        static final int TRANSACTION_cancel = 2;
        static final int TRANSACTION_timeout = 3;
        static final int TRANSACTION_getComponentProcessName = 4;

        public Stub() {
            this.attachInterface(this, "com.billy.cc.core.component.remote.IRemoteCCService");
        }

        public static IRemoteCCService asInterface(IBinder obj) {
            if (obj == null) {
                return null;
            } else {
                IInterface iin = obj.queryLocalInterface("com.billy.cc.core.component.remote.IRemoteCCService");
                return (IRemoteCCService)(iin != null && iin instanceof IRemoteCCService ? (IRemoteCCService)iin : new IRemoteCCService.Stub.Proxy(obj));
            }
        }

        public IBinder asBinder() {
            return this;
        }

        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            String _arg0;
            switch(code) {
            case 1:
                data.enforceInterface("com.billy.cc.core.component.remote.IRemoteCCService");
                RemoteCC _arg0;
                if (0 != data.readInt()) {
                    _arg0 = (RemoteCC)RemoteCC.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }

                IRemoteCallback _arg1 = com.billy.cc.core.component.remote.IRemoteCallback.Stub.asInterface(data.readStrongBinder());
                this.call(_arg0, _arg1);
                reply.writeNoException();
                return true;
            case 2:
                data.enforceInterface("com.billy.cc.core.component.remote.IRemoteCCService");
                _arg0 = data.readString();
                this.cancel(_arg0);
                reply.writeNoException();
                return true;
            case 3:
                data.enforceInterface("com.billy.cc.core.component.remote.IRemoteCCService");
                _arg0 = data.readString();
                this.timeout(_arg0);
                reply.writeNoException();
                return true;
            case 4:
                data.enforceInterface("com.billy.cc.core.component.remote.IRemoteCCService");
                _arg0 = data.readString();
                String _result = this.getComponentProcessName(_arg0);
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            case 1598968902:
                reply.writeString("com.billy.cc.core.component.remote.IRemoteCCService");
                return true;
            default:
                return super.onTransact(code, data, reply, flags);
            }
        }

        private static class Proxy implements IRemoteCCService {
            private IBinder mRemote;

            Proxy(IBinder remote) {
                this.mRemote = remote;
            }

            public IBinder asBinder() {
                return this.mRemote;
            }

            public String getInterfaceDescriptor() {
                return "com.billy.cc.core.component.remote.IRemoteCCService";
            }

            public void call(RemoteCC remoteCC, IRemoteCallback callback) throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();

                try {
                    _data.writeInterfaceToken("com.billy.cc.core.component.remote.IRemoteCCService");
                    if (remoteCC != null) {
                        _data.writeInt(1);
                        remoteCC.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }

                    _data.writeStrongBinder(callback != null ? callback.asBinder() : null);
                    this.mRemote.transact(1, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }

            }

            public void cancel(String callId) throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();

                try {
                    _data.writeInterfaceToken("com.billy.cc.core.component.remote.IRemoteCCService");
                    _data.writeString(callId);
                    this.mRemote.transact(2, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }

            }

            public void timeout(String callId) throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();

                try {
                    _data.writeInterfaceToken("com.billy.cc.core.component.remote.IRemoteCCService");
                    _data.writeString(callId);
                    this.mRemote.transact(3, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }

            }

            public String getComponentProcessName(String componentName) throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();

                String _result;
                try {
                    _data.writeInterfaceToken("com.billy.cc.core.component.remote.IRemoteCCService");
                    _data.writeString(componentName);
                    this.mRemote.transact(4, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }

                return _result;
            }
        }
    }
}

RemoteCCService继承于IRemoteCCService.Stub,在别人帮他把通信做好的前提下,它来实现具体请求到了服务端,它要做什么。首先是一个内部静态单例Holder模式的写法。这个比我们常用的double check,多了一个延迟加载的特性。

/**
 * 跨进程调用组件的Binder
 * @author billy.qi
 * @since 18/6/24 11:31
 */
public class RemoteCCService extends IRemoteCCService.Stub {

    private Handler mainThreadHandler;

    //-------------------------单例模式 start --------------
    /** 单例模式Holder */
    private static class RemoteCCServiceHolder {
        private static final RemoteCCService INSTANCE = new RemoteCCService();
    }
    private RemoteCCService(){
        mainThreadHandler = new Handler(Looper.getMainLooper());
    }
    /** 获取RemoteCCService的单例对象 */
    public static RemoteCCService getInstance() {
        return RemoteCCService.RemoteCCServiceHolder.INSTANCE;
    }
    //-------------------------单例模式 end --------------

    @Override
    public void call(final RemoteCC remoteCC, final IRemoteCallback callback) throws RemoteException {
        if (isInvalidate()) {
            return;
        }
        String componentName = remoteCC.getComponentName();
        final String callId = remoteCC.getCallId();
        if (CC.VERBOSE_LOG) {
            CC.verboseLog(callId, "receive call from other process. RemoteCC: %s", remoteCC.toString());
        }
        if (!ComponentManager.hasComponent(componentName)) {
            CC.verboseLog(callId, "There is no component found for name:%s in process:%s", componentName, CCUtil.getCurProcessName());
            doCallback(callback, callId, CCResult.error(CCResult.CODE_ERROR_NO_COMPONENT_FOUND));
            return;
        }
        final CC cc = CC.obtainBuilder(componentName)
                .setActionName(remoteCC.getActionName())
                .setParams(remoteCC.getParams())
                .setCallId(remoteCC.getCallId())
                .withoutGlobalInterceptor()
                .build();
        if (remoteCC.isMainThreadSyncCall()) {
            mainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    CCResult ccResult = cc.call();
                    doCallback(callback, callId, ccResult);
                }
            });
        } else {
            cc.callAsync(new IComponentCallback() {
                @Override
                public void onResult(CC cc, CCResult result) {
                    doCallback(callback, callId, result);
                }
            });
        }
    }

    private boolean isInvalidate() {
        //未开启跨app调用时进行跨app调用视为无效调用
        return !CC.isRemoteCCEnabled() && getCallingUid() != Process.myUid();
    }

    private static void doCallback(IRemoteCallback callback, String callId, CCResult ccResult) {
        try {
            RemoteCCResult remoteCCResult;
            try{
                remoteCCResult = new RemoteCCResult(ccResult);
                if (CC.VERBOSE_LOG) {
                    CC.verboseLog(callId, "callback to other process. RemoteCCResult: %s", remoteCCResult.toString());
                }
            }catch(Exception e){
                remoteCCResult = new RemoteCCResult(CCResult.error(CCResult.CODE_ERROR_REMOTE_CC_DELIVERY_FAILED));
                if (CC.VERBOSE_LOG) {
                    CC.verboseLog(callId, "remote CC success. But result can not be converted for IPC. RemoteCCResult: %s", remoteCCResult.toString());
                }
            }
            callback.callback(remoteCCResult);
        } catch (RemoteException e) {
            e.printStackTrace();
            CC.verboseLog(callId, "remote doCallback failed!");
        }
    }

    @Override
    public void cancel(String callId) throws RemoteException {
        if (isInvalidate()) {
            return;
        }
        CC.cancel(callId);
    }

    @Override
    public void timeout(String callId) throws RemoteException {
        if (isInvalidate()) {
            return;
        }
        CC.timeout(callId);
    }

    @Override
    public String getComponentProcessName(String componentName) throws RemoteException {
        if (isInvalidate()) {
            return null;
        }
        return ComponentManager.getComponentProcessName(componentName);
    }

    private static final ConcurrentHashMap<String, IRemoteCCService> CACHE = new ConcurrentHashMap<>();
    private static final byte[] LOCK = new byte[0];

    private static Uri getDispatcherProviderUri(String processName) {
        return Uri.parse("content://" + processName + "." + RemoteProvider.URI_SUFFIX + "/cc");
    }

    static IRemoteCCService get(String processNameTo) {
        IRemoteCCService service = CACHE.get(processNameTo);
        if (service == null && CC.getApplication() != null) {
            synchronized (LOCK) {
                service = CACHE.get(processNameTo);
                if (service == null) {
                    service = getService(processNameTo);
                    if (service != null) {
                        CACHE.put(processNameTo, service);
                    }
                }
            }
        }
        return service;
    }

    static void remove(String processName) {
        CACHE.remove(processName);
    }

    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) {
                    e.printStackTrace();
                }
            }
        }
    }
}

CACHE是对进程名与对应的RemoteCCService的缓存。因为RemoteCCService这个获取过程是一个通过ContentProvider跨进程通信,比较好耗费资源,第二次会从CACHE直接获取,也算是个优化。

    private static final ConcurrentHashMap<String, IRemoteCCService> CACHE = new ConcurrentHashMap<>();
    private static final byte[] LOCK = new byte[0];

    private static Uri getDispatcherProviderUri(String processName) {
        return Uri.parse("content://" + processName + "." + RemoteProvider.URI_SUFFIX + "/cc");
    }

    static IRemoteCCService get(String processNameTo) {
        IRemoteCCService service = CACHE.get(processNameTo);
        if (service == null && CC.getApplication() != null) {
            synchronized (LOCK) {
                service = CACHE.get(processNameTo);
                if (service == null) {
                    service = getService(processNameTo);
                    if (service != null) {
                        CACHE.put(processNameTo, service);
                    }
                }
            }
        }
        return service;
    }

    static void remove(String processName) {
        CACHE.remove(processName);
    }

    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) {
                    e.printStackTrace();
                }
            }
        }
    }

CC.isRemoteCCEnabled是我们项目中设置的。还判断了是否为同一个uid。

private boolean isInvalidate() {
        //未开启跨app调用时进行跨app调用视为无效调用
        return !CC.isRemoteCCEnabled() && getCallingUid() != Process.myUid();
    }

cancel ,timeout使用了CC提供的静态方法。callId一般都是自动追加生成,干什么能没一个凭证id嘛。getComponentProcessName具体实现是在ComponentManager中,稍等分析。

   @Override
    public void cancel(String callId) throws RemoteException {
        if (isInvalidate()) {
            return;
        }
        CC.cancel(callId);
    }

    @Override
    public void timeout(String callId) throws RemoteException {
        if (isInvalidate()) {
            return;
        }
        CC.timeout(callId);
    }

    @Override
    public String getComponentProcessName(String componentName) throws RemoteException {
        if (isInvalidate()) {
            return null;
        }
        return ComponentManager.getComponentProcessName(componentName);
    }

RemoteCC相当于一个业务bean。携带了参数,组件名,动作名(同一个组件的多个方法,),callId(自动生成的自身标识),是否主线程同步调用。以上是可被序列化的。call 方法首先对componentName的存在性进行验证,接着下面CC的写法类似我们用户程序员的写法,下面的if-else根据isMainThreadSyncCall在主线程或者在线程池中被调用。cc.callAsync实现中又委托ComponentManager具体实现提交线程池。CC相当于任务内容的实现,具体的任务执行又交给ComponentManager。

 @Override
    public void call(final RemoteCC remoteCC, final IRemoteCallback callback) throws RemoteException {
        if (isInvalidate()) {
            return;
        }
        String componentName = remoteCC.getComponentName();
        final String callId = remoteCC.getCallId();
        if (CC.VERBOSE_LOG) {
            CC.verboseLog(callId, "receive call from other process. RemoteCC: %s", remoteCC.toString());
        }
        if (!ComponentManager.hasComponent(componentName)) {
            CC.verboseLog(callId, "There is no component found for name:%s in process:%s", componentName, CCUtil.getCurProcessName());
            doCallback(callback, callId, CCResult.error(CCResult.CODE_ERROR_NO_COMPONENT_FOUND));
            return;
        }
        final CC cc = CC.obtainBuilder(componentName)
                .setActionName(remoteCC.getActionName())
                .setParams(remoteCC.getParams())
                .setCallId(remoteCC.getCallId())
                .withoutGlobalInterceptor()
                .build();
        if (remoteCC.isMainThreadSyncCall()) {
            mainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    CCResult ccResult = cc.call();
                    doCallback(callback, callId, ccResult);
                }
            });
        } else {
            cc.callAsync(new IComponentCallback() {
                @Override
                public void onResult(CC cc, CCResult result) {
                    doCallback(callback, callId, result);
                }
            });
        }
    }

public class RemoteCC implements Parcelable {

    private Map<String, Object> params;

    private String componentName;
    private String actionName;
    private String callId;
    private boolean isMainThreadSyncCall;

    private Map<String, Object> localParams;
    。。。。。

ComponentManager.call代码。我们具体把自己的业务包装成Component接口的现实。那一定就有疑问。到底在哪里被执行。上面分析了CC这个任务类被踢来踢去,最终到了ComponentManager.call脚下。这个框架的一个核心概念就是链式调用,而我们的任务执行算是最后一环ValidateInterceptor.getInstance()。 之前创建chain,根据cc.isWithoutGlobalInterceptor将全局的拦截链是否加进去。全局拦截和静态组件都一样是通过字节码处理技术,在事先static静态代码段中被初始化的。chain.addInterceptors(cc.getInterceptors())这个是用户在当次任务中设置一些拦截器。 chain.addInterceptor(ValidateInterceptor.getInstance());
顾名思义就是在真正执行前做的验证,通过了,我们定义的组件call当然就被调用了。

  /**
     * 组件调用统一入口
     * @param cc 组件调用指令
     * @return 组件调用结果(同步调用的返回值)
     */
    static CCResult call(CC cc) {
        String callId = cc.getCallId();
        Chain chain = new Chain(cc);
        if (!cc.isWithoutGlobalInterceptor()) {
            chain.addInterceptors(INTERCEPTORS);
        }
        chain.addInterceptors(cc.getInterceptors());
        // 有效性校验放在自定义拦截器之后执行,优先执行自定义拦截器,让其可以拦截到所有组件调用
        // 执行实际调用的拦截器在校验有效性结束后再添加
        chain.addInterceptor(ValidateInterceptor.getInstance());
        ChainProcessor processor = new ChainProcessor(chain);
        //异步调用,放到线程池中运行
        if (cc.isAsync()) {
            if (CC.VERBOSE_LOG) {
                CC.verboseLog(callId, "put into thread pool");
            }
            CC_THREAD_POOL.submit(processor);
            //异步调用时此方法返回null,CCResult通过callback回调
            return null;
        } else {
            //同步调用,直接执行
            CCResult ccResult;
            try {
                ccResult = processor.call();
            } catch (Exception e) {
                ccResult = CCResult.defaultExceptionResult(e);
            }
            if (CC.VERBOSE_LOG) {
                CC.verboseLog(callId, "cc finished.CCResult:" + ccResult);
            }
            //同步调用的返回结果,永不为null,默认为CCResult.defaultNullResult()
            return ccResult;
        }
    }

ValidateInterceptor类采用一样的Holder单例写法。一番验证后,根据组件的存在位置分别最后加入一环LocalCCInterceptor,RemoteCCInterceptor,SubProcessCCInterceptor 。

class ValidateInterceptor implements ICCInterceptor {

    //-------------------------单例模式 start --------------
    /** 单例模式Holder */
    private static class ValidateInterceptorHolder {
        private static final ValidateInterceptor INSTANCE = new ValidateInterceptor();
    }
    private ValidateInterceptor (){}
    /** 获取ValidateInterceptor的单例对象 */
    static ValidateInterceptor getInstance() {
        return ValidateInterceptorHolder.INSTANCE;
    }
    //-------------------------单例模式 end --------------

    @Override
    public CCResult intercept(Chain chain) {
        CC cc = chain.getCC();
        String componentName = cc.getComponentName();
        int code = 0;
        Boolean notFoundInCurApp = null;
        if (TextUtils.isEmpty(componentName)) {
            //没有指定要调用的组件名称,中止运行
            code = CCResult.CODE_ERROR_COMPONENT_NAME_EMPTY;
        } else if (cc.getContext() == null) {
            //context为null (没有设置context 且 CC中获取application失败)
            code = CCResult.CODE_ERROR_CONTEXT_NULL;
        } else {
            if (!ComponentManager.hasComponent(componentName)) {
                //当前进程中不包含此组件,查看一下其它进程中是否包含此组件
                notFoundInCurApp = TextUtils.isEmpty(ComponentManager.getComponentProcessName(componentName));
                if (notFoundInCurApp && !CC.isRemoteCCEnabled()) {
                    //本app内所有进程均没有指定的组件,并且设置了不会调用外部app的组件
                    code = CCResult.CODE_ERROR_NO_COMPONENT_FOUND;
                    CC.verboseLog(cc.getCallId(),"componentName=" + componentName
                            + " is not exists and CC.enableRemoteCC is " + CC.isRemoteCCEnabled());
                }
            }
        }
        if (code != 0) {
            return CCResult.error(code);
        }
        //执行完自定义拦截器,并且通过有效性校验后,再确定具体调用组件的方式
        if (ComponentManager.hasComponent(componentName)) {
            //调用当前进程中的组件
            chain.addInterceptor(LocalCCInterceptor.getInstance());
        } else {
            if (notFoundInCurApp == null) {
                notFoundInCurApp = TextUtils.isEmpty(ComponentManager.getComponentProcessName(componentName));
            }
            if (notFoundInCurApp) {
                //调用设备上安装的其它app(组件单独运行的app)中的组件
                chain.addInterceptor(RemoteCCInterceptor.getInstance());
            } else {
                //调用app内部子进程中的组件
                chain.addInterceptor(SubProcessCCInterceptor.getInstance());
            }
        }
        chain.addInterceptor(Wait4ResultInterceptor.getInstance());
        // 执行上面添加的拦截器,开始执行组件调用
        return chain.proceed();
    }
}

LocalCCRunnable 这个就是封装我们任务的执行(跨越千山万水boolean callbackDelay = component.onCall(cc) 终于在这里被干了)。LocalCCInterceptor.intercept中对执行线程做了判断。分别提交到不同线程执行。

class LocalCCInterceptor implements ICCInterceptor {

    //-------------------------单例模式 start --------------
    /** 单例模式Holder */
    private static class LocalCCInterceptorHolder {
        private static final LocalCCInterceptor INSTANCE = new LocalCCInterceptor();
    }
    private LocalCCInterceptor (){}
    /** 获取LocalCCInterceptor的单例对象 */
    static LocalCCInterceptor getInstance() {
        return LocalCCInterceptorHolder.INSTANCE;
    }
    //-------------------------单例模式 end --------------

    @Override
    public CCResult intercept(Chain chain) {
        CC cc = chain.getCC();
        IComponent component = ComponentManager.getComponentByName(cc.getComponentName());
        if (component == null) {
            CC.verboseLog(cc.getCallId(), "component not found in this app. maybe 2 reasons:"
                    + "\n1. CC.enableRemoteCC changed to false"
                    + "\n2. Component named \"%s\" is a IDynamicComponent but now is unregistered"
            );
            return CCResult.error(CCResult.CODE_ERROR_NO_COMPONENT_FOUND);
        }
        try {
            String callId = cc.getCallId();
            if (CC.VERBOSE_LOG) {
                CC.verboseLog(callId, "start component:%s, cc: %s", component.getClass().getName(), cc.toString());
            }
            boolean shouldSwitchThread = false;
            LocalCCRunnable runnable = new LocalCCRunnable(cc, component);
            if (component instanceof IMainThread) {
                //当前是否在主线程
                boolean curIsMainThread = Looper.myLooper() == Looper.getMainLooper();
                //该action是否应该在主线程运行
                Boolean runOnMainThread = ((IMainThread) component).shouldActionRunOnMainThread(cc.getActionName(), cc);
                //是否需要切换线程执行 component.onCall(cc) 方法
                shouldSwitchThread = runOnMainThread != null && runOnMainThread ^ curIsMainThread;
                if (shouldSwitchThread) {
                    runnable.setShouldSwitchThread(true);
                    if (runOnMainThread) {
                        //需要在主线程运行,但是当前线程不是主线程
                        ComponentManager.mainThread(runnable);
                    } else {
                        //需要在子线程运行,但当前线程不是子线程
                        ComponentManager.threadPool(runnable);
                    }
                }
            }
            if (!shouldSwitchThread) {
                //不需要切换线程,直接运行
                runnable.run();
            }
            //兼容以下情况:
            //  1. 不需要切换线程,但需要等待异步实现调用CC.sendCCResult(...)
            //  2. 需要切换线程,等待切换后的线程调用组件后调用CC.sendCCResult(...)
            if (!cc.isFinished()) {
                chain.proceed();
            }
        } catch(Exception e) {
            return CCResult.defaultExceptionResult(e);
        }
        return cc.getResult();
    }


    static class LocalCCRunnable implements Runnable {
        private final String callId;
        private CC cc;
        private IComponent component;
        private boolean shouldSwitchThread;

        LocalCCRunnable(CC cc, IComponent component) {
            this.cc = cc;
            this.callId = cc.getCallId();
            this.component = component;
        }

        void setShouldSwitchThread(boolean shouldSwitchThread) {
            this.shouldSwitchThread = shouldSwitchThread;
        }

        @Override
        public void run() {
            if (cc.isFinished()) {
                return;
            }
            try {
                boolean callbackDelay = component.onCall(cc);
                if (CC.VERBOSE_LOG) {
                    CC.verboseLog(callId, component.getName() + ":"
                            + component.getClass().getName()
                            + ".onCall(cc) return:" + callbackDelay
                    );
                }
                if (!callbackDelay && !cc.isFinished()) {
                    CC.logError("component.onCall(cc) return false but CC.sendCCResult(...) not called!"
                            + "\nmaybe: actionName error"
                            + "\nor if-else not call CC.sendCCResult"
                            + "\nor switch-case-default not call CC.sendCCResult"
                            + "\nor try-catch block not call CC.sendCCResult."
                    );
                    //没有返回结果,且不是延时回调(也就是说不会收到结果了)
                    setResult(CCResult.error(CCResult.CODE_ERROR_CALLBACK_NOT_INVOKED));
                }
            } catch(Exception e) {
                setResult(CCResult.defaultExceptionResult(e));
            }
        }

        private void setResult(CCResult result) {
            if (shouldSwitchThread) {
                //若出现线程切换
                // LocalCCInterceptor.intercept会执行chain.proceed()进入wait状态
                // 需要解除wait状态
                cc.setResult4Waiting(result);
            } else {
                cc.setResult(result);
            }
        }
    }
}

在SubProcessCCInterceptor实现中。ProcessCrossTask包装了跨进程的调用,所有的RemoteCCService获取都是通过我们上面说的ContentProvider那套。SubProcessCCInterceptor多了对cancel,timeout的判断。service.call(remoteCC, new IRemoteCallback.Stub()执行了跨进程的调用。

class SubProcessCCInterceptor implements ICCInterceptor {

    private static final ConcurrentHashMap<String, IRemoteCCService> CONNECTIONS = new ConcurrentHashMap<>();

    //-------------------------单例模式 start --------------
    /** 单例模式Holder */
    private static class SubProcessCCInterceptorHolder {
        private static final SubProcessCCInterceptor INSTANCE = new SubProcessCCInterceptor();
    }
    SubProcessCCInterceptor(){}
    /** 获取SubProcessCCInterceptor的单例对象 */
    static SubProcessCCInterceptor getInstance() {
        return SubProcessCCInterceptorHolder.INSTANCE;
    }
    //-------------------------单例模式 end --------------

    @Override
    public CCResult intercept(Chain chain) {
        String componentName = chain.getCC().getComponentName();
        String processName = ComponentManager.getComponentProcessName(componentName);
        return multiProcessCall(chain, processName, CONNECTIONS);
    }

    CCResult multiProcessCall(Chain chain, String processName
            , ConcurrentHashMap<String, IRemoteCCService> connectionCache) {
        if (processName == null) {
            return CCResult.error(CCResult.CODE_ERROR_NO_COMPONENT_FOUND);
        }
        CC cc = chain.getCC();
        //主线程同步调用时,跨进程也要在主线程同步调用
        boolean isMainThreadSyncCall = !cc.isAsync() && Looper.getMainLooper() == Looper.myLooper();
        ProcessCrossTask task = new ProcessCrossTask(cc, processName, connectionCache, isMainThreadSyncCall);
        ComponentManager.threadPool(task);
        if (!cc.isFinished()) {
            //执行 Wait4ResultInterceptor
            chain.proceed();
            //如果是提前结束的,跨进程通知被调用方
            if (cc.isCanceled()) {
                task.cancel();
            } else if (cc.isTimeout()) {
                task.timeout();
            }
        }
        return cc.getResult();
    }

    protected IRemoteCCService getMultiProcessService(String processName) {
        CC.log("start to get RemoteService from process %s", processName);
        IRemoteCCService service = RemoteCCService.get(processName);
        CC.log("get RemoteService from process %s %s!", processName, (service != null ? "success" : "failed"));
        return service;
    }

    class ProcessCrossTask implements Runnable {

        private final CC cc;
        private final String processName;
        private final ConcurrentHashMap<String, IRemoteCCService> connectionCache;
        private final boolean isMainThreadSyncCall;
        private IRemoteCCService service;

        ProcessCrossTask(CC cc, String processName, ConcurrentHashMap<String, IRemoteCCService> connectionCache, boolean isMainThreadSyncCall) {
            this.cc = cc;
            this.processName = processName;
            this.connectionCache = connectionCache;
            this.isMainThreadSyncCall = isMainThreadSyncCall;
        }

        @Override
        public void run() {
            RemoteCC processCrossCC = new RemoteCC(cc, isMainThreadSyncCall);
            call(processCrossCC);
        }

        private void call(RemoteCC remoteCC) {
            try {
                service = connectionCache.get(processName);
                if (service == null) {
                    //app内部多进程
                    service = getMultiProcessService(processName);
                    if (service != null) {
                        connectionCache.put(processName, service);
                    }
                }
                if (cc.isFinished()) {
                    CC.verboseLog(cc.getCallId(), "cc is finished before call %s process", processName);
                    return;
                }
                if (service == null) {
                    CC.verboseLog(cc.getCallId(), "RemoteService is not found for process: %s", processName);
                    setResult(CCResult.error(CCResult.CODE_ERROR_NO_COMPONENT_FOUND));
                    return;
                }
                if (CC.VERBOSE_LOG) {
                    CC.verboseLog(cc.getCallId(), "start to call process:%s, RemoteCC: %s"
                            , processName, remoteCC.toString());
                }
                service.call(remoteCC, new IRemoteCallback.Stub() {
                    @Override
                    public void callback(RemoteCCResult remoteCCResult) throws RemoteException {
                        try {
                            if (CC.VERBOSE_LOG) {
                                CC.verboseLog(cc.getCallId(), "receive RemoteCCResult from process:%s, RemoteCCResult: %s"
                                        , processName, remoteCCResult.toString());
                            }
                            setResult(remoteCCResult.toCCResult());
                        } catch(Exception e) {
                            e.printStackTrace();
                            setResult(CCResult.error(CCResult.CODE_ERROR_REMOTE_CC_DELIVERY_FAILED));
                        }
                    }
                });
            } catch (DeadObjectException e) {
                connectionCache.remove(processName);
                call(remoteCC);
            } catch (Exception e) {
                e.printStackTrace();
                setResult(CCResult.error(CCResult.CODE_ERROR_REMOTE_CC_DELIVERY_FAILED));
            }
        }

        void setResult(CCResult result) {
            cc.setResult4Waiting(result);
        }

        void cancel() {
            try {
                service.cancel(cc.getCallId());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        void timeout() {
            try {
                service.timeout(cc.getCallId());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

RemoteCCInterceptor其实包含了SubProcessCCInterceptor,只不过多了唤醒,连接功能。在MAX_CONNECT_TIME_DURATION时间内尝试,最后调用SubProcessCCInterceptor.multiProcessCall.

class RemoteCCInterceptor extends SubProcessCCInterceptor {

    private static final ConcurrentHashMap<String, IRemoteCCService> REMOTE_CONNECTIONS = new ConcurrentHashMap<>();

    //-------------------------单例模式 start --------------
    /** 单例模式Holder */
    private static class RemoteCCInterceptorHolder {
        private static final RemoteCCInterceptor INSTANCE = new RemoteCCInterceptor();
    }
    private RemoteCCInterceptor(){}
    /** 获取{@link RemoteCCInterceptor}的单例对象 */
    static RemoteCCInterceptor getInstance() {
        return RemoteCCInterceptorHolder.INSTANCE;
    }
    //-------------------------单例模式 end --------------

    @Override
    public CCResult intercept(Chain chain) {
        String processName = getProcessName(chain.getCC().getComponentName());
        if (!TextUtils.isEmpty(processName)) {
            return multiProcessCall(chain, processName, REMOTE_CONNECTIONS);
        }
        return CCResult.error(CCResult.CODE_ERROR_NO_COMPONENT_FOUND);
    }

    private String getProcessName(String componentName) {
        String processName = null;
        try {
            for (Map.Entry<String, IRemoteCCService> entry : REMOTE_CONNECTIONS.entrySet()) {
                try {
                    processName = entry.getValue().getComponentProcessName(componentName);
                } catch(DeadObjectException e) {
                    String processNameTo = entry.getKey();
                    RemoteCCService.remove(processNameTo);
                    IRemoteCCService service = RemoteCCService.get(processNameTo);
                    if (service == null) {
                        String packageName = processNameTo.split(":")[0];
                        boolean wakeup = RemoteConnection.tryWakeup(packageName);
                        CC.log("wakeup remote app '%s'. success=%b.", packageName, wakeup);
                        if (wakeup) {
                            service = getMultiProcessService(processNameTo);
                        }
                    }
                    if (service != null) {
                        try {
                            processName = service.getComponentProcessName(componentName);
                            REMOTE_CONNECTIONS.put(processNameTo, service);
                        } catch(Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                }
                if (!TextUtils.isEmpty(processName)) {
                    return processName;
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
        return processName;
    }

    void enableRemoteCC() {
        //监听设备上其它包含CC组件的app
        listenComponentApps();
        connect(RemoteConnection.scanComponentApps());
    }

    private static final String INTENT_FILTER_SCHEME = "package";
    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);
    }

    private void connect(List<String> packageNames) {
        if (packageNames == null || packageNames.isEmpty()) {
            return;
        }
        for (String pkg : packageNames) {
            ComponentManager.threadPool(new ConnectTask(pkg));
        }
    }

    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);
            }
        }
    }

    private static final int MAX_CONNECT_TIME_DURATION = 1000;
    @Override
    protected IRemoteCCService getMultiProcessService(String packageName) {
        long start = System.currentTimeMillis();
        IRemoteCCService service = null;
        while (System.currentTimeMillis() - start < MAX_CONNECT_TIME_DURATION) {
            service = RemoteCCService.get(packageName);
            if (service != null) {
                break;
            }
            SystemClock.sleep(50);
        }
        CC.log("connect remote app '%s' %s. cost time=%d"
                , packageName
                , service == null ? "failed" : "success"
                , (System.currentTimeMillis() - start));
        return service;
    }

}

粗略的分析下CC框架中一些常见的技术点。我们将在自己的项目中继续尝试使用他,学习他。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值