多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?未完待續吧,或者沒有下文了。