Binder學習之旅(一)

最近在看一些framework方面的東西,但是最後都繞不過binder,於是我再轉向去了解binder的知識,但是看得我一頭霧水。唉,仔細想想,我好想都沒有正兒八經的寫過跨進程通信的代碼,那麼,我就來手寫一個跨進程的demo玩玩,同時也正好記錄所遇到的問題。

AIDL

AndroidStudio對AIDL開發支持真的很好啊,在新建文件的時候可以直接選擇AIDL文件編寫,AS會自動幫我們生成對應的java接口文件。我是從《Android開發藝術探索》上看到的AIDL編程,所以案例也用了同一個。

首先定義Book類和Book.aidl

package com.example.myapplication.aidl;

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

/**
 * Created by 洪彬 on 2020-05-16.
 */
public class Book implements Parcelable {

    private String bookName;
    private String author;

    public Book() {
    }

    protected Book(Parcel in) {
        bookName = in.readString();
        author = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(bookName);
        dest.writeString(author);
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

package com.example.myapplication.aidl;
parcelable Book;

需要注意的有兩點:Book因爲是要自定義的類,所以要實現Parcelable接口,並補全相關代碼;Book.java和Book.aidl包路徑必須相同。

接着就寫“服務端”需要實現的功能了

package com.example.myapplication.aidl;
import com.example.myapplication.aidl.Book;

interface IBookManager {

    List<Book> getAllBooks();
    
    void addBook(in Book book);
    
}

這裏需要注意的是在接口類中如果要引用自定義類的時候要import進來。(雖然很像廢話,因爲不import語法上不會報錯,所以我在寫的時候忘記引入了)

可以注意到,addBook方法的入參前面有個in,這個是aidl中的tag,指定了數據流的方向,分爲in、out、inout,代表客戶端流向服務端、服務端流向客戶端、雙向流動。

好啦,這樣寫完之後編譯一下代碼,就能在build目錄下找到IBookManager.java,這個java文件就是我們需要的。文件有點長,AndroidStudio幫我們補足了代碼。這裏也有個注意點,如果gradle找不到這個java文件,我們需要到build.gradle的android{}中加上這麼一段,來指定java文件的查找路徑:

sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
        aidl.srcDirs = ['src/main/aidl']
    }
}

========= 一條手動分割線 ==========

寫完AIDL之後,下面要用兩個app來驗證進程間通信,我分爲ServiceApp和ClientApp,通過ClientApp向ServiceApp發送請求以及獲取結果來驗證。

ServiceApp

服務端的核心就是Service,Service中的Stub中實現了getAllBooks()和addBook()兩個方法。

public class BookService extends Service {

    private List<Book> books = new ArrayList<>();
    private IBookManager.Stub manager = new IBookManager.Stub(){
        @Override
        public List<Book> getAllBooks() throws RemoteException {
            return books;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            books.add(book);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return manager;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setBookName("《彬彬仔的Binder之旅》");
        book.setAuthor("彬彬仔");
        books.add(book);
    }

}

ClientApp

public class MainActivity extends AppCompatActivity implements ServiceConnection {

    private IBookManager bookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //點擊綁定
        findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent serviceIntent = new Intent();
                serviceIntent.setComponent(new ComponentName("com.example.myapplication",
                        "com.example.myapplication.aidl.BookService"));
                bindService(serviceIntent,MainActivity.this, Context.BIND_AUTO_CREATE);
            }
        });
        //點擊向服務端加一本書
        findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if(bookManager != null){
                        Book book = new Book();
                        book.setBookName("《彬彬仔的Binder之旅Ⅱ》");
                        book.setAuthor("彬彬仔");
                        bookManager.addBook(book);
                    }
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        //點擊獲取服務端所有的書籍,並打印
        findViewById(R.id.get).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if(bookManager != null){
                        List<Book> list = bookManager.getAllBooks();
                        if(list != null){
                            for(Book book:list){
                                Logger.getLogger("ClientApp", book.getBookName() + "-" +book.getAuthor());
                            }
                        }
                    }
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        bookManager = IBookManager.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        bookManager = null;
    }
}

從代碼上看,ClientApp中的Service在啓動的時候回自動添加一本書,在ClientApp中執行獲取方法看看實際打印的:

2020-05-16 17:14:31.329 17990-17990/com.nuonuo.myapplication D/ClientApp: 《彬彬仔的Binder之旅》-彬彬仔
2020-05-16 17:14:31.329 17990-17990/com.nuonuo.myapplication D/ClientApp: over

哦豁~
試一下添加再查看:

2020-05-16 17:15:31.011 17990-17990/com.nuonuo.myapplication D/ClientApp: 《彬彬仔的Binder之旅》-彬彬仔
2020-05-16 17:15:31.011 17990-17990/com.nuonuo.myapplication D/ClientApp: 《彬彬仔的Binder之旅Ⅱ》-彬彬仔
2020-05-16 17:15:31.011 17990-17990/com.nuonuo.myapplication D/ClientApp: over

哦豁哦豁哦豁哦豁哦豁哦豁哦豁~

看來是成功了。開心得像個800斤的小孩。

這是進程間通信的一小步,也是我學習binder的一大步。

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