Android-Binder(一)

魯迅認識的那隻猹發表於2018-08-13

Android-Binder(一)

學習自

《Android開發藝術探索》 www.jianshu.com/p/bdef9e317… blog.csdn.net/u011240877/…

Binder漫談

Binder是IBinder介面的一個實現類,inder是連線系統的各種Manager和其對應的Service的橋樑,Binder主要用於Service中。

Android系統的分層

對Android系統的分層瞭解了之後,我們會對IPC有一個更清晰的認知,所以我們來看一看系統的分層

2628445-566420f14d7f4c79.png | left | 486x740

我們來依次看一下各層的職責:

  • Linux Kernel(Linux核心層), 這一層中主要是各種硬體的驅動,Binder IPC的驅動也是在此層的
  • HAL(硬體抽象層),對核心層的封裝,為系統服務層提提供可呼叫的介面,以JNI的方式。
  • Android System Service(Android 系統服務層), Android系統的核心服務,為應用層提供呼叫的介面
  • Binder IPC Proxys(Binder IPC的代理層),是應用層和系統服務層之間的橋樑,實現跨程式通訊
  • Application Freamwork(應用程式框架層), 這一程就是我們的SDK,提供我們日常開發所用到的類庫

其中Linux核心層好硬體抽象層,都是以C/C++來實現,硬體抽象層會編譯為so檔案,已JNI的方式提供給系統服務層呼叫,系統服務層通過Java實現,該層中的服務隨著手機的開機而啟動(不關機就會一直執行),這些服務負責Activity的管理,Window的管理等等。因為系統服務層是通過Java實現的所以,他們肯定是執行與一個獨立的 Dalvik 中。

因為我們程式設計師自己開發的程式和系統的服務都分別執行在不同的虛擬機器中,之間的通訊就只能靠 IPC技術來完成了,為了方便Coder呼叫系統服務介面Google提供了一系統服務相對應的Manager。其呼叫關係如下:

image.png | left | 794x112

從AIDL開始

AIDL(Android Interface Definition Language) Androd 介面定義語言,通過AIDL可以實現IPC,通過AIDL定義的介面我們可以完成客戶端和服務的跨程式通訊,使用了AIDL的話在編譯的時候系統會自動幫我們生成 Binder .

AIDL支援的資料型別

  1. Java 的基本資料型別
  2. List 和 Map
    • 元素必須是 AIDL 支援的資料型別
    • Server 端具體的類裡則必須是 ArrayList 或者 HashMap
  3. 其他 AIDL 生成的介面
  4. 實現 Parcelable 的實體

建立AIDL相關的檔案

建立需要的實體類 Book 實現Parcelable介面

package top.littledavid.studyipc.beans

import android.os.Parcel
import android.os.Parcelable

/**
 * Created by IT on 7/30/2018.
 */
class Book(val bookId: Int, val bookName: String, val author: String) : Parcelable {
    constructor(parcel: Parcel) : this(
            parcel.readInt(),
            parcel.readString(),
            parcel.readString()) {
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(bookId)
        parcel.writeString(bookName)
        parcel.writeString(author)
    }

    override fun describeContents(): Int {
        return 0
    }
    
    override fun toString(): String {
        return "Book(bookId=$bookId, bookName='$bookName', author='$author')"
    }

    companion object CREATOR : Parcelable.Creator<Book> {
        override fun createFromParcel(parcel: Parcel): Book {
            return Book(parcel)
        }

        override fun newArray(size: Int): Array<Book?> {
            return arrayOfNulls(size)
        }
    }
}
複製程式碼

建立AIDL目錄,隨便輸入一名稱,建立起目錄解構即可,然後把檔案刪掉,我們自家定義我們自己的檔案

image.png | left | 784x577

建立實體類對映檔案 Book.aidl 要在aidl 下與Book 實體類相同的包下才行,否則會報錯,無法通過編譯,實體類的包介面是 top.littledavid.studyipc.beans->beans 所以aidl下的 Book.aidl包也要一樣才行

//實體對映包名一定要和實體定義的包名相同
package top.littledavid.studyipc.beans;
//定義對映
parcelable Book;
複製程式碼

定義 IBookManager.aidl 其中定義了IPC操作的方法

// IBookManager.aidl
package top.littledavid.studyipc;

// Declare any non-default types here with import statements
import top.littledavid.studyipc.beans.Book;

interface IBookManager {
    List<top.littledavid.studyipc.beans.Book> getBookList();
    //這是如果不是使用的基礎資料型別,一定要使用方向型別標識 : in ,out ,inout
    void addBook(in Book book);
}

複製程式碼

整體目錄解構如下

image.png | left | 452x531

編譯成功後會在 專案路徑\app\build\generated\source\aidl\debug\top\littledavid\studyipc (包名可能不同)下生成一個 IBookManager.java ,其中實現了IPC相關的程式碼。

建立Service

class BookService : Service() {
    private lateinit var mBookList: MutableList<Book>

    override fun onBind(intent: Intent?): IBinder {
        mBookList = mutableListOf()
        Log.e("TAG", "OnBind")
        return mIBinder
    }

    //實現AIDL定義的介面
    private val mIBinder = object : IBookManager.Stub() {
        override fun getBookList(): List<Book> {
            return this@BookService.mBookList.toList()
        }

        override fun addBook(book: Book) {
            this@BookService.mBookList.add(book)
        }
    }
}
複製程式碼

在Minefast檔案中配置服務

<service
    android:name=".BookService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote" />
複製程式碼

進行IPC

在Activity中進行IPC

class MainActivity : AppCompatActivity() {
    private var mIBookManager: IBookManager? = null

    private val mConn = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
            mIBookManager = null
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            //將IBinder轉換為AIDL介面
            mIBookManager = IBookManager.Stub.asInterface(service)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //開啟服務
        val serviceBindIntent = android.content.Intent(this, BookService::class.java)
        this.bindService(serviceBindIntent, mConn, Context.BIND_AUTO_CREATE)
    }

    //新增書籍資訊
    fun addBook(view: View) {
        val book = Book(1, "Android開發藝術探索", "任玉剛老師")
        this.mIBookManager!!.addBook(book)

        loadBookInfo()
    }

    private fun loadBookInfo() {
        val bookList = mIBookManager!!.bookList
        val stringBuilder = StringBuilder()
        bookList.forEach {
            stringBuilder.append(it.toString() + "\r\n")
        }
        this.bookInfoTV.text = stringBuilder.toString()
    }

    override fun onDestroy() {
        super.onDestroy()
        this.unbindService(mConn)
    }
}
複製程式碼

總結

磕磕絆絆的總算完成了AIDL的IPC,非常感謝參考文件中國的博主的無私奉獻。下一章我們來看一看Binder的原理。

相關文章