美文网首页
Andromeda:适用于多进程架构的组件通信框架(下)

Andromeda:适用于多进程架构的组件通信框架(下)

作者: Innocencellh | 来源:发表于2018-05-29 20:54 被阅读0次

转载自:https://mp.weixin.qq.com/s/67YL9daArYy3dYS_aWyO1w

提升服务提供方的进程优先级

bindService()实质上是做了以下事情:

  • 获取服务提供方的binder
  • client端通过bind操作,让Service所在进程的优先级提高


    bindService过程图.png

    现在我们来思考一个问题:虽然bind操作对用户不可见,但是怎么知道bind哪个Service呢?

在编译时,会为每个进程都插桩一个StubService, 并且在StubServiceMatcher这个类中,插入进程名与StubService的对应关系(编译时通过javassist插入代码),这样根据进程名就可以获取对应的StubService.而IDispatcher的getRemoteService()方法中获取的BinderBean就包含有进程名信息。

生命周期管理

借鉴Glide的方式,即利用Fragment/Activity的FragmentManager创建一个监听用的Fragment, 这样当Fragment/Activity回调onDestroy()时,这个监听用的Fragment也会收到回调,在这个回调中进行unbind操作即可。


回调监听原理图.png

当时其实有考虑过是否借助Google推出的Arch componentss来处理生命周期问题,但是考虑到还有的团队没有接入这一套,加上arch components的方案其实也变过多次,所以就暂时采用了这种方案,后面会视情况决定是否借助arch components的方案来进行生命周期管理 。

IPCCallback

对于耗时操作,我们直接在client端的work线程调用是否可以?虽然可以,但是server端可能仍然需要把耗时操作放在自己的work线程中执行,执行完毕之后再回调结果,所以这种情况下client端的work线程就有点多余。所以为了使用方便,就需要一个IPCCallback, 在server端处理耗时操作之后再回调。
对于需要回调的AIDL接口,其定义如下:

interface IBuyApple {
        int buyAppleInShop(int userId);
        void buyAppleOnNet(int userId,IPCCallback callback);
    }
}

而client端的调用如下:

IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
        if (null == buyAppleBinder) {
            return;
        }
        IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
        if (null != buyApple) {
            try {
                buyApple.buyAppleOnNet(10, new IPCCallback.Stub() {
                    @Override
                    public void onSuccess(Bundle result) throws RemoteException {
                       ...
                    }

                    @Override
                    public void onFail(String reason) throws RemoteException {
                       ...
                    }
                });

            } catch (RemoteException ex) {
                ex.printStackTrace();
            }
        }
}

但是考虑到回调是在Binder线程中,而绝大部分情况下调用者希望回调在主线程,所以lib封装了一个BaseCallback给接入方使用,如下:

IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
        if (null == buyAppleBinder) {
            return;
        }
        IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
        if (null != buyApple) {
            try {
                buyApple.buyAppleOnNet(10, new BaseCallback() {
                    @Override
                    public void onSucceed(Bundle result) {
                       ...
                    }

                    @Override
                    public void onFailed(String reason) {
                        ...
                    }
                });

            } catch (RemoteException ex) {
                ex.printStackTrace();
            }
        }
}

事件总线

Andromeda中Event的定义如下:

public class Event implements Parcelable {
    
        private String name;
    
        private Bundle data;
        
        ...
    }

即 事件=名称+数据,通信时将需要传递的数据存放在Bundle中。
其中名称要求在整个项目中唯一,否则可能出错。 由于要跨进程传输,所以所有数据只能放在Bundle中进行包装。

事件订阅

事件订阅很简单,首先需要有一个实现了EventListener接口的对象。 然后就可以订阅自己感兴趣的事件了,如下:

  Andromeda.subscribe(EventConstants.APPLE_EVENT,MainActivity.this);

其中MainActivity实现了EventListener接口,此处表示订阅了名称为EventConstnts.APPLE_EVENT的事件。

事件发布

    Bundle bundle = new Bundle();
    bundle.putString("Result", "gave u five apples!");
    Andromeda.publish(new Event(EventConstants.APPLE_EVENT, bundle));

InterStellar

InterStellar支持IPC修饰符in, out, inout和oneway,借助InterStellar, 可以像定义本地接口一样定义远程接口,如下:

public interface IAppleService {

       int getApple(int money);

       float getAppleCalories(int appleNum);

       String getAppleDetails(int appleNum,  String manifacture,  String tailerName, String userName,  int userId);

       @oneway
       void oneWayTest(Apple apple);

       String outTest1(@out Apple apple);

       String outTest2(@out int[] appleNum);

       String outTest3(@out int[] array1, @out String[] array2);

       String outTest4(@out Apple[] apples);

       String inoutTest1(@inout Apple apple);

       String inoutTest2(@inout Apple[] apples);

   }

而接口的实现也跟本地服务的实现完全一样,如下:

public class AppleService implements IAppleService {

    @Override
    public int getApple(int money) {
        return money / 2;
    }

    @Override
    public float getAppleCalories(int appleNum) {
        return appleNum * 5;
    }

    @Override
    public String getAppleDetails(int appleNum, String manifacture, String tailerName, String userName, int userId) {
        manifacture = "IKEA";
        tailerName = "muji";
        userId = 1024;
        if ("Tom".equals(userName)) {
            return manifacture + "-->" + tailerName;
        } else {
            return tailerName + "-->" + manifacture;
        }
    }

    @Override
    public synchronized void oneWayTest(Apple apple) {
        if(apple==null){
            Logger.d("Man can not eat null apple!");
        }else{
            Logger.d("Start to eat big apple that weighs "+apple.getWeight());
            try{
                wait(3000);
                //Thread.sleep(3000);
            }catch(InterruptedException ex){
                ex.printStackTrace();
            }
            Logger.d("End of eating apple!");
        }
    }

    @Override
    public String outTest1(Apple apple) {
        if (apple == null) {
            apple = new Apple(3.2f, "Shanghai");
        }
        apple.setWeight(apple.getWeight() * 2);
        apple.setFrom("Beijing");
        return "Have a nice day!";
    }

    @Override
    public String outTest2(int[] appleNum) {
        if (null == appleNum) {
            return "";
        }
        for (int i = 0; i < appleNum.length; ++i) {
            appleNum[i] = i + 1;
        }
        return "Have a nice day 02!";
    }

    @Override
    public String outTest3(int[] array1, String[] array2) {
        for (int i = 0; i < array1.length; ++i) {
            array1[i] = i + 2;
        }
        for (int i = 0; i < array2.length; ++i) {
            array2[i] = "Hello world" + (i + 1);
        }

        return "outTest3";
    }

    @Override
    public String outTest4(Apple[] apples) {
        for (int i = 0; i < apples.length; ++i) {
            apples[i] = new Apple(i + 2f, "Shanghai");
        }

        return "outTest4";
    }

    @Override
    public String inoutTest1(Apple apple) {
        Logger.d("AppleService-->inoutTest1,apple:" + apple.toString());
        apple.setWeight(3.14159f);
        apple.setFrom("Germany");
        return "inoutTest1";
    }

    @Override
    public String inoutTest2(Apple[] apples) {
        Logger.d("AppleService-->inoutTest2,apples[0]:" + apples[0].toString());
        for (int i = 0; i < apples.length; ++i) {
            apples[i].setWeight(i * 1.5f);
            apples[i].setFrom("Germany" + i);
        }
        return "inoutTest2";
    }}
}

可见整个过程完全不涉及到AIDL.
那它是如何实现的呢?

本质上AIDL编译之后生成的Proxy其实是提供了接口的静态代理,那么我们其实可以改成动态代理来实现,将服务方法名和参数传递到服务提供方,然后调用相应的方法,最后将结果回传即可。

总结

Andromeda的意义在于同时融合了本地通信和远程通信,只有做到这样,我觉得才算完整地解决了组件通信的问题。其实跨进程通信都是在binder的基础上进行封装,Andromeda的创新之处在于将binder与Service进行剥离,从而使服务的使用更加灵活。

最后附上Andromeda的开源地址 https://github.com/iqiyi/Andromeda

谢谢~

相关文章

网友评论

      本文标题:Andromeda:适用于多进程架构的组件通信框架(下)

      本文链接:https://www.haomeiwen.com/subject/gatzjftx.html