学习编程 2018-01-02
如果一条进程能够拥有足够多的资源,且不会被系统kill掉的话,让程序运行在一条进程上是最好的选择。但是系统资源是按进程来分配的,每条进程资源分配是有个上限的,而且当我们的APP退到后台之后,系统会根据系统资源使用情况,回收部分后台进程资源。
具有推送或后台播放音乐等功能的APP,在APP被退到后台之后,为了保持良好的用户体验,则需要在后台保持运行状态。而这些功能模块的运行可以脱离主程序而运行,为了保持后台运行,且不干预系统回收进程资源的前提下,我们将这些功能拆分到小而独立的进程当中。
满足什么条件才需要拆分独立进程呢?
在Android应用程序中跨进程通讯是非常常见的,我们常用的四大组件均支持跨进程通讯。本文中我们重点看下Service跨进程通讯方式。
Android提供的Service跨进程调用方式:
通过AIDL定义跨进程接口提供给业务调用是Android应用程序开发中,最为常用的跨进程调用实现的方式,AIDL接口提供了同步与异步调用的支持,基本能满足所有的跨进程调用需求。
通过AIDL接口调用跨进程逻辑需要如下几个步骤:
代码实现需要如下几个步骤:
通过Messenger调用跨进程逻辑需要如下几个步骤:
代码实现需要如下几个步骤:
Messenger.send(Message)
接口向远端发出调用命令以实现跨进程调用;Android系统直接提供的AIDL和Messenger的方式存在的一些问题:
最初设计IPCInvoker就是为了解决AIDL和Messenger存在的问题,让跨进程调用变得更简单。下面我们来看看IPCInvoker是如何做跨进程调用的。
从IPCInvoker调用模型图中,可以看出是AIDL的一个扩展,Service端作为Task执行的容器,而由调用者来决定Task的逻辑实现。下面一起看一下IPCInvoker的简单使用。
同步调用跨进程逻辑
public class IPCInvokeSample_InvokeByType { private static final String TAG = "IPCInvokerSample.IPCInvokeSample_InvokeByType"; public static void invokeSync() { Bundle bundle = new Bundle(); bundle.putString("name", "AlbieLiang"); bundle.putInt("pid", android.os.Process.myPid()); IPCString result = IPCInvoker.invokeSync(PushProcessIPCService.PROCESS_NAME, bundle, IPCRemoteInvoke_BuildString.class); Log.i(TAG, "invoke result : %s", result); } private static class IPCRemoteInvoke_BuildString implements IPCRemoteSyncInvoke<Bundle, IPCString> { @Override public IPCString invoke(Bundle data) { String msg = String.format("name:%s|fromPid:%s|curPid:%s", data.getString("name"), data.getInt("pid"), android.os.Process.myPid()); Log.i(TAG, "build String : %s", msg); return new IPCString(msg); } } }
IPCInvoker实现跨进程调用主要分为两部分:
IPCRemoteInvoke_BuildString
类中的invoke
函数的实现就是跨进程逻辑,这里只是输出了一行log,并把msg
作为返回值return了;IPCString result = IPCInvoker.invokeSync(xxx)
便是跨进程调用,这里的调用把需要在远端进程执行的逻辑的class作为参数传入了IPCInvoker。看到上面示例代码,是不是根本没有感觉到这是在写跨进程代码?也没看到有连接Service的过程了!对的IPCInvoker设计的初衷就是让跨进程调用变得简单,就像Handler.post
一个Runnable一样简单。
如果大家进一步思考上述示例中的代码,并对比AIDL和Messenger的跨进程实现,可以很明显看出IPCInvoker的跨进程实现是高内聚的,跨进程逻辑不用写在Service里面,这样业务逻辑就不用与Service产生任何的耦合了,Service在IPCInvoker框架中只是一个执行远端逻辑的容器。
下面我们在看一下IPCInvoker异步调用跨进程逻辑代码是如何写的
异步调用跨进程逻辑
public class IPCInvokeSample_InvokeByType { private static final String TAG = "IPCInvokerSample.IPCInvokeSample_InvokeByType"; public static void invokeAsync() { Bundle bundle = new Bundle(); bundle.putString("name", "AlbieLiang"); bundle.putInt("pid", android.os.Process.myPid()); IPCInvoker.invokeAsync(PushProcessIPCService.PROCESS_NAME, bundle, IPCRemoteInvoke_PrintSomething.class, new IPCRemoteInvokeCallback<IPCString>() { @Override public void onCallback(IPCString data) { Log.i(TAG, "onCallback : %s", data.value); } }); } private static class IPCRemoteInvoke_PrintSomething implements IPCRemoteAsyncInvoke<Bundle, IPCString> { @Override public void invoke(Bundle data, IPCRemoteInvokeCallback<IPCString> callback) { String result = String.format("name:%s|fromPid:%s|curPid:%s", data.getString("name"), data.getInt("pid"), android.os.Process.myPid()); callback.onCallback(new IPCString(result)); } } }
异步调用是通过调用IPCInvoker.invokeAsync
实现的,这个接口与IPCInvoker.invokeSync
相比多了一个IPCRemoteInvokeCallback
参数,IPCRemoteInvokeCallback
的onCallback函数的回调依赖远端逻辑的主动调用,onCallback可以被多次调用。
上面使用的是IPCInvoker中最为基本和最简单的两个接口IPCInvoker.invokeSync
和IPCInvoker.invokeAsync
,可以很明显看出IPCInvoker相比普通的AIDL和Messenger实现的跨进程调用更为直观,接口更容易使用。
IPCInvoker组件里面还包括了几大模块:
到目前为止本文还没介绍如何接入IPCInvoker,其实IPCInvoker的接入也是非常简单的,这里就不展开说明了,大家可以通过阅读《接入IPCInvoker》来接入IPCInvoker。IPCInvoker的wiki文档可以到《IPCInvoker wiki》中详细阅读。
欢迎使用IPCInvoker,有兴趣的同学可以一起维护该项目。(项目地址为github.com/AlbieLiang/…)