Android AIDL 學習使用

來源:https://developer.android.com/guide/components/aidl

        最近沒事看了下Google對於AIDL的說明,自己理解了一下,做一下筆記,筆記中的代碼都是自己敲出來的,之前自己看別人的文章服務端和客戶端都在一個應用裏,雖然指定了UID但是總感覺差了點,於是就自己試了一下服務端和客戶端在不同的應用中的寫法

一   google官方說明

The Android Interface Definition Language (AIDL) is similar to other IDLs you might have worked with. It allows you to define the programming interface that both the client and service agree upon in order to communicate with each other using interprocess communication (IPC). On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious to write, so Android handles it for you with AIDL.

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

Before you begin designing your AIDL interface, be aware that calls to an AIDL interface are direct function calls. You should not make assumptions about the thread in which the call occurs. What happens is different depending on whether the call is from a thread in the local process or a remote process. Specifically:

  • Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but should instead create the interface by implementing a Binder).
  • Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.
  • The oneway keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from the Binder thread pool as a normal remote call. If oneway is used with a local call, there is no impact and the call is still synchronous.

看了google對aidl的定義,因爲是英文的,看的有點懵懵懂懂的,如果有英文比較好的大佬能幫忙翻譯理解一下,然後用通俗易懂的方式留下言就萬分感謝了

我自己理解了一下,大概就是有多個應用訪問同一個服務而且想要在服務端做多線程處理的時候google才建議使用aidl

二  使用aidl

     1 創建aidl文件IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.example.cslrcj.myaidltest1;
import com.example.cslrcj.myaidltest1.Persion;
// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void add(in Persion persion);
    List<Persion> getPersion();
    int getPid();
}

      由於aidl本身支持數據類型如下:

  • Java 編程語言中的所有原語類型(如 intlongcharboolean 等等)
  • String
  • CharSequence
  • List

    List 中的所有元素都必須是以上列表中支持的數據類型、其他 AIDL 生成的接口或您聲明的可打包類型。 可選擇將 List 用作“通用”類(例如,List<String>)。另一端實際接收的具體類始終是 ArrayList,但生成的方法使用的是 List 接口。

  • Map

    Map 中的所有元素都必須是以上列表中支持的數據類型、其他 AIDL 生成的接口或您聲明的可打包類型。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)。 另一端實際接收的具體類始終是 HashMap,但生成的方法使用的是 Map 接口。
    但是在實際工作中我們可能需要傳一些自己定義的類,如果這樣的話我們必須將我們自己定義的類序列化,不然是不能傳的,我這裏面也是加了一個自己定義的類Persion,貼下一我的代碼:
    在IMyAidlInterface.aidl文件的同級目錄下創建Persion.aidl

// IPersion.aidl
package com.example.cslrcj.myaidltest1;

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

parcelable Persion;

aidl文件是有些編寫規則的:

1 除了aidl本身支持的那些數據類型,其他我們自己定義的如果有用到必須用import導入纔可以

2  方法可帶零個或多個參數,返回值或空值
3  所有非原語參數都需要指示數據走向的方向標記。可以是 in、out 或 inout(見以下示例)。 ----默認是in 這個最好標記正確,比較對內存開銷比較大

2  服務端編寫

創建一個service,實現onbind方法返回自己的aidl,代碼如下:

public class MyIntentService extends IntentService {
    @Override
    public IBinder onBind(Intent intent) {
        return myBinder;
        //return super.onBind(intent);
    }

    public IBinder myBinder = new IMyAidlInterface.Stub() {
        @Override
        public void add(Persion persion) throws RemoteException {

        }

        @Override
        public List<Persion> getPersion() throws RemoteException {
            return null;
        }

        @Override
        public int getPid() throws RemoteException {
            return Process.myUid();
        }
    };

當客戶端(如 Activity)調用 bindService() 以連接此服務時,客戶端的 onServiceConnected() 回調會接收服務的 onBind() 方法返回的 mBinder 實例。

3 客戶端使用

由於是在不同的應用中,所以我們需要將aidl相關的配置都複製到客戶端,並且包名都應該一樣纔可以使用,客戶端代碼:

public class MainActivity extends AppCompatActivity {
    IMyAidlInterface myAidlInterface;
    List<Persion> mPersionList = new ArrayList<>();
    private TextView tv_show_persion;
    int serviceUid;
    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            try {

                myAidlInterface.add(new Persion(12, "lisi"));
                mPersionList = myAidlInterface.getPersion();
                serviceUid = myAidlInterface.getPid();

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_show_persion = findViewById(R.id.tv_show_persion);
        Intent mIntent = new Intent("com.example.cslrcj.myaidltest1.MyIntentService");
        mIntent.setPackage("com.example.cslrcj.myaidltest1");
        bindService(mIntent, serviceConnection, Service.BIND_AUTO_CREATE);
    }

這樣差不多就行了,附帶一下項目的結構圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章