前面我们将了本地service的基本用法,今天来介绍的就是远程服务,就是Service端和Client端分别在不同的进程。

这里就不得不提到AIDL了。

什么是AIDL:

大家可以看一下官方文档的定义,简单来说AIDL就是Android系统提供的一套帮助开发者快速实现binder的工具。而什么是binder呢?binder是Android系统实现进程间通讯的机制。这个再再再后面讲……

使用AIDL:

既然是C/S模型那么为了方便演示,这里我们就直接创建2个mode,一个aidlService(服务端),一个aidlClient(客户端)。

android之AIDL实例详解_第1张图片

aidlService端:

创建aidl文件:直接在项目main目录右键-new-aidl,然后输入文件名称即可。

android之AIDL实例详解_第2张图片

系统会默认新建一个和Java目录平级的aidl目录,下面是和我们报名相同的路径,然后是我们创建的aidl文件。

android之AIDL实例详解_第3张图片

先来编写接口:

package com.example.aidlService;import com.example.aidlService.bean.BookBean;interface IMyAidlInterface {    String getString();    BookBean getBook();}

可以看到创建的aidl文件是一个接口,我们定义了2个方法,一个getString返回一个String,getBook返回我们自定义的实体类。这里讲一下aidl支持的数据类型:

除了基本数据类型外还有String、CharSequence、List、map、实现Parcelable的类。其中Parcelable接口类需要自己写import导入,就像上面的import com.example.aidlService.bean.BookBean;类,就是为了导入BookBean。

Parcelable接口是Android提供的可序列化的接口,简单看一下BookBean的实现:

class BookBean() : Parcelable {    var name = ""    var price = 0.0f    constructor(name: String, price: Float) : this() {        this.name = name        this.price = price    }    constructor(parcel: Parcel) : this() {        name = parcel.readString().toString()        price = parcel.readFloat()    }    override fun writeToParcel(dest: Parcel?, flags: Int) {        dest?.let {            it.writeString(name)            it.writeFloat(price)        }    }    override fun describeContents(): Int = 0    override fun toString(): String {        return "BookBean(name='$name', price=$price)"    }    companion object CREATOR : Parcelable.Creator {        override fun createFromParcel(parcel: Parcel): BookBean {            return BookBean(parcel)        }        override fun newArray(size: Int): Array {            return arrayOf()        }    }}

Parcelable接口主要需要我们实现一个带Parcel参数的构造方法和writeToParcel方法,以及Creator接口,Creator接口主要是用来创建我们的BookBean实例的,在writeToParcel方法中我们需要将需要保存的属性通过Parcel的writexxx方法写入,然后在带有Parcel参数的构造方法中农使用readxxx读取出来。需要注意的是两边写入和读取的顺序要相同。

编写完aidl接口后我们需要让android studio同步一下代码,让它自动生成aidl文件对应的java文件。可以点击Build菜单的make mode来重新编译整个项目。

编译成功之后会在build目录下生成一个aidl同名的Java接口文件

android之AIDL实例详解_第4张图片

里面的内容暂时先不用管,我们后面再说。先看怎么使用这个接口。

接下来我们就需要创建Service了

android之AIDL实例详解_第5张图片

创建 一个AIDLService文件,这就是我们的Service,还记得上一章四大组件之Service(1)讲到的Service的启动方式和对应的生命周期函数吗?因为是要给客户端提供接口的,所以客户端必须使用bindService方法来启动我们的service,所以我们这里的Service只需要实现onBind并且返回Ibinder的接口实现类。

看看代码:

class AIDLService : Service() {    override fun onBind(intent: Intent): IBinder {        return myAidlInterfaceBinder    }    private val myAidlInterfaceBinder = object : IMyAidlInterface.Stub() {        override fun getBook(): BookBean {            Log.d("ZLog AIDLService", "getBook: ")            return BookBean("android 从入门到放弃", 0.0f)        }        override fun getString(): String {            Log.d("ZLog AIDLService", "getString: ")            return "这是服务端返回的String"        }    }}

重点是我们在onBind中返回的是什么?我们这里的myAidlInterfaceBinde是一个直接继承ImyAidlInterface.Stub的类。IMyAIDLInterface是我们自己创建的aidl接口,那Stub是哪来的呢?并且还包含了我们定义的2个接口方法getString和getBook。回到我们刚刚创建好ImyAidlInterface.aidl后重新编译了项目生成的ImyAidlInterface.java接口,这个时候再来看看它里面的内容:

public interface IMyAidlInterface extends android.os.IInterface {    /**     * Default implementation for IMyAidlInterface.     */    public static class Default implements com.example.aidlService.IMyAidlInterface {        @Override        public java.lang.String getString() throws android.os.RemoteException {            return null;        }        @Override        public com.example.aidlService.bean.BookBean getBook() throws android.os.RemoteException {            return null;        }        @Override        public android.os.IBinder asBinder() {            return null;        }    }    /**     * Local-side IPC implementation stub class.     */    public static abstract class Stub extends android.os.Binder implements com.example.aidlService.IMyAidlInterface {        private static final java.lang.String DESCRIPTOR = "com.example.aidlService.IMyAidlInterface";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.example.aidlService.IMyAidlInterface interface,         * generating a proxy if needed.         */        public static com.example.aidlService.IMyAidlInterface asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.example.aidlService.IMyAidlInterface))) {                return ((com.example.aidlService.IMyAidlInterface) iin);            }            return new com.example.aidlService.IMyAidlInterface.Stub.Proxy(obj);        }        @Override        public android.os.IBinder asBinder() {            return this;        }        @Override        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {            java.lang.String descriptor = DESCRIPTOR;            switch (code) {                case INTERFACE_TRANSACTION: {                    reply.writeString(descriptor);                    return true;                }                case TRANSACTION_getString: {                    data.enforceInterface(descriptor);                    java.lang.String _result = this.getString();                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }                case TRANSACTION_getBook: {                    data.enforceInterface(descriptor);                    com.example.aidlService.bean.BookBean _result = this.getBook();                    reply.writeNoException();                    if ((_result != null)) {                        reply.writeInt(1);                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);                    } else {                        reply.writeInt(0);                    }                    return true;                }                default: {                    return super.onTransact(code, data, reply, flags);                }            }        }        private static class Proxy implements com.example.aidlService.IMyAidlInterface {            private android.os.IBinder mRemote;            Proxy(android.os.IBinder remote) {                mRemote = remote;            }            @Override            public android.os.IBinder asBinder() {                return mRemote;            }            public java.lang.String getInterfaceDescriptor() {                return DESCRIPTOR;            }            @Override            public java.lang.String getString() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                java.lang.String _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);                    if (!_status && getDefaultImpl() != null) {                        return getDefaultImpl().getString();                    }                    _reply.readException();                    _result = _reply.readString();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override            public com.example.aidlService.bean.BookBean getBook() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                com.example.aidlService.bean.BookBean _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    boolean _status = mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);                    if (!_status && getDefaultImpl() != null) {                        return getDefaultImpl().getBook();                    }                    _reply.readException();                    if ((0 != _reply.readInt())) {                        _result = com.example.aidlService.bean.BookBean.CREATOR.createFromParcel(_reply);                    } else {                        _result = null;                    }                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            public static com.example.aidlService.IMyAidlInterface sDefaultImpl;        }        static final int TRANSACTION_getString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_getBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);        public static boolean setDefaultImpl(com.example.aidlService.IMyAidlInterface impl) {            if (Stub.Proxy.sDefaultImpl == null && impl != null) {                Stub.Proxy.sDefaultImpl = impl;                return true;            }            return false;        }        public static com.example.aidlService.IMyAidlInterface getDefaultImpl() {            return Stub.Proxy.sDefaultImpl;        }    }    public java.lang.String getString() throws android.os.RemoteException;    public com.example.aidlService.bean.BookBean getBook() throws android.os.RemoteException;}

内容很多,我们直接看里面的Stub类,它继承了我们上一章说的Binder类,并且实现了我们定义的ImyAidlInterface接口,所以我们可以在onBind中返回它。

其中有一个特殊的方法 asInterface()它接收一个Ibinder参数,返回的是我们定义的接口。看到这里大概有的朋友也知道了,这个方法就是客户端拿到我们定义的接口的方法。

好了,我们接着往下,接着我们需要配置一下service,在manifest.xml中我们给service增加一个action

                                                

我们添加了一个com.example.aidlService.Service的action,并且配置了service 的process属性,表示我们这个service是以一个独立的进程运行的。当然,这个配置不是必须的。

至此Service端的工作就做完了,接下来开始Client端。

首先要做的第一件事就是我们需要把service端所有的aidl都拷贝到client来,并且如果有传递了自定义的数据类也要拷贝过来,保证包名相同!保证包名相同!保证包名相同!

android之AIDL实例详解_第6张图片

 

这样client端才能正常使用相关内容

然后就是在客户端启动service:

private lateinit var aidlServiceIntent: Intent    private lateinit var aidlInterface: IMyAidlInterface    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        aidlServiceIntent = Intent()        aidlServiceIntent.action = "com.example.aidlService.Service"        aidlServiceIntent.component =            ComponentName("com.example.aidlService", "com.example.aidlService.AIDLService")        bindService(aidlServiceIntent, conn, Context.BIND_AUTO_CREATE)        acMainBtGetData.setOnClickListener {            if (this::aidlInterface.isInitialized) {                acMainTvMsg.text = aidlInterface.book.toString()Log.d("ZLog MainActivity", "调用服务端getBook: ${aidlInterface.book}")                Log.d("ZLog MainActivity", "调用服务端getString: ${aidlInterface.book}")            } else {                acMainTvMsg.text = "绑定失败"            }        }    }    private val conn = object : ServiceConnection {        override fun onServiceDisconnected(name: ComponentName?) {        }        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {            Log.d("ZLog MainActivity", "onServiceConnected: $service")            aidlInterface = IMyAidlInterface.Stub.asInterface(service)        }}

这里bindService的intent跟上一章将的不太一样,因为这里我们是没有Service的,只能通过action和包名来告诉系统我们要注册的service在哪。Android 5.0之后不允许隐式启动service,所以我们这里首先设置了intent的action,就是我们在服务端的manifest中添加的action。下面设置了ComponentName,第一个参数是服务端的包名,第二参数是服务端service的完整类名。这里有一个问题,因为这里的C、S都是我们自己写的,所以是知道包名这些参数的,如果是第三方或者其他人写的,而且只提供了action怎么办呢?推荐一个下面的方法来获取:

private fun createExplicitFromImplicitIntent(implicitIntent: Intent): Intent? {        val resolveInfo = packageManager.queryIntentServices(implicitIntent, 0)        if (resolveInfo.size != 1) {            return null        }        val serviceInfo = resolveInfo[0]        val packageName = serviceInfo.serviceInfo.packageName        val className = serviceInfo.serviceInfo.name        val component = ComponentName(packageName, className)        val explicitIntent = Intent(implicitIntent)        explicitIntent.component = component        return explicitIntent    }

这个方法的参数Intent只需要设置action即可,然后通过packageManager获取一个ResolveInfo列表,正常情况这个列表里面符合条件的就一项,然后通过ResolveInfo就可以拿到包名和类名了。

然后我们看绑定的ServiceConnection方法,在onServiceConnected方法中我们使用之前讲的IMyAidlInterface.Stub.asInterface方法就能获取IMyAIDLInterface接口了。

在button的点击事件中我们来调用接口的相关方法。

这里有一点要注意的是我们需要先启动service端的apk,在启动client端的apk。

好了,到这里aidl的基本用法就讲完了。

更多相关文章

  1. Android网络编程之通过Get方法实现
  2. android控件的监听绑定方法
  3. 全面的Android文件目录解析和获取方法(包含对6.0系统的说明)
  4. Android官方的文档中提到了模拟器中设置代理服务器的方法,即在命
  5. 如何向Android的framework里添加新类 &&& android修改开放类方法
  6. Android swap分区作用及swapper软件设置方法
  7. android Matrix处理图片原理及方法整理

随机推荐

  1. Android(安卓)Parcelable Example (转)
  2. Android(安卓)圆角矩形ImageView
  3. Android(安卓)xml资源文件animal动画解析
  4. android:persistentDrawingCache用法说明
  5. Android(安卓)电源管理
  6. android Html Package机制说明
  7. Android(安卓)自定义ViewPager 实现轮播
  8. Android(安卓)获取网络图片
  9. android 5.0 新特性
  10. Android(安卓)文件操作