实例讲解Android中的AIDL内部进程通信接口使用

首先描述下我们想要实现的内容,我们希望在一个应用中通过点击按钮,去操作另一个进程中应用的音乐播放功能。

如图,我们点击“播放”时,系统就会去远程调用我们提供的一个service(与当前service不是同一个应用哦),然后操作service中的音乐播放,点击“停止”则会终止播放。想要重新播放的话,必须先点“销毁service”,再点播放按钮哦。(至于这里为什么要先点销毁按钮才能播放,完全是为了给大家展示下,远程调用service时,怎么去解绑service)。

在这个例子中,我们用到了一个非常重要的概念,AIDL。

AIDL(android interface definition language)android接口描述语言,其主要的作用就是允许我们在不同的进程间进行通信。

我们都知道每个应用程序都运行在各自的进程中,并且android平台是不允许不同进程间进行直接的对象数据等传递的。如果我们非要进行进程间的通讯,那么我们就必须将我们的数据对象分解成一个个微小的、可以被操作系统理解的数据单元,然后有序的通过进程边界的检查。

如果让我们自己实现,那么这个过程都愁死一批批的程序人了。幸好android,为我们解决了这个问题,这就是AIDL出现的原因了。

AIDL (Android Interface Definition Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

注意:AIDL只支持方法,不能定义静态成员,并且方法也不能有类似public等的修饰符;AIDL运行方法有任何类型的参数和返回值,在java的类型中,以下的类型使用时不需要导入包(import),基本数据类型、String、Map、List.当然为了避免出错,建议只要使用了,就导入包。使用AIDL的步骤:

服务端(提供服务):

第一步:定义一个*.aidl文件,该文件里是符合aidl语言规范的接口定义,里面定义了外部应用可以访问的方法。当我们保存该文件的时候,eclipse会自动为我们在gen文件夹下生成一个相应的java接口文件。例如RemoteServiceInterface.java

第二步:定义一个自己的service, 并将其注册到androidManifest.xml文件中,例如:

<service android:name="MyRemoteService"> <intent-filter> <action android:name="cn.com.chenzheng_java.remote"/> </intent-filter> </service>

注意这里一定要提供一个intent-filter,我们的客户端进程就是通过该action访问到服务端进程的哦。

我们都知道,在实现自己的service时,为了其他应用可以通过bindService来和我们的service进行交互,我们都要实现service中的onBind()方法,并且返回一个继承了Binder的内部类;在这里,eclipse自动为我们生成的RemoteServiceInterface.java中有一个实现了Binder的内部类,RemoteServiceInterface.Stub。AIDL要求我们,在这里不能再直接去实现Binder类了,而是去实现,AIDL提供给我们的Stub类。 实现stub类的同时,AIDL还要求我们同时实现我们在接口中定义的各种服务的具体实现。至此为止,我们的服务端已经和我们的aidl文件绑定到一起了哦。

客户端:

第一步:客户端要想使用该服务,肯定要先知道我们的服务在aidl文件中到底对外提供了什么服务,对吧?所以,第一步,我们要做的就是,将aidl文件拷贝一份到客户端的程序中(这里一定要注意,包路径要和服务端的保持一致哦,例如服务端为cn.com.chenzheng_java.remote.a.aidl,那么在客户端这边也应该是这个路径)。

第二步:我们都知道,想要和service交互,我们要通过bindService方法,该方法中有一个ServiceConnection类型的参数。而我们的主要代码便是在该接口的实现中。

第三步:在ServiceConnection实现类的onServiceConnected(ComponentName name, IBinder service)方法中通过类似remoteServiceInterface = RemoteServiceInterface.Stub.asInterface(service);方式就可以获得远程服务端提供的服务的实例,然后我们就可以通过remoteServiceInterface 对象调用接口中提供的方法进行交互了。(这里的关键是通过*.Stub.asInterface(service);方法获取一个aidl接口的实例哦)

我们前面在服务端中说过了,必须提供一个intent-filter来匹配请求是否合法,所以我们在客户端访问服务的时候,还必须传递包含了匹配action的Intent哦。

下边整体是代码:远程服务端:

RemoteServiceInterface.aidl

package cn.com.chenzheng_java.remote; /**AIDL的语法和Interface的语法稍微有些不同, *它里面只能有方法,并且java中的那些修饰词如public等,在这里是不支持的. *当我们在eclipse中添加一个以.aidl结尾的AIDL文件时,如果你的格式正确,那么 *在gen目录下,你就会看到系统根据你提供AIDL文件自动为你生成的相应的java类 *@author chenzheng_java */    interface RemoteServiceInterface {     void startMusic();    void stopMusic(); } 

MyRemoteService.java

package cn.com.chenzheng_java.remote;  import java.io.IOException;  import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.IBinder; import android.os.RemoteException; /**  * @description 远程service  * @author chenzheng_java  *  */ public class MyRemoteService extends Service {   MediaPlayer mediaPlayer;   @Override   public IBinder onBind(Intent intent) {     return new MyBinder();   }   @Override   public void onCreate() {     /*      * MediaPlayer.create方法第一个参数实际上为context对象,这里我们直接传递service给它,      * 是因为service本身也是继承了context的。      */      mediaPlayer = MediaPlayer.create(MyRemoteService.this, R.raw.aiweier);     super.onCreate();   }      /**    * @description 这里一定要注意,继承的不再是Binder,而是系统自动为我们生成的    *       Binder的一个内部类,叫做Stub。我们通过继承Stub类,然后实现AIDL    *       中定义的方法,便等于对接口的方法进行了具体的实现。    * @author chenzheng_java    *    */   private class MyBinder extends RemoteServiceInterface.Stub{      public void startMusic() throws RemoteException {               mediaPlayer.start();            }      public void stopMusic() throws RemoteException {       mediaPlayer.stop();       try {         mediaPlayer.prepare();       } catch (IllegalStateException e) {         e.printStackTrace();       } catch (IOException e) {         e.printStackTrace();       }     }        }  } 

RemoteServiceActivity.java

package cn.com.chenzheng_java.remote;  import android.app.Activity; import android.os.Bundle; /**  * 很多人不理解,这里面基本上什么也没实现,为什么还要提供呢?  * 其实原因是这样的,service代码我们写好了,也在配置文件中注册了,  * 这样,手机系统就会识别了吗?不是的哦,你至少得将该service所在的  * 应用运行一次才可以哦。要不手机怎么知道你添加了一个service啊,对吧!  * @author chenzheng_Java  *  */ public class RemoteServiceActivity extends Activity {   @Override   public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.main);   } } 

androidManifest.xml

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="cn.com.chenzheng_java.remote"    android:versionCode="1"    android:versionName="1.0">   <uses-sdk android:minSdkVersion="8" />    <application android:icon="@drawable/icon" android:label="@string/app_name">     <activity android:name=".RemoteServiceActivity"          android:label="@string/app_name">       <intent-filter>         <action android:name="android.intent.action.MAIN" />         <category android:name="android.intent.category.LAUNCHER" />       </intent-filter>     </activity>        <service android:name="MyRemoteService">     <intent-filter>     <action android:name="cn.com.chenzheng_java.remote"/>     </intent-filter>   </service>    </application> </manifest> 

客户端:

activity代码:

package cn.com.chenzheng_java;  import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import cn.com.chenzheng_java.remote.RemoteServiceInterface; /***  * @author chenzheng_java  * @description 通过当前activity去调用不同进程中的远程service  */ public class LocalServiceActivity extends Activity implements OnClickListener {   String ACTION_NAME = "cn.com.chenzheng_java.remote";   boolean flag = false;   Button button_start ;   Button button_stop ;   Button button_destroy ;      @Override   public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.music);          button_start = (Button) findViewById(R.id.button1);     button_stop = (Button) findViewById(R.id.button2);     button_destroy = (Button) findViewById(R.id.button3);          button_start.setOnClickListener(this);     button_stop.setOnClickListener(this);     button_destroy.setOnClickListener(this);   }    RemoteServiceInterface remoteServiceInterface ;      private class MyServiceConnection implements ServiceConnection{     public void onServiceConnected(ComponentName name, IBinder service) {       remoteServiceInterface = RemoteServiceInterface.Stub.asInterface(service);       try {         Log.i("flag", flag+"");         if(flag){           Log.i("通知", "已经开始唱歌");           remoteServiceInterface.startMusic();         }else{           Log.i("通知", "已经停止唱歌");           remoteServiceInterface.stopMusic();         }                         } catch (RemoteException e) {         e.printStackTrace();       }            }      public void onServiceDisconnected(ComponentName name) {            }        }      private MyServiceConnection serviceConnection = new MyServiceConnection();      public void onClick(View v) {     if(v == button_start){       flag = true;       Intent intent = new Intent(ACTION_NAME);       /**        * Context.BIND_AUTO_CREATE 当绑定service时,如果发现尚未create,那么就先create一个,然后绑定        */       bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);     }          if(v == button_stop){       Log.i("通知", "已经点击了停止按钮");       flag = false;       Intent intent = new Intent(ACTION_NAME);       bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);       try {         remoteServiceInterface.stopMusic();       } catch (RemoteException e) {         e.printStackTrace();       }     }          if(v == button_destroy){       flag = false;       Intent intent = new Intent(ACTION_NAME);       bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);       unbindService(serviceConnection);     }        } } 

music.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="match_parent" android:layout_height="match_parent">   <Button android:text="播放" android:id="@+id/button1"     android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>   <Button android:text="停止" android:id="@+id/button2"     android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>   <Button android:text="销毁service" android:id="@+id/button3"     android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </LinearLayout> 

其他没有粘贴出来的代码都是由系统默认生成的。

AIDL与传递对象

除了上面我们提到的通过service提供音乐播放等类似的服务之外,我们还可以通过service将对象传递回来哦,你知道怎么用吗,先看例子:

当我们点击“获取”时,会从另一个线程的service中获取一个对象,然后将里面的内容读出来。

对于aidl实现以对象的方式交互。主要步骤如下:

服务端:第一:定义一个实体类,这里是Beauty,定义一个服务接口aidl文件RemoteBeauty.aidl,这里有一点需要注意,我们引用自定义的实体类到aidl中时需要通过import导入包,但是你会发现,即使你导入了包,还是提示找不到,这时候,你要做的是,建一个以实体类名称命名的aidl文件,如Beauty.aidl,在里面添加一句pracelable Beauty。

第二:开始编写Beauty,这里一定要注意,它一定要实现Pracelable接口,该接口是一个序列化的接口,功能和serializable相似,但是功能更加的迅速。此外,在该Beauty内部一定要声明一个public static final Pracelable.Creator<T>CREATOR对象!!除了里面的那个T代表实体类之外,其他的都不准改变哦。

第三:在androidManifest.xml中注册service。并定义好访问该service的action字符串。

客户端:客户端这边相应的要简单很多,但是要注意的一点是,要将实体类还有aidl文件都拷贝过来哦,而且要保证路径完全一致!!Beauty.java

package cn.com.chenzheng_java.service;  import android.os.Parcel; import android.os.Parcelable; /**  *  * @author chenzheng_java  * @description Parcelable是android提供的一个比serializable效率更高的序列号接口  *       这里必须要继承Parcelable哦,不序列号怎么可以传递……对吧?!  * 在实体类我们要做两件重要的事情:  * 第一:实现Parcelable接口  * 第二:定义一个Parcelable.Creator类型的CREATOR对象  * 第三:要提供一个Beauty.aidl文件,其中内容为parcelable Beauty,定义了之后,在其他aidl文件中引用Beauty时便不会提示出错了。  * @since 2011/03/18  *  */ public class Beauty implements Parcelable {    String name ;   int age ;   String sex ;      public String getName() {     return name;   }    public void setName(String name) {     this.name = name;   }    public int getAge() {     return age;   }    public void setAge(int age) {     this.age = age;   }    public String getSex() {     return sex;   }    public void setSex(String sex) {     this.sex = sex;   }    @Override   public int describeContents() {     return 0;   }    /**    * 将对象序列号    * dest 就是对象即将写入的目的对象    * flags 有关对象序列号的方式的标识    * 这里要注意,写入的顺序要和在createFromParcel方法中读出的顺序完全相同。例如这里先写入的为name,    * 那么在createFromParcel就要先读name    */   @Override   public void writeToParcel(Parcel dest, int flags) {              dest.writeString(name);       dest.writeInt(age);       dest.writeString(sex);   }   /**    * 在想要进行序列号传递的实体类内部一定要声明该常量。常量名只能是CREATOR,类型也必须是    * Parcelable.Creator<T>    */   public static final Parcelable.Creator<Beauty> CREATOR = new Creator<Beauty>() {          /**      * 创建一个要序列号的实体类的数组,数组中存储的都设置为null      */     @Override     public Beauty[] newArray(int size) {       return new Beauty[size];     }          /***      * 根据序列号的Parcel对象,反序列号为原本的实体对象      * 读出顺序要和writeToParcel的写入顺序相同      */     @Override     public Beauty createFromParcel(Parcel source) {       String name = source.readString();       int age = source.readInt();       String sex = source.readString();       Beauty beauty = new Beauty();       beauty.setName(name);       beauty.setAge(age);       beauty.setSex(sex);              return beauty;     }   };       } 

RemoteService.java

package cn.com.chenzheng_java.service;  import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; /**  *  * @author chenzheng_java  * @description 提供服务的service  *  */ public class RemoteService extends Service {    @Override   public IBinder onBind(Intent intent) {     Log.i("通知", "执行了OnBind");     return new MyBinder();   }       private class MyBinder extends RemoteBeauty.Stub{      @Override     public Beauty getBeauty() throws RemoteException {              Beauty beauty = new Beauty();       beauty.setName("feifei");       beauty.setAge(21);       beauty.setSex("female");              return beauty;     }}          } 

ServiceActivity.java

package cn.com.chenzheng_java.service;  import android.app.Activity; import android.os.Bundle; /**  * @description 进程之间对象数据的传递  * @author chenzheng_java  *  */ public class ServiceActivity extends Activity {   @Override   public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.main);   } } 

Beauty.aidl

parcelable Beauty; RemoteBeauty.aidlpackage cn.com.chenzheng_java.service; import cn.com.chenzheng_java.service.Beauty;  interface RemoteBeauty {    Beauty getBeauty();    } 

manifest.xml

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="cn.com.chenzheng_java.service"    android:versionCode="1"    android:versionName="1.0">   <uses-sdk android:minSdkVersion="8" />    <application android:icon="@drawable/icon" android:label="@string/app_name">     <activity android:name=".ServiceActivity"          android:label="@string/app_name">       <intent-filter>         <action android:name="android.intent.action.MAIN" />         <category android:name="android.intent.category.LAUNCHER" />       </intent-filter>     </activity>  <!-- service开始 -->   <service android:name="RemoteService">     <intent-filter>       <action android:name="cn.com.chenzheng_java.remote2"/>     </intent-filter>   </service> <!-- service结束 -->   </application> </manifest> 

客户端:

ClientActivity.java

package cn.com.chenzheng_java.client;  import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import cn.com.chenzheng_java.service.Beauty; import cn.com.chenzheng_java.service.RemoteBeauty;  public class ClientActivity extends Activity implements OnClickListener {   TextView textView ;   Button button ;   String actionName = "cn.com.chenzheng_java.remote2";   RemoteBeauty remoteBeauty;      @Override   public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.main);     textView = (TextView) findViewById(R.id.textView1);     button = (Button) findViewById(R.id.button1);          button.setOnClickListener(this);   }    private class MyServiceConnection implements ServiceConnection{      @Override     public void onServiceConnected(ComponentName name, IBinder service) {       Log.i("通知", "链接成功!");       remoteBeauty = RemoteBeauty.Stub.asInterface(service);       try {         Beauty beauty = remoteBeauty.getBeauty();         textView.setText("美女 姓名:"+beauty.getName()+" 年龄:"+beauty.getAge() +" 性别:"+beauty.getSex());                         } catch (RemoteException e) {         e.printStackTrace();       }     }      @Override     public void onServiceDisconnected(ComponentName name) {            }        }   MyServiceConnection connection = new MyServiceConnection();   @Override   public void onClick(View v) {     Intent intent = new Intent(actionName);     bindService(intent, connection, Context.BIND_AUTO_CREATE);   } } 

另外Beauty.java 以及RemoteBeauty.aidl都是从服务端系统中拷贝过来的哦。

如果你想你的service在系统开机时自启动。可以在service的androidManifest.xml中加上这样的配置。

<receiver android:name=".StartBroadcastReceiver">       <intent-filter>  <action android:name="android.intent.action.BOOT_COMPLETED"/>      </intent-filter>    </receiver>

做对的事情比把事情做对重要。

实例讲解Android中的AIDL内部进程通信接口使用

相关文章:

你感兴趣的文章:

标签云: