2019-08-19android
關鍵字:AIDL、跨進程通訊、Service與AIDL安全
Linux 操做系統爲了安全性的緣由,將不一樣應用的活動範圍,或者說權限範圍限定在一塊專有的內存空間中。每一個應用都有屬於本身的專屬內存領域,且沒法訪問其它應用的專屬領域。但需求永遠是豐富多變的,跨進程訪問,或者說宏觀一點,跨應用訪問這個需求在平常項目開發中是常有的事。爲了打破這種應用沒法訪問其它應用的專屬內存空間的限制,就衍生出了各類「跨進程通信」技術。AIDL 也算是一種跨進程通信方式。架構
AIDL(Android Interface Definition Language),Android接口定義語言,它是 Android 提供的用於進程間通信的機制。app
其實,要作進程間通訊仍是挺複雜的。但 Android 爲了讓開發更簡單高效,將複雜、重複的工做都封裝好了,對上層就開放了一些配置信息的接口而已,這樣一來,開發者就能夠僅僅經過配置本身專有的信息,好比定義接口名稱、參數列表與返回值類型等等,再經由編譯轉換之後就自動實現了本來複雜難懂的跨進程通訊功能了。這種開發模式就是 AIDL。ide
AIDL 的模式既能夠應用在 APK 開發中,也能夠應用在 framework 層的開發中。它們的使用方式大同小異,都是編寫相關 AIDL 接口信息文件並執行編譯便可。AIDL 的進程間通訊與其它進程間通訊機制同樣,都是使用 C/S 架構的。測試
AIDL 的語法規則仍是比較簡單的,同時也正是由於簡單,它功能也不會有多豐富,不過做爲一款跨進程通信的模塊,已經夠了。lua
全部 AIDL 相關的信息都得定義在後綴爲 .aidl 的文件中。對於文件名並無太多的要求,只不過它要求與 Java 的類定義同樣,接口類名稱與文件名稱要一致。而且咱們還約定俗成地將 AIDL 類名稱以大寫字母 I 開頭。spa
跨進程通訊在微觀上是要將一個應用內的數據由對象序列化爲字節流傳送出去,並接收來自外部的對象字節流來實現的。這就意味着 AIDL 不可能支持全部數據類型的使用。不過所幸,受 AIDL 支持的類型也已經足夠知足各類需求了。操作系統
AIDL 語法支持 8 種基本類型:3d
一、byte
二、char
三、short
四、int
五、long
六、float
七、double
八、boolean
支持的字符串類型有兩種:
一、String
二、CharSequence
支持的集合類型也有兩種:
一、List
二、Map
關於集合類型的存儲類型,也必須得是受 AIDL 支持的類型纔可。
同時,AIDL 的語法還支持全部實現了 Parcelable 接口的類型。
AIDL 的書寫規則與定義普通的 Java 接口沒有什麼區別。定義包名,再定義接口類名,接口內部再定義抽象方法。就這麼簡單。不過必定要注意,AIDL 文件的存儲路徑必定要與所定義的包名一致。
AIDL 最普遍與最簡單的應用是與四大組件之一 Serivce 的配合使用了。咱們都知道,啓動一個 Serivce 有兩種方式:一、經過 startService 的方式;二、經過 bindService 的方式。 經過 binService 方式啓動的 Service 所返回的對象類型,就能夠理解爲是 AIDL 跨進程通訊類型。
public abstract IBinder onBind(Intent intent);
下面咱們來演示一下最簡單的 APK 開發 AIDL 的方式。
首先是服務端的定義。咱們在 Android Studio 上演示。建立一個 APK 工程。在開始的時候它什麼也沒有,筆者這邊甚至連 MainActivity 都沒有,它的結構以下圖所示:
而後咱們直接來建立 AIDL 文件,具體操做以下圖所示:
而後便會自動多出一些文件:
再而後即可以去編寫本身須要的接口方法了。筆者這邊定義了兩個示例方法:
再接下來,即是建立 Service 去實現這些抽象方法了。Service 中的任務也算單一,將前面咱們定義的 ITest 的實例在 onBind 方法中返回便可。那這裏就涉及到要去實現 ITest 接口。咱們能夠另外建立一個代碼文件來實現它,也能夠直接在 Service 類內部來實現它。筆者這裏爲了方便,就直接在 Service 類內部以匿名內部類的形式來實現它了。
在 Serivce 中要導入正確的 AIDL 接口類:
其次是建立匿名內部類的實現。注意這裏實現的不是 ITest 接口,而是 ITest.Stub 抽象類。若是不懂就不用糾結它,記住這種寫法就行,直接在本身定義的接口後加上 .Stub 便可:
完整的 Service 代碼以下所示:
package com.tst.aidlservice; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import com.tst.aidlservice.ITest; public class MyService extends Service { public MyService() { } @Override public IBinder onBind(Intent intent) { return its; } private ITest.Stub its = new ITest.Stub() { @Override public boolean isSingular(int value) throws RemoteException { return value % 2 != 0; } @Override public String whatTime() throws RemoteException { return "I do not know!"; } }; }
至此,服務端就算是完成了。接下來是客戶端 APK 的開發。一樣是新建一個 APK 工程。而後將工程視圖切換成「Project」模式,並在 main 目錄下右鍵,新建名稱爲 aidl 的目錄,以下圖所示:
aidl 目錄建立之後則是要再建立一個與 AIDL 文件中的包名一致的目錄層級,以下圖所示:
再而後,就是將服務端中的 AIDL 文件拷貝進這個目錄中去。注意,是原封不動地拷貝,千萬不能修改 AIDL 文件的什麼信息。
而後要執行一下 Make Project 命令以建立 AIDL 的索引。
在 Make Project 完成之後,即可以開始去綁定 Service 了。筆者這裏爲了方便,選擇在 Activity 的 onResume 方法中綁定,並在綁定成功後直接調用演示接口。整個代碼不長,以下所示:
import com.tst.aidlservice.ITest; public class MainActivity extends AppCompatActivity { private ITest its; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onResume() { super.onResume(); Intent it = new Intent(); it.setClassName("com.tst.aidlservice", "com.tst.aidlservice.MyService"); bindService(it, sc, Context.BIND_AUTO_CREATE); } private ServiceConnection sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { its = ITest.Stub.asInterface(iBinder); try { boolean ret = its.isSingular(33); Log.d("Tst", "is 33 singluar? " + ret); String ret2 = its.whatTime(); Log.d("Tst", "What time now? " + ret2); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; }
而後編譯服務端與客戶端,將它們安裝進手機中去測試。發現運行一切正常。
這就是最最簡單的 AIDL 通信實例。還有個更高級一點的用法,就是 AIDL 中要用到實現了 Parcelable 接口的類的形式。但這種實例,等之後有須要的時候再去實現吧。