Service是什么?
在android中Service是只在后台执行操作且没有页面的组件。Service可以被Service、Activity、ContentProvider启动/绑定,但是不可以被Broadcast启动/绑定。换句话说,Activity、ContentProvider可以绑定到Service并与之交互,甚至进行IPC通讯。
服务根据用途不同而分为两类:启动的服务和绑定的服务。
Service的基本用法。
要实现自己的Service去完成一些后台操作,必须继承自Service,并重写它的onCreate(), onStartCommand(), onDestroy()方法。
public class MyService extends Service{ public static final String TAG = "MyService"; @Override public void onCreate(){ super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId){ return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy(){ super.onDestroy(); } @Override public IBinder onBind(Intent intent){ return null; } }复制代码
然后在androidManifest.xml中进行注册
复制代码
这样一个完整的Service就完成了,现在我们只需要启动它就可以了。启动Service的方法和启动Activity的方法类似:
Intent intent = new Intent(this, MyService.class); startService(intent);复制代码
既然有启动Service,那么肯定有停止Service,停止Service的方法为:
Intent intent = new Intent(this, MyService.class); stopService(intent);复制代码
注意:无论启动多少次Service,只要Service还没有被stop,那么Service#onCreate方法始终只执行一次。换言之,如果Service已经被启动了,Service#onCreate就不再执行了,但onStartCommand每次都会执行。
Service虽然被用来处理一些后台任务,但它运行在主线程(MainThread),因此如果直接在onStartCommand()中执行耗时操作,则会阻塞主线程,具体表现为:除非onStartCommand()的耗时操作执行完毕,否则界面上无法进行任何操作。所以如果要在Service中执行耗时操作(例如网络请求,IO流处理),则必须创建Thread来完成这些耗时操作。那么有的朋友可能会问,既然后台的耗时操作要使用Thread来执行,那么又为什么使用Service呢?直接使用Thread就可以了呀!其实使用Service的原因在于android系统将Service的优先级设置的很高,也就是说,当系统因为内存不足而要回收某些任务时,Thread会比Service先被销毁来释放内存,而且Thread的存活与否与启动它的宿主(例如Activity)的销毁与否无关,那么如果宿主被销毁了,我们就无法控制Thread的存活了,但是Service则不同,我们可以随时随地的控制Service的存活。
其实,android还提供了一个叫做IntentService的组件来实现耗时操作的Service,也就是说,如果你使用IntentService替代Service,那么就不需要new一个Thread来执行耗时操作了,IntentService本身就提供了这种机制。
那么,现在我们来考虑一下:首先在Activity中启动Service,如果之后该Activity被finish掉了,那么此时Service会被destroy吗?答案是NO。想想看,既然Service是用来执行后台操作的,那么它肯定要独立于启动它的组件,否则Service就无法一直在后台执行了。但是,后面我们要说的绑定Service,则不是这样,如果绑定Service的客户端被销毁了,则该Service也被销毁,具体我们后边再来详细说明。
Service#onStartCommand()的返回值
在上一节我们注意到在Service#onStartCommand中返回了一个int值,那么这个值有什么作用呢?
Service#onStartCommand()可以返回四个值:START_STICKY, START_NOT_STICKY, START_REDELIVER_INTENT, START_STICKY_COMPATIBILITY.
START_STICKY:返回该值则表示如果Service所在的进程被系统kill之后,将该Service置于started(已启动)状态,但是不会将第一次启动Service时的intent值恢复。因为该Service处于started状态,所以会保证重启Service之后再次执行onStartCommand()方法,此时如果没有intent传递过来,则将以null object的形式去调用。
START_NOT_STICKY:返回该值则表示如果Service所在的进程被系统kill之后,则系统不会主动重启该Service。
START_REDELIVERY_INTENT:见名知意,返回该值则表示如果Service所在的进程被系统kill之后,将该Service置于started(已启动)状态并将之前的intent值恢复。
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,而且不保证Service所在的进程被kill之后,Service会被重启。
IntentService的基本用法及原理
正如上面所说的,IntentService是一种特殊的service,可以直接在里面执行耗时操作。
IntentService的基本用法
首先我们肯定要继承IntentService,然后实现onHandleIntent(Intent intent)方法。
public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { if (intent != null) { //do something consuming time. try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }复制代码
然后在AndroidManifest中注册:
复制代码
之后我们像启动/停止Service一样去启动/停止IntentService即可。之后我们就可以在IntentService#onHandleIntent中执行耗时操作。
我们看到在注册service时,有时会使用android:exported这个属性,这个属性用来说明该Service是否可以被外部应用使用。默认为false。
IntentService原理解析
在android中,通常使用Handler来实现线程切换及协作,使用HandlerThread来创建一个带有Looper的线程。那么我们来看一下IntentService的源码,看看它是不是使用了Handler和HandlerThread来实现的。
public abstract class IntentService extends Service { private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1);//停止自己 } } /** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public IntentService(String name) { super(); mName = name; } /** * Sets intent redelivery preferences. Usually called from the constructor * with your preferred semantics. * *If enabled is true, * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_REDELIVER_INTENT}, so if this process dies before * {@link #onHandleIntent(Intent)} returns, the process will be restarted * and the intent redelivered. If multiple Intents have been sent, only * the most recent one is guaranteed to be redelivered. * *
If enabled is false (the default), * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent * dies along with it. */ public void setIntentRedelivery(boolean enabled) { mRedelivery = enabled; } @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } /** * You should not override this method for your IntentService. Instead, * override {@link #onHandleIntent}, which the system calls when the IntentService * receives a start request. * @see android.app.Service#onStartCommand */ @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { mServiceLooper.quit(); } /** * Unless you provide binding for your service, you don't need to implement this * method, because the default implementation returns null. * @see android.app.Service#onBind */ @Override public IBinder onBind(Intent intent) { return null; } /** * This method is invoked on the worker thread with a request to process. * Only one Intent is processed at a time, but the processing happens on a * worker thread that runs independently from other application logic. * So, if this code takes a long time, it will hold up other requests to * the same IntentService, but it will not hold up anything else. * When all requests have been handled, the IntentService stops itself, * so you should not call {@link #stopSelf}. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. */ @WorkerThread protected abstract void onHandleIntent(Intent intent);}复制代码
从源码我们可以看出,IntentService正是使用了Handler和HandlerThread来实现的,具体实现流程如下: 首先,我们要启动IntentService,那么则肯定会先执行onCreate方法,我们来看看其源码:
@Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); }复制代码
在onCreate中,首先使用HandlerThread来创建一个带有Looper的线程,指定该线程的名字叫做mName,然后启动线程,接着得到该线程的Looper对象,得到Looper之后再去初始化Handler。至此,就完成了线程创建和线程切换的准备工作。
接着会去调用IntentService#onStartCommand()【先忽略返回值吧?】,而onStartCommand又调用了onStart(),那么我们来看看onStart()方法的具体实现:
@Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); }复制代码
从源码可以看出,在onStart中向Handler发送了一个Message,handler在接收到该Message之后会回调Handler#handlerMessage(Msg)方法,我们再来看看ServiceHandler的实现:
private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1);//停止自己 }}复制代码
可以看到,在handleMessage方法中执行了耗时操作onHandleIntent,执行完耗时操作之后就停止自己。
至此,IntentService的实现原理已经非常清楚了:使用了HandlerThread来创建线程执行耗时操作,使用Handler来发送消息。
注意: IntentServie执行完耗时操作之后会自动停止
在此,还有一个隐藏的知识点需要说明:我们已经知道,多次启动Service之后,Service#onCreate()只会执行一次,Service#onStartCommand()则会执行多次,那么在IntentService中,handler就会多次发送消息并执行耗时操作。由于Handler的消息机制使用了队列,那么在IntentService中,多次启动Service之后,这些耗时操作也会排队执行。
在此有的朋友就会问了:在IntentService中,执行完耗时操作之后会stopSelf,那么此时有多个任务排队等待处理,这岂不是自相矛盾吗?事实上不是如此,接下来我们一一说明:
我们可以看到在Service#onStartCommand(Intent intent, int flags, int startId)和stopSelf(int startId)中有一个参数startId,该参数标识每次启动Service的请求。startId初始值为1,如果在启动Service时,该Service已经被启动且未执行完毕,那么startId递增;如果启动Service时,该Service之前未被启动或已经自停止,则startId为1。startId标识了启动Service这个请求和对应的intent。所以每次stopSelf(startId)时,会关闭当前startId对应的Service,和队列中其他的Service无关。
Bound Service
前面我们使用启动Service的方式使得Service来完成一些后台任务,但是启动Service的客户端和Service之间并没有做任何交互,那么二者如果要进行交互,则必须使用绑定Service的方式。
我们知道四大组件都可以通过设置android:process=":remote"属性让其本身处于另一个进程,所以当我们绑定Service时,就有本地绑定和远程绑定之分。很明显本地绑定是指Service和客户端处于同一个进程,远程绑定则是Service和客户端处于不同的进程。其实使用startService(intent)启动Service时,也有本地启动和远程启动之分,但是通过startService(intent)启动的Service和客户端之间并没有做任何交互,而远程服务通常提供一些系统服务,需要客户端和Service之间进行交互,因此启动Service时无需使用远程模式。
本地绑定
首先我们来看一下本地绑定的基本用法。在最开始的MyService中,有一个onBind()方法,我们让它返回null
@Override public IBinder onBind(Intent intent){ return null; }复制代码
其实onBind方法就是实现绑定服务的关键方法,要实现绑定服务必须要返回一个IBinder对象。
public class MyService extends Service{ public static final String TAG = "MyService"; @Override public void onCreate(){ super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId){ return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy(){ super.onDestroy(); } @Override public IBinder onBind(Intent intent){ return new MyBinder(); } public class MyBinder extends Binder { public void doSomething(){ //do something } } }复制代码
虽然我们返回了一个IBinder对象,但是我们如何在客户端来接受这个IBinder对象呢?系统为我们实现了ServiceConnection这个类来实现客户端与服务的连接。
private ServiceConnection conn = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service){ MyBinder mb = (MyService.MyBinder)service; mb.doSomething(); } /** * 当服务连接异常终止后调用 */ @Override public void onServiceDisconnected(ComponentName name){ }}..../** * 自定义方法,来实现绑定Service. */private void bindService(){ Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, conn, BIND_AUTO_CREATE);}/** * 自定义方法,来实现解绑Service. */private void unbindService(){ unbindService(conn); }复制代码
从这段代码我们可以看出,通过使用ServiceConnection来建立客户端与Service的连接。当连接成功之后会回调ServiceConnection#onServiceConnected(ComponentName name, IBinder service)方法,将IBinder对象传递过来,这样我们就可以像操作自己的方法一样去操作Service中Binder对象的方法。
注意:Service只能unbind一次,否则会报错: java.lang.IllegalArgumentException: Service not registered
远程绑定
现在我们来想想,如果我们直接将MyService改为远程服务,会有什么效果呢?不妨来做一个测试:直接设置MyService的android:process=":remote"。设置完之后,当执行bindService时,报以下错误:
AndroidRuntime: FATAL EXCEPTION: main Process: cn.codemperor, PID: 10998java.lang.ClassCastException: android.os.BinderProxy cannot be cast to cn.codemperor.service.MyService$MyBinder复制代码
咦?很奇怪!怎么会有android.os.BinderProxy这个东东呢?我们在任何地方都没有声明或使用它呀,它是怎么来的呢?是怎样和Service关联起来的呢?
要解决这个问题,必须先知道,远程绑定或者说访问远程服务都是IPC的,什么是IPC呢?IPC就是Inter Process Communication,即进程间通讯。在android中实现继承间通讯的方式是AIDL(Android Interface Define Language),即android接口定义语言。接下来我们先详细了解一下AIDL。
AIDL基本概念及用途
首先我们应该明白AIDL的作用是:实现p1进程与p2进程之间的通信。也就是说AIDL提供了一种机制:只要定义了AIDL文件,并在AIDL文件中规定业务逻辑,然后系统就会自动根据业务逻辑实现p1与p2之间的通信。具体表现如下:
AIDL文件-->定义业务逻辑 | 系统生成实现进程间通信的类文件-->生成Stub接口 | 实现Stub接口-->实现业务逻辑 复制代码
这样一看,我们局很明确的知道AIDL实现进程间通讯的机制。其中Stub是Binder的子类。
那么接下来我们通过一个实例来具体说明一下远程绑定的实现
远程服务实例--数据访问
首先,我们新建IDataAccess.aidl文件,并在启动数据访问方法accessData。
**注意:**在aidl文件中,不能出现private, public, protect修饰符。在AIDL文件中仅能支持基本类型、Map、List、自定义类型。因为自定义类型和其他类型的用法有所区别,我们先来看看非自定义类型的用法。
package cn.codempero.summary;// Declare any non-default types here with import statementsinterface IDataAccess { boolean accessData(); /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);}复制代码
然后系统会在/build/generated/source/aidl/debug/packageName/文件夹下自动生成IDataAccess.java文件,这个接口就实现了IPC。我们来看一下它的源代码
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\LAB\\Development\\Summary\\app\\src\\main\\aidl\\cn\codemperor\summary\\IDataAccess.aidl */package cn.codemperor.summary;// Declare any non-default types here with import statementspublic interface IDataAccess extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements cn.codempero.summary.IDataAccess{private static final java.lang.String DESCRIPTOR = "cn.codempero.summary.IDataAccess";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an cn.codempero.summary.IDataAccess interface, * generating a proxy if needed. */public static cn.codempero.summary.IDataAccess asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof cn.codempero.summary.IDataAccess))) {return ((cn.codempero.summary.IDataAccess)iin);}return new cn.codempero.summary.IDataAccess.Stub.Proxy(obj);}@Override public android.os.IBinder asBinder(){return this;}@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{switch (code){.case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_accessData:{data.enforceInterface(DESCRIPTOR);boolean _result = this.accessData();reply.writeNoException();reply.writeInt(((_result)?(1):(0)));return true;}case TRANSACTION_basicTypes:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();long _arg1;_arg1 = data.readLong();boolean _arg2;_arg2 = (0!=data.readInt());float _arg3;_arg3 = data.readFloat();double _arg4;_arg4 = data.readDouble();java.lang.String _arg5;_arg5 = data.readString();this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements cn.codempero.summary.IDataAccess{private android.os.IBinder mRemote;Proxy(android.os.IBinder remote){mRemote = remote;}@Override public android.os.IBinder asBinder(){return mRemote;}public java.lang.String getInterfaceDescriptor(){return DESCRIPTOR;}@Override public boolean accessData() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();boolean _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_accessData, _data, _reply, 0);_reply.readException();_result = (0!=_reply.readInt());}finally {_reply.recycle();_data.recycle();}return _result;}/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(anInt);_data.writeLong(aLong);_data.writeInt(((aBoolean)?(1):(0)));_data.writeFloat(aFloat);_data.writeDouble(aDouble);_data.writeString(aString);mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_accessData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}public boolean accessData() throws android.os.RemoteException;/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;}复制代码
我们可以看到,在IDataAccess接口里有一个叫做Stub的类,它实现了IDataAccess接口并继承自Binder,那么该类就是用来实现IPC通讯的本地Stub类。
那么现在我们还需要创建一个远程服务来实现具体的业务逻辑accessData。
public class MyRemoteService extends Service { private static final String TAG = "MyRemoteService"; public MyRemoteService(){} public IBinder onBind(Intent intent){ } public class DataAccessBinder extends IDataAccess.Stub { @Override public boolean accessData(){ Log.i(TAG, "You can access Me!"); return true; } @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString){} }}复制代码
接下来我们就需要将MyRemoteService注册在androidManifest.xml中
复制代码
那么现在我们只需要像绑定本地服务一样绑定远程服务即可(真的可以吗?回想一下讲解远程绑定是的第一个异常
)。private ServiceConnection conn = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service){ MyRemoteService.MyBinder mb = (MyRemoteService.MyBinder)service; try { mb.endCall(); } catch (RemoteException e) { e.printStackTrace(); } } /** * 当服务连接异常终止后调用 */ @Override public void onServiceDisconnected(ComponentName name){ }}..../** * 自定义方法,来实现绑定Service. */private void bindService(){ Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, conn, BIND_AUTO_CREATE);}/** * 自定义方法,来实现解绑Service. */private void unbindService(){ unbindService(conn); }复制代码
卧槽!卧槽!卧槽!怎么会奔溃了呢?什么玩意儿?客官别急,请先尝试一下下面的方法:
private ServiceConnection conn = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service){ IDataAccess iDataAccess = IDataAccess.Stub.asInterface(service); try { iDataAccess.accessData(); } catch (RemoteException e) { e.printStackTrace(); } } /** * 当服务连接异常终止后调用 */ @Override public void onServiceDisconnected(ComponentName name){ }}复制代码
哎呀!卧槽!对了!
我们来分析一下IDataAccess.Stub.asInterface(service)究竟做了什么
public static cn.codempero.summary.IDataAccess asInterface(android.os.IBinder obj){ if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof cn.codempero.summary.IDataAccess))) { return ((cn.codempero.summary.IDataAccess)iin); } return new cn.codempero.summary.IDataAccess.Stub.Proxy(obj);}复制代码
看了代码之后,仔细一想,对哟,通过AIDL访问的可是远程服务哎,是不处于同一个进程的呀!那么传递过来的IBinder对象不可能是本进程的IDataAccess呀,所以必须使用代理来访问。
说了这么多,远程Service使用起来如此复杂,好像还不知道为什么要用远程Service呢?
远程Service通常用来提供一些进程间共享的服务,通俗一点来说就是为手机上的App提供一个公共的服务,每个App都可以访问这个服务。
呵呵呵,那怎么在其他的App中访问远程服务呢?毕竟不同的App有不同的UID呀!
在其他应用程序中访问远程服务
最终我们的目的是:所有具有权限的应用程序都可以访问远程服务。那么只需要遵循以下流程即可访问到远程服务:
1. 新建名为ClientTest项目
2. 将IDataAccess.aidl同包路径一起拷贝过来(该路径需要和ClientTest路径同级)
|——src | └── main | | | └── java | | | └── cn.codemperor.clienttest | | | └── cn.codemepror.summary | | | | └── IDataAccess.aidl 复制代码
3. 使用隐式Intent来访问远程服务
但是,呵呵呵,android5.0及以上可是禁止通过隐式intent的方式来访问Service的呀。所以呢?我们来了解一下shareUserId吧!
在说shareUserId之前,我们先来说一下Android中的UID和PID吧!
我们已经知道,一个应用程序可以有多个进程,每个进程都有自己的PID,那么android中是如何来唯一标识应用程序的呢?linux是支持多用户多进程的,所以设计师采用UID来唯一标识一个用户,但是Android中只支持单用户,所以Google就将UID来唯一标识一个应用程序了,应用程序之间也可以通过UID来实现数据共享了。呵呵哒,说了这么多,具体应该怎么实现呢?so easy!只需要让两个应用程序拥有相同的shareUserId即可。
应用程序A
复制代码
应用程序B
复制代码
哈哈哈,现在我们就可以使用显示intent的方式来绑定远程Service了(当然也可以通过同样的方式启动Activity,本地Service,BroadcastReceiver)。但是机智的朋友就会问了:如果我知道了你的shareUserId,那么我就可以随意访问你的应用程序咯?哈哈,Google怎会如此lowB,开发者都知道在打包App时,都会有自己的签名文件,而且该签名文件可是机密哟。不同签名的App可是不能实现数据共享的。
在远程服务中使用自定义类型
前面我们提到,在远程服务中,仅支持几种类型的数据,虽然支持自定义类型,但用法有所不同。
首先我们定义一个Cat类型。据,要再远程服务中使用自定义类型必须。
package cn.codemperor.summary.bean;public class Cat implement Parcelable{ public static final Parcelable.CreatorCREATOR = new Parcelable.Creator () { public Cat createFromParcel(Parcel in) { return new Cat(in); } public Cat[] newArray(int size) { return new Cat[size]; } }; String name; String host; private Cat(Parcel in) { name = in.readString(); host = in.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeString(host); }}复制代码
然后在Cat所在的包下创建一个和自定义类型Cat同名的aidl文件,里面的内容固定为:
// Cat.aidlpackage cn.codemperor.summary.bean;// Declare Rect so AIDL can find it and knows that it implements// the parcelable protocol.parcelable Cat;复制代码
注意:Cat.aidl和Cat.java的包名必须一致,否则会查找不到。
那么,现在我们就可以使用Cat类型了
//导入Cat类,必须写import cn.codemperor.summary.bean.Cat;interface IDataAccess { boolean accessData(); Cat getCat(); /** * 如果要在参数中使用自定义类型,必须添加in修饰符 **/ void setCat(in Cat cat); /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);}复制代码
最后在DataAccessBinder中重写getCat(), setCat(in Cat cat)即可。
哈哈,前方高能,前方高能,前方高能 经过测试,使用getCat()返回的Cat对象不为空,但是cat.name, cat.host为null。
其他进程间通讯的方式
Messager
该部分较为简单,查看用法。
一些Service保活的方法
前台Service
具体参见,作者写的浅显易懂。
onStartCommand的返回值
不用再说了吧,哈哈哈哈哈哈
一些小知识点
android:process的值
(1). android:process="com.xxx.xxxx.remote" 完整的命名方式,属于全局进程,其它应用通过ShareUID方式可以和它跑在同一个进程中。
(2). android:process=":remote",进程以“:”开头的进程属于当前应用的私有进程,其它应用的组件不可以和它跑在同一个进程中,“:”命名的进程会在当前的进程名之前附加上包名如:“com.xxx.xxxx:remote”。