• 进程间的对话——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~

  • 相关阅读:
    用Python完成一个汇率转换器
    鸿蒙如何用JS开发智能手表App
    鸿蒙如何用JS开发智能手表App
    SAP Spartacus SplitViewComponent Migration 的一个具体例子
    SAP Spartacus B2B 页面 Popover Component 的条件显示逻辑
    SAP Spartacus 升级时关于 schematics 的更新
    SAP Spartacus B2B 页面 Disable 按钮的显示原理
    SAP Spartacus B2B 页面 Disable Confirmation 对话框的显示原理
    通过 Feature Level 动态控制 SAP Spartacus 的页面显示
    SAP Commerce Cloud Build Manifest Components
  • 原文地址:https://www.cnblogs.com/fishbone-lsy/p/5327500.html
Copyright © 2020-2023  润新知