AIDL实现IPC通信

今天心血来潮来写一篇关于如何在AS下结合AIDL创建和使用远程service。在此之前我先跟大家解释下什么是远程service?以及为什么要使用远程service?相信不管对于安卓新手还是老手而言,对于service并不感到陌生,所以这里就不跟讨论其基础概念和一些知识点了。所谓的远程service意思其实就是提供一个独立于某个app进程而创建的服务,这个服务可以提供给多个app共同使用,当然了,这些app必须拥有访问这个远程service的某种协议或者说接口,而这正是我们今天博文的另一个重点,就是AIDL,所谓的AIDL其实就是安卓接口定义语言,在本篇当中,它将用于提供一个我们访问远程service的一个接口。如果我的表述不是很清楚或者你完全听不懂,那么没关系,我们通过代码和截图一步步帮你搞懂我在本篇所要表达的内容,相信你能明白,因为本篇的内容很简单。


一、"服务端"项目

按照国际惯例,我们先来打开Android Studio并创建一个项目,项目名称随你定,这里我就不演示了。然后在项目中创建一个service。下面我会贴出这个service的截图和代码:


[java]  view plain  copy
  1. public class MyService extends Service {  
  2.   
  3.     @Override  
  4.     public void onCreate() {  
  5.         super.onCreate();  
  6.         Log.i("mylog""onCreate");  
  7.     }  
  8.   
  9.     @Override  
  10.     public int onStartCommand(Intent intent, int flags, int startId) {  
  11.         Log.i("mylog","onStartCommand");  
  12.         return super.onStartCommand(intent, flags, startId);  
  13.     }  
  14.   
  15.     @Nullable  
  16.     @Override  
  17.     public IBinder onBind(Intent intent) {  
  18.         Log.i("mylog","onTestBind");  
  19.         return null;  
  20.     }  
  21.   
  22.   
  23.     @Override  
  24.     public boolean onUnbind(Intent intent) {  
  25.         Log.i("mylog","unBind");  
  26.         return super.onUnbind(intent);  
  27.     }  
  28.   
  29.     @Override  
  30.     public void onDestroy() {  
  31.         Log.i("mylog","onDestroy");  
  32.         super.onDestroy();  
  33.     }  

可以发现,这个自定义的service类中并没有什么特别的东西,都是些重写的方法,当我们调用bindService方法时,就会调用onBind方法了,我们后面会通过logcat来跟踪。我们往下继续。。。。

接下来,我们为了让这个service变为远程的service,需要在AndroidManifest.xml文件中注册并加一个:reomote标识,具体代码如下:

[html]  view plain  copy
  1. <service android:name=".service.MyService"  
  2.        android:process=":remote">  
  3.   
  4.    </service>  


然后,我们来开始本篇的重点,我们那就是使用AIDL来实现我们远程service的使用,我们首先在AS下来创建一个AIDL文件,右键app,然后new一个aidl文件,如下图:


然后输入文件名,点击finish完成创建。请注意这里的AIDL文件所在的路径(包名)要跟项目的包名保持一致。


下面我把我创建出来的AIDL文件代码也贴出来,其实很简单,因为我只写了一个sayHelloWorld方法而已:

[java]  view plain  copy
  1. // IMyAidlService.aidl  
  2. package com.example.marktrace003.test;  
  3.   
  4. // Declare any non-default types here with import statements  
  5.   
  6. interface IMyAidlService {  
  7.     /** 
  8.      * Demonstrates some basic types that you can use as parameters 
  9.      * and return values in AIDL. 
  10.      */  
  11.     void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,  
  12.             double aDouble, String aString);  
  13.   
  14.   
  15.              void sayHelloWorld();  
  16. }  


然后点击Bulid->Make project生成我们的java文件。


这时你会发现我们已经可以调用这个接口并实现它的方法了。接下请注意,我们为了让其他app能够找到我们共享的远程service,需要在服务中加入intent-filter标识,并指定我们的AIDL文件路径。也就是说AndroidManifest.xml文件中的注册代码部分将变为如下:

[html]  view plain  copy
  1. <service android:name=".service.MyService"  
  2.       android:process=":remote">  
  3.   
  4.       <intent-filter>  
  5.           <action android:name="com.example.marktrace003.test.IMyAidlService"/>  
  6.       </intent-filter>  
  7.   
  8.   </service>  

最后,我们完善下MyService这个类,实现sayHelloWorld方法,并在onBind方法中返回实现了这个方法的binder:

[java]  view plain  copy
  1. import android.app.Service;  
  2. import android.content.Intent;  
  3. import android.os.IBinder;  
  4. import android.os.RemoteException;  
  5. import android.support.annotation.Nullable;  
  6. import android.util.Log;  
  7.   
  8. import com.example.marktrace003.test.IMyAidlService;  
  9.   
  10. /** 
  11.  * Created by Marktrace003 on 2016/7/4. 
  12.  */  
  13. public class MyService extends Service {  
  14.   
  15.     @Override  
  16.     public void onCreate() {  
  17.         super.onCreate();  
  18.         Log.i("mylog""onCreate");  
  19.     }  
  20.   
  21.     @Override  
  22.     public int onStartCommand(Intent intent, int flags, int startId) {  
  23.         Log.i("mylog","onStartCommand");  
  24.         return super.onStartCommand(intent, flags, startId);  
  25.     }  
  26.   
  27.     @Nullable  
  28.     @Override  
  29.     public IBinder onBind(Intent intent) {  
  30.         Log.i("mylog","onTestBind");  
  31.         return binder;  
  32.     }  
  33.   
  34.     IMyAidlService.Stub binder = new IMyAidlService.Stub() {  
  35.         @Override  
  36.         public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {  
  37.   
  38.         }  
  39.   
  40.         @Override  
  41.         public void sayHelloWorld() throws RemoteException {  
  42.             Log.i("mylog","Hello World");  
  43.         }  
  44.     };  
  45.   
  46.     @Override  
  47.     public boolean onUnbind(Intent intent) {  
  48.         Log.i("mylog","unBind");  
  49.         return super.onUnbind(intent);  
  50.     }  
  51.   
  52.     @Override  
  53.     public void onDestroy() {  
  54.         Log.i("mylog","onDestroy");  
  55.         super.onDestroy();  
  56.     }  
  57. }  


二、“客户端”项目

接下来,我们就可以来新建一个项目了,在这个项目中我们将访问我们上面“服务端”项目的MyService,也就是实现远程service的访问。

同样的,项目名由你来定,然后我们接下来要做的就是把我们编译生成的那个AIDL java文件拷贝到我们的这个新项目中,如何找到这个文件呢?其实不难,在“服务端”项目中,我们切换到package模式下就能找到。


接着将我们的IMyAidlService文件拷贝到我们的新项目即“客户端”项目中,注意这里文件的存放路径要跟“服务端”项目的存放路径一致,也就是“客户端”IMyAidlService的包名要跟服务端存放的包名相同,如下图:


好了,接下来我们“客户端”项目中新建一个Activity,在MainActivity中,我们将演示远程访问“服务端”项目的service,下面直接贴出这个MainActivity的代码:

[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity implements View.OnClickListener{  
  2.   
  3.     private Button bind_btn;  
  4.     private IMyAidlService iMyAidlService;  
  5.     private ServiceConnection connection = new ServiceConnection() {  
  6.         @Override  
  7.         public void onServiceConnected(ComponentName componentName, IBinder iBinder) {  
  8.               iMyAidlService = IMyAidlService.Stub.asInterface(iBinder);  
  9.   
  10.             try {  
  11.   
  12.                 iMyAidlService.sayHelloWorld();  
  13.   
  14.             } catch (RemoteException e) {  
  15.                 e.printStackTrace();  
  16.             }  
  17.         }  
  18.   
  19.         @Override  
  20.         public void onServiceDisconnected(ComponentName componentName) {  
  21.   
  22.         }  
  23.     };  
  24.     @Override  
  25.     protected void onCreate(Bundle savedInstanceState) {  
  26.         super.onCreate(savedInstanceState);  
  27.         setContentView(R.layout.activity_main);  
  28.   
  29.         bind_btn = (Button)findViewById(R.id.bind_btn);  
  30.         bind_btn.setOnClickListener(this);  
  31.     }  
  32.   
  33.     @Override  
  34.     public void onClick(View view) {  
  35.         switch (view.getId()){  
  36.             case R.id.bind_btn:  
  37.                 Intent intent = new Intent("com.example.marktrace003.test.IMyAidlService");  
  38.                 intent.setPackage("com.example.marktrace003.test"); //Android5.0之后需要指定共享Service所在应用的应用包名,否则会抛异常  
  39.                 bindService(intent,connection,BIND_AUTO_CREATE);  
  40.                 break;  
  41.         }  
  42.     }  
  43. }  
说明一下,由于我们已经导入了AIDL文件,所以我们直接可以使用这个接口,然后我们一开始定义ServiceConnection,当bindService时,我们传入这个connection,触发onServiceConnected方法,然后调用sayHelloWorld方法。请注意,我们这里的Intent所传入的这个action正是我们在前面“服务端”项目中的AndroidManifest.xml文件中注册service时加入的那个action,也正是因为这样我们才能顺利的找到这个远程的service。那么这里还有一点需要说明的是,Google认为隐式调用这种调用方式不安全,由于Android5.0中已经禁用Intent隐式调用,所以采用隐式调用会抛出异常,那么为了保证我们的程序不会抛出异常,需要在这里指定一下包名,而包名自然就是我们“服务端”项目的那个包名了。


好了,接下来我们运行一下我们这个新建的“客户端”项目。

点击一下绑定按钮,按钮布局我没给出,自己拖一个,我们主要在logcat中看下运行效果图。



看看上图,你会发现不仅打印出了Hello World,而且还调用了onBind方法,也就是说已经访问到了我们那个远程service了。哈哈,是不是很神奇。那么我们本篇的内容到此为止也就结束了,有错误或遗漏之处还请广大读者指出,谢谢大家的阅读!咱们下期见!


下面是另一个实例:

在AIDL实现IPC通信,调用远程服务端的方法。但是,远程服务端并不能主动给客户端返回信息。在很多情况下是需要远程服务端主动给客户端返回数据,客户端只需要进行监听即可,这是典型的观察者模式。这篇文章主要来解决一下这个问题。

1、首先是AIDL接口定义

这里定义了三个接口,首先是 IMyAidlInterface.aidl;这个接口主要是用于客户端注册和解注册回调接口,这样服务端就可以往客户端回传数据。

[java]  view plain  copy
  1. package com.csda.aidl.service;  
  2. import com.csda.aidl.service.Person;  
  3. import com.csda.aidl.service.IOnNewPersonArrivedListener;  
  4. interface IMyAidlInterface {  
  5.     List<Person> getPersonList();  
  6.     void addPeroson(in Person person);  
  7.     void registListener(IOnNewPersonArrivedListener listener);  
  8.     void unregistListener(IOnNewPersonArrivedListener listener);  
  9. }  
然后是IOnNewPersonArrivedListener.aidl,这个是回调接口,用于往客户端回传信息。由于AIDL接口中不支持一般的interface,所以接口也得是aidl接口类型,如下所示:

[java]  view plain  copy
  1. package com.csda.aidl.service;  
  2. import com.csda.aidl.service.Person;  
  3. interface IOnNewPersonArrivedListener {  
  4.   void onNewPersonArrived(in Person person);  
  5. }  
还有定义的实体类Person.aidl
[java]  view plain  copy
  1. package com.csda.aidl.service;  
  2. parcelable Person;  

在Android Studio中AIDL文件的位置如上,


因为在AIDL文件中,并不是所有的数据类型都是可以使用的,那么AIDL文件支持哪些数据类型呢?如下所示:

● 基本数据类型(int、long、char、boolean、double等);

● String和CharSequence;

● List:只支持ArrayList,里面每个元素都必须能够被AIDL支持;

● Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value

● Percelable:所有实现了Parecelable接口的对象

● AIDL:所有的AIDL接口本身也可以在AIDL文件中使用


另外,如果AIDL文件中用到了自定义的Parcelable对象,必须新建一个和他同名的AIDL文件,并在其中声明他为Parcelable类型,同时在引用的AIDL文件中必须导入这个AIDL,如上的IOnNewPersonArrivedListener.aidl


继续定义用于传送的实体类数据封装:

Person实体类:

[java]  view plain  copy
  1. package com.csda.aidl.service;  
  2.   
  3. import android.os.Parcel;  
  4. import android.os.Parcelable;  
  5.   
  6. /** 
  7.  * Created by Administrator on 2017/3/29. 
  8.  */  
  9. public class Person implements Parcelable {  
  10.     public static final Creator<Person> CREATOR = new Creator<Person>() {  
  11.         @Override  
  12.         public Person createFromParcel(Parcel in) {  
  13.             return new Person(in);  
  14.         }  
  15.   
  16.         @Override  
  17.         public Person[] newArray(int size) {  
  18.             return new Person[size];  
  19.         }  
  20.     };  
  21.     private String name;  
  22.   
  23.     public Person(String name) {  
  24.         this.name = name;  
  25.     }  
  26.   
  27.     protected Person(Parcel in) {  
  28.         name = in.readString();  
  29.     }  
  30.   
  31.     public String getName() {  
  32.         return name;  
  33.     }  
  34.   
  35.     public void setName(String name) {  
  36.         this.name = name;  
  37.     }  
  38.   
  39.     @Override  
  40.     public String toString() {  
  41.         return "Person{" +  
  42.                 "name='" + name + '\'' +  
  43.                 '}';  
  44.     }  
  45.   
  46.     @Override  
  47.     public int describeContents() {  
  48.         return 0;  
  49.     }  
  50.   
  51.     @Override  
  52.     public void writeToParcel(Parcel dest, int flags) {  
  53.         dest.writeString(name);  
  54.     }  
  55.   
  56. }  
AIDL文件声明完毕后,我们可以先运行编译:

然后会在


中看到这两个编译出来的文件

2:创建Service

   接着我们定义我们的Service

[java]  view plain  copy
  1. package com.csda.aidl.service;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.os.RemoteCallbackList;  
  7. import android.os.RemoteException;  
  8. import android.support.annotation.Nullable;  
  9. import android.util.Log;  
  10.   
  11. import java.util.List;  
  12. import java.util.concurrent.CopyOnWriteArrayList;  
  13. import java.util.concurrent.atomic.AtomicBoolean;  
  14.   
  15. /** 
  16.  * Created by vegetable on 2017/3/29. 
  17.  */  
  18. public class AIDLService extends Service {  
  19.     //RemoteCallbackList是专门用于删除跨进程listener的接口,它是一个泛型,支持管理任意的AIDL接口  
  20.     private RemoteCallbackList<IOnNewPersonArrivedListener> mListener=new RemoteCallbackList<>();  
  21.     private CopyOnWriteArrayList<Person> persons=new CopyOnWriteArrayList<>();  
  22.     private AtomicBoolean isServiceDestory=new AtomicBoolean(false);  
  23.   
  24.     @Override  
  25.     public void onCreate() {  
  26.         super.onCreate();  
  27.         persons.add(new Person("小乐"));  
  28.         new Thread(new ServiceWork()).start();  
  29.     }  
  30.   
  31.     @Nullable  
  32.     @Override  
  33.     public IBinder onBind(Intent intent) {  
  34.         return new IMyService();  
  35.     }  
  36.   
  37.     @Override  
  38.     public void onDestroy() {  
  39.         isServiceDestory.set(true);  
  40.         super.onDestroy();  
  41.     }  
  42.   
  43.     public class IMyService extends IMyAidlInterface.Stub {  
  44.   
  45.         @Override  
  46.         public List<Person> getPersonList() throws RemoteException {  
  47.             return persons;  
  48.         }  
  49.         //客户端可以通过调用这个方法想服务端发送消息  
  50.         @Override  
  51.         public void addPeroson(Person person) throws RemoteException {  
  52.             //进行相应处理  
  53.             Log.i("添加人数","添加人数"+persons.size());  
  54.             persons.add(person);  
  55.         }  
  56.   
  57.         @Override  
  58.         public void registListener(IOnNewPersonArrivedListener listener) throws RemoteException {  
  59.              mListener.register(listener);  
  60.         }  
  61.   
  62.         @Override  
  63.         public void unregistListener(IOnNewPersonArrivedListener listener) throws RemoteException {  
  64.             mListener.unregister(listener);  
  65.         }  
  66.     }  
  67.     private void onNewPerson(Person person)throws Exception{  
  68.         persons.add(person);  
  69.         int n=mListener.beginBroadcast();  
  70.         for(int i=0;i<n;i++){  
  71.             IOnNewPersonArrivedListener l=mListener.getBroadcastItem(i);  
  72.             if (l!=null){  
  73.                 try {  
  74.                     l.onNewPersonArrived(person);//服务端通过这个向客户端发送消息  
  75.                 } catch (RemoteException e) {  
  76.                     e.printStackTrace();  
  77.                 }  
  78.             }  
  79.         }  
  80.         mListener.finishBroadcast();  
  81.     }  
  82.     private class ServiceWork implements Runnable{  
  83.         @Override  
  84.         public void run() {  
  85.             while (!isServiceDestory.get()){  
  86.                 try {  
  87.                     Thread.sleep(5000);  
  88.                 }catch (Exception e){  
  89.   
  90.                 }  
  91.                 int i=persons.size()+1;  
  92.                 Person person=new Person("小乐"+i);  
  93.                 try {  
  94.                     onNewPerson(person);  
  95.                 } catch (Exception e) {  
  96.                     e.printStackTrace();  
  97.                 }  
  98.             }  
  99.         }  
  100.     }  
  101. }  
值得一提的是:

 1:RemoteCallbackList是专门用于删除跨进程listener的接口,它是一个泛型,支持管理任意的AIDL接口。

       为什么要使用它呢?因为在服务端进行注册客户端发送过来的listener时,Binder会把客户端传递过来的对象重新转化并生成一个新的对象,因为对象是不能进行跨进程直接传输的,对象的跨进程传世都是反序列化的过程,这就是为什么AIDL中自定义对象都必须实现Parcelable的原因

 2:使用RemoteCallbackList,我们无法像操作list一样去操作它,它并不是一个list,遍历的时候

      int n=mListener.beginBroadcast();
        for(int i=0;i<n;i++){
            IOnNewPersonArrivedListener l=mListener.getBroadcastItem(i);
            if (l!=null){
                try {
                    l.onNewPersonArrived(person);//服务端通过这个向客户端发送消息
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        mListener.finishBroadcast();

      这样去进行,其中beginBroadcast和finishBroadcast必须要配对出现,哪怕是要获取RemoteCallbackList中的元素个数。

在Manifest文件里面注册Service:

[java]  view plain  copy
  1. <service  
  2.             android:name=".AIDLService"  
  3.             android:process=":remote">  
  4.             <intent-filter>  
  5.                 <action android:name="com.example.lambert.aidlproject.MyService" />  
  6.             </intent-filter>  
  7.         </service>  
在Service当中加了几个action,用于别的组件通过Intent隐式启动此Service。

3:客户端的实现

首先客户端也必须添加AIDL文件


这个AIDL的目录名需要跟服务端相同,使用的传输对象也需要跟服务端相同


客户端界面主要是由三个按钮:绑定、解除绑定、向服务器发送消息,然后还有一个显示状态的文本控件。

布局文件如下所示:

[java]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:paddingBottom="@dimen/activity_vertical_margin"  
  8.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  9.     android:paddingRight="@dimen/activity_horizontal_margin"  
  10.     android:paddingTop="@dimen/activity_vertical_margin"  
  11.     tools:context="com.csda.aidl.client.MainActivity"  
  12.     android:orientation="vertical">  
  13.     <Button  
  14.         android:id="@+id/btn_bind"  
  15.         android:layout_width="wrap_content"  
  16.         android:layout_height="wrap_content"  
  17.         android:text="bind"/>  
  18.     <Button  
  19.         android:id="@+id/btn_unbind"  
  20.         android:layout_width="wrap_content"  
  21.         android:layout_height="wrap_content"  
  22.         android:text="unbind"/>  
  23.     <Button  
  24.         android:id="@+id/btn_get"  
  25.         android:layout_width="wrap_content"  
  26.         android:layout_height="wrap_content"  
  27.         android:text="send"/>  
  28.     <TextView  
  29.         android:id="@+id/tv"  
  30.         android:layout_width="wrap_content"  
  31.         android:layout_height="wrap_content"  
  32.         />  
  33. </LinearLayout>  

主Activity如下

[java]  view plain  copy
  1. package com.csda.aidl.client;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.Handler;  
  10. import android.os.IBinder;  
  11. import android.os.Message;  
  12. import android.os.RemoteException;  
  13. import android.view.View;  
  14. import android.widget.Button;  
  15. import android.widget.TextView;  
  16.   
  17. import com.csda.aidl.service.IMyAidlInterface;  
  18. import com.csda.aidl.service.IOnNewPersonArrivedListener;  
  19. import com.csda.aidl.service.Person;  
  20.   
  21. public class MainActivity extends Activity implements View.OnClickListener {  
  22.     private IMyAidlInterface mService;  
  23.     private Button btn_bind, btn_get,btn_unbind;  
  24.     private TextView tv;  
  25.     private Handler handler=new Handler(){  
  26.         @Override  
  27.         public void handleMessage(Message msg) {  
  28.             super.handleMessage(msg);  
  29.             switch (msg.what){  
  30.                 case 1:  
  31.                     Person person=(Person) msg.obj;  
  32.                     tv.setText(person.getName());  
  33.                     break;  
  34.             }  
  35.         }  
  36.     };  
  37.     private IOnNewPersonArrivedListener listener=new IOnNewPersonArrivedListener.Stub(){  
  38.   
  39.         @Override  
  40.         public void onNewPersonArrived(Person person) throws RemoteException {  
  41.             handler.obtainMessage(1,person).sendToTarget();  
  42.         }  
  43.     };  
  44.     private ServiceConnection connection = new ServiceConnection() {  
  45.         @Override  
  46.         public void onServiceConnected(ComponentName name, IBinder service) {  
  47.             mService = IMyAidlInterface.Stub.asInterface(service);  
  48.             try {  
  49.                 //设置死亡代理  
  50.                 service.linkToDeath(mDeathRecipient, 0);  
  51.             } catch (RemoteException e) {  
  52.                 e.printStackTrace();  
  53.             }  
  54.             try {  
  55.                 mService.registListener(listener);  
  56.             } catch (RemoteException e) {  
  57.                 e.printStackTrace();  
  58.             }  
  59.         }  
  60.   
  61.         @Override  
  62.         public void onServiceDisconnected(ComponentName name) {  
  63.             mService = null;  
  64.         }  
  65.     };  
  66.   
  67.     @Override  
  68.     protected void onCreate(Bundle savedInstanceState) {  
  69.         super.onCreate(savedInstanceState);  
  70.         setContentView(R.layout.activity_main);  
  71.         init();  
  72.     }  
  73.   
  74.     private void init() {  
  75.         btn_bind = (Button) findViewById(R.id.btn_bind);  
  76.         btn_get = (Button) findViewById(R.id.btn_get);  
  77.         btn_unbind=(Button) findViewById(R.id.btn_unbind);  
  78.         tv = (TextView) findViewById(R.id.tv);  
  79.         btn_bind.setOnClickListener(this);  
  80.         btn_unbind.setOnClickListener(this);  
  81.         btn_get.setOnClickListener(this);  
  82.     }  
  83.   
  84.     @Override  
  85.     public void onClick(View v) {  
  86.         switch (v.getId()) {  
  87.             case R.id.btn_bind:  
  88.                 bind();  
  89.                 break;  
  90.             case R.id.btn_unbind:  
  91.                 unbind();  
  92.                 break;  
  93.             case R.id.btn_get:  
  94.                 try {  
  95.                     mService.addPeroson(new Person("周盖"));  
  96.                 } catch (RemoteException e) {  
  97.                     e.printStackTrace();  
  98.                 }  
  99.                 break;  
  100.         }  
  101.     }  
  102.     private void bind(){  
  103.         Intent intent = new Intent();  
  104.         intent.setAction("com.example.lambert.aidlproject.MyService");  
  105.         //从 Android 5.0开始 隐式Intent绑定服务的方式已不能使用,所以这里需要设置Service所在服务端的包名  
  106.         intent.setPackage("com.csda.aidl.service");  
  107.         bindService(intent, connection, Context.BIND_AUTO_CREATE);  
  108.     }  
  109.   
  110.     /** 
  111.      * 监听Binder是否死亡 
  112.      */  
  113.     private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {  
  114.         @Override  
  115.         public void binderDied() {  
  116.             if (mService == null) {  
  117.                 return;  
  118.             }  
  119.             mService.asBinder().unlinkToDeath(mDeathRecipient, 0);  
  120.             mService = null;  
  121.             //重新绑定  
  122.             bind();  
  123.         }  
  124.     };  
  125.   
  126.   
  127.     private void unbind(){  
  128.         if (connection != null&&mService.asBinder().isBinderAlive()) {  
  129.             try {  
  130.                 mService.unregistListener(listener);  
  131.             } catch (RemoteException e) {  
  132.                 e.printStackTrace();  
  133.             }  
  134.             unbindService(connection);  
  135.         }  
  136.     }  
  137.     @Override  
  138.     protected void onDestroy() {  
  139.         if (connection != null&&mService.asBinder().isBinderAlive()) {  
  140.             try {  
  141.                 mService.unregistListener(listener);  
  142.             } catch (RemoteException e) {  
  143.                 e.printStackTrace();  
  144.             }  
  145.             unbindService(connection);  
  146.         }  
  147.         super.onDestroy();  
  148.     }  
  149.   
  150. }  
需要说明的是:

1:在执行bindService的时候,代码如下所示,第三个参数有几个可选项,一般选Context.BIND_AUTO_CREATE,意思是如果在绑定过程中,Service进程被意外杀死了,系统还会自动重新启动被绑定的Service。所以当我们点击KILL PROCESS按钮的时候会杀死Service进程,但是马上又会自动重启,重新调用onServiceConnected方法重新绑定。当然,这个参数还有别的一些选择。

 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  

2:点击unbind按钮的时候,需要先解注册之前注册的IRemoteServiceCallback回调接口,然后再unbindService。

3:从 Android 5.0开始 隐式Intent绑定服务的方式已不能使用,所以这里需要设置Service所在服务端的包名

4:如果服务端的onNewPersonArrived方法比较耗时的话,请确保该方法运行在非UI线程,同样,如果服务端处理客户端的方法也比较耗时的话,客户端的方法调用也需要运行在非UI线程中

至此,客户端与服务端通过AIDL互相通信介绍到此。

后话

另外,在AIDL中我们还可以加入权限验证

1:第一种方法时在onBind中验证:

        验证方式有多种,比如使用permission验证,这种方式我们之间在

[java]  view plain  copy
  1. <permission android:name="com.csda.aidl.service.ACCESS_BOOK_SERVICE"  
  2.     android:protectionLevel="normal"/>  
在onbind方法中:

  

[java]  view plain  copy
  1. @Nullable  
  2.     @Override  
  3.     public IBinder onBind(Intent intent) {  
  4.         int check=checkCallingPermission("com.csda.aidl.service.ACCESS_BOOK_SERVICE");  
  5.         if (check== PackageManager.PERMISSION_DENIED){  
  6.             return null;  
  7.         }  
  8.         return new IMyService();  
  9.     }  
如果想要绑定到服务中,只需要在它的配置文件中加入:

[java]  view plain  copy
  1. <uses-permission android:name="com.csda.aidl.service.ACCESS_BOOK_SERVICE"/>  

这样就可以绑定到服务中

ps:如果服务端和客户端是两个工程,则在Service中无法验证客户端的权限,因为onBinde方法不是一个binder调用的,它运行在服务端的UI线程,因此在onBind中只能验证服务端的权限,这样就木有意义了,所以推荐使用第二种。
第二种方法,在服务端的onTransact方法中进行权限验证,如果验证失败直接返回false,这也服务端也不会执行AIDL中的方法,从而达到保护服务端的效果。具体的验证方式很多,可以采用permission验证,实现和第一种一样,还可以采用pid和uid来做验证,通过getCallingUid和getCallingPid可以拿到客户端所属应用的uid和pid,通过这种方式可以做一些验证工作,比如包名。下面既验证了permission,又验证了报名。一个应用如果想远程调用服务的方法,首先要使用我们刚才定义的权限,并且包名相同,否则就会调用失败

2:另外一种是在服务端的Binder重写onTransact方法

[java]  view plain  copy
  1. @Override    
  2.         public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {    
  3.             String packageName = null;    
  4.             int callingPid = getCallingPid();    
  5.             int callingUid = getCallingUid();    
  6.             Log.i(TAG, "callingPid = " + callingPid + ",callingUid = " + callingUid);    
  7.             String[] packagesForUid = BookService.this.getPackageManager().getPackagesForUid(callingUid);    
  8.             if (packagesForUid != null && packagesForUid.length > 0) {    
  9.                 packageName = packagesForUid[0];    
  10.             }    
  11.             Log.i(TAG, "packageName = " + packageName);    
  12.             if (TextUtils.isEmpty(packageName) || !"com.csda.aidl.client".equals(packageName)) {    
  13.                 return false;    
  14.             }    
  15.             return super.onTransact(code, data, reply, flags);    
  16.         }    

或者直接检测权限

[java]  view plain  copy
  1. public class IMyService extends IMyAidlInterface.Stub {  
  2.     @Override  
  3.     public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {  
  4.         int check=checkCallingPermission("com.csda.aidl.service.ACCESS_BOOK_SERVICE");  
  5.         if (check== PackageManager.PERMISSION_DENIED){  
  6.             return false;  
  7.         }  
  8.         return super.onTransact(code, data, reply, flags);  
  9.     }  
  10.   
  11.     @Override  
  12.     public List<Person> getPersonList() throws RemoteException {  
  13.         return persons;  
  14.     }  
  15.     //客户端可以通过调用这个方法想服务端发送消息  
  16.     @Override  
  17.     public void addPeroson(Person person) throws RemoteException {  
  18.         //进行相应处理  
  19.         Log.i("添加人数","添加人数"+persons.size());  
  20.         persons.add(person);  
  21.     }  
  22.   
  23.     @Override  
  24.     public void registListener(IOnNewPersonArrivedListener listener) throws RemoteException {  
  25.          mListener.register(listener);  
  26.     }  
  27.   
  28.     @Override  
  29.     public void unregistListener(IOnNewPersonArrivedListener listener) throws RemoteException {  
  30.         mListener.unregister(listener);  
  31.     }  
  32. }  
3:还可以为Service指定android:permission属性等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值