博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Service、IntentService、AIDL简介
阅读量:6487 次
发布时间:2019-06-23

本文共 23413 字,大约阅读时间需要 78 分钟。

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.Creator
CREATOR = 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”。

转载于:https://juejin.im/post/5a3d2707518825698e72434b

你可能感兴趣的文章
PAT、PMT、SDT详解
查看>>
Spring MVC request flow
查看>>
Tcl与Design Compiler (二)——DC综合与Tcl语法结构概述
查看>>
C#通过Redis实现分布式锁
查看>>
dhcp 过程
查看>>
eclipse 修改maven项目的jdk版本
查看>>
vue - 插槽slot
查看>>
【Python】Docx解析
查看>>
JAVASCRIPT实现绚丽TAB选项卡
查看>>
软件包管理 之 Fedora / Redhat 软件包管理指南
查看>>
mac iTunes启动失败,声称iTunes文件夹被锁定
查看>>
【转】PHP的异常处理类Exception的使用及说明
查看>>
Android 按两次back键退出 -- 效率最高版
查看>>
The property delegate of CALayer cause Crash.
查看>>
使用连接来代替in和not in(使用外连接技巧)
查看>>
C#实现汉字转换为拼音缩写的代码
查看>>
onvif 开发之video streamer---onvif实现功能和经验
查看>>
254. Factor Combinations
查看>>
VS2013上利用InstallShield2013LimitedEdition/C#生成安装包
查看>>
javaWeb项目中到底什么是单例,多例
查看>>