进程间的对话——aidl(一)

  众所周知,在Android系统中,系统允许单个app使用的内存是有限的,这个限制因手机而异。但有时候,我们需要一个计算量较大的后台任务,不希望它占用前台太多的内存。此时,我们可以用Service。通常的Service是在本app的内存中的,接下来我们就记录一种方法,为Service新开一个进程。由于是新开的进程,所以系统会为它分配专门的内存空间,缓解了app前台使用内存的压力。

  这个例子,是以一个图书管理的进程为例子。后台进程负责管理图书,拥有添加图书和获取图书列表的方法。

  所以,首先,我们需要一个图书类。由于我们将跨进程传输这个对象,所以它必须可以序列化。

// Book.java
public class Book implements Parcelable{

    public int bookId;
    public String bookName;

    public Book(int bookId , String bookName){
        this.bookId = bookId;
        this.bookName = bookName;
    }

    private Book(Parcel in) {
        bookId = in.readInt();
        bookName = 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.writeInt(bookId);
        dest.writeString(bookName);
    }
}

  图书类比较简单,拥有两个成员属性,id和name。然后有一个public的构造方法,其他是继承了Parcelable接口生成的。由于我们需要用AIDL来传输它,我们还需要一个AIDL文件。  

// Book.aidl
package com.dream.fishbonelsy.aidldemo.aidl;

parcelable Book;

接下来我们需要一个Book的管理接口。所谓管理接口,即后台进程将提供哪些方法供前台使用,我们需要用AIDL来建立这个中间协定。

// IBookManager.aidl
package com.dream.fishbonelsy.aidldemo.aidl;

import com.dream.fishbonelsy.aidldemo.aidl.Book;interface IBookManager {

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

这个接口提供了getBookList()和addBook( in Book book)两个方法,见文知意,这两个方法的作用很明确。

写好了这个接口。我们就可以Clean Project----->Rebuild Project。

Android Studio会我们自动生成IBookManager.java。这个文件比较隐蔽在build---->generated---->source---->aidl---->debug---->包名+.aidl中。我们可以先不要改它写的是什么,因为我们暂时不需要修改它。我们只需要知道,它通过我们建立的接口,以IBinder为通信方式,为我们实现了跨进程通信的功能。

完成了通信接口,我们该去完成后台任务了。

// BookManagerService.java
public class BookManagerService extends Service {

    AtomicBoolean mIsServiceRunning = new AtomicBoolean(true);
    CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private final IBookManager.Stub mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            synchronized (mBookList) {
                return mBookList;
            }
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            synchronized (mBookList) {
                if (!mBookList.contains(book)) {
                    mBookList.add(book);
                }
            }
        }
    };

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

    @Override
    public void onDestroy() {
        mIsServiceRunning.set(false);
        super.onDestroy();
    }
}

  在Service中获得IBookManager代理的Binder。在onBind中将其返回。我们则只需要在IBookManager.Stub类,实现我们定义好的方法getBookList()和addBook(Book book)。唯一要注意的就是,这所有的数据都是需要线程安全的,因为作为后台,可能有多个前台需要来访问。

  此外,我们还需要在AndroidManifest中对这个Service进行配置,确保它运行在单独的进程中。 

        <service
            android:name=".service.BookManagerService"
            android:process=":remote"
            >
            <intent-filter>
                <action android:name="com.dream.fishbonelsy.aidldemo.service"/>
            </intent-filter>
        </service>

  写好了这个后台任务,前台的工作就比较简单了。 

  MainActivity.java: 

    IBookManager mService;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService =  IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    Intent intent = new Intent("com.dream.fishbonelsy.aidldemo.service");
    bindService(intent, connection, Context.BIND_AUTO_CREATE);

  只需要两部,我们就可以绑定后台服务,并获取服务的代理IBookManager mService。

  这样,就可以调用IBookManager中的方法,并通知后台服务执行它。

        btn = (Button) findViewById(R.id.test_btn_id);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {                    
                    if (mService != null && mService.getBookList() != null){
                        Log.d("tag", "mService.getBookList().size()==" + mService.getBookList().size());
                        if (mService.getBookList().size() > 0){
                            Log.d("tag", "the name of the first book ==" + mService.getBookList().get(0).bookName);
                        }
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        btn2 = (Button) findViewById(R.id.test_btn_id2);
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (mService != null && mService.getBookList() != null) {
                        mService.addBook(new Book(mService.getBookList().size() + 1, "Random Name =" + Math.random()));
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

   这只是一个简单的版本,其中还有很多坑等待解决和优化,将在后面的博客的记录。

PS:附上文件结构图,这个是坑,容易犯错。

Done~

原文地址:https://www.cnblogs.com/fishbone-lsy/p/5327500.html