Android里Service可以分为两种情况,local service和remote service,其中remote service既可以是同个app中的又可以是另外一个app中的。
区分是否local还是remote就看service和activity是不是运行在同一个进程中。默认情况下如果我们没有在manifest中指定service的process。像这样
则Service和Activity会运行在同个进程中的同个线程,即UI主线程,所以不要在service中直接运行耗时操作,否则会阻塞UI界面。我再另外写一篇文章来试验证明
大家都知道不同进程之间是不能相互访问的。如果我们的确要访问呢?Android帮我们想好了办法,Binder和AIDL(Android Interface Definition Language)。Binder是Activity同Service通信必须要使用的,不太了解Binder的同学建议先了解下,而AIDL则不是必须的。在local service中因为activity和service运行在同一个进程中,所以我们可以在service中创建自己的Binder然后实现函数直接把Serveice实体返回给Activity,这样Activity中就可以直接调用service的任意方法。而remote Service我们必须用到进程间通信的方法了,AIDL。AIDL的详解网上有不少教程,但有些描述的比较繁琐,过后我会写一篇文章专门来阐述。
1,local service
其实本质就是service和activity都没有特别制定process属性,所以他们运行在同一个进程中。要注意,此时不管activity还是service的崩溃都会导致整个进程被关闭。local service和activity通信可以自定义Binder类,然后通过onBind函数将自定义类返回,再在自定义类中将Service实体返回给client。这是最简单的方法。
但是这种方式实现的service没法直接移植到remote service中。
2,local service的AIDL访问方式
1)创建aidl文件,
package com.example.zhenghao.zhservice; interface IZhAidlInterface { String getZhString(); }
编译之后会在build目录下产生IZhAidlInterface.java文件,其中包含IZhAidlInterface.stub和IZhAidlInterface.stub.proxy内部类。具体AIDL机制我就不在这里讲了,IZhAidlInterface类和IZhAidlInterface.stub类是我们需要直接打交道的。
2)service中创建自定义类,继承自IZhAidlInterface.stub
public class ZhAidlImpl extends IZhAidlInterface.Stub { @Override public String getZhString() throws RemoteException { return "hello from service via AIDL"; } }
3)通过onBind接口将ZhAidlImpl实例返回
public IBinder onBind(Intent intent) { return new ZhAidlImpl(); }
4)client中定义ServiceConnection类
ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { aidl = IZhAidlInterface.Stub.asInterface(service); try { out.setText(aidl.getZhString()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } };重点讲一下IZhAidlInterface.Stub.asInterface(service);这一句,点进去看一下。
public static com.example.zhenghao.zhservice.IZhAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.zhenghao.zhservice.IZhAidlInterface))) { return ((com.example.zhenghao.zhservice.IZhAidlInterface) iin); } return new com.example.zhenghao.zhservice.IZhAidlInterface.Stub.Proxy(obj); }obj.queryLocalInterface(DESCRIPTOR)首先检查正在绑定的service是否是本地service,如果是本地service则直接将service中IZhAidlInterface.Stub的实现类对象返回,在本例中就是ZhAidlImpl。如果不是本地service,那么service和client就不是运行在同一个进程中,必须通过进程间通信的方式相互访问。那么会返回IZhAidlInterface.Stub.Proxy。
具体的进程间通信怎么实现的,我们不去了解也不会影响使用。
我们在ServiceConnection的onServiceConnected方法中就可以得到实现了IZhAidlInterface接口的实体。后面就是函数访问的方式来进行进程间通信了。
3, remote service的AIDL访问。
在同一个android项目中我们可以通过设置Manifest文件中的process属性让service运行在独立的进程,也可以是另外一个apk中的service。前面一种情况,我们只需要将上边讲的本地service的AIDL访问中的service做如下修改即可。
android:name="service.ZhService"
android:process=":remote">
android:name="
myService"/>
我们重点讲解下后面的跨apk的进程间通信。
1)Android Studio项目中再创建一个module,然后将第一个module中编译之后生成的IZhAidlInterface.java拷贝过来。
2)在client中参照上边的例子创建自定义ServiceConnection。
3)通过包名和类名或者Action的方式绑定service。注:很多比较老的教程里会教大家用隐式Intent的方式来绑定service,但是在Android 5.0以后Google已经禁止了隐式Intent的请求方式。
我提供两种显式的请求方式
Intent intent = new Intent(); intent.setPackage("com.example.zhenghao.zhservice"); intent.setAction("myService");
和
Intent intent = new Intent(); intent.setComponent(new ComponentName("com.example.zhenghao.zhservice", "service.ZhService"));这两种请求方式都是对应上边的service。
后面的通信就跟上边第二个例子讲的一致了。
最后我抛出一个很多人容易混淆的问题,看看大家理解的怎样。我可能会写一篇文章来专门阐述。
1、同个apk中的Activity和Service是否运行在同一个进程中?
2、不同的apk中Activity是否运行在不同进程中?如果是,那么是否有办法让不同apk中的Activity运行在同一个进程中?
3、多进程和多线程的区别?