android IPC机制(四)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013762572/article/details/84232103

IPC机制我写过三个系列:
android IPC机制(一)
android IPC机制(二)
android IPC机制(三)

基本上是从最简单的IPC讲起,从最初的Serializable & Parcelable,到Binder对象,最后到Messeager对象。今天主要是来总结一下AIDL。学习的过程是一个循序渐进的过程,以前有很多东西不了解,随着工作的深入,慢慢会有自己的体会和见解。

AIDL是什么?

AIDL全称是Andorid Interface Definition Language(Andorid接口定义语言),是Andorid中独有的IPC通信机制。在Andorid中,进程之间是无法相互通信的,AIDL的出现就是为了解决此类问题,并提供了统一的模板,使得此类复杂的问题变得清晰简单。我们只需要按照规定编写aidl接口,然后系统会自动给我们生成相应的java文件。

对于IPC进程间通信,我这里使用一张图来描述一下他们之间大致的关系:
在这里插入图片描述

我们的Client和Server属于不同的进程,通过bindService进行绑定,并返回AIDL对象作为通信的句柄,client进程通过AIDL对象,最终通过Binder对象完成信息传递到Server进程,Server进程也可以通过Binder对象将信息传递给Client进程,最终完成进程间通信,一下就是根据这个步骤来总结通信的。

1. 支持的数据类型

基本数据类型(int,long,char,boolean,double,char,byte,long);
String和CharSequence;
List:只支持ArrayList,里面有个元素都必须能够被AIDL支持;
Map:只支持HashMap,里面的每个元素都必须被AIDL,包括key和value;
Parcelable:所有实现了Paecelable接口;
AIDl:所有AIDL接口本身也可以在AIDL文件中使用。
自己自定义的Parcelable对象和AIDL对象必须要显示import进来,不管它们是否和当前的AIDL文件位于同一个包内。

出了基本类型之外,其他参数类型必须标上方向:in、out、inout。
in 表示输入型参数
out 表示输出型参数
inout 表示输入输出型参数
与传统的接口相比,aidl接口只支持方法,不支持静态常量

如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并将其声明为Parcelable类型。

2. 创建服务端和客户端

下面写一个简单例子来描述AIDL的工作流程:
我们首先创建了两个项目:aidlClient和aidlServer:
在这里插入图片描述

在aidlClient中,代码结构如下:

其中我们可以看出java代码中有Book实体类,实现了android.os.Parcelable接口,具体实现为:

public class Book implements Parcelable {

    public String name ;

    public Book(String name) {
        this.name = name;
    }

    protected Book(Parcel in) {
        name = 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 String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }
}

同样我们创建AIDL文件BookAIDL.aidl,定义addBookgetBookList两个方法:

// BookAIDL.aidl
package com.xinghe.aidlclient;

// Declare any non-default types here with import statements
import com.xinghe.aidlclient.entity.Book ;
import com.xinghe.aidlclient.OnNewBookArrivedListener ;

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

因为我们用到了Book这个实体类,所以我们也要实现Book这个aidl文件

//Book.aidl
package com.xinghe.aidlclient.entity;

parcelable Book ;

其中需要注意的是Book.adil的路径需要和src/main中路径一致:
在这里插入图片描述

最后我们需要复制Book.java文件、Book.aidl文件和BookAIDL.aidl文件到服务端aidlServer端:

client端代码接口 server端代码结构
在这里插入图片描述 在这里插入图片描述

两端的代码结构已经清晰,现在需要make module一下,使系统自动为我们创建adil文件。自动生成的文件在:/projectName/build/generated/source/aidl/debug/packagename/aidl_file_name

3. 启动服务端,客户端请求

在aidlServer中,我们创建一个服务启动服务端:

class BookManagerService : Service() {
    private val TAG: String = BookManagerService::class.java.simpleName
    
    private val mBookList = CopyOnWriteArrayList<Book>()

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "BookManagerService service has bean created")
        mBookList.add(Book("java"))
        mBookList.add(Book("kotlin"))
    }

    override fun onBind(intent: Intent?): IBinder? {
        return mBinder
    }

    private val mBinder = object : BookAIDL.Stub() {
      
        override fun addBook(book: Book?) {
            mBookList.add(book!!)
        }

        override fun getBookList(): MutableList<Book> {
            return mBookList.toMutableList()
        }
    }

    private fun whenNewBookComing(book: Book) {
 
}

AndroidManifest.xml中注册,并设置为可导出模式,因为需要被其他程序访问得到:

    <service android:name="com.xinghe.aidlserver.BookManagerService" 
     android:exported="true"/>

然后找地方启动该Service:

fun startService(view: View) {
        startService(Intent(this,BookManagerService::class.java))
}

此时需要注意的服务端包名和此Service名称,分别为:

//packname : com.xinghe.aidlserver
//serviceName : com.xinghe.aidlserver.BookManagerService

4. 客户端绑定

我们在客户端绑定服务端,并执行addBookgetBookList方法。由于Service不属于本程序,因此需要Component去绑定远程Service:

class MainActivity : AppCompatActivity() {

    private var mBookAIDL: BookAIDL?=null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startBindService()

        init()
    }

    private fun init() {
        id_add_book.setOnClickListener {
            if (null == mBookAIDL) {
                Log.d("TAG","book aidl is null")
                return@setOnClickListener
            }

            mBookAIDL!!.addBook(Book("newBook"))
        }

        id_get_book.setOnClickListener {
            if(null == mBookAIDL) {
                Log.d("TAG","book aidl is null")
            }

            Log.d("TAG","book list : ${mBookAIDL!!.bookList}")
        }
    }

	//开始绑定远程的Service
    private fun startBindService() {
        val intent = Intent()

        //启动第三方Service 使用Component
        intent.component = ComponentName("com.xinghe.aidlserver","com.xinghe.aidlserver.BookManagerService")
        val result = bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE)

        Log.d("bindService","bind result : $result")
    }


    private val serviceConnection = object:ServiceConnection{
        override fun onServiceDisconnected(name: ComponentName?) {
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mBookAIDL = BookAIDL.Stub.asInterface(service)
    }

    override fun onDestroy() {
        unbindService(serviceConnection)
        super.onDestroy()
    }

5. 数据通信

我们首先需要启动server端,在server启动BookService服务,然后再启动客户端Service。
结果为:
在这里插入图片描述

在这里插入图片描述
可以看出,我们的服务端与客户端通信完成了,两个不同的程序之间完成了一次通信,也证明了aidl有效果了。

AIDL的数据监听

目标:如果服务端存在数据更新,那么客户端需要及时被通知。就相当于一般所说的监听器,但是在多进程中,无法使用普通的Interface,所以也需要创建一个类似接口的aidl文件。如果存在图书更新,那么此时我们创建一个图书更新的aidl文件接口。

//OnNewBookArrivedListener.aidl

package com.xinghe.aidlclient;

import com.xinghe.aidlclient.entity.Book ;

interface OnNewBookArrivedListener {
    void whenNewBookArrived(in Book book);
}

此时我们的BookAIDL.aidl需要更改一下:

// BookAIDL.aidl
package com.xinghe.aidlclient;

// Declare any non-default types here with import statements
import com.xinghe.aidlclient.entity.Book ;
import com.xinghe.aidlclient.OnNewBookArrivedListener ;

interface BookAIDL {

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

    void registerBookArrivedListener(OnNewBookArrivedListener listener);
    void unRegisterBookArrivedListener(OnNewBookArrivedListener listener);

}

新增了注册监听的方法和反注册的方法,然后替换掉旧的BookAIDL.aidl文件,重新写入BookManagerService逻辑:

class BookManagerService : Service() {
    private val TAG: String = BookManagerService::class.java.simpleName

    private val mBookList = CopyOnWriteArrayList<Book>()

    //监听器集合
    private val mOnNewBookArrivedListenerList = CopyOnWriteArrayList<OnNewBookArrivedListener>()

    private val mIsServiceDestroyed = AtomicBoolean(false)

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "BookManagerService service has bean created")
        mBookList.add(Book("java"))
        mBookList.add(Book("kotlin"))

		//开一个子线程,子线程每5s添加一本书
        startToWork()
    }

    override fun onBind(intent: Intent?): IBinder? {
        return mBinder
    }

    private val mBinder = object : BookAIDL.Stub() {
    
		//注册监听器
        override fun registerBookArrivedListener(listener: OnNewBookArrivedListener?) {
            if (!mOnNewBookArrivedListenerList.contains(listener)) {
                mOnNewBookArrivedListenerList.add(listener)
            }

            Log.d("registerListener", "the current list size : ${mOnNewBookArrivedListenerList.size}")

        }
	
		//反注册监听器
        override fun unRegisterBookArrivedListener(listener: OnNewBookArrivedListener?) {
            if (mOnNewBookArrivedListenerList.contains(listener)) {
                mOnNewBookArrivedListenerList.remove(listener)
            }

            Log.d("unRegisterListener", "the current list size : ${mOnNewBookArrivedListenerList.size}")
        }

        override fun addBook(book: Book?) {
            mBookList.add(book!!)
        }

        override fun getBookList(): MutableList<Book> {
            return mBookList.toMutableList()
        }
    }

	//当新增Book时,通知到每一个监听器
    private fun whenNewBookComing(book: Book) {
        val size = mOnNewBookArrivedListenerList.size
        for (i in 0 until size) {
            mOnNewBookArrivedListenerList[i]?.whenNewBookArrived(book)
        }
    }

	//子线程添加书籍
    private fun startToWork() {
        Thread() {
            kotlin.run {
                while (!mIsServiceDestroyed.get()){
                    try{
                        Thread.sleep(5000)
                    }catch (e:InterruptedException) {
                        e.printStackTrace()
                    }

                    val newBook = Book("new Book${System.currentTimeMillis()}")
                    mBookList.add(newBook)

                    try{
                        whenNewBookComing(newBook)
                    }catch (e: RemoteException){
                        e.printStackTrace()
                    }
                }
            }
        }.start()
    }
}

然后再客户端注册一下我们的监听器:

    private fun startBindService() {
        val intent = Intent()

        //启动第三方Service 使用Component
        intent.component = ComponentName("com.xinghe.aidlserver","com.xinghe.aidlserver.BookManagerService")
        val result = bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE)

        Log.d("bindService","bind result : $result")
    }


    private val serviceConnection = object:ServiceConnection{
        override fun onServiceDisconnected(name: ComponentName?) {
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mBookAIDL = BookAIDL.Stub.asInterface(service)
            //注册监听器
	       	mBookAIDL?.registerBookArrivedListener(onNewBookArrivedListener)
        }
    }

    override fun onDestroy() {
        unbindService(serviceConnection)
        
		//取消注册事件
        if(mBookAIDL?.asBinder()?.isBinderAlive!!){
            mBookAIDL?.unRegisterBookArrivedListener(onNewBookArrivedListener)
        }

        super.onDestroy()
    }

	//监听事件
    private val onNewBookArrivedListener = object : OnNewBookArrivedListener.Stub() {
        override fun whenNewBookArrived(book: Book?) {
            Log.d("whenNewBookArrived","new book : $book")
        }
    }

重新启动server之后,在客户端启动之后,可以看到

可以看到我们的监听已经起作用了。

但是你退出时,本应该退出的,但是我们可以日志:
在这里插入图片描述
监听器中数据依旧还存在一个,意味着我们并没有unRegisterBookArrivedListener完成。

为什么呢?明明已经走到这一步了。这里的原因是因为我们此时用到的通信工具是Binder,Binder将客户端传递过来的对象重新转化并生成一个新的对象。虽然我们在注册和反注册过程中使用的是同一个客户端对象,但是通过Binder传递到服务端之后,却会产生新的对象。对象是不能跨进程传输,但是可以通过序列化/反序列化在进程之间传输。这也就是为什么AIDL中自定义对象都必须实现Parcelable接口的原因。

RemoteCallbackList

那么怎样才能实现正常的注册/反注册功能呢? 答案就是RemoteCallbackList。这是一个andorid系统的一个类,专门用于提供用于删除进程的listener接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口,基本代码如下:

public class RemoteCallbackList<E extends IInterface> 

工作原理很简单,在其内部有一个ArrayMap保存了所有的AIDL回调,这个Map的key是IBinder类型的,value是Callback类型的,但是Callback中封装了真正的listener:

    private final class Callback implements IBinder.DeathRecipient {
        final E mCallback;
        final Object mCookie;

        Callback(E callback, Object cookie) {
            mCallback = callback;
            mCookie = cookie;
        }

        public void binderDied() {
            synchronized (mCallbacks) {
                mCallbacks.remove(mCallback.asBinder());
            }
            onCallbackDied(mCallback, mCookie);
        }
    }

多次跨进程传输客户端的同一个对象在服务器端生成不同的对象,但是这些新生成的对象有一个共同点,那就是他们底层的Binder对象是同一个,按照这个特性,就可以实现我们注册/反注册AIDL接口的方案。当客户端反注册时,遍历服务端所有的listener,找出那个和注册具有相同的Binder对象的服务端listener,然后移除即可,这就是RemoteCallbackList为我们所有的事情。同事RemoteCallbackList还有一个很有用的功能,那就是客户端进程终止之后,它可以自动移除客户端所有注册的listener,原因是这个CallBack实现了IBinder.DeathRecipient,在客户端进程终止后,执行binderDied方法,该方法中会移除listener对象。

那么我们需要对Server的BookManagerService做一下改进,具体如下:

 private val mOnNewBookRemoteCallback = RemoteCallbackList<OnNewBookArrivedListener>()

 private val mBinder = object : BookAIDL.Stub() {
    
		//注册监听器
        override fun registerBookArrivedListener(listener: OnNewBookArrivedListener?) {
            //if (!mOnNewBookArrivedListenerList.contains(listener)) {
            //    mOnNewBookArrivedListenerList.add(listener)
            //}
			
			mOnNewBookRemoteCallback.register(listener)
			
            Log.d("registerListener", "the current list size : ${mOnNewBookArrivedListenerList.size}")

        }
	
		//反注册监听器
        override fun unRegisterBookArrivedListener(listener: OnNewBookArrivedListener?) {
            //if (mOnNewBookArrivedListenerList.contains(listener)) {
            //    mOnNewBookArrivedListenerList.remove(listener)
            //}
            
            mOnNewBookRemoteCallback.unregister(listener)

            Log.d("unRegisterListener", "the current list size : ${mOnNewBookArrivedListenerList.size}")
        }

        override fun addBook(book: Book?) {
            mBookList.add(book!!)
        }

        override fun getBookList(): MutableList<Book> {
            return mBookList.toMutableList()
        }
    }

然后需要更改的是监听通知方法whenNewBookComing,具体如下:

    private fun whenNewBookComing(book: Book) {
//        val size = mOnNewBookArrivedListenerList.size
//        for (i in 0 until size) {
//            mOnNewBookArrivedListenerList[i]?.whenNewBookArrived(book)
//        }

        val size = mOnNewBookRemoteCallback.beginBroadcast()
        for (i in 0 until size) {
            mOnNewBookRemoteCallback.getBroadcastItem(i)?.whenNewBookArrived(book)
        }
        mOnNewBookRemoteCallback.finishBroadcast()
    }

好了,大功告成了,重新启动Server,此时Client不需要做任何改动只需要重新绑定即可,然后我们就可以注册和反注册我们的listener了。

猜你喜欢

转载自blog.csdn.net/u013762572/article/details/84232103