最近在看一些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的一大步。