AIDL

来源:互联网 发布:更相减损术的c语言 编辑:程序博客网 时间:2024/06/02 18:45
 AIDL介绍
    AIDL表示“Android接口定义语言”:Android Interface Definition Language.
    使用AIDL定义的接口,会被开发工具生成为可实现远程访问的接口。
    进程间通信模式是组件间通信的“高级”应用方式,与一般组件间通信的区别在于:接口需要使用AIDL定义。
    由于存在2个应用程序进行通信,因此,提供服务的应用程序通常被称之为“服务端”,而调用它的
    接口的方法的应用程序则被称之为“客户端”。
    AIDL接口:
        使用java中创建接口的方式即可创建AIDL的接口,区别在于:
     AIDL的接口的原文件扩展名为.aidl,一般的接口的源文件的扩展名为java。
     AIDL的接口不允许使用public或任何访问权限修饰符去修饰该接口,它
     默认即被修饰为public。
在创建了AIDL接口后,开发工具会在gen下生成匹配的JAVA源文件。
使用AIDL绑定远程Service
     在开发工具生成的AIDL接口的JAVA源文件中,定义了名为stub的内部类,
     继承了android.os.Binder,并实现了AIDL的接口,因此,开发人员在创建Service中的IBinder
     的实现类时,仅需继承该Stub类即可。
     由开发人员定义的AIDL接口中的抽象方法,都被定义为抛出远程异常(android.os.RemoteException),
     在实现这些抽象方法时需要处理。
     在AIDL接口的Stub内部类中,存在asInterface(IBinder obj)方法,用于将IBinder对象
     转换为AIDL接口的实现类对象,因此,在绑定Service的组件中,可通过该方法得到AIDL接口的实现类对象。
 使用AIDL与一般绑定服务的区别
       使用AIDL与一般绑定服务的区别:
    【服务端】使用AIDL接口
    【服务端】创建AIDL接口的内部类Stub的子类对象,并将其作为Service中
    onBind()的返回值;
    【服务端】必须配置Service是可以隐式激活的
    【客户端】将服务端的AIDL文件复制到客户端
    【客户端】在ServiceConnection的onServiceConnected()中,使用AIDL接口的
       Stub的asinterface()方法,将参数IBinder转换为AIDL接口的对象。
    服务端开发步骤:
         开发AIDL接口源文件,并确保扩展名是.aidl(如果先创建的*.java源文件后改名,则需刷新)
 在Service中开发AIDL接口的实现类,并实现AIDL接口中定义的抽象方法;
 实现Service类中的onBind()方法,并将AIDL的实现类的对象作为该方法的返回值
 注册Service类,并配置<intent-filter>
 部署服务端应用程序到设备上
   客户端开发步骤
         1、将服务端的AIDL相关文件复制到客户端中,注意:相关文件的包名、类名是不允许修改的
2、开发ServiceConnection接口中的实现类
3、使用隐式意图绑定服务端的Service
         4、在ServiceConnection接口中的实现类的onServiceConnected()方法中,通过AIDL的内部类Stub的asInterface()方法

,将IBinder对象转换为AIDL接口的对象。

一个简单的小案例:

         服务端:  

             MainActivity:

       

package com.edu.hpu.service;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}}
Service:

package com.edu.hpu.service;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;public class PlayerService extends Service{@Overridepublic void onCreate() {// TODO Auto-generated method stubLog.d("AIDL","服务端:PlayerService onCreate");super.onCreate();}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubLog.d("AIDL","服务端:PlayerService onBind");return new InnerBinder();} private class InnerBinder extends PlayerAIDL.Stub{@Overridepublic void playMusic() throws RemoteException {// TODO Auto-generated method stubLog.d("AIDL","服务端:PlayerService playMusic");}@Overridepublic void pauseMusic() throws RemoteException {// TODO Auto-generated method stubLog.d("AIDL","服务端:PlayerService pauseMusic");}  }}

PlayerAIDL.aidl

package com.edu.hpu.service;interface PlayerAIDL {     void playMusic();     void pauseMusic();}

布局没有任何改变,初始的即可,

需要在AndroidMainfest.xml中配置信息:

  

<service         android:name="com.edu.hpu.service.PlayerService">        <intent-filter>            <action android:name="com.edu.hpu.service.intent.action.REMOTE_PLAYER_SERVICE"/>            <category android:name="android.intent.category.DEFAULT"/>        </intent-filter>    </service>
应用端:

     需要将相对应的aidl包跟文件都复制到应用端,

MainActivity:

package com.example.receiveservice;import com.edu.hpu.service.PlayerAIDL;import android.app.Activity;import android.content.ComponentName;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.Menu;import android.view.MenuItem;public class MainActivity extends Activity {private ServiceConnection conn = null;PlayerAIDL player;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.i("AIDL","客户端:MainActivity.onCreate");conn = new InnerServiceConnection();Intent service = new Intent("com.edu.hpu.service.intent.action.REMOTE_PLAYER_SERVICE");bindService(service,conn,BIND_AUTO_CREATE);}private class InnerServiceConnection implements ServiceConnection{@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// TODO Auto-generated method stubLog.i("AIDL","客户端:MainActivity.onServiceConnected");player = PlayerAIDL.Stub.asInterface(service);try {player.playMusic();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {player.pauseMusic();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubunbindService(conn);super.onDestroy();}}

以上的就是这个简单的小项目,先将服务端配置到虚拟器上,然后将客户端配置到虚拟器上,运行成功,就会在客户端打印出服务端的信息。

注意:
         绑定远程Service时,组件的相关方法执行顺序:
   1、【客户端】绑定远程Service,即调用bindService()方法;
   2、【服务端】Service的生命周期开始,即onCreate()->onBind();
   3、【客户端】ServiceConnection中onServiceConnected()方法被回调。
  在客户端执行bindService()时,由于服务端的Service尚未启动,更没有返回IBinder
  对象,则客户端中onServiceConnected()方法还没有被回调,因此,直接在客户端bindService()后马上
  调用远程服务可能导致NullPointerException.

使用AIDL远程播放音乐

服务端:

   MainActivity:

package com.example.service1;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}}

Service:

package com.example.service1;import java.io.IOException;import android.app.Service;import android.content.Intent;import android.media.MediaPlayer;import android.media.MediaPlayer.OnCompletionListener;import android.os.Environment;import android.os.IBinder;import android.os.RemoteException;public class PlayerService extends Service {private MediaPlayer player;private int Current;private String[] musics = new String[] {Environment.getExternalStorageDirectory().getAbsolutePath()+ "/Music/number1.mp3",Environment.getExternalStorageDirectory().getAbsolutePath()+ "/Music/number2.mp3",Environment.getExternalStorageDirectory().getAbsolutePath()+ "/Music/number3.mp3" };private int currentMusicIndex = 0;@Overridepublic void onCreate() {// TODO Auto-generated method stubplayer = new MediaPlayer();player.setOnCompletionListener(new OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {// TODO Auto-generated method stubdoNext();}});super.onCreate();}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn new InnerBinder();}private void doPlay() {player.reset();try {player.setDataSource(musics[currentMusicIndex]);player.prepare();player.seekTo(Current);player.start();Current = 0;} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalStateException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}private void doPause() {if (player.isPlaying()) {Current = player.getCurrentPosition();player.pause();}}private void doPrevious() {currentMusicIndex--;if (currentMusicIndex < 0) {currentMusicIndex = musics.length - 1;}Current = 0;doPlay();}private void doNext() {currentMusicIndex++;if (currentMusicIndex >= musics.length) {currentMusicIndex = 0;}Current = 0;doPlay();}private class InnerBinder extends PlayerAIDL.Stub {@Overridepublic void play() throws RemoteException {// TODO Auto-generated method stub               doPlay();}@Overridepublic void pause() throws RemoteException {// TODO Auto-generated method stub              doPause();}@Overridepublic void previous() throws RemoteException {// TODO Auto-generated method stub              doPrevious();}@Overridepublic void next() throws RemoteException {// TODO Auto-generated method stub             doNext();}}@Overridepublic void onDestroy() {// TODO Auto-generated method stubplayer.release();player = null;super.onDestroy();}}

需要提前将音乐添加到虚拟机中,我在这里边添加了三首歌曲,

PlayerAIDL.aidl

package com.example.service1;interface PlayerAIDL {      void play();      void pause();      void previous();      void next();}

布局里边不用修改,就按照默认的就可以,

重要的一步,在AndroidMainfest.xml中配置Service:

<service android:name="com.example.service1.PlayerService">            <intent-filter>                <action android:name="com.example.service1.REMOTE_PLAYER_SERVICE_1"/>                 <category android:name="android.intent.category.DEFAULT" />            </intent-filter>        </service>


应用端:


相应的,需要将服务端的相对应的.aidl所在的包名以及文件都拷贝到应用端,这里不再做介绍,

MainActivity:

package com.example.service2;import com.example.service1.PlayerAIDL;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.view.Menu;import android.view.MenuItem;import android.view.View;public class MainActivity extends Activity {private InnerServiceConnection conn;private PlayerAIDL player;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);conn = new InnerServiceConnection();Intent service = new Intent("com.example.service1.REMOTE_PLAYER_SERVICE_1");bindService(service, conn, BIND_AUTO_CREATE);}public void doPlay(View view) {            try {player.play();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void doPause(View view) {       try {player.pause();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void doPrevious(View view) {                try {player.previous();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void doNext(View view) {           try {player.next();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}private class InnerServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// TODO Auto-generated method stubplayer = PlayerAIDL.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubunbindService(conn);super.onDestroy();}}

布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"     >    <ImageButton        android:id="@+id/pause"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignTop="@+id/play"        android:layout_marginLeft="58dp"        android:onClick="doPause"        android:layout_toRightOf="@+id/play"        android:src="@android:drawable/ic_media_pause" />    <ImageButton        android:id="@+id/play"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:onClick="doPlay"        android:layout_alignParentTop="true"        android:layout_marginLeft="87dp"        android:layout_marginTop="36dp"        android:src="@android:drawable/ic_media_play" />    <ImageButton        android:id="@+id/previous"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignLeft="@+id/play"        android:onClick="doPrevious"        android:layout_below="@+id/play"        android:layout_marginTop="49dp"        android:src="@android:drawable/ic_media_previous" />    <ImageButton        android:id="@+id/next"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignLeft="@+id/pause"        android:onClick="doNext"        android:layout_alignTop="@+id/previous"        android:src="@android:drawable/ic_media_next" /></RelativeLayout>
AndroidMainfest.xml中的相关信息无需配置,直接将服务端的应用部署到虚拟机上边,然后将应用端的应用部署到虚拟机上边,运行应用端的应用,就会实现一个跨应用程序获取Service的一个能够进行歌曲播放、暂停、上一曲、下一曲的功能。

AIDL中的数据类型
 在AIDL接口中,如需使用以下数据类型,无需导包语句即可直接使用:
    JAVA的基本数据类型,包括:byte,short,int,long,float,double,char,boolean.
    String与CharSequence。
    List与Map。
如需使用自定义的数据类型,或List,Map中封装自定义数据类型的数据,则要求:
   1、自定义的数据类型实现Parcelable接口i;
   2、编写自定义数据类型的*.aidl文件;
   3、无论自定义数据类型与AIDL接口文件是否在同一个包中,在使用自定义数据类型时,都需在AIDL接口文件中
   显式的使用import语句导入;
   4、将自定义数据类型的*.java源文件与*.aidl源文件复制到客户端。
   自定义数据类型的*.aidl文件:
       需存在与*.java源文件相同的package语句;
       添加parcelable类名;语句。
      Parcelable接口:基于任何复合数据类型都是基本数据类型封装后的集合,因此,各种复合数据类型都可以被拆分为
      基本数据类型进行传输。
      parcelable接口约定了数据被序列化传输的方式。
      在parcelable接口中,定义了一下抽象方法:
              public int describeContents()(用于内容接口的描述)
     public void writeToParcel(Parcel dest,int flags)(writeToParcel()方法用于将当前类的成员写入到Parcel
     容器中,重写该方法时,调用Parcel类的write系列方法即可,例如writeInt(),writeString()等。)
  通常开发人员仍自定义readFromParcel(Parcel parcel)方法,以对应必须重写的writeToParcel()方法,用于从Parcel容器中
  读取数据。
  读取数据时,顺序必须与写入数据完全相同!

 public void writeToParcel(Parcel parcel,int flags){       parcel.writeInt(age);       parcel.writeString(name);       parcel.writeString(from);   }   public void readFromParcel(Parcel parcel){       this.age = parcel.readInt();       this.name = parcel.readString();       this.from = parcel.readString();   }

必须创建静态常量CREATOR,实现Parcelable.Creator<T>接口:
      该常量必须被修饰为public static final ,且常量名必须为
      CREATOR;
      重写接口中的抽象方法。
使用AIDL访问自定义类型数据
     功能:服务端提供getStudentList()方法,返回List<Student>类型数据。
                    Student类型中至少封装3个属性,且至少其中2个属性的数据类型是相同的,
   例如2个String类型和1个int类型。
 客户端可获取服务端提供的数据

下面的是相对应的案例:

服务端:

        MainActivity:

package com.example.servicemen;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}}

Student.java

package com.example.servicemen;import android.os.Parcel;import android.os.Parcelable;public class Student implements Parcelable{    private String name;    private String from;    private int age;            public Student(String name, String from, int age) {this.name = name;this.from = from;this.age = age;}        @Overridepublic String toString() {return "Student [name=" + name + ", from=" + from + ", age=" + age+ "]";}public Student(Parcel source) {    // TODO Auto-generated constructor stub    readFromParcel(source);    }    public String getName() {return name;}public void setName(String name) {this.name = name;}public String getFrom() {return from;}public void setFrom(String from) {this.from = from;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>(){@Overridepublic Student createFromParcel(Parcel source) {// TODO Auto-generated method stubreturn new Student(source);}@Overridepublic Student[] newArray(int size) {// TODO Auto-generated method stubreturn new Student[size];}    };@Overridepublic int describeContents() {// TODO Auto-generated method stubreturn 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {// TODO Auto-generated method stubdest.writeString(name);dest.writeString(from);dest.writeInt(age);}private void readFromParcel(Parcel from){this.name = from.readString();this.from = from.readString();this.age = from.readInt();}}

Service:

package com.example.servicemen;import java.util.ArrayList;import java.util.List;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;public class StudentService extends Service{@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn new InnerBinder();}private class InnerBinder extends StudentAIDL.Stub{@Overridepublic List<Student> getStudentList() throws RemoteException {// TODO Auto-generated method stubList<Student> students = new ArrayList<Student>();students.add(new Student("asjdasd","fdghfg",30));students.add(new Student("ssasd","saafg",60));students.add(new Student("sliuliud","ililfg",90));return students;}}}

Student.aidl:

package com.example.servicemen;parcelable Student;

StudentAIDL.aidl:

package com.example.servicemen;import com.example.servicemen.Student;interface StudentAIDL {     List<Student> getStudentList();     }
相应的需要在AndroidMainfest.xml中配置相关的信息:

<service             android:name="com.example.servicemen.StudentService">            <intent-filter>                <action android:name="com.example.servicemen.intent.action.STUDENT_ACTION"/>                <category android:name="android.intent.category.DEFAULT"/>            </intent-filter>        </service>

服务端的布局使用默认的即可,不用做更改,

应用端:相应的需要将Student.sidl,StudentAIDL.aidl,Student.java及他们所在的包名都复制到客户端,

MainActivity:

package com.example.service4;import java.util.List;import com.example.servicemen.Student;import com.example.servicemen.StudentAIDL;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.view.Menu;import android.view.MenuItem;public class MainActivity extends Activity {  private ServiceConnection conn;    private StudentAIDL service;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);conn = new InnerServiceConnection();Intent service = new Intent("com.example.servicemen.intent.action.STUDENT_ACTION");bindService(service, conn, BIND_AUTO_CREATE);}private class InnerServiceConnection implements ServiceConnection{@Overridepublic void onServiceConnected(ComponentName name, IBinder arg0) {// TODO Auto-generated method stubservice = StudentAIDL.Stub.asInterface(arg0);try {List<Student> students = service.getStudentList();    for (Student student : students) {System.out.println("student" + student);}} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}}@Overrideprotected void onDestroy() {// TODO Auto-generated method stub    unbindService(conn);super.onDestroy();}}

布局不用做任何的修改,先将服务端部署到虚拟机上,然后运行应用端,查看logcat,就会看到服务端的打印的信息。


0 0
原创粉丝点击