AIDL跨进程通信的使用

项目链接:https://github.com/liaozhoubei/AIDLService

AIDL:Android Interface Definition Language,即Android接口定义语言,这是一种用于Android的跨进程通信(IPC)的工具。
为什么有AIDL呢?这是因为在android中一个进程不允许直接访问另一个进程的内存。
进程是一个应用在手机上执行的过程,这是一个动态的概念,一个app的主进程可以从这个app打开算起,然后直接退出app,这时就算是一个进程的结束。
在android中允许有多进程,因此多进程通信可以在自己的应用中进行通信,也可以在不同应用中通信,而这便是AIDL的使用方向。
但是AIDL有个缺点,那就是比较耗资源,如非必要,就不适用AIDL的为好

AIDL的使用方法

我们知道了AIDL是用来干什么的,那么我们就开始学习如何使用AIDL吧!

我们先建立一个AIDLService项目,这个项目用来模拟远程服务端口的,即我们想通过AIDL调用这个项目中的方法,这就是跨进程通信。

由于只是一个小Demo,所以并没有做太复杂的东西,我们只想通过AIDLService这个进程进行一次基本数据类型的传递,即进行简单的计算,然后将计算结果传回去客户端中。
其次,我们还想通过AIDLService将一个Object对象传给客户端。

那么我们就开始吧。

AIDLService服务端: 创建一个AIDL文件

首先必须要有一个AIDL接口文件,即后缀名为aidl的文件,在这里我们是IService.aidl,代码如下

interface IService {
    // 传入两个正整数,返回他们的和
    int add(int num1, int num2);
    // 传入一个Person对象,返回person集合
    List<Person> addPerson(in Person p);
}

看上去是不是很熟悉,没错,这其实就是一个interface接口而已,区别在于它并没有public或者private等修饰符。
其中addPerson(in Person p)中我们看到有个in Person p,这个in是什么呢?简单的说表示这个Person是从外面传进来的(可以自行搜索其中的意思)。

除此之外还需要创建一个Person.aidl,传递序列号之后的Person对象,代码很简单,如下:

parcelable Person;

就这么一行代码。

AIDLService服务端: 创建一个RemoteService远程服务

RemoteService远程服务中将实现IService.aidl中的两个方法,让后通过IBinder传出(IBinder可以让外界调用服务中的方法)。具体代码如下:

public class RemoteService extends Service {
    public final String TAG = this.getClass().getSimpleName();
    private ArrayList<Person> persons;

    public RemoteService() {
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        persons = new ArrayList<>();
        return iBinder;
    }



    private IBinder iBinder = new IService.Stub() {
        @Override
        public int add(int num1, int num2) throws RemoteException {
            return num1 + num2;
        }

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

当然,我们不要忘记建立Person.java,而且要对这个类实现序列号,因为Object必须实现序列号才能同传输,代码如下:

public class Person implements Parcelable {
    private String name;
    private int age;

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

    protected Person(Parcel in) {
        // 从序列号中取出数据,要按照writeToParcel()写入的顺序一样取出
        this.name = in.readString();
        this.age = in.readInt();
    }



    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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

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

    /**
     * 将数据写入序列化中
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 写入时的顺序决定取出数据时的顺序
        dest.writeString(name);
        dest.writeInt(age);
    }


    /**
     * 必须实现的方法,写法基本固定
     */
    public static final Creator<Person> CREATOR = new Creator<Person>() {
        /**
         * 从序列化中取出一个个的字段
         * @param in
         * @return
         */
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

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

OK,这样我们的AIDLService服务端就已经完成了,剩下的就是使用客户端调用了。

AIDLClient客户端

客户端用来调用AIDLService中的两个方法,那么如何调用呢?
首先必须要创建AIDLClient客户端。

然后我们直接把AIDLService当中的Person.aidl和IService.aidl两个文件拷贝到,需要注意的是这里两个文件所在的包名必须与在AIDLService时的包名保持一致。
还需要把Person.java拷贝过来,包名也需要保持一致。

AIDLClient客户端需要一个简单的界面,activity_main.xml代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.aidlclient.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center_horizontal"
        >

        <EditText
            android:id="@+id/num1"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+" />

        <EditText
            android:id="@+id/num2"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="=" />

        <EditText
            android:id="@+id/result"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <Button
        android:id="@+id/btn_addNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="调用"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center_horizontal"
        >

        <EditText
            android:id="@+id/name"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+" />

        <EditText
            android:id="@+id/age"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="=" />

        <EditText
            android:id="@+id/getPerson"
            android:layout_width="50dp"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <Button
        android:id="@+id/btn_addPerson"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="调用Person"/>

</LinearLayout>

非常简单,这里就不做解释了。

AIDLClient客户端:调用远程服务

MainActivity中的布局写好了,那么我们就开始进行远程吧。方法其实就使用本地服务基本一样。代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText num1;
    private EditText num2;
    private EditText result;
    private EditText name;
    private EditText age;
    private EditText getPerson;
    private Button btn_1;
    private Button btn_2;

    IService iService;

    /**
     * 创建ServiceConnection与服务进行链接
     */
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iService = IService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        bindService();

    }

    private void initView() {
        num1 = (EditText) findViewById(R.id.num1);
        num2 = (EditText) findViewById(R.id.num2);
        result = (EditText) findViewById(R.id.result);

        btn_1 = (Button) findViewById(R.id.btn_addNumber);
        btn_2 = (Button) findViewById(R.id.btn_addPerson);

        name = (EditText) findViewById(R.id.name);
        age = (EditText) findViewById(R.id.age);
        getPerson = (EditText) findViewById(R.id.getPerson);

        btn_1.setOnClickListener(this);
        btn_2.setOnClickListener(this);
    }

    /**
     * 与远程服务进行绑定
     */
    private void bindService() {
        Intent intent = new Intent();
        // 传入AIDL的包名,以及调用远程服务的全类名
        intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.RemoteService"));
        bindService(intent, conn, Context.BIND_AUTO_CREATE);

    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_addNumber:
                int num1 = Integer.parseInt(this.num1.getText().toString());
                int num2 = Integer.parseInt(this.num2.getText().toString());


                try {
                    int num = iService.add(num1, num2);
                    result.setText(num + "");
                    Toast.makeText(getApplicationContext(), num + "来到了", Toast.LENGTH_LONG).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;

            case R.id.btn_addPerson:
                String p1 = name.getText().toString();
                int num3 = Integer.parseInt(age.getText().toString());
                Person person = new Person(p1, num3);
                try {
                    ArrayList<Person> persons = (ArrayList<Person>) iService.addPerson(person);
                    Toast.makeText(getApplicationContext(), persons.toString(), Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}

在这里,我们看到基本与调用本地的服务的方法没多大区别,只是在使用bindService()方法时,我们要设置包名,即设置我们要访问的服务的信息,

intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.RemoteService"));

总结

AIDL的使用并不难,而且在调用的时候也就象使用普通的服务一样,区别只在双方都必须有aidl文件,其中表明能够被调用的方法而已。
下面总结一下aidl多进程通信的使用步骤

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

推荐阅读更多精彩内容