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)

时间: 09-03

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

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

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

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

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

1.ContentProvider简单介绍 在Android中有些数据(如通讯录.音频.视频文件等)是要供非常多应用程序使用的.为了更好地对外提供数据,Android系统给我们提供了Content Provider使用,通过它能够訪问上面所说的数据.比如非常多音乐播放器中的扫描功能事实上就用到了Content Provider功能(当然,也有的播放器是自己去实现更底层的功能). 这种优点是统一管理,比方添加了某个音频文件,底层就会将这种变化通知Content Provider.从而当应用程序訪问

linux 单机跨进程通信

一般来说通过网络通信(比如tcp,udp)或者共享内存的方式肯定可以实现跨进程通信,但现在这里要说的是比较偏但实用的几个方法:利用unix域通信(普通网络连接),利用unix域通信(socketpair通信),以及pipe方式. 一. 利用unix域通信(普通网络连接) socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.

Android跨进程通信

由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于android系统中4种应用程序组件:Activity.Content Provider.Broadcast和Service. 其中Activity可以跨进程调用其他应用程序的Activity: Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然也可以对其他

Android-Messenger跨进程通信

http://blog.csdn.net/lmj623565791/article/details/47017485 一.概述 我们可以在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收到处理的结果. 有这么几个特点: 基于Message,相信大家都很熟悉 支持回调的方式,也就是服务端处理完成长任务可以和客户端交互 不需要编写aidl文件 此外,还

messenger跨进程通信出现ClassNotFoundException...

利用messenger实现remote进程的service和主进程通信的时候报错com.xxx.xxx.xxx.bean.xxxbean on path: DexPathList[[directory .],nativeLibraryDirectories=[/vendor/lib, /data/cust/lib, /system/lib]] 看log发现错误处是在client解析从service发过来的message中的bundle对象, 网上搜了有人说是因为类加载器的问题,然后自己在   d

使用Unix域套接字进行跨进程通信

Unix域套接字简介 <Unix环境高级编程>中对Unix域套接字有如下介绍: 虽然socketpair函数创建相互连接的一对套接字,但是每一个套接字都没有名字.这意味着无关进程不能使用它们. 我们可以命名unix域套接字,并可将其用于告示服务.但是要注意的是,UNXI与套接字使用的地址不同与因特网域套接字. UNIX域套接字的地址由sockaddr_un结构表示. 在linux2.4.22中,sockaddr_un结构按下列形式定义在有文件 struct sockaddr_un{ sa_fa