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的一大步。

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