AIDL初探、使用常见错误

来源:互联网 发布:免费cc顶级域名2016 编辑:程序博客网 时间:2024/06/02 14:15

1.前言 AIDL的的应用场景

AIDL接口 实现Binder接口 Message实现接口 IPC进程间通信 IPC进程间通信 IPC进程间通信 服务处理多线程 服务处理多线程 多个应用程序

这是AIDL谷歌官方文档的解释,当每列下面的条件都满足,就是合适的使用场景。
可以看到AIDL的处理的情况是最复杂的:只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。

2.AIDL的简单使用-基本类型

服务端:
首先新建一个AIDL文件夹,里面新建AIDL文件
新建一个AIDL
AIDL接口文件里可以简单写一个求和的函数,注意这里参数的类型是默认支持所有常见的基本类型。(8种基本类型除了short、String、List、map等)如下:

interface IMyAidlInterface {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    int  addSum(int a,int b);    //List<Person> addPerson(in Person person);}

然后编译一下(会自动生成AIDL对应的java文件),新建服务端服务如下:

public class MyService extends Service {    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        //返回binder        return iBinder;    }   //IMyAidlInterface.Stub()是AIDL自动生成java文件里的,涉及原理先不讲   //在这里我们自己实现了这个addSum(int a, int b)    private IBinder iBinder=new IMyAidlInterface.Stub(){        @Override        public int addSum(int a, int b) throws RemoteException {            return a+b;        }    };}

注意注册你的服务等细节,服务段这里就完成了。
客户端:
复制服务端的AIDL文件,并让他在客户端项目中处于一样的包名下。因为我们要保证生成的java文件是一模一样的。
如图:
这里写图片描述
然后在主activity中,创建拷贝AIDL的实例,并绑定服务,具体逻辑不说了。

private IMyAidlInterface iMyAidlInterface;    private ServiceConnection conn=new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {             iMyAidlInterface=IMyAidlInterface.Stub.asInterface(iBinder);        }        @Override        public void onServiceDisconnected(ComponentName componentName) {            iMyAidlInterface =null;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent=new Intent();        intent.setComponent(new ComponentName("android.com.server","android.com.server.MyService"));        // 绑定服务,可设置或触发一些特定的事件        bindService(intent,conn,BIND_AUTO_CREATE);        TextView textView= (TextView) findViewById(R.id.AIDL);        textView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                if(iMyAidlInterface!=null) {                    try {                        int i = iMyAidlInterface.addSum(1, 2);                        Toast.makeText(MainActivity.this, i + "", Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }           }}

这里有两个容易犯的错误:
第一个是绑定服务创建Intent的时候必须显式声明,否则会报错 IllegalArgumentException: Service Intent must be explicit

//报错bindService(new Intent("com.wcc.androiddevelopartexplore.service.MyAidl"), conn, BIND_AUTO_CREATE);  //必须显式声明:Intent intent=new Intent();        intent.setComponent(new ComponentName("android.com.server","android.com.server.MyService"));//或者下面这种写法,并在服务端service注册时添加过滤  //             <intent-filter>  //             <action android:name="android.com.server.MyService" />  //             </intent-filter>        Intent intent = new Intent("android.com.server.MyService");        intent.setPackage("android.com.server");

第二个是在 onCreate()方法中bindService(intent,conn,BIND_AUTO_CREATE);绑定服务后,
直接调用实例iMyAidlInterface.addSum(1, 2);
bindService()是异步执行的,并不会直接执行,而是先执行onCreate中其他代码,此时会报空指针错误,因为iMyAidlInterface还没有被赋值。
iMyAidlInterface的赋值在服务成功连接ServiceConnection回调时。
所以要么在onServiceConnected方法中写调用,或者写到点击事件里。参考前面代码片

  private ServiceConnection conn=new ServiceConnection() {        @Override      public void onServiceConnected(ComponentName componentName, IBinder iBinder) {             iMyAidlInterface=IMyAidlInterface.Stub.asInterface(iBinder);//            int i = 0;//            try {//                i = iMyAidlInterface.addSum(6, 2);//            } catch (RemoteException e) {//                e.printStackTrace();//            }//            Toast.makeText(MainActivity.this, i + "", Toast.LENGTH_SHORT).show();        }        @Override        public void onServiceDisconnected(ComponentName componentName) {            iMyAidlInterface =null;        }    };

最后结果:先启动服务端,再启动客户端,客户端调用服务端服务计算1+2返回结果
这里写图片描述
3.AIDL的简单使用-自定义类型
- 服务端
首先自定义Person类,实现Parcelable接口

public class Person implements Parcelable{    @Override    public String toString() {        return name+"/"+age;    }    public Person(String name, int age){       this.name=name;       this.age=age;   }    public Person(Parcel in){        this.name=in.readString();        this.age=in.readInt();    }    private String name;    private int age;    @Override    public int describeContents() {        return 0;    }    public  static final Parcelable.Creator<Person> CREATOR= new Parcelable.Creator<Person>(){        @Override        public Person createFromParcel(Parcel parcel) {            return new Person(parcel);        }        @Override        public Person[] newArray(int size) {            return new Person[size];        }    };    @Override    public void writeToParcel(Parcel parcel, int i) {        parcel.writeString(name);        parcel.writeInt(age);    }    public String getName() {        return name;    }    public void setName(String name) {        name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

修改AIDL接口添加方法

package android.com.server;// Declare any non-default types here with import statements//此处手动引入Personimport android.com.server.Person;interface IMyAidlInterface {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    int  addSum(int a,int b);    //此处必须指定自定义类型是输入修改(in) 还是输出修改(out) 还是都行(inout)    List<Person> addPerson(in Person person);}

所有非原语参数都需要指示数据走向的方向标记。可以是 in、out 或 inout。
原语默认为 in,不能是其他方向。
注意:您应该将方向限定为真正需要的方向,因为编组参数的开销极大。

再新建Person.aidl
内容

package android.com.server;

parcelable Person;
编译保证无错误

最后修改服务端service实现addPerson()

public class MyService extends Service {    private ArrayList<Person> persons;    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {       persons=new ArrayList<>();        return iBinder;    }    private IBinder iBinder=new IMyAidlInterface.Stub(){        @Override        public int addSum(int a, int b) throws RemoteException {            return a+b;        }        @Override        public List<Person> addPerson(Person person) throws RemoteException {            persons.add(person);            return persons;        }    };}
  • 客户端
    复制AIDL新文件以及Person类到客户端,并保证其都和服务端处于一样的包名下
    这里写图片描述
 public void onClick(View view) {                if(iMyAidlInterface!=null) {                    try {                        int i = iMyAidlInterface.addSum(1, 2);                        Toast.makeText(MainActivity.this, i + "", Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                    try {                        ArrayList<Person> list= (ArrayList<Person>) iMyAidlInterface.addPerson(new Person("name",1));                        Toast.makeText(MainActivity.this, list.toString()+ "", Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }            }

结果:每次点击添加一个人显示
这里写图片描述

>
源码例子:下载链接

0 0
原创粉丝点击