一、了解AIDL语言:

  在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。在Android中, 则采用AIDL(Android Interface Definition Language:接口描述语言)方式实现。

  AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。

二、应用AIDL实现和远程服务实现进程通信:

  假设A应用需要与B应用进行通信,调用B应用中的queryStudent(int no)方法,B应用以Service方式向A应用提供服务。

  需要下面四个步骤:

  1> 服务端:

    1.1 在B应用中创建*.aidl文件,aidl文件的定义和接口的定义很相类,如:在com.example.aidl包下创建StudentQuery.aidl文件,内容如下:

interface StudentQuery {    String queryStudent(int no);}

  当完成aidl文件创建后,eclipse会自动在项目的gen目录中同步生成StudentQuery.java接口文件。接口文件中生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其它辅助方法。值得关注的是asInterface(IBinder iBinder),它返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端在onServiceConnected(ComponentName name, IBinder service)方法引用该对象时不能直接强转成接口类型的实例,而应该使用asInterface(IBinder iBinder)进行类型转换。

  1.2 编写Aidl文件时,需要注意下面几点:

  1.接口名和aidl文件名相同。  2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static3.Aidl默认支持的类型包话java基本类型(intlong、boolean等)和(String、List、Map、CharSequence),使用这些类型时不需要import声明。对于List和Map中的   
   元素类型必须是Aidl支持的类型。如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口。
4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。 5.在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。 6.Java原始类型默认的标记为in,不能为其它标记。

  1.3 服务实现:

package com.example.remote.service;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import com.example.aidl.StudentQuery;//远程通讯public class StudentQueryService extends Service {    private String[] students = { "tom remote", "jerry remote", "lili remote",            "lucy remote" };    private IBinder binder = new StudentBinder();    public String query(int no) {        if (no > 0 && no < 5) {            return students[no - 1];        }        return null;    }    @Override    public IBinder onBind(Intent arg0) {        return binder;    }    private final class StudentBinder extends StudentQuery.Stub {        @Override        public String queryStudent(int no) throws RemoteException {            return query(no);        }    }}

  1.4 在配置文件中配置服务:

    配置了意图过滤器的动作名称“com.student.query”

<service android:name="com.example.remote.service.StudentQueryService">    <!-- 通过隐式意图激活服务 -->               <intent-filter >         <action android:name="com.student.query"/>    </intent-filter></service>

  2> 服务端:

    2.1 把B应用中aidl文件所在package连同aidl文件一起拷贝到客户端A应用,eclipse会自动在A应用的gen目录中为aidl文件同步生成StudentQuery.java接口文件,接下来就可以在A应用中实现与B应用通信,代码如下:

package com.example.remoteserviceclient;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.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import com.example.aidl.StudentQuery;public class MainActivity extends Activity {    private EditText studentno;    private TextView studentmsg;    //客户端和服务之间的一个连接    private ServiceConnection connection = new StudentServiceConnection();    //接收服务返回的IBinder对象,以便调用服务里面的方法。它其实是对服务方法的封装。    private StudentQuery student;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                studentno =(EditText)this.findViewById(R.id.studentNum);        studentmsg =(TextView)this.findViewById(R.id.txtShow);        Button btn = (Button) this.findViewById(R.id.btnQuery);        btn.setOnClickListener(new StudentListener());        //启动服务         Intent serviceIntent = new Intent("com.student.query");        bindService(serviceIntent, connection, BIND_AUTO_CREATE);                    }    //实现服务连接    private final class StudentServiceConnection implements ServiceConnection{        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            student= StudentQuery.Stub.asInterface(service);             Log.i("MainActivity", student.toString());        }        @Override        public void onServiceDisconnected(ComponentName name) {            connection=null;        }        }    //响应点击事件    private final class StudentListener implements OnClickListener{        @Override        public void onClick(View v) {            String no = studentno.getText().toString();            String name="";            try {                name = student.queryStudent(Integer.valueOf(no));            } catch (NumberFormatException e) {                e.printStackTrace();            } catch (RemoteException e) {                e.printStackTrace();            }            studentmsg.setText(name);        }    }    //当activity销毁时,解除服务    @Override    public void onDestroy() {        unbindService(connection);    }        @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.main, menu);        return true;    }}

三、进程间传递自定义类型参数:

  Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),如果要传递自定义的类型该如何实现呢?

要传递自定义类型,首先要让自定义类型支持parcelable协议,实现步骤如下:

  1>自定义类型必须实现Parcelable接口,并且实现Parcelable接口的public void writeToParcel(Parcel dest, int flags)方法 。

  2>自定义类型中必须含有一个名称为CREATOR的静态成员,该成员对象要求实现Parcelable.Creator接口及其方法。

  3> 创建一个aidl文件声明你的自定义类型。

  Parcelable接口的作用:实现了Parcelable接口的实例可以将自身的状态信息(状态信息通常指的是各成员变量的值)写入Parcel,也可以从Parcel中恢复其状态。 Parcel用来完成数据的序列化传递。

  1> 创建自定义类型,并实现Parcelable接口,使其支持parcelable协议。如:在cn.itcast.domain包下创建Person.java:

package cn.itcast.domain;import android.os.Parcel;import android.os.Parcelable;public class Person implements Parcelable {    private Integer id;    private String name;    public Person() {    }    public Person(Integer id, String name) {        this.id = id;        this.name = name;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        // 把javanbean中的数据写到Parcel        dest.writeInt(this.id);        dest.writeString(this.name);    }    // 添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {        @Override        public Person createFromParcel(Parcel source) {            // 从Parcel中读取数据,返回person对象            return new Person(source.readInt(), source.readString());        }        @Override        public Person[] newArray(int size) {            return new Person[size];        }    };}

  2> 在自定义类型所在包下创建一个aidl文件对自定义类型进行声明,文件的名称与自定义类型同名。

  package com.example.remote.service;

  parcelable Person;

3> 在接口aidl文件中使用自定义类型,需要使用import显式导入,本例在cn.itcast.aidl包下创建IPersonService.aidl文件,内容如下:

package cn.itcast.aidl;import cn.itcast.domain.Person;interface IPersonService {      void save(in Person person);}

  4> 在实现aidl文件生成的接口(本例是IPersonService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:

public class ServiceBinder extends IPersonService.Stub {       @Override       public void save(Person person) throws RemoteException {      Log.i("PersonService", person.getId()+"="+ person.getName());       } }

  5> 创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:

public class PersonService extends Service {  private ServiceBinder serviceBinder = new ServiceBinder();  @Override  public IBinder onBind(Intent intent) {  return serviceBinder;  }public class ServiceBinder extends IPersonService.Stub {       @Override       public void save(Person person) throws RemoteException {  Log.i("PersonService", person.getId()+"="+ person.getName());       }}}

其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:

<service android:name=".PersonService" >  <intent-filter>  <action android:name="cn.itcast.process.aidl.PersonService " />  </intent-filter></service>

6> 把应用中的aidl文件和所在package一起拷贝到客户端应用的src目录下,eclipse会自动在客户端应用的gen目录中为aidl文件同步生成IPersonService.java接口文件,接下来再把自定义类型文件和类型声明aidl文件及所在package一起拷贝到客户端应用的src目录下。

最后就可以在客户端应用中实现与远程服务的通信,代码如下:

public class ClientActivity extends Activity {  private IPersonService personService;   @Override  public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.main);  this.bindService(new Intent("cn.itcast.process.aidl.PersonService"), this.serviceConnection, BIND_AUTO_CREATE);//绑定到服务  }   @Override  protected void onDestroy() {  super.onDestroy();  this.unbindService(serviceConnection);//解除服务  }   private ServiceConnection serviceConnection = new ServiceConnection() {  @Override  public void onServiceConnected(ComponentName name, IBinder service) {  personService = IPersonService.Stub.asInterface(service);  try {  personService.save(new Person(56,"liming"));  } catch (RemoteException e) {  Log.e("ClientActivity", e.toString());  }  }  @Override  public void onServiceDisconnected(ComponentName name) {  personService = null;  }  };}

  四、图解:

单来说,local_service就是指处于当前进程中的service,remote service就是不同进程中的service。

这两者的区别在于,local_servie由于是同一进程的不同线程中,因此不需要编写aidl文件来实现ipc机制,也不需要通过ipc机制来bind,服务端只要new一个Binder出来就行了。
类似这样
Publicclass ServiceBinder extends Binder implements IService;
...
ServiceBinder serviceBinder = new ServiceBinder();

remote service由于不在同一进程中,因此需要编写aidl文件来实现ipc通讯。
此外服务端的binder对象是通过stub来取得的。
IService.Stub serviceBinder = new ICountService.Stub()

访问这两种service的方法一样,只不过访问remote servie的时候需要捕获异常。

更多相关文章

  1. Android(安卓)自定义View的监听事件
  2. Android开发之文件操作详解
  3. 04 ImageView中图片保存到文件
  4. (三) 在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱
  5. Android(安卓)studio 下出现"Cannot resolve symbol R"的解决方
  6. 一种巧妙的绕过Android锁屏密码的方法
  7. android adapter的体系
  8. 分析你的第一个 Android(安卓)程序
  9. 初次修改 android app 代码

随机推荐

  1. 深入理解PHP原理之变量作用域(Scope in P
  2. PHP二维数组(或任意维数组)转换成一维数组
  3. php如何计算两个时间戳之间相差的日时分
  4. 深入理解PHP原理之变量(Variables inside
  5. PHP提取多维数组指定一列的方法大全
  6. php访问url的四种方式
  7. php自动生成sitemap
  8. php 数组排序函数
  9. PHP+Swoole的闭包写法
  10. php统计2个数据中同时出现的次数最多的单