在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. 如何提高mysql的安全性?

    1.如果 MySQL 客户端和服务器端的连接需要跨越并通过不可信任的网络,那么需要使用 ssh 隧道来加密该连接的通信.2.使用 set password 语句来修改用户的密码,先“mysql -u ...

  2. A Multigrid Tutorial中涉及到的难点词汇

    Multigrid Tutorial中涉及的词汇: Elliptic PDEs 椭圆型偏微分方程 Lawrence Livermore National Laboratory 劳伦斯利福摩尔国家实验室 ...

  3. 当Java遇见了Html--Jsp九大内置对象篇

    jsp内置对象对象是web容器创建的一组对象,不使用new关键词久可以使用的内置对象. 九大内置对象包括以下: out --JspWriter request --ServletRequest rep ...

  4. PHP:(一)安装并使用PHP

    php的安装分为两个部分:环境安装配置和开发工具 一.集成环境安装配置 (一)安装 选择:wampserver或者xampp 我采用的是xampp. 在http://www.sourceforce.n ...

  5. Shell学习——数值运算

    在Bash shell中,可以利用let.(( )).[]执行基本的算术操作,在高级操作时,使用expr和bc两个工具1.let[root@client02 ~]# no1=4[root@client ...

  6. asp.net高并发网站解决方案【未完成版本】

    场景:假设现在是一个电商网站,今天要举办活动,有10个商品低价销售,但是会来抢购的人会特别多,最后只有十个人可以成功的买到商品   明确2个问题 1.访问量:抢票时间断用户访问量 2.并发:1秒内请求 ...

  7. 43_2.VUE学习之--不使用组件computed计算属性超简单的实现美团购物车原理

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. PLC状态机编程第四篇-历史状态处理

    今天我们接着上次的控制任务,加入历史状态,这个任务会比较复杂,象这样的任务我们倾向于自动生成PLC程序,自己写容易出错.但为了演示,我们可以尝试一下.言归正传,下面是我们的控制任务. 控制任务 这次的 ...

  9. 状压DP详解(位运算)

    前言: 状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v.S代表的就是一种状态(二进制表示),比如 ...

  10. POJ 2084 Catalan

    Game of Connections Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 8772   Accepted: 43 ...