Android跨进程通信 Binder AIDL 知识点总结

一、IPC跨进程通讯的四种方式

Android系统中应用程序之间不能共享内存,在android SDK中提供了4种用于跨进程通讯的方式。这4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provider、Broadcast和Service。

1、Activity可以跨进程调用其他应用程序的Activity。

进程内(同一个应用程序)
跨进程访问

2、Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回)

Content Provider存在的目的向其他应用程序共享数据和允许其他应用程序对数据进行增、删、改操作

3、Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播

广播(Broadcast):广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据

发送一条广播
接收广播: 继承BroadcastReceiver
接收者在Manifest文件中写明要接收的广播

4、Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。

二、AIDL(Android Interface Definition Language Android接口定义语言)

三、Service Binder

1,什么是service?

Service是一个可以在后台执行长时间操作而不使用用户界面的应用组件。既然它不使用用户界面,那么它怎么知道应该什么时候开始执行什么操作呢?答案是——它可以与其他的引用组件形成一些联系,从而可以根据其传来的信息在合适的时候执行合适的操作。
一般来讲,这种联系分为两种:startService()以及bindService()。这两种联系都可以使得一个service开始运行,但是在其他方面有着诸多不同,看一下对比:

(1)启动service的方式:

startService :在其他组件中调用startService()方法后,服务即处于启动状态
bindService:在其他组件中调用bindService()方法后,服务即处于启动状态

(2)停止service的方式:

startService :service中调用stopSelf()方法,或者其他组件调用stopService()方法后,service将停止运行
bindService:所有与service绑定的组件都被销毁,或者它们都调用了unbindService()方法后,service将停止运行

(3)service与启动它的组件之间的通信方式:
startService :没有提供默认的通信方式,启动service后该service就处于独立运行状态
bindService:可以通过 ServiceConnection进行通信,组件可以与service进行交互、发送请求、获取结果,甚至是利用IPC跨进程执行这些操作

(4)service的生命周期
startService :一旦启动,service即可在后台无限期运行,即使启动service的组件已被销毁也不受其影响,直到其被停止
onCreate() - onStartCommand() - onDestory()

bindService: 当所有与其绑定的组件都取消绑定(可能是组件被销毁也有可能是其调用了unbindService()方法后,service将停止
onCreate() - onBind() - onUnbind() - onDestory()

注: 1.以上“其他组件”不包括Broadcast receiver,其无法直接启动或绑定service。 2.startService()与bindService()并不冲突,同一个service可能既有组件调用了startService()启动它,又有组件与它进行了绑定。当同一个service与其他组件同时存在这两种联系时,其生命周期会发生变化,必须从两种方法的角度看service均停止才能真正停止。

2,如何创建一个service?

创建一个类继承自Service(或它的子类,如IntentService),重写里面的一些关键的回调方法,如onStartCommand(),onBind()等
在Manifests文件里面为其声明,并根据需要配置一些其他属性。

讲道理,这一切跟新建一个Activity灰常的像,跟孪生兄弟一样——包括其里面的一些关键方法,都充斥着一种浓浓的基情。
onCreate() 在每个service的生命周期中这个方法会且仅会调用一次,并且它的调用在onStartCommand()以及onBind()之前,我们可以在这个方法中进行一些一次性的初始化工作。

onStartCommand() 当其他组件通过startService()方法启动service时,此方法将会被调用。

onBind() 当其他组件通过bindService()方法与service相绑定之后,此方法将会被调用。这个方法有一个IBinder的返回值,这意味着在重写它的时候必须返回一个IBinder对象,它是用来支撑其他组件与service之间的通信的——另外,如果你不想让这个service被其他组件所绑定,可以通过在这个方法返回一个null值来实现。

onDestroy() 这是service一生中调用的最后一个方法,当这个方法被调用之后,service就会被销毁。所以我们应当在这个方法里面进行一些资源的清理,比如注册的一些监听器什么的。

在Manifests文件里进行声明的时候,只有android:name属性是必须要有的,其他的属性都可以没有。但是有的时候适当的配置可以让我们的开发进行地更加顺利,所以了解一下注册一个service可以声明哪些属性也是很有必要的。

<service android:enabled=["true" | "false"]
    android:exported=["true" | "false"] 
    android:icon="drawable resource" 
    android:isolatedProcess=["true" | "false"] 
    android:label="string resource"
    android:name="string" 
    android:permission="string" 
    android:process="string" >
</service>

android:enabled : 如果为true,则这个service可以被系统实例化,如果为false,则不行。默认为true

android:exported : 如果为true,则其他应用的组件也可以调用这个service并且可以与它进行互动,如果为false,则只有与service同一个应用或者相同user ID的应用可以开启或绑定此service。它的默认值取决于service是否有intent filters。如果一个filter都没有,就意味着只有指定了service的准确的类名才能调用,也就是说这个service只能应用内部使用——其他的应用不知道它的类名。这种情况下exported的默认值就为false。反之,只要有了一个filter,就意味着service是考虑到外界使用的情况的,这时exported的默认值就为true

android:icon : 一个象征着这个service的icon

android:isolatedProcess : 如果设置为true,这个service将运行在一个从系统中其他部分分离出来的特殊进程中,我们只能通过Service API来与它进行交流。默认为false。

android:label : 显示给用户的这个service的名字。如果不设置,将会默认使用<application>的label属性。

android:name : 这个service的路径名,例如“com.lypeer.demo.MyService”。这个属性是唯一一个必须填的属性。

android:permission : 其他组件必须具有所填的权限才能启动这个service。

android:process : service运行的进程的name。默认启动的service是运行在主进程中的。

下面是一个例子:

public class ServiceDemo extends Service { 
    private static final String TAG = "ServiceDome";
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
       //只在service创建的时候调用一次,可以在此进行一些一次性的初始化操作 
     } 

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) { 
       Log.d(TAG, "onStartCommand");
      //当其他组件调用startService()方法时,此方法将会被调用
      //在这里进行这个service主要的操作 
      return super.onStartCommand(intent, flags, startId); 
    }

     @Nullable
     @Override public IBinder onBind(Intent intent) { 
         Log.d(TAG, "onBind"); 
        //当其他组件调用bindService()方法时,此方法将会被调用
        //如果不想让这个service被绑定,在此返回null即可
        return null;
      }
     @Override
     public void onDestroy() {
         Log.d(TAG, "onDestroy");
         //service调用的最后一个方法
        //在此进行资源的回收        
        super.onDestroy();
    }
}

<!--in manifests --><service android:name=".ServiceDemo"/>

3,如何启动service?

前面讲了两种启动service的方式,但是都讲的比较概括,这里就详细的讲一下这两种方式。

3.1,startService()

另一个组件通过调用startService()方法,就可以启动一个特定的service,并且这将导致service中的onStartCommand()方法被调用。

在调用startService()方法的时候,其他组件需要在方法中传递一个intent参数,然后service将会在onStartCommand()中接收这个intent,并获取一些数据。

比如此时某个Activity要将一些数据存入数据库中,我就可以通过intent把数据传入service,然后让service去进行连接数据库,存储数据等操作,而此时用户可以执行其他的任何操作——甚至包括销毁那个Activity——这并不会影响service存储数据这件事。

当一个service通过这种方式启动之后,它的生命周期就已经不受启动它的组件影响了,它可以在后台无限期的运行下去,只要service自身没有调用stopSelf()并且其他的组件没有调用针对它的stopService()。

另外,如果确定了使用这种方式启动service并且不希望这个service被绑定的话,那么也许除了传统的创建一个类继承service之外我们有一个更好的选择——继承IntentService

如果是扩建Service类的话,通常情况下我们需要新建一个用于执行工作的新线程,因为默认情况下service将工作于应用的主线程,而这将会降低所有正在运行的Activity的性能。而IntentService就不同了。它是Service的子类,它使用工作线程来注意的处理所有的startService请求。如果你不要求这个service要同时处理多个请求,那么继承这个类显然要比直接继承Service好到不知道哪里去了。(感觉跟HandlerThread似的)

IntentService已经做了这些事:

(1)创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent
(2)创建工作队列,用于将一个 Intent 逐一传递给 onHandleIntent() 实现,这样的话就永远不必担心多线程问题了
(3)在处理完所有启动请求后停止服务,从此妈妈再也不用担心我忘记调用 stopSelf() 了
(4)提供 onBind() 的默认实现(返回 null)
(4)提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现

因此我们只需要实现onHandleIntent()方法来完成具体的功能逻辑就可以了。例如:

// 现在在一个Activity里面
Intent intent = new Intent(MainActivity.this, IntentServiceDemo.class);
startService(intent);

public class IntentServiceDemo extends IntentService { 
    public IntentServiceDemo(String name) { 
    super(name); 
    //构造方法
 } 
@Override
 protected void onHandleIntent(Intent intent) {
 //在这里根据intent进行操作
 }
}

相比上面的继承service实现,这个确实要简单许多。但是要注意的是,如果需要重写其他的方法,比如onDestroy()方法,一定不要删掉它的父类实现!因为它的父类实现里面也许包括了对工作线程还有工作队列的初始化以及销毁等操作,如果没有了的话很容易出问题。

但是,如果你有让service同时处理多个请求的需求,这个时候就只能去继承Service了。这个时候就要自己去处理工作线程那些事。下面是一个官方的例子:

public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    HandlerThread thread = new     HandlerThread("ServiceStartArguments",
        Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting",    Toast.LENGTH_SHORT).show();

      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done",     Toast.LENGTH_SHORT).show();
  }
}

比起IntentService来,显然做的工作要多的多的多。但是,由于是自己处理的对onStartCommand的调用,它可以同时执行多个请求——虽然官方的栗子里没有这样做。但是如果你想这样做,就可以为每一个请求创建一个线程,然后立即运行这些请求。

另外,我们注意到onStartCommand()的返回值是一个很奇怪的值START_STICKY,这是个什么呢?或者说这个方法的返回值是用来干嘛的呢?

事实上,它的返回值是用来指定系统对当前线程的行为的。它的返回值必须是以下常量之一:

(1)START_NOT_STICKY : 如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。

(2)START_STICKY : 如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但绝对不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

(3)START_REDELIVER_INTENT : 如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

除此之外,继承service来实现的service,一定要记得,停止这个service。因为除非系统必须回收内存资源,否则系统不会停止或销毁service。事实上,如果这个service是与可见组件绑定,其几乎永远不会被停止或销毁。在这种情况下,如果你忘记了在其工作完成之后将其停止,势必会造成系统资源的浪费以及电池电量的消耗,而这应当是我们要极力避免的。

3.2,bindService()

这是一种比startService更复杂的启动方式,同时使用这种方式启动的service也能完成更多的事情,比如其他组件可向其发送请求,接受来自它的响应,甚至通过它来进行IPC等等。我们通常将绑定它的组件成为客户端,而称它为服务器。

如果要创建一个支持绑定的service,我们必须要重写它的onBind()方法。这个方法会返回一个IBinder对象,它是客户端用来和服务器进行交互的接口。而要得到IBinder接口,我们通常有三种方式:继承Binder类,使用Messenger类,使用AIDL。

这三种方式也是各有各的好处以及缺点,更多的讲解将放在下一篇博客中进行。http://blog.csdn.net/luoyanglizi/article/details/51594016

参考:
http://blog.csdn.net/luoyanglizi/article/details/51980630
http://blog.csdn.net/luoyanglizi/article/details/51594016

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容