首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Android 進程通信機制深度解析:AIDL 完整指南

     編輯:Java 綜合教學

什麼是 AIDL?

AIDL 全稱 Android Interface Definition Language,即 Android 接口描述語言。雖然名稱聽起來很專業,但其本質只是一個生成進程間通信(IPC)接口的輔助工具。它以 .aidl 文件形式存在,開發者只需在其中定義進程間通信的接口,編譯時 IDE 會自動根據接口文件生成可供項目使用的 .java 文件,這類似於我們常說的「語法糖」。

AIDL 的語法與 Java 基本一致,唯一的細微差別在於導包機制。在 Java 中,同一包下的類無需導包,但在 AIDL 中必須顯式聲明導包。

AIDL 核心機制詳解

我們以一個圖書管理系統為例,採用 C/S 架構:具體管理功能由服務端進程實現,客戶端僅需調用相應接口。

定義 AIDL 接口

在項目中創建 aidl 包,包含三個文件:Book.java、Book.aidl、IBookManager.aidl。

// Book.java - 實體類,實現 Parcelable 接口以支持進程間傳輸
package com.example.aidl;

public class Book implements Parcelable {
    int bookId;
    String bookName;
    
    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }
    // ... Parcelable 相關方法實現
}
// Book.aidl - 實體類在 AIDL 中的聲明
package com.example.aidl;
parcelable Book;
// IBookManager.aidl - 服務端與客戶端通信接口
package com.example.aidl;
import com.example.aidl.Book;

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

關鍵點說明:

  • Book.java:實體類,實現 Parcelable 接口才能在進程間傳輸
  • Book.aidl:實體類在 AIDL 中的聲明文件
  • IBookManager:通信接口,注意參數前需加方向標識:in(輸入)、out(輸出)、inout(輸入輸出)

編譯器自動生成的三個核心類

編譯後,Android Studio 會自動生成一個包含三個靜態內部類的 .java 文件:

1. IBookManager 接口

public interface IBookManager extends android.os.IInterface {
    public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException;
    public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException;
}

2. Stub 抽象類(服務端使用)

public static abstract class Stub extends android.os.Binder implements net.bingyan.library.IBookManager {
    private static final java.lang.String DESCRIPTOR = "net.bingyan.library.IBookManager";
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    
    public Stub() { this.attachInterface(this, DESCRIPTOR); }
    
    public static net.bingyan.library.IBookManager asInterface(android.os.IBinder obj) { ... }
    
    @Override public android.os.IBinder asBinder() { return this; }
    
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 
            throws android.os.RemoteException { ... }
}

3. Proxy 代理類(客戶端使用)

private static class Proxy implements net.bingyan.library.IBookManager {
    private android.os.IBinder mRemote;
    Proxy(android.os.IBinder remote) { mRemote = remote; }
    
    @Override public android.os.IBinder asBinder() { return mRemote; }
    public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; }
    
    @Override public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException { ... }
    @Override public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException { ... }
}

三大類職責解析

  • IBookManager:定義的接口,繼承 android.os.IInterface,作為服務端與客戶端通信的「窗口」
  • Stub:繼承 Binder 並實現 IBookManager,用於服務端,需由服務端實現具體業務邏輯
  • Proxy:代理類,實現 IBookManager,封裝了底層 Binder 通信機制,客戶端通過它進行遠程方法調用

Proxy 類深度分析

Proxy 的構造方法接收 IBinder 類型參數 remote,代表服務端。以 addBook() 為例:

@Override
public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        if (book != null) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0);
        } else {
            _data.writeInt(0);
        }
        mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

核心機制:

  • 使用兩個 Parcel 對象:_data 發送請求數據,_reply 接收響應數據
  • 核心調用 mRemote.transact() 發起遠程方法調用(RPC),這是一個耗時操作
  • 對象序列化通過 writeToParcel(),反序列化通過 CREATOR.createFromParcel()
  • Parcel 打包/解包順序必須嚴格對應

AIDL 通信流程圖

Stub 類關鍵方法分析

asInterface(IBinder obj)

public static net.bingyan.library.IBookManager asInterface(android.os.IBinder obj) {
    if (obj == null) return null;
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (iin != null && iin instanceof net.bingyan.library.IBookManager) {
        return (net.bingyan.library.IBookManager) iin;
    }
    return new net.bingyan.library.IBookManager.Stub.Proxy(obj);
}

作用:將 Stub 轉換為 IBookManager 接口。同進程直接轉型,跨進程返回 Proxy 代理。

onTransact(int code, Parcel data, Parcel reply, int flags)

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 
        throws android.os.RemoteException {
    switch (code) {
        case INTERFACE_TRANSACTION: { ... }
        case TRANSACTION_addBook: {
            data.enforceInterface(DESCRIPTOR);
            Book _arg0 = (0 != data.readInt()) ? Book.CREATOR.createFromParcel(data) : null;
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
        case TRANSACTION_getBookList: { ... }
    }
    return super.onTransact(code, data, reply, flags);
}

關鍵點:

  • transact() 是遠程調用發起,onTransact() 是響應處理
  • 運行在服務端的 Binder 線程池中,若需更新 UI 必須用 Handler
  • 通過 code 標識調用哪個方法(TRANSACTION_addBook、TRANSACTION_getBookList)

完整通信流程圖

AIDL 實戰:圖書管理系統

服務端實現

public class BookManagerService extends Service {
    private final List<Book> mLibrary = new ArrayList<>();
    
    private IBookManager mBookManager = new IBookManager.Stub() {
        @Override
        public void addBook(Book book) throws RemoteException {
            synchronized (mLibrary) {
                mLibrary.add(book);
                Log.d("BookManagerService", "now our library has " + mLibrary.size() + " books");
            }
        }
        
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mLibrary;
        }
    };
    
    @Override
    public IBinder onBind(Intent intent) {
        return mBookManager.asBinder();
    }
}

在 Manifest 中註冊並指定獨立進程:

<service android:process=":remote" android:name=".BookManagerService"/>

客戶端實現

public class Client extends AppCompatActivity {
    private TextView textView;
    private IBookManager bookManager;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.library_book_manager_system_client);
        
        Intent i = new Intent(Client.this, BookManagerService.class);
        bindService(i, conn, BIND_AUTO_CREATE);
        
        Button addABook = findViewById(R.id.button);
        addABook.setOnClickListener(v -> {
            if (bookManager == null) return;
            try {
                bookManager.addBook(new Book(0, "book"));
                textView.setText(getString(R.string.book_management_system_book_count, 
                    String.valueOf(bookManager.getBookList().size())));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        });
        textView = findViewById(R.id.textView);
    }
    
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
        }
        
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("Client", name.toString());
        }
    };
}

運行效果演示

每次點擊按鈕,客戶端通過 AIDL 成功向服務端添加一本書,驗證了跨進程通信功能正常。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved