搞清楚AIDL

AIDL是 Android Interface definition language的缩写,一看就明白,它是一种Android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。
client app可通过aidl调用直接唤醒server app,类似于intent启动另一个app的activity,相比而言,在5.0以后,broadcast已经无法唤醒另一个未启动过的app。

AIDL的使用

最终效果
client调用aidl接口向server打招呼,server回调client的callback实现回应招呼。

客户端向服务端打招呼

服务端回调,回应客户端打招呼

一般是这样用的,client远程调用server端提供的service接口执行任务,server端回调,异步通知client端任务执行的状态。
客户端:

MainActivity

public class MainActivity extends Activity {

    private Button bindBtn;
    private Button greetBtn;
    private Button unbindBtn;
    private int count = 0;

    private IGreetBinder iGreetBinder;
    private Handler handler = new Handler();


    private IGreetCallback mCallback = new IGreetCallback.Stub() {

        @Override
        public void greetBack(final Person someone) throws RemoteException {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this,"greet back"+someone,Toast.LENGTH_LONG).show();
                }
            });
        }
    };

    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("ServiceConnection", "onServiceConnected() called");
            iGreetBinder = IGreetBinder.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //This is called when the connection with the service has been unexpectedly disconnected,
            //that is, its process crashed. Because it is running in our same process, we should never see this happen.
            Log.i("ServiceConnection", "onServiceDisconnected() called");
        }
    };

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

        bindBtn = (Button) findViewById(R.id.bindBtn);
        bindBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("android.intent.action.AIDLService");
                bindService(intent, conn, Context.BIND_AUTO_CREATE);

                bindBtn.setEnabled(false);
                greetBtn.setEnabled(true);
                unbindBtn.setEnabled(true);
            }
        });

        greetBtn = (Button) findViewById(R.id.greetBtn);
        greetBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    count++;
                    String retVal = iGreetBinder.greet(new Person(count,"person"+count),mCallback);
                    Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
                }
            }
        });

        unbindBtn = (Button) findViewById(R.id.unbindBtn);
        unbindBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(conn);

                bindBtn.setEnabled(true);
                greetBtn.setEnabled(false);
                unbindBtn.setEnabled(false);
            }
        });
    }
}

build.gradle

    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

服务端:


AIDLService

public class AIDLService extends Service {

    private static final String TAG = "AIDLService";
    IGreetBinder.Stub stub = new IGreetBinder.Stub() {
        @Override
        public String greet(final Person someone, final IGreetCallback iGreetCallback) throws RemoteException {
            Log.i(TAG, "greet() called");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(100);
                        iGreetCallback.greetBack(someone);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            return "hello, " + someone;
        }
    };
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind() called");
        return stub;
    }

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

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

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

AndroidManifest.xml

<service android:name=".AIDLService">
    <intent-filter>
        <action android:name="android.intent.action.AIDLService" />
        // 需定义action,提供给客户端调起;
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

build.gradle

    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

AIDL部分
服务端和客户端共用aidl部分。
Person.java

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

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

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

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

    public void readFromParcel(Parcel in){
        name = in.readString();
        age = in.readInt();
    }

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

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

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

传递Person需要实现Parcelable接口。Aidl支持基本数据类型(int、long、char 等)String 和 CharSequence,List:只支持ArrayList,里面的每个元素都必须被AIDL支持。,Map: 只支持HashMap, 里面的每个元素都必须被AIDL支持。Parcelable: 所有实现了Parcelable接口的对象。
Parcelable 接口
Parcelable 可以是 android 系统将 objectes 分解成可以被进程处理的原始种类必须提供一个名为CREATOR的static final属性 该属性需要实现android.os.Parcelable.Creator接口,writeToParcel 注意写入变量和读取变量的顺序应该一致,不然得不到正确的结果,readFromParcel 注意读取变量和写入变量的顺序应该一致,不然得不到正确的结果。
Person.aidl

package com.test.aidldemo;
parcelable Person;

IGreetBinder.aidl

package com.test.aidldemo;
import com.test.aidldemo.IGreetCallback;
import com.test.aidldemo.Person;
interface IGreetBinder {
       String greet(inout Person person,IGreetCallback cb);
}

添加服务端对客户端的回调。
IGreetCallback.aidl

package com.test.aidldemo;
import com.test.aidldemo.Person;

interface IGreetCallback {
    void greetBack(inout Person person);
}

使用Person类型参数时,需要提供方向指示符,其包括in、out、和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。如不设置方向指示符,将报错,基本类型int、String等无需设置方向指示符。

源码

client端:https://github.com/woshizmxin/AidlClientDemo
server端:https://github.com/woshizmxin/AidlServerDemo

在该demo运行在5.0以上设备上,会报错
** IllegalArgumentException: Service Intent must be explicit**
解决办法参考: Service Intent must be explicit的解决方法

使用Messager

如果想做app进程间通信,aidl写起来还是比较麻烦的,meassager则相当于是一个简化版本。它基于Message,相信大家都很熟悉,不需要编写aidl文件。
AIDL和Messager区别:

  1. Messenger本质也是AIDL,只是进行了封装,开发的时候不用再写.aidl文件。
    结合我自身的使用,因为不用去写.aidl文件,相比起来,Messenger使用起来十分简单。但前面也说了,Messenger本质上也是AIDL,故在底层进程间通信这一块,两者的效率应该是一样的。
  2. 在service端,Messenger处理client端的请求是单线程的,而AIDL是多线程的。使用AIDL的时候,service端每收到一个client端的请求时,就会启动一个线程(非主线程)去执行相应的操作。而Messenger,service收到的请求是放在Handler的MessageQueue里面,Handler大家都用过,它需要绑定一个Thread,然后不断poll message执行相关操作,这个过程是同步执行的。
    当然是有办法解决的,在server端,Messenger的Handler可以只当作一个转发器,不处理请求,只转发请求到相应的处理线程(多是相应的HandlerThread),这样也可以达到异步的效果。
  3. client的方法,使用AIDL获取返回值是同步的,而Messenger是异步的。Messenger只提供了一个方法进行进程间通信,就是send(Message msg)方法,发送的是一个Message,没有返回值,要拿到返回值,需要把client的Messenger作为msg.replyTo参数传递过去,service端处理完之后,在调用客户端的Messenger的send(Message msg)方法把返回值传递回client,这个过程是异步的,而AIDL你可以自己指定方法,指定返回值,它获取返回值是同步的。

具体使用请移步:
Android 基于Message的进程间通信 Messenger完全解析

参考文章

Android中使用AIDL时的跨进程回调—Server回调Client
Service Intent must be explicit的解决方法
Android 基于Message的进程间通信 Messenger完全解析

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

推荐阅读更多精彩内容