android 远程Service以及AIDL的跨进程通信

在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文件。

来自为知笔记(Wiz)

时间: 2024-10-20 04:56:08

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

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

在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的.另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用,此前我们都是用Eclipse来实现的,这次我们看看在Android Studio中使用AI

如何定义AIDL进行跨进程通信

当进程A要去调用进程B中的service时,并实现通信,我们通常都是通过AIDL来操作的 工程A: 首先在我们自己的包com.wzp.aidlservice中创建一个RemoteService.aidl文件,在里面我们自定义一个接口,含有方法getService().ADT插件会在gen目录下自动生成一个RemoteService.java文件,该类中含有一个名为RemoteService.Stub的内部类,该内部类中含有AIDL文件接口的getService()方法 例如: package c

Android学习笔记二十六.跨进程调用Service(AIDL Service)

跨进程调用Service(AIDL Service) 一.AIDL Service 1.什么是AIDL Service? AIDL,即Android Interface Definition Language.是Android用于定义远程接口,AIDL接口定义语言的语法比较简单,这种接口定义语言并不是真正的编程语言,它只是定义两个进程之间的通信接口.AIDL的语法与Java接口很相似,但存在如下几点差异: (1)AIDL定义接口的源代码必须以.aidl结尾; (2)AIDL接口中用到数据类型,除

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

转载请注明出处:http://blog.csdn.net/bettarwang/article/details/40947481 最近有一个需求就是往程序中加入大数据的采集点,但是因为我们的Android程序包含两个进程,所以涉及到跨进程通信的问题.现将Android中的跨进程通信方式总结如下. Android中有4种跨进程通信方式,分别是利用AIDL Service.ContentProvider.Broadcast.Activity实现. 1.利用AIDL Service实现跨进程通信 这是

Android基础笔记(十二)- 使用AIDL来进行跨进程通信

绑定服务调用服务里方法的过程 音乐盒小案例 利用服务注册特殊广播接收者 使用AIDL来进行跨进程通信 绑定服务调用服务里方法的过程 整个Activty绑定Service并调用其中方法的过程可以体现为下面的一张图,其中的核心是通过借助中间人IBinder来达到调用Service中方法的目的.. 接下来在明确一下调用过程的代码步骤: ①首先服务里有一个方法需要被调用 ②定义一个中间人对象(继承Bidner类的内部类MyBinder) ③在onBind方法中把我们自己定义的中间人返回MyBinder

【朝花夕拾】性能优化篇之(八)AIDL与Android跨进程通信

一.Linux进程间通信 1.进程隔离 在操作系统中,进程与进程间的内存和数据都是不共享的.两个进程就好像大海中相互独立的两个岛屿,各自生活在互相平行的两个世界中,互不干扰,各自为政.这样做的目的,是为了避免进程间相互操作数据的现象发生,从而引起各自的安全问题.为了实现进程隔离,采用了虚拟地址空间,两个进程各自的虚拟地址不同,从逻辑上来实现彼此间的隔离. 马克思主义哲学说,人是一切社会关系的总和.任何一个个体都不可能完全隔离于外界,都不可避免地与外界"互通有无".进程也一样,时不时需要

Android Messenger 跨进程通信

如果你需要在不同进程间通信,你可以在Service中使用Messenger来实现进程中通信. 如果使用这种方式,Service中需要定义一个Handler对象(负责对客户端发送过来的Message进行响应). Messenger可以共享给client一个IBinder对象,client通过这个IBinder对象向Service发送Message,而前面提到的Handler对象是这一切的基础. 注:使用这种方式进行通信是不支持多线程的. 那就让我们来看看使用这种方式进行通信吧! 注:Service

解读Android之Service(3)AIDL

本文翻译自android官方文档,结合自己测试,整理如下. Android Interface Definition Language(AIDL)能够让我们定义自己的编程接口,该接口可以使得客户端和service之间进行跨进程通信(interprocess communication,IPC).通常,在android中无法直接跨进程通信.因此,需要把传递的对象分解成系统可以识别的原始状态(数据),并将它们跨进程序列化marshalling.由于marshalling过程繁琐,因此android通

【朝花夕拾】一篇文章搞懂Android跨进程通信

前言 只要是面试中高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Android开发高级工程师必须要跨过的一道坎.如果您还对这方面的知识还做不到如数家珍,那就和我一起来攻克它吧! 本文主要包含了如下内容: 其行文脉络大致如下,希望能加深读者对这方面内容的记忆:(1)Android基于Linux系统,所以先说系统进程相关知识和Linux IPC.(2)总结Android的IPC