天天看點

Android綁定多個aidl,android aidl 多`module`版的實作

多module版,pojo類型資料的雙向傳遞 [service client]

android中如何進行跨程序通信的?

aidl是什麼?

android中如何通過aidl實作跨程序通信?

最簡單的aidl如何實作?

多module之間如何通過aidl進行通信?

單module實作aidl和多module實作aidl有何差別?

多App之間如何通過aidl進行通信?

以上這些問題,我不打算全部回答。因為我也不知道怎麼回答。

關于第一個問題,和第二個問題,我相信官方文檔以及一些大神的部落格已經有了很詳盡的介紹,這裡我就贅述了。

第三個問題 android中如何通過aidl實作跨程序通信? 這個問題,還是太寬泛了,我也不回答了。

關于 如何實作最簡單的aidl,請看這篇

好了,現在來本篇的重點 多module之間如何通過aidl進行通信?

那我們就一步步開始吧。

後面的問題呢?别急啊...慢慢來。

關于“多module之間如何通過aidl進行通信”我準備分3個小點來說明:

- 如何實作多`module`之間,基本類型的資料的單向傳遞 [service --> client]

- 如何實作多`module`之間,自定義類型的`pojo`對象的單向傳遞 [service --> client]

- 如何實作多`module`之間,自定義類型的`pojo`對象的雙向傳遞 [service client]

從代碼開始:

首先,我建立一個android project,由于我使用的是android studio,是以預設建立的項目就是一個安卓項目。

建立完成項目之後,預設會生成一個module名為app。

然後,我會再建兩個module,名為service和aidl。

關于如何在android studio中建立一個module,這個大家都知道的吧。其中一種方法是:[菜單欄 --> File --> New --> New Module... --> Android Liarbry ]

好了,現在準備工作完成了。多module已經建立起來了。

解釋一下service是用來作為服務的,讓app去調用的。aidl是專門用來存放.aidl檔案以及對應的pojo類的。

接下來,我們去實作一下服務吧,不然跨程序通信是沒法建立起來的。

1.在service所在的module中,我建立一個類繼承android.app.Service,比如RemoteService。看起來差不多這個樣子:

public class RemoteService extends Service {

public RemoteService() {

}

@Override

public IBinder onBind(Intent intent) {

throw new UnsupportedOperationException("Not yet implemented");

}

}

2.現在我們去修改一下清單檔案。讓RemoteService處在一個單獨的程序。為什麼要這麼幹?因為如果不修改,那麼無論是在哪個module實作的Service都是在主程序的。 好了,現在我們看一下修改之後的目前服務所在的節點的樣子:

android:name=".RemoteService"

android:enabled="true"

android:exported="true"

android:process=":remote" />

好了,我們為實作跨程序通信又跨出了一步。因為現在的确有多個程序了。而且有多個module了。

3.現在呢,我們就得去定義.aidl檔案了。

為什麼現在就要定義.aidl檔案呢? 因為我們知道,跨程序通信本質上是通過bindService的方法綁定一個遠端服務,以便實作資料的互動。而跨程序的,記憶體又無法共享,可以通過已知的共享記憶體IBinder去實作跨程序的資料互動。ps:這個解說可能存在錯誤,請大家不要深信。

4.那麼,我們準備通過跨程序傳遞什麼呢?假設,我現在想通過aidl傳遞一個500以内的随機數給調用者。那麼,.aidl差不多長這樣的:

// RemoteBinder.aidl

package com.pythoncat.aidl;

// Declare any non-default types here with import statements

interface RemoteBinder {

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,

double aDouble, String aString);

int getResult();

}

**注意:這個.aidl檔案是在aidl所在module中定義的。**至于能不能直接在sevcie所在module中定義,我猜測也是可以的,但是沒有去驗證。放在一個單獨的module中的好處就是,以後查閱起來友善。

5.現在,我們定義好了RemoteBinder.aidl,然後rebuild一下工程。之後,會自動生成一些類,這些我就不去深究了,因為和主題無關。

6.rebuild完成之後,我們去完善剛才定義的RemoteService。重寫onBind()方法。完善之後的RemoteService差不多長這樣的:

public class RemoteService extends Service {

public RemoteService() {

}

@Override

public IBinder onBind(Intent intent) {

return new MyBinder();

}

private class MyBinder extends RemoteBinder.Stub{

@Override

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

}

@Override

public int getResult() throws RemoteException {

return new Random().nextInt(500);

}

}

}

現在差不多大功告成了,就等着調用者來調用了。

7.現在我們去app所在module。這裡面預設給我們提供了一個MainActivity。但是我不打算對它做什麼。我給它改名為LauncherActivity。并且在裡面添加兩個按鈕,準備讓他們分别實作界面的跳轉。第二個按鈕點選之後就跳轉到AidlActivity。當然這個AidlActivity也是在app所在module的,而且是剛剛建立的。

8.在AidlActivity裡面呢,我準備弄一個textview,用于顯示,将來通過aidl擷取的500以内的随機數。,和一個按鈕,按鈕的點選就是觸發擷取的條件。

9.好了,UI部分算是完成了。現在呢,我們先讓我們的AidlActivity去綁定服務。怎麼去綁定一個服務,相信大家都會的。現在呢,我們就來看一下,綁定服務之後,AidiActivity長什麼樣子的:

public class AidlActivity extends AppCompatActivity {

private ServiceConnection mConn;

private RemoteBinder mRemoteBinder;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_aidl);

TextView tvShowResult = (TextView) findViewById(R.id.aidl_show_tv);

TextView tvParcelable = (TextView) findViewById(R.id.aidl_show_parcelable_tv);

tvParcelable.setText(getString(R.string.show_about_parcelable_service, ""));

tvShowResult.setText(getString(R.string.show_about_normal_service, ""));

Button btnExecute = (Button) findViewById(R.id.aidl_ui_btn);

btnExecute.setOnClickListener(v -> {

if (mRemoteBinder != null) {

try {

mRemoteBinder.setResult(1024);

int result = mRemoteBinder.getResult();

tvShowResult.setText(getString(R.string.show_about_normal_service, result));

LogUtils.e("aidl result===" + result);

} catch (RemoteException e) {

LogUtils.e("bind remote service fail...");

LogUtils.e(e);

}

}

});

mConn = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

mRemoteBinder = RemoteBinder.Stub.asInterface(service);

}

@Override

public void onServiceDisconnected(ComponentName name) {

mRemoteBinder = null;

}

};

bindService(new Intent(get(), RemoteService.class), mConn, Context.BIND_AUTO_CREATE);

}

public AppCompatActivity get() {

return this;

}

@Override

protected void onDestroy() {

super.onDestroy();

if (mConn != null) {

unbindService(mConn);

mConn = null;

}

}

}

需要注意的是IBinder對象的擷取方式:mRemoteBinder = RemoteBinder.Stub.asInterface(service);

ok,似乎一切已經完成了。那麼我們就跑起來吧。... 果不其然,我們從LaunchrActivity界面進到AidlActivity先,然後,我們每點一次按鈕,textview上就出現一個500以内的随機數,幾乎每次都不相同。

這麼看來,我們已經實作了多module之間的aidl通信。

-的确,剛才已經實作了。但是剛才的實作僅僅是資料的單向傳遞[service-->client],而且是基本類型的資料的單向傳遞。

-現在呢,我們開始第二小節的說明,也就是如何實作多module之間的pojo類型的資料的單向傳遞 [service --> client]

還是從代碼開始:

首先在aidl所在module中定義一個pojo。比如這個樣子:

public class Duck {

public String name;

public long id;

}

為什麼是在aidl所在module中定義pojo,而不是service所在module? 因為,在這裡定義是為了讓.aidl能夠找到對應的pojo類。

如果在其他module中定義,就要讓其他module成為目前moudle的依賴。ps:關于此段的說明,我不能保證正确性,需要通過代碼驗證。

現在呢,就是要讓Duck實作Parcelable接口。至于怎麼實作,我隻說一句:android studio有相關的插件,自動生成Parcelable接口的實作。

然後,定義該pojo對應的.aidl檔案。差不多長這樣子的:

// Duck.aidl

package com.pythoncat.aidl.bean;

// Declare any non-default types here with import statements

parcelable Duck;

注意包的對應

接下來,就是定義傳輸pojo的接口方法了。比如:Duck getDuck();。那麼,我們去建立一個.aidl檔案,映射一個Ibinder。裡面有傳輸pojo的方法。ok,定義之後的新的.aidl差不多長這樣的:

// RemoteBinder.aidl

package com.pythoncat.aidl;

// Declare any non-default types here with import statements

import com.pythoncat.aidl.bean.Duck;

interface RemoteBinder {

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,

double aDouble, String aString);

int getResult();

Duck getDuck();

}

再次rebuild項目。

之後,會提示你報錯了。因為你的RemoteService$MyBinder不是抽象類,并且有未實作的方法。那麼我們就去實作該抽象方法。實作之後,Mybinder差不多長這樣子:

public class MyBinder extends RemoteBinder.Stub {

private Duck duck;

private int result;

@Override

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

}

@Override

public int getResult() throws RemoteException {

if (result == 0) {

return new Random().nextInt(500);

} else {

return this.result;

}

}

@Override

public Duck getDuck() throws RemoteException {

if (this.duck == null) {

Duck d = new Duck();

d.id = 10086;

d.name = "DuckTang";

return d;

} else {

this.duck.name = this.duck.name + "&Jerry";

return this.duck;

}

}

}

ok,我們的服務算是搞定了,接下來就是看調用者AidlActivity了。那麼,我們就去調用者中添加擷取pojo對象的方法,并顯示在textview中。差不多就是這樣子的:

Duck d = mRemoteBinder.getDuck();

tvParcelable.setText(getString(R.string.show_about_parcelable_service, d.toString()));

當然我又新加了一個textview,這個也要告訴你嗎?

現在呢,完整的AidlActivity是這樣的:

package com.pythoncat.ipcorservice2.activity;

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.widget.Button;

import android.widget.TextView;

import com.apkfuns.logutils.LogUtils;

import com.pythoncat.aidl.RemoteBinder;

import com.pythoncat.aidl.bean.Duck;

import com.pythoncat.ipcorservice2.R;

import com.pythoncat.service.RemoteService;

public class AidlActivity extends AppCompatActivity {

private ServiceConnection mConn;

private RemoteBinder mRemoteBinder;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_aidl);

TextView tvShowResult = (TextView) findViewById(R.id.aidl_show_tv);

TextView tvParcelable = (TextView) findViewById(R.id.aidl_show_parcelable_tv);

tvParcelable.setText(getString(R.string.show_about_parcelable_service, ""));

tvShowResult.setText(getString(R.string.show_about_normal_service, ""));

Button btnExecute = (Button) findViewById(R.id.aidl_ui_btn);

btnExecute.setOnClickListener(v -> {

if (mRemoteBinder != null) {

try {

int result = mRemoteBinder.getResult();

tvShowResult.setText(getString(R.string.show_about_normal_service, result));

LogUtils.e("aidl result===" + result);

Duck d = mRemoteBinder.getDuck();

tvParcelable.setText(getString(R.string.show_about_parcelable_service, d.toString()));

LogUtils.e("aidl parcelable===" + d.toString());

} catch (RemoteException e) {

LogUtils.e("bind remote service fail...");

LogUtils.e(e);

}

}

});

mConn = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

mRemoteBinder = RemoteBinder.Stub.asInterface(service);

}

@Override

public void onServiceDisconnected(ComponentName name) {

mRemoteBinder = null;

}

};

bindService(new Intent(get(), RemoteService.class), mConn, Context.BIND_AUTO_CREATE);

}

public AppCompatActivity get() {

return this;

}

@Override

protected void onDestroy() {

super.onDestroy();

if (mConn != null) {

unbindService(mConn);

mConn = null;

}

}

}

似乎已經結束了,那就跑起來吧。run app...。果不其然,我們的界面上看到了在Mybinder中定義的new Duck()對象。

現在,我們就完成第二小節的說明。完成了多module之間通過aidl進行pojo類型的資料的單向傳遞 [service --> client]

但是,最後一個問題還沒有解決,就是雙向的傳遞 [service client]

好了,那我們就去實作一下吧。

首先,在RemoteBinder中新添加一個方法void setDuck(Duck d);。添加之後,差不多長這樣的:

// RemoteBinder.aidl

package com.pythoncat.aidl;

// Declare any non-default types here with import statements

import com.pythoncat.aidl.bean.Duck;

interface RemoteBinder {

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,

double aDouble, String aString);

int getResult();

void setResult(in int result);

Duck getDuck();

void setDuck(in Duck duck); // in is ok

}

至于上面為什麼要加上in,這個涉及到aidl中定向tag相關的知識點,這裡我不多介紹了。

再次rebuild項目。結束之後,發現又報錯了,說你的MyBinder不是抽象類,而且有沒有實作的抽象方法void setDuck(Duck d)。于是,就實作一下。那麼,現在。MyBinder長這樣子:

public class MyBinder extends RemoteBinder.Stub {

private Duck duck;

private int result;

@Override

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

}

@Override

public int getResult() throws RemoteException {

if (result == 0) {

return new Random().nextInt(500);

} else {

return this.result;

}

}

@Override

public void setResult(int result) throws RemoteException {

this.result = result;

}

@Override

public Duck getDuck() throws RemoteException {

if (this.duck == null) {

Duck d = new Duck();

d.id = 10086;

d.name = "DuckTang";

return d;

} else {

this.duck.name = this.duck.name + "&Jerry";

return this.duck;

}

}

@Override

public void setDuck(Duck duck) throws RemoteException {

this.duck = duck;

}

}

ok,服務這邊算是結束了。接下來,去看調用者AidlActivity是如何去傳遞一個pojo對象給服務。看起來應該是這樣子的:

if (mRemoteBinder != null) {

try {

mRemoteBinder.setResult(1024);

int result = mRemoteBinder.getResult();

tvShowResult.setText(getString(R.string.show_about_normal_service, result));

LogUtils.e("aidl result===" + result);

Duck duck = new Duck();

duck.name = "pythonCat";

duck.id = 12306;

mRemoteBinder.setDuck(duck);

Duck d = mRemoteBinder.getDuck();

tvParcelable.setText(getString(R.string.show_about_parcelable_service, d.toString()));

LogUtils.e("aidl parcelable===" + d.toString());

} catch (RemoteException e) {

LogUtils.e("bind remote service fail...");

LogUtils.e(e);

}

}

好了,現在一切結束了。跑起來吧。... 果然,和我們預想的一樣,現在界面顯示的我們傳遞過去的Duck對象。而不是,先前在服務中定義的Duck對象了。

那麼,毫無疑問,我們完成了之前的問題。多module之間如何通過aidl實作資料的雙向傳遞。

哈哈哈哈哈,我們完成了。

然後呢?

多App之間如何進行aidl?未完待續吧,或者沒有下文了。