Android10.0 Binder通信原理(九)-AIDL Binder示例

摘要:本節主要來講解Android10.0 Binder中如何使用AIDL

閱讀本文大約需要花費20分鐘。

文章首發微信公衆號:IngresGe

專注於Android系統級源碼分析,Android的平臺設計,歡迎關注我,謝謝!

[Android取經之路] 的源碼都基於Android-Q(10.0) 進行分析

[Android取經之路] 系列文章:

《系統啓動篇》

  1. Android系統架構
  2. Android是怎麼啓動的
  3. Android 10.0系統啓動之init進程
  4. Android10.0系統啓動之Zygote進程
  5. Android 10.0 系統啓動之SystemServer進程
  6. Android 10.0 系統服務之ActivityMnagerService
  7. Android10.0系統啓動之Launcher(桌面)啓動流程
  8. Android10.0應用進程創建過程以及Zygote的fork流程
  9. Android 10.0 PackageManagerService(一)工作原理及啓動流程
  10. Android 10.0 PackageManagerService(二)權限掃描
  11. Android 10.0 PackageManagerService(三)APK掃描
  12. Android 10.0 PackageManagerService(四)APK安裝流程

《日誌系統篇》

  1. Android10.0 日誌系統分析(一)-logd、logcat 指令說明、分類和屬性
  2. Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
  3. Android10.0 日誌系統分析(三)-logd、logcat讀寫日誌源碼分析
  4. Android10.0 日誌系統分析(四)-selinux、kernel日誌在logd中的實現​

《Binder通信原理》

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入門篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++實例分析
  5. Android10.0 Binder通信原理(五)-Binder驅動分析
  6. Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊
  7. Android10.0 Binder通信原理(七)-Framework binder示例
  8. Android10.0 Binder通信原理(八)-Framework層分析
  9. Android10.0 Binder通信原理(九)-AIDL Binder示例
  10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub設計模式​​​​​​​
  11. Android10.0 Binder通信原理(十一)-Binder總結

0.什麼是AIDL

AIDL:Android Interface Definition Language,即Android接口定義語言。

Android系統中的進程之間不能共享內存,因此,需要提供一些機制在不同進程之間進行數據通信。爲了使其他的應用程序也可以訪問本應用程序提供的服務,Android系統採用了遠程過程調用(Remote Procedure Call,RPC)方式來實現。與很多其他的基於RPC的解決方案一樣,Android使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。我們知道4個Android應用程序組件中的3個(Activity、BroadcastReceiver和ContentProvider)都可以進行跨進程訪問,另外一個Android應用程序組件Service同樣可以。因此,可以將這種可以跨進程訪問的服務稱爲AIDL(Android Interface Definition Language)服務。

 

下面將通過一個示例來說明兩個APP之間的AIDL通信。

 

1.工程環境準備

1)通過Android Studio 首先創建一個項目  New Project ->Empty Activity,Name:AIDLDemo, Pakcage:com.android.myservice ,用作Server

 

2)在項目中再創建一個Module,用來做Client, 在項目文件上 右鍵 ->New-> Module -> Phone & Tablet Module, 名稱填client  -> Empty Activity

3)這樣Server和Client的兩個環境就準備好了

接下來開始填代碼

 

 

2.服務端設計

2.1 創建一個AIDL 文件 IMyService

在服務的文件夾app 中,執行下面的步驟:

右鍵 -> New -> AIDL->AIDL File, 名稱爲IMyService

AIDL創建完成

填入一個add的函數,我們用來做加法計算:

Code:

// IMyService.aidl
package com.android.myservice;

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

interface IMyService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    int add(int num1, int num2);
}

選擇 Build -> Make Module "app",會把AIDL進行編譯,會自動生成IMyService 這個服務接口,其中實現了stub、proxy的class,以及TRANSACTION的code,用來通信處理

2.2 服務實現

在Framework層我們還可以使用addService進行服務註冊,但是在應用層,我們不具備相應的權限,只能通過集成Service,開放Service,讓Client進行bind。

在JAVA->com.android.myservice 上新建一個Java Class---MyService

package com.android.myservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {
static final String TAG = "MyTestService";
//服務實體
    IMyService.Stub mStub = new IMyService.Stub() {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public int add(int num1, int num2) throws RemoteException {
            Log.d(TAG,"add");
            //服務的接口實現,這裏做一個加法運算
            return num1 + num2;
        }
    };
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind");
        return mStub;//通過ServiceConnection在activity中拿到MyService
    }
}

2.3 AndroidManifest.xml配置

在AndroidManifest.xml中配上Service的信息,其中enable:ture設置可用,exported:ture對外暴露, 這樣其他的Activity才能訪問。


<service android:name=".MyService"
    android:enabled="true"
    android:exported="true">
    <!--enable:ture設置可用
       exported:ture對外暴露 -->
    <intent-filter>
       <action android:name="com.android.myservice.myservice"/>
    </intent-filter>
</service>

執行編譯,服務端準備完成,編譯一個APK進入手機\模擬器

 

3.Client端設計

3.1 AIDL拷貝

把服務端的AIDL以及包目錄完整的拷貝到client的mian目錄下,讓Client和Server的服務對象對等。

接着執行編譯 Build-> Make Module "Client",對應的IMyService.java也在client中編譯出來

3.2 Client的UI實現

在layout->activity_main.xml 中添加相應的控件,效果如下:

佈局:


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/toAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="計算"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/result" />

    <Button
        android:id="@+id/bindbtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="綁定服務"
        app:layout_constraintStart_toStartOf="@+id/toAdd"
        app:layout_constraintTop_toBottomOf="@+id/toAdd" />

    <Button
        android:id="@+id/unbindbtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解除綁定"
        app:layout_constraintStart_toStartOf="@+id/bindbtn"
        app:layout_constraintTop_toBottomOf="@+id/bindbtn" />

    <EditText
        android:id="@+id/num1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="#ececec"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/num2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="#ececec"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/add" />

    <EditText
        android:id="@+id/result"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="#eceaea"
        android:inputType="none"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/value" />

    <TextView
        android:id="@+id/add"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="+"
        android:textSize="25sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/num1" />

    <TextView
        android:id="@+id/value"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp"
        android:text="="
        android:textSize="25sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/num2" />

</androidx.constraintlayout.widget.ConstraintLayout>

3.3 Client服務綁定和功能實現

通過bindService進行服務的綁定,unbindService 進行服務的解綁


package com.android.client;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.android.myservice.IMyService;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
       static final String TAG = "AIDLClient";
    IMyService myService;
    private EditText num1;
    private EditText num2;
    private EditText result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate ");
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        num1 = (EditText) findViewById(R.id.num1);
        num2 = (EditText) findViewById(R.id.num2);
        result = (EditText) findViewById(R.id.result);

        Button toAdd = (Button) findViewById(R.id.toAdd);
        Button bindbtn = (Button) findViewById(R.id.bindbtn);
        Button unbindbtn = (Button) findViewById(R.id.unbindbtn);
        toAdd.setOnClickListener(this);
        bindbtn.setOnClickListener(this);
        unbindbtn.setOnClickListener(this);
    }

    private ServiceConnection connection = new ServiceConnection() {
        //綁定上服務的時候
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            //接受到了遠程的服務
            Log.d(TAG, "onServiceConnected: ");
            myService = IMyService.Stub.asInterface(service);
        }

        // 當服務斷開的時候調用
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.d(TAG, "onServiceDisconnected: ");
            //回收資源
            myService = null;

        }
    };

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bindbtn://綁定服務
                Log.d(TAG, "start to bindMyServce");
                bindMyService();
                break;
            case R.id.unbindbtn://解除綁定
                Log.d(TAG, "start to unbindMyServce");
                unbindMyService();
                break;
            case R.id.toAdd://計算數據
                int number1 = Integer.valueOf(num1.getText().toString());
                int number2 = Integer.valueOf(num2.getText().toString());
                try {
                    Log.d(TAG, "start to add");
                    int valueRes = myService.add(number1, number2);
                    result.setText(valueRes + "");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }

    private void bindMyService() {
        Intent intent = new Intent();
        intent.setAction("com.android.myservice.myservice");
        intent.setPackage("com.android.myservice"); // server's package name
        bindService(intent, connection, BIND_AUTO_CREATE);
        new AlertDialog.Builder(this)
                .setTitle("Tips")
                .setMessage("綁定成功!")
                .setPositiveButton("確定",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                                int whichButton) {

                            }
                        }).show();
        Log.d("AIDLClient", "bindMyService: bind on end");
    }

    private void unbindMyService() {
        unbindService(connection);
        new AlertDialog.Builder(this)
                .setTitle("Tips")
                .setMessage("解除綁定成功!")
                .setPositiveButton("確定",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                                int whichButton) {

                            }
                        }).show();
        Log.d("AIDLClient", "unbindMyService: unbind on end");
    }
}

執行編譯,生成client.apk,在手機\模擬器中展示

 

4.測試:

點擊“綁定服務”,成功後彈出“綁定成功”:

log:
   Client:

03-21 19:32:49.986 30794 30794 D AIDLClient: start to bindMyServce
03-21 19:32:50.023 30794 30794 D AIDLClient: bindMyService: bind on end
03-21 19:32:50.044 30794 30794 D AIDLClient: onServiceConnected:
03-21 19:32:57.062 30794 30794 D AIDLClient: start to add

Server:

03-21 19:32:49.996 31091 31091 D MyTestService: onCreate
03-21 19:32:49.997 31091 31091 D MyTestService: onBind

在輸入框分別輸入1,2, 點擊計算,執行“1+2”,結果爲3,從服務端返回成功

log:
Client:

 03-21 19:32:57.062 30794 30794 D AIDLClient: start to add

Server:

03-21 19:32:57.063 31091 31160 D MyTestService: add

點擊“解除綁定”,成功後彈出“解除綁定成功”:

log:
Client:

03-21 19:35:57.109 30794 30794 I AIDLClient: start to unbindMyServce
03-21 19:35:57.147 30794 30794 D AIDLClient: unbindMyService: unbind on end

下一節專門來講一講AIDL的原理

我的微信公衆號:IngresGe

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