在Android中,Service是运行在主线程中的,如果在Service中处理一些耗时的操作,就会导致程序出现ANR.


但如果将本地的Service转换成一个远程的Service,就不会出现这样的问题了.

转换成远程Service非常简单,只需要在注册Service的时候将他的android:process的属性制定成 :remote就可以了.


重新运行项目,你会发现,不会出现ANR了.
为什么将MyService转换成远程Service后就不会导致程序ANR了呢?这是由于,使用了远程Service后,MyService已经在另外一个进程当中运行了,所以只会阻塞该进程中的主线程,并不会影响到当前的应用程序。
为了证实一下MyService现在确实已经运行在另外一个进程当中了,我们分别在MainActivity的onCreate()方法和MyService的onCreate()方法里加入一行日志,打印出各自所在的进程id,如下所示:

Log.e("info", "process id is " + Process.myPid());    

运行: 

可以看到,不仅仅是进程id不同了,就连应用程序包名也不一样了,MyService中打印的那条日志,包名后面还跟上了:remote标识。

那既然远程Service这么好用,干脆以后我们把所有的Service都转换成远程Service吧,还省得再开启线程了。其实不然,远程Service非但不好用,甚至可以称得上是较为难用。一般情况下如果可以不使用远程Service,就尽量不要使用它。

下面就来看一下它的弊端吧,首先将MyService的onCreate()方法中让线程睡眠的代码去除掉,然后重新运行程序,并点击一下Bind Service按钮,你会发现程序崩溃了!为什么点击Start Service按钮程序就不会崩溃,而点击Bind Service按钮就会崩溃呢?这是由于在Bind Service按钮的点击事件里面我们会让MainActivity和MyService建立关联,但是目前MyService已经是一个远程Service了,Activity和Service运行在两个不同的进程当中,这时就不能再使用传统的建立关联的方式,程序也就崩溃了。

那么如何才能让Activity与一个远程Service建立关联呢?这就要使用AIDL来进行跨进程通信了(IPC)。

AIDL(Android Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

下面我们就来一步步地看一下AIDL的用法到底是怎样的。首先需要新建一个AIDL文件,在这个文件中定义好Activity需要与Service进行通信的方法。新建MyAIDLService.aidl文件,代码如下所示:

  1. package com.example.servicetest.MyAIDLService;
  2. interface MyAIDLService {
  3. int plus(int a, int b);
  4. String toUpperCase(String str);
  5. }
点击保存后,会在gen目录下生成一个对应的Java文件.
然后修改MyService中的代码,在里面实现刚刚定义好的MyAIDLService接口
    MyAIDLService.Stub mBinder=new Stub() {  //做相应的处理
        
        @Override
        public String toUpperCase(String str) throws RemoteException {
            if(str!=null){
                return str.toUpperCase();
            }
            return null;
        }
        
        @Override
        public int plus(int a, int b) throws RemoteException {
            return a+b;
        }
    };
    public IBinder onBind(Intent intent) {
        return mBinder;

}

这里先是对MyAIDLService.Stub进行了实现,重写里了toUpperCase()和plus()这两个方法。这两个方法的作用分别是将一个字符串全部转换成大写格式,以及将两个传入的整数进行相加。然后在onBind()方法中将MyAIDLService.Stub的实现返回。这里为什么可以这样写呢?因为Stub其实就是Binder的子类,所以在onBind()方法中可以直接返回Stub的实现。
接下来修改MainActivity中的代码,如下所示:

  1. public class MainActivity extends Activity implements OnClickListener {
  2. Button btn_start,btn_stop,btn_bind,btn_unbind;
  3. MyAIDLService mAIDLService;
  4. ServiceConnection connection=new ServiceConnection() {
  5. @Override
  6. public void onServiceDisconnected(ComponentName name) {
  7. }
  8. @Override
  9. public void onServiceConnected(ComponentName name, IBinder service) {
  10. mAIDLService=MyAIDLService.Stub.asInterface(service);
  11. try {
  12. int result=mAIDLService.plus(8, 7);
  13. String upperStr = mAIDLService.toUpperCase("hello world");
  14. Log.d("info", "--result---" + result);
  15. Log.d("info", "--upperStr---" + upperStr);
  16. } catch (RemoteException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. };
  21. @Override
  22. protected void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.activity_main);
  25. btn_start=(Button) findViewById(R.id.btn_start);
  26. btn_stop=(Button) findViewById(R.id.btn_stop);
  27. btn_bind=(Button) findViewById(R.id.btn_bind);
  28. btn_unbind=(Button) findViewById(R.id.btn_unBind);
  29. btn_start.setOnClickListener(this);
  30. btn_stop.setOnClickListener(this);
  31. btn_bind.setOnClickListener(this);
  32. btn_unbind.setOnClickListener(this);
  33. Log.e("info", "process id is " + Process.myPid());
  34. }
  35. @Override
  36. public void onClick(View v) {
  37. if(v.getId()==R.id.btn_start){
  38. Intent intent=new Intent(this,MyService.class);
  39. startService(intent);
  40. }
  41. else if(v.getId()==R.id.btn_stop){
  42. Intent intent=new Intent(this, MyService.class);
  43. stopService(intent);
  44. }
  45. else if(v.getId()==R.id.btn_bind){
  46. Intent intent=new Intent(this, MyService.class);
  47. bindService(intent, connection,BIND_AUTO_CREATE);
  48. }
  49. else if(v.getId()==R.id.btn_unBind){
  50. unbindService(connection);
  51. }
  52. }
  53. }
我们只是修改了ServiceConnection中的代码。可以看到,这里首先使用了MyAIDLService.Stub.asInterface()方法将传入的IBinder对象传换成了MyAIDLService对象,接下来就可以调用在MyAIDLService.aidl文件中定义的所有接口了。这里我们先是调用了plus()方法,并传入了3和5作为参数,然后又调用了toUpperCase()方法,并传入hello world字符串作为参数,最后将调用方法的返回结果打印出来。
点击Bind Service按钮


由此可见,我们确实已经成功实现跨进程通信了,在一个进程中访问到了另外一个进程中的方法。

不过你也可以看出,目前的跨进程通信其实并没有什么实质上的作用,因为这只是在一个Activity里调用了同一个应用程序的Service里的方法。而跨进程通信的真正意义是为了让一个应用程序去访问另一个应用程序中的Service,以实现共享Service的功能。那么下面我们自然要学习一下,如何才能在其它的应用程序中调用到MyService里的方法。

第一步:修改AndroidManifest.xml中的代码,给MyService加上一个action,如下所示:

这就说明,MyService可以响应带有com.example.servicetest.MyAIDLService这个action的Intent.

第二步:新建一个android项目,在这个项目中远程调用当前MyService中的方法.
新项目中的Activity如果想要和MyService建立关联其实也不难,首先需要将MyAIDLService.aidl文件从ServiceTest项目中拷贝过来,注意要将原有的包路径一起拷贝过来,完成后项目的结构如下图所示:

 
第三步:打开新项目的activity_main.xml.在布局文件中加入一个Button
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:paddingBottom="@dimen/activity_vertical_margin"
  6. android:paddingLeft="@dimen/activity_horizontal_margin"
  7. android:paddingRight="@dimen/activity_horizontal_margin"
  8. android:paddingTop="@dimen/activity_vertical_margin"
  9. tools:context=".MainActivity" >
  10. <Button
  11. android:layout_width="wrap_content"
  12. android:layout_height="wrap_content"
  13. android:id="@+id/btn"
  14. android:text="Bind Service"
  15. />
  16. </RelativeLayout>
第四步:打开MainActivity.java
  1. public class MainActivity extends Activity {
  2. private MyAIDLService mAIDLService;
  3. private ServiceConnection connection=new ServiceConnection() {
  4. @Override
  5. public void onServiceDisconnected(ComponentName arg0) {
  6. }
  7. @Override
  8. public void onServiceConnected(ComponentName name, IBinder service) {
  9. mAIDLService=MyAIDLService.Stub.asInterface(service);
  10. try {
  11. int result=mAIDLService.plus(50, 48);
  12. String upperStr=mAIDLService.toUpperCase("hello world");
  13. Log.e("info","---result---"+result);
  14. Log.e("info","upperStr--"+upperStr);
  15. } catch (RemoteException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. };
  20. Button btn;
  21. @Override
  22. protected void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.activity_main);
  25. btn=(Button) findViewById(R.id.btn);
  26. btn.setOnClickListener(new OnClickListener() {
  27. @Override
  28. public void onClick(View v) {
  29. Intent intent=new Intent("com.example.myAIDL");
  30. bindService(intent, connection, BIND_AUTO_CREATE);
  31. }
  32. });
  33. }
  34. }

这部分代码大家一定会非常眼熟吧?没错,这和在ServiceTest的MainActivity中的代码几乎是完全相同的,只是在让Activity和Service建立关联的时候我们使用了隐式Intent,将Intent的action指定成了com.example.servicetest.MyAIDLService。
在当前Activity和MyService建立关联之后,我们仍然是调用了plus()和toUpperCase()这两个方法,远程的MyService会对传入的参数进行处理并返回结果,然后将结果打印出来。

这样的话,ClientTest中的代码也就全部完成了,现在运行一下这个项目,然后点击Bind Service按钮,此时就会去和远程的MyService建立关联,观察LogCat中的打印信息如下所示:


不用我说,大家都已经看出,我们的跨进程通信功能已经完美实现了。

不过还有一点需要说明的是,由于这是在不同的进程之间传递数据,Android对这类数据的格式支持是非常有限的,基本上只能传递Java的基本数据类型、字符串、List或Map等。那么如果我想传递一个自定义的类该怎么办呢?这就必须要让这个类去实现Parcelable接口,并且要给这个类也定义一个同名的AIDL文件。


android 远程Service以及AIDL的跨进程通信的更多相关文章

  1. 不依赖AIDL的跨进程通信

    http://blog.csdn.net/lmj623565791/article/details/38461079 如果知道AIDL和binder的原理,可以简单写一个不依赖AIDL的跨进程通信 不 ...

  2. 一个简单的demo学习Android远程Service(AIDL的使用)

    这是milo很早之前写在论坛上的一个帖子,现在整理出来,milo也复习一下一般来说Android 的四大组件都是运行在同一个进程中的,但远程Service运行在不同的进程里.这进程间的通信是使用了An ...

  3. Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

    在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...

  4. 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇

    前言 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Andro ...

  5. Android四大组件应用系列5——使用AIDL实现跨进程调用Service

    一.问题描述 Android应用程序的四大组件中Activity.BroadcastReceiver.ContentProvider.Service都可以进行跨进程.在上一篇我们通过ContentPr ...

  6. Android中的跨进程通信方法实例及特点分析(一):AIDL Service

    转载请注明出处:http://blog.csdn.net/bettarwang/article/details/40947481 近期有一个需求就是往程序中增加大数据的採集点,可是由于我们的Andro ...

  7. Service官方教程(11)Bound Service示例之2-AIDL 定义跨进程接口并通信

    Android Interface Definition Language (AIDL) 1.In this document Defining an AIDL Interface Create th ...

  8. AIDL跨进程通信

    Android跨进程通信会用到AIDL,当然跨进程通信不一定要用AIDL,像广播也是可以的,当然这里用到AIDL相对比较安全一些: AIDL允许传递基本数据类型(Java 的原生类型如int/long ...

  9. 【朝花夕拾】跨进程通信,你只知道AIDL,就OUT了

    一.前言 提起跨进程通信,大多数人首先会想到AIDL.我们知道,用AIDL来实现跨进程通信,需要在客户端和服务端都添加上aidl文件,并在服务端的Service中实现aidl对应的接口.如果还需要服务 ...

随机推荐

  1. CDH4.5.0下安装snappy

    编译源代码 http://www.cnblogs.com/chengxin1982/p/3862289.html 测试参考 http://blog.jeoygin.org/2012/03/java-c ...

  2. 【PC-x86-x64】JDK 32bit与64bit的区别及x64 PC的发展历程【转】

    一次偶然分析的机会: 在进行Minecraft也就是所谓的我的世界游戏的时候,在对局域网进行开放的时候,我的是64bit的JDK,而我同学的是32bit的JDK,所以在进行局域网链接的时候就会出现In ...

  3. django验证码功能

    1.目的 现在我们一般访问网页都需要输入验证码,比如博客园,有的甚至是通过手机验证码实时登录.这样做的目的主要还是为了防止其他人的恶意访问,比如爬虫,下面就来看看验证码是如何实现的 2.StringI ...

  4. HTML第三章:表单

    第三章:表单 表单标签form:<form></form>//相当于一张记录用户信息的单子    常用属性:method:表单的提交方式,常用的值有两个             ...

  5. css的position定位终极总结

    relative相对定位是相对于自己的位置定位,absolute绝对定位是向上级一级一级搜索有position属性的div,如果没有找到就相对于body定位

  6. ES6的数组方法之Array.from

    首先说说什么是数组:数组在类型划分上归为Object,属于比较特殊的对象,数组的索引值类似于对象的key值. 数组的几个注意点: 1.数组的长度是可读属性,不可更改,数组的长度根据索引最大值. 2.数 ...

  7. 爬虫学习(十二)——bs4实践案例

    实践项目————诗词名句网<三国演义>小说爬取 import osimport reimport timeimport urllib.requestimport urllib.parsef ...

  8. 在github上查找star最多的项目

    如何在github上查找star最多的项目 在search中输入stars:>1 就可以查找所有有star的项目,然后右上角根据自己的需要筛选 当我输入stars:>10000的时候,就会 ...

  9. 【shopex】真正可用的app开发机制

    shopex的app开发机制详解   网上流传的shopex4.8.5的app开发教程,不仅说得不明不白,而且由于版本问题,照着做根本是做不成的. 知其然,亦要知其所以然. shopex提供了的一个干 ...

  10. 通信服务器哈希Socket查找(Delphi)

    在Socket通信服务器的开发中,我们经常会需要Socket与某个结构体指针进行绑定.当连接量很大时,意味着需要个高效的查找方法 Delphi中提供了哈希算法类,以此类为基础,修改出Socket专用M ...