zoukankan      html  css  js  c++  java
  • 进程间的对话——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~

  • 相关阅读:
    [C#]App.Config
    [转][JS]修改链接中的参数
    [转][Oracle]常见报错及处理
    [转]截图软件分享
    [转][C#]手写 Socket 服务端
    3.6的pprint写法改变了:pprint.pprint()
    版本优化-test
    python爬取豆瓣小组700+话题加回复啦啦啦python open file with a variable name
    爬豆瓣被封的解决方案
    去除列表中字符串中的空格换行等
  • 原文地址:https://www.cnblogs.com/fishbone-lsy/p/5327500.html
Copyright © 2011-2022 走看看