天天看點

【Android IPC】AIDl使用詳解The end

感覺安卓的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方式栗子的包名檔案夾。
【Android IPC】AIDl使用詳解The end

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。
【Android IPC】AIDl使用詳解The end
【Android IPC】AIDl使用詳解The end

如上圖神奇的事情發生了,編譯器自動幫我們在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。
【Android IPC】AIDl使用詳解The end

(3)修改MusicService

之前的接口不用啦,換成了aidl檔案,MusicService内的MyBind類實作接口肯定會報錯啦!!!這裡也要修改啦!!!
【Android IPC】AIDl使用詳解The end
可以看到,其實也就是讓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如下。
【Android IPC】AIDl使用詳解The end

(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類.

【Android IPC】AIDl使用詳解The end

#bookmanager.aidl

【Android IPC】AIDl使用詳解The end
如上在紅線1出new的aidl檔案,系統在main目錄下幫我們建立了同包名的目錄。然後建立aidl檔案。這時我們直接使用Book類,然後build一下報錯:
【Android IPC】AIDl使用詳解The end

解決#建立Book同包名,同類名的Book.aidl

【Android IPC】AIDl使用詳解The end
【Android IPC】AIDl使用詳解The end
不是同類名嗎?此處我們先寫Bookl寫Book會報錯。我們先寫Bookl生成aidl檔案我們在refactor即可。
【Android IPC】AIDl使用詳解The end
删除 interface 在使用parcelable 定義下Book類。
【Android IPC】AIDl使用詳解The end
顯式導包就可以使用了,build也不會報錯了。

The end