Android 进程间通信AIDL(一)

AIDL是什么

AIDL,即Android Interface Definition Language(Android接口定义语音),提到AIDL,不得不先引入Android系统中的Binder,Binder是Android跨进程通信的一种方式,众所周知,Android系统是基于linux内核,系统会为每一个应用分配一个单独的虚拟机,每个应用拥有自己独立的进程,当然,一个应用也可以有多个进程,如果我们的应用需要实现进程间通信,Android并没有采用linux已有的通信方式,如Socket、管道等,而是采用一种了新的通信方式Binder,Binder在平时应用层开发可能用到的不多,但是如果查看Android系统源码就会发现,Android系统中很多地方用到了Binder,它是ServiceManager连接各种Manager(ActivityManager,WindowManager等)与其相对应的ManagerService的桥梁,对于应用层来说,通过Binder,客户端与服务端可以进行跨进程通信,客户端可以访问服务端提供的方法或数据,AIDL正是为了定义客户端与服务端进程间通信时相互都认可的接口,当我们创建AIDL接口时,Android SDK会为我们自动生成对应的binder类,通过这个binder可以实现进程间通信。

如何使用AIDL

AIDL基本使用有三个步骤:

  1. 创建一个Service和AIDL文件
  2. 实现接口
  3. 向客户端公开接口

需要注意的是,AIDL支持下列数据类型:

  • 基本数据类型(int long double char 等等)
  • CharSequence
  • String
  • List
  • Map
  • Parcelable

这里选择用自定义对象来作为数据传递,首先我们新建一个Person类并且实现Parcelable接口。需要注意的是,如果需要通过ADIL进行复杂对象传递的话,该对象必须实现Parcelable接口

package com.zx.aidl.demo;
import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable {


    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int 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) {
        dest.writeInt(this.id);
        dest.writeString(this.name);
    }

    public Person() {
    }

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

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

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

接下来分别创建Person.aidl、IRemoteService.aidl和Service
Person.aidl

package com.zx.aidl.demo;
parcelable Person;

IRemoteService.aidl

package com.zx.aidl.demo;
import  com.zx.aidl.demo.Person;

interface IRemoteService {
void addPerson(in Person person);
List<Person> getPersons();
}

IRemoteService 这个接口中定义了俩个方法,需要注意的是AIDL接口中,只能定义方法,并不支持定义静态常量,当我们在AIDL文件中引用到其他自定义类型时,需要创建一个与该类同名的AIDL文件,如上面的Person.aidl ,并在文件中声明该类为parcelable,这里的parcelable首字母是小写,同时需要在引用该类的AIDL接口中import它的包名,接下来创建服务端的Service

package com.zx.aidl.demo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class RemoteService extends Service {

    private static final  String TAG = RemoteService.class.getSimpleName();

    private CopyOnWriteArrayList<Person> persons = new CopyOnWriteArrayList<>();

    @Override public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate() executed");
    }

    @Override public int onStartCommand(Intent intent,  int flags, int startId) {
        Log.i(TAG, "onStartCommand() executed");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy() executed");
    }

    @Override public boolean onUnbind(Intent intent) {
        Log.i(TAG, "onUnbind() executed");
        return super.onUnbind(intent);
    }

    private IRemoteService.Stub binder = new IRemoteService.Stub() {
        @Override public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override public List<Person> getPersons() throws RemoteException {
            return persons;
        }
    };


    @Nullable @Override public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind() executed");
        return binder;
    }
}

到这里,已经完成了上面所述的三个步骤;
1.创建AIDL和Service
2.实现接口

  private IRemoteService.Stub binder = new IRemoteService.Stub() {
        @Override public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override public List<Person> getPersons() throws RemoteException {
            return persons;
        }
    };

3.向客户端公开接口

 @Nullable @Override public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind() executed");
        return binder;
    }

第二步实现接口的时候发现这么一个类IRemoteService.Stub,我们之前并没有创建过这个类,那这个类到底从哪来的?



IRemoteService其实是编译器根据IRemoteService.aidl这个文件为我们自动生成的一个Java类,Stub是IRemoteService的一个内部抽象类


,我们先不管这个类,接下来分析AIDL原理的时候,再去分析这个类,在RemoteService这个里,创建了一个CopyOnWriteArrayList来保存数据,为什么采用它呢,这里主要是提醒大家考虑线程并发的问题,也就是多个客户端跟服务端通信的问题,CopyOnWriteArrayList可以避免多线程并发引发的问题,这里我们不对这它进行分析,如果感兴趣,可自行去了解,我们在RemoteService里对它的生命周期方法进行了log打印,顺便查看下客户端服务端通信时,Service的生命周期。,不要忘了在AndroidManifest中注册Service
 <service android:name=".RemoteService"
        android:process=":remote"
        >
    </service>
  </application>

服务端工作已完成,接下来直接上客户端布局以及Activity代码


package com.zx.aidl.demo;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btnBind;
    private Button btnUnbind;
    private EditText etId;
    private EditText etName;
    private Button btnAdd;
    private Button btnGetPersons;
    private TextView tvContent;
    private IRemoteService iRemoteService;

    private ServiceConnection serviceConnection = new ServiceConnection() {

        @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
             iRemoteService = IRemoteService.Stub.asInterface(iBinder);
        }

        @Override public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnBind = (Button) findViewById(R.id.btn_bind);
        btnUnbind = (Button) findViewById(R.id.btn_unbind);
        btnAdd = (Button) findViewById(R.id.btn_add);
        etId = (EditText) findViewById(R.id.et_id);
        etName = (EditText) findViewById(R.id.et_name);
        btnGetPersons = (Button) findViewById(R.id.btn_get);
        tvContent = (TextView) findViewById(R.id.tv_content);
        btnBind.setOnClickListener(this);
        btnUnbind.setOnClickListener(this);
        btnGetPersons.setOnClickListener(this);
        btnAdd.setOnClickListener(this);
    }

    @Override public void onClick(View view) {
        switch (view.getId()) {
            case  R.id.btn_bind :
                bindService(new Intent(this,RemoteService.class),serviceConnection, Context.BIND_AUTO_CREATE);
                break;
            case  R.id.btn_unbind:
                unbindService(serviceConnection);
                break;
            case  R.id.btn_add:
                addPerson();
                break;
            case  R.id.btn_get :
                getPersons();
                break;
        }
    }

    private void getPersons() {
        try {
            List<Person> personList = iRemoteService.getPersons();
            if (personList != null) {
                StringBuilder sb = new StringBuilder();
                for (Person person : personList) {
                    sb.append("id :").append(person.getId()).append(" ").append("姓名:").append(person.getName()).append("\n");
                }
                tvContent.setText(sb.toString());
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private void addPerson() {
        String id = etId.getText().toString();
        String name = etName.getText().toString();
        if (! TextUtils.isEmpty(id) && ! TextUtils.isEmpty(name)) {
            try {
                Person person = new Person();
                person.setId(Integer.parseInt(id));
                person.setName(name);
                iRemoteService.addPerson(person);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端实现其实很简单,首先通过bindService方法绑定服务端Service并建立连接,在ServiceConnection 的onServiceConnected方法回调内接受服务端Service onBind方法返回的binder对象并把它转换为我们需要的
IRemoteService 实例,然后通过IRemoteService实例去调用它本身的addPerson()与getPersons()方法,这样就达到了客户端调用服务端的方法功能,其实也就是客户端与服务端通信,接下来首先看下客户端bindService()与unBindService()方法时,Service会执行那些生命周期方法。


与startService()方法不同的是,bindService时,Service只执行了onCreate()与onBind()方法,同理,执行unBindService()时,Service执行了与之对应的onUnbind()与onDestroy()方法,所以当我们绑定解绑一个Service时,它的生命周期为:onCreate()--->onBind()--->onUnbind()--->onDestory()
回到正题,看下客户端与服务端数据传递,首先我们在之前已经在服务端RemoteService实现的接口的回调方法里加几行log日志打印

   private IRemoteService.Stub binder = new IRemoteService.Stub() {
        @Override public void addPerson(Person person) throws RemoteException {
            Log.e(TAG, " addPerson() executed ");
            Log.e(TAG,person.getId()+"");
            Log.e(TAG,person.getName());
            persons.add(person);
        }

        @Override public List<Person> getPersons() throws RemoteException {
            Log.e(TAG, " getPersons() executed ");
            if (persons.size() > 0) {
                for (Person person : persons) {
                    Log.e(TAG,person.getId()+"");
                    Log.e(TAG,person.getName());
                }
            }
            return persons;
        }
    };

通过日志发现,每次在MainActivity中通过调用IRemoteService的addPerson和getPersons方法时,都会执行服务端binder的对应addPerson和getPersons回调方法,至于其中其中的原理,下篇分析AIDL原理时,再详细分析。。以上就是AIDL基本的使用,当然如果在项目中使用AIDL,业务肯定要复杂的多,本文只是抛砖引玉,真正使用的时候,根据项目相应业务去做处理。。。
AIDL原理分析

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

推荐阅读更多精彩内容