感覺安卓的ipc還有有些門檻的,初學時無非是看網上的文章或者看開發藝術那本書上的demo。看書上的demo時作者直接就使用了’‘實作Parcelable接口的資料類型’’,非基本資料類型,初學時老是遇坑。網上的文章點贊多的好多也是仿寫作者的demo。其他的文章可能寫的不夠系統,這就造成自己比着demo寫老是出現bug,最終放棄了。。。經曆過這種感受後,自己就決定趁着這次溫習知識的過程來詳細的總結下aidl寫法。達到知新的效果,同時幫助大家快速入門這塊進階知識!!!!
一、基礎
1、概念
aidl:android interface definition language即安卓接口定義語言,安卓ipc方式之一。
2、為啥設計這種語言
為了跨程序通信啊,這門語言就像一個橋梁吧兩個app,确切的說應該是吧兩個程序聯系起來,使他們可以進行交流通信。
3、aidl文法
文法十分簡單,和java幾乎一樣。
(1)檔案字尾
我們平時寫的代碼檔案為.java而aidl檔案為.aidl檔案和java檔案一樣studio/idea提供快速建立方式。
(2)支援資料的類型
- java的基本資料類型:byte,short,int,long,float,double,boolean,char
- String 類型。
- CharSequence類型
- List隻支援ArrayList,list中的元素類型必須是aidl支援類型。
- Map隻支援HashMap,map中的元素類型必須是aidl支援類型,包括key value。
- Parcelable:所有實作了Parcelable接口的類(具體使用參考下栗子)
- aidl接口本身:所有的aidl接口本身也可以在aidl檔案中使用。
二、栗子:使用基本資料類型、不同app之間進行aidl
1、基本資料類型是常見的資料類型,使用起來坑少的多,并且service基礎文章《Android四大元件—Service》中通過bindService方式開啟的栗子也實踐過,這裡在之前的栗子上改寫平滑深入更易使大家接受。
2、由于app的四大元件一般預設運作在主程序,是以兩個app就是兩個不同的程序啦!兩個app之間傳遞一句資訊就是簡單的通信啦。
基于這兩點分析我們就來個栗子:
1、回顧MusicService
使用aidl進行ipc時,工程的檔案夾、很重要,這裡就特别回顧下之前bindService方式栗子的包名檔案夾。
2、換湯不換藥操作:修改之前的接口MusicManager
1、其實安卓的元件和service通信模型就是C/S模型。比如《Android四大元件—Service》中activity中通過bindService方式開啟MusicService,然後通過MusicManager接口調用方法和MusicService通信。我們可以了解為activity就是用戶端,MusicService就是服務端。
2、使用aidl方式進行ipc時服務端我們還使用這個MusicService,但是MusicManager.java檔案要變成MusicManager.aidl檔案(具體操作如下圖)
3、為啥變MusicManager.aidl???因為如果用戶端還是使用MusicManager。這個類假如是MusicManager.java接口的話,我們如何在另一個app中使用它?根本沒法使用?是以這裡使用MusicManager.aidl,因為安卓系統使用bind對aidl進行了封裝。另一個app中移植下aidl檔案後兩app就可以進行ipc了。
(1)通過編譯器建立aidl檔案
手動建立也行,隻是下圖生成的aidl檔案夾、子檔案夾(包名)、aidl檔案都要我們手動建立,然後rebuild project。
如上圖神奇的事情發生了,編譯器自動幫我們在main檔案夾下建立個aidl檔案,然後建立同包名的檔案夾(com.sunnyday.administrator.servicedemo.services)在這個檔案夾下建立了個aidl檔案
ps:可能會遇到如下坑,原因分析可能是gradle的buildToolsVersion "29.0.2"版本不相容,吧這句删除即可。親測屢試不爽。
com.android.ide.common.workers.WorkerExecutorException:
1 exception was raised by workers:
java.lang.RuntimeException:
java.lang.RuntimeException:
java.io.IOException:
com.android.ide.common.process.ProcessException:
Error while executing process
D:\androidevn\sdk\build-tools\29.0.0\aidl.exe with arguments
{-pD:\androidevn\sdk\platforms\android-29\framework.aidl
-oE:\AsProjects\servicedemo\app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out
-IE:\AsProjects\servicedemo\app\src\debug\aidl -IE:\AsProjects\MY\app\src\main\aidl
-IC:\Users\zb\.gradle\wrapper\dists\gradle-4.4-all\caches\transforms-2\files-2.1\4b6854fbce58e1d04f4d14eeade6ab12\aidl
-IC:\Users\zb\.gradle\wrapper\dists\gradle-4.4-all\caches\transforms-2\files-2.1\3702cc676a28c3d70a3a170f6be0425e\aidl
-dC:\Users\zb\AppData\Local\Temp\aidl4795291112600340041.d E:\AsProjects\servicedemo\app\src\main\aidl\com\example\mytest\PlayManager.aidl}
(2)簡單修改MusicManager.aidl檔案
删除無用代碼,添加方法。rebuild project。
(3)修改MusicService
之前的接口不用啦,換成了aidl檔案,MusicService内的MyBind類實作接口肯定會報錯啦!!!這裡也要修改啦!!!
可以看到,其實也就是讓MyBind類繼承了MusicManager.Stub類。其實Stub是系統幫我們生成的MusicManager的内部類。這裡我們無需多管直接這樣繼承即可。
(4)服務端完工#運作app
至此,服務端就完工啦!我們隻需運作app,等待其他的程序開啟這個服務即可。
3、建立用戶端app
建立個工程,我這裡取名為ClientDemo
(1)代碼
public class MainActivity extends AppCompatActivity {
MusicManager musicManager;
private ServiceConnection conn;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//隐式啟動
intent = new Intent();
intent.setAction("com.sunnyday.administrator.servicedemo.services.MusicService");
intent.setPackage("com.sunnyday.administrator.servicedemo");
conn = new MyServiceConnection();
}
// 綁定按鈕
public void bindRemoteService(View view) {
bindService(intent, conn, BIND_AUTO_CREATE);
}
// 播放按鈕
public void PlayMusic(View view) {
try {
if (null != musicManager) {
Log.i("233", "test: ");
musicManager.playMusic();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 獲得執行個體
musicManager = MusicManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
如上開啟方式很簡單,隐式開啟。我們先點選綁定按鈕,再點選播放按鈕log如下。
(2)注意點
1、Service android:exported=“true”,否則綁定失敗
2、這裡除了設定action之外還要設定下包名否則會報錯( Service Intent must be explicit: Intent { act=com.example.mytest.MyService } 必須使用顯示intent)
三、Aidl中使用非預設支援資料類型注意點
aidl 檔案預設支援了常見的資料類型,如果我們想在aidl檔案中使用自定義對象這時我們需要注意如下:
1、自定義對象實作序列化接口
2、自定義對象在aidl中導包
栗子:類Book實作了paecelable接口,BookManager.aidl檔案要使用Book類。這時我們需要另外建立Book.aidl檔案然後在BookManager.aidl檔案中paecelable關鍵字聲明Book。這時BookManager.aidl才能使用Book類.
#bookmanager.aidl
如上在紅線1出new的aidl檔案,系統在main目錄下幫我們建立了同包名的目錄。然後建立aidl檔案。這時我們直接使用Book類,然後build一下報錯:
解決#建立Book同包名,同類名的Book.aidl
不是同類名嗎?此處我們先寫Bookl寫Book會報錯。我們先寫Bookl生成aidl檔案我們在refactor即可。
删除 interface 在使用parcelable 定義下Book類。
顯式導包就可以使用了,build也不會報錯了。