Android中AIDL的使用过程

1、ALDL

AIDL(Android Interface Define Language)是IPC进程间通信的一种方式,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码.

2、默认支持的数据类型

  • 基本数据类型(int、long、char、boolean、double等)
  • String,CharSequence
  • List(只支持ArrayList,里面的每个元素都必须被AIDL支持)
  • Map(只支持HashMap, 里面的每个元素都必须被AIDL支持,包括key和value)
  • Parcelable(所有实现了Parcelable接口的对象)
  • AIDL接口:所有的AIDL接口本身也可以在AIDL文件中使用。

自定义的Parceable对象和AIDL对象必须要显式的import进来,不管它们是否和当前的AIDL文件在同一个包中。如果AIDL文件用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。除此之外,AIDL除了基本类型,其他类型的参数都必须标上方向:in、out或者inout,in标上输入型参数,out表示输出型参数,inout表示输入输出型参数

3、AIDL和Messager的区别

  • Messager以串行的方式来处理客户端发来的消息,如果有大量的消息同时发送到服务端,服务端仍然只能一个一个的去处理;故Messager不适用大量并发的请求
  • Messenger主要是为了传递消息:对于需要跨进程调用服务端的方法,这种情景不适用Messenger。
  • Messenger的底层实现是AIDL,系统为我们做了封装从而方便上层的调用。
  • AIDL适用于大量并发的请求,以及涉及到服务端方法调用的情况

4、AIDL使用步骤

1、新建一个项目作为服务端AIDLService,在项目中新建AIDL文件;然后build生成ICalculateInterface.aidl文件

package com.wuc.aidltest;
interface ICalculateInterface {
     //计算两个数的和
     int addNum(int num1,int num2);
}

2、新建一个IRemoteService

package com.wuc.aidltest;
public class IRemoteService extends Service {

    private IBinder mIBinder = new ICalculateInterface.Stub() {

        @Override
        public int addNum(int num1, int num2) throws RemoteException {
            return num1 + num2;
        }
    };
   //客户端绑定service时会执行
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mIBinder;
    }
}

我在测试过程中报错,错误信息为

java.lang.SecurityException: Not allowed to bind to service Intent { cmp=com.wuc.aidltest/.IRemoteService }

错误原因是没有配置 android:exported="true"这个属性;它的主要作用是:是否支持其它应用调用当前组件。 默认值:如果包含有intent-filter 默认值为true; 没有intent-filter默认值为false。

<service
            android:name=".IRemoteService"
            android:exported="true"/>

3、新建一个客户端AIDLClient,把服务端AIDLService里的AIDL文件拷贝到客户端AIDLClient(包名要一致);然后build工程。然后在Activity中绑定服务

package com.wuc.aidlclient;
public class MainActivity extends AppCompatActivity {

    private AppCompatEditText mEdt_num1;
    private AppCompatEditText mEdt_num2;
    private AppCompatButton mBtn_calculate;
    private AppCompatTextView mTxt_result;
    private ICalculateInterface mICalculateInterface;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //判断Binder是否死忙
            //boolean binderAlive = service.isBinderAlive();
            //用于将服务端的Binder对象转换为客户端需要的AIDL接口类型的对象
            mICalculateInterface = ICalculateInterface.Stub.asInterface(service);
            try {
                //给binder设置死忙代理,当Binder死忙时就可以收到通知
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //连接断开,释放AIDL Binder对象
            mICalculateInterface = null;
            Log.d(TAG, "binder died");
        }
    };

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mICalculateInterface == null) {
                return;
            }
            //移除之前绑定的代理并重新绑定远程服务
            mICalculateInterface.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mICalculateInterface = null;
            bindService();
        }
    };

    @SuppressLint("CutPasteId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mEdt_num1 = findViewById(R.id.edt_num1);
        mEdt_num2 = findViewById(R.id.edt_num2);
        mTxt_result = findViewById(R.id.txt_result);
        mBtn_calculate = findViewById(R.id.btn_calculate);

        mBtn_calculate.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View v) {
                int num1 = Integer.parseInt(mEdt_num1.getText().toString());
                int num2 = Integer.parseInt(mEdt_num2.getText().toString());
                try {
                    int num = mICalculateInterface.addNum(num1, num2);
                    mTxt_result.setText("结果:" + num);
                } catch (RemoteException e) {
                    e.printStackTrace();
                    mTxt_result.setText("计算错误");
                }
            }
        });

        bindService();
    }

    private void bindService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.wuc.aidltest",
                "com.wuc.aidltest.IRemoteService"));
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

显示界面如下图:


aidl_calculate.png

4、自定义类型
4.1 自定义类型要实现Parcelable接口,下面代码中创建一个Person类并实现了Parcelable接口

package com.wuc.aidltest;
public class Person implements Parcelable {

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    protected Person(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public String getName() {
        return name == null ? "" : name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }

    @Override
    public String toString() {
        return name + ":" + age;
    }
}

4.2 接下来新建一个同名的Person.aidl文件,在Person.aidl中申明自定义的类型和它的完整包名,注意这边parcelable是小写的,不是Parcelable接口,一个自定类型需要一个这样同名的AIDL文件;Person.aidl文件的内容如下:

//Person.aidl
package com.wuc.aidltest;
parcelable Person;

4.3 在ICalculateInterface及入口中导入AIDL类型
如:import com.wuc.aidltest.Person;

package com.wuc.aidltest;
import com.wuc.aidltest.Person;
interface ICalculateInterface {
     //计算两个数的和
     int addNum(int num1,int num2);
     List<Person> addPerson(in Person person);
}

4.4 定义接口方法,build后在Service中做具体实现

package com.wuc.aidltest;
public class IRemoteService extends Service {

    /**
     * CopyOnWriteArrayList支持并发读写,AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,
     * 会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步,这里使用CopyOnWriteArrayList来进行自动的线程同步
     * <p>
     * 因为AIDL中所支持的是抽象的List,二List只是一个接口,因此虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中
     * 会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端,所以采用CopyOnWriteArrayList是可以的,类似的
     * 还有ConcurrentHashMap
     */
    private CopyOnWriteArrayList<Person> mPersonList;
    private IBinder mIBinder = new ICalculateInterface.Stub() {

        @Override
        public int addNum(int num1, int num2) throws RemoteException {
            return num1 + num2;
        }

        @Override
        public List<Person> addPerson(Person person) throws RemoteException {
            mPersonList.add(person);
            return mPersonList;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mPersonList = new CopyOnWriteArrayList<>();
        return mIBinder;
    }
}

最后将我们的AIDL文件和自定义类型的java一并拷贝到AIDLClient中,注意包名都要一样
然后在Activity中使用自定义类型的AIDL接口

package com.wuc.aidlclient;
public class MainActivity extends AppCompatActivity {
    private AppCompatButton mBtn_calculate;
    private ICalculateInterface mICalculateInterface;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //用于将服务端的Binder对象转换为客户端需要的AIDL接口类型的对象
            mICalculateInterface = ICalculateInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @SuppressLint("CutPasteId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtn_calculate = findViewById(R.id.btn_calculate);

        mBtn_calculate.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View v) {
                try {
                    List<Person> personList = mICalculateInterface.addPerson(new Person("wuc", 22));
                    Log.d("aidl", personList.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        bindService();
    }

    private void bindService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.wuc.aidltest",
                "com.wuc.aidltest.IRemoteService"));
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

效果图如下:


aidl_define.png

下图是客户端和服务端的目录结构图;(注意包名的一致

aidl.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容