kotlin优雅实现AIDL

AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似。 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。

实现AIDL有下面几个步骤

1、定义.aidl文件,您开发每个包含 .aidl 文件的应用时,Android SDK 工具都会生成一个基于该 .aidl 文件的 IBinder 接口,并将其保存在项目的 gen/ 目录中。服务必须视情况实现 IBinder 接口。然后客户端应用便可绑定到该服务,并调用 IBinder 中的方法来执行 IPC。aidl支持的类型有string、charSequence、list、map等。
定义服务接口时,请注意:方法可带零个或多个参数,返回值或空值。所有非原语参数都需要指示数据走向的方向标记。可以是 in、out 或 inout(见以下示例)。原语默认为 in,不能是其他方向
示例:

interface Data {
    //查询数据
    List<String> getData();
    //添加数据
    void setData(String s);
}

2、定义一个服务,用户绑定返回一个IBinder

class MyService : Service() {
    //返回IBinder、DataAiDl
    override fun onBind(intent: Intent): IBinder? {
        return DataAiDl()
    }
}

DataAiDl.kt的代码实现,注意看注释

/**
 * Created by aiiage on 2018/8/9.
 * DataAiDl继承Data.Stub()实现AIDL
 */
class DataAiDl : Data.Stub(){
    //设置数据
    override fun setData(s: String) {
        Log.e("MainActivity",s)
        list.add(s)
    }
    var list= ArrayList<String>()
    //添加数据
    override fun getData(): MutableList<String> {
        return list
    }
}

3、在activity中连接并绑定服务,代码如下

class MainActivity : BaseActivity(),View.OnClickListener {
    private var TAG="MainActivity"
    private var i=0
    //由AIDL文件生成的Java类
    private var data: Data? = null
    //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中
    private var mBound=false
    //定义数据链表
    var list= ArrayList<String>()
   //控件点击事件
    override fun onClick(p0: View) {
       //如果与服务端的连接处于未连接状态,则尝试连接
        if (!mBound) {
            attemptToBindService()
            Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show()
            return
        }
        if(data==null)
            return
        when(p0.id){
            R.id.addDataBty->{
                data!!.setData("data"+i)
                i++
            }
            R.id.seeDataBty->{
                 list= data!!.data as ArrayList<String>
                var sb=StringBuffer()
                for (d in list)
                {
                    sb.append(d).append("\n")
                }
                textView.text=sb
            }
        }
    }
   //获取布局文件id
    override fun getContentViewLayoutID(): Int {
        return R.layout.activity_main
    }
    //初始化控件
    override fun initView(savedInstanceState: Bundle?) {
        addDataBty.setOnClickListener(this)
        seeDataBty.setOnClickListener(this)
    }
    //连接服务器
    private fun attemptToBindService() {
        val intent = Intent()
        Log.e(TAG, " connected now")
        intent.action = "com.andream.aiiage.Data" //在AndroidManifest.xml进行配置隐形启动action
        intent.`package` = "com.andream.aiiage.aidlkotlin" //你的包名
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
    }

    private val mServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            Log.e(TAG, "service connected")
            data = Data.Stub.asInterface(service)
            mBound = true
            if (data != null) {
                try {
                    list = data!!.data as ArrayList<String>
                } catch (e: RemoteException) {
                    e.printStackTrace()
                }

            }
        }
        override fun onServiceDisconnected(name: ComponentName) {
            Log.e(TAG, "service disconnected")
            mBound = false
        }
    }
    override fun onStart() {
        super.onStart()
        attemptToBindService()
    }

    override fun onStop() {
        super.onStop()
        unbindService(mServiceConnection)
    }
}

4、记得在在AndroidManifest.xml进行配置隐形启动action来启动service

  <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name ="com.andream.aiiage.Data"/>
            </intent-filter>
        </service>

5、总结:AIDL可以进行跨进程通讯,配置和使用起来简单。如果你想把某个类跨进程传递的话,该类必须支持 Parcelable 接口。支持 Parcelable 接口很重要,因为 Android 系统可通过它将对象分解成可编组到各进程的原语。

6、注意:AIDL 接口的调用是直接函数调用。 您不应该假设发生调用的线程。 视调用来自本地进程还是远程进程中的线程,实际情况会有所差异。 具体而言:

来自本地进程的调用在发起调用的同一线程内执行。如果该线程是您的主 UI 线程,则该线程继续在 AIDL 接口中执行。 如果该线程是其他线程,则其便是在服务中执行您的代码的线程。 因此,只有在本地线程访问服务时,您才能完全控制哪些线程在服务中执行(但如果真是这种情况,您根本不应该使用 AIDL,而是应该通过实现 Binder 类创建接口)。
来自远程进程的调用分派自平台在您的自有进程内部维护的线程池。 您必须为来自未知线程的多次并发传入调用做好准备。 换言之,AIDL 接口的实现必须是完全线程安全实现。
oneway 关键字用于修改远程调用的行为。使用该关键字时,远程调用不会阻塞;它只是发送事务数据并立即返回。接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自 Binder 线程池的常规调用进行接收。 如果 oneway 用于本地调用,则不会有任何影响,调用仍是同步调用。

7、工程目录和效果图
这里写图片描述

这里写图片描述

8、附录
baseactivity.java代码

  abstract class BaseActivity : AppCompatActivity() {
    /**
     * 获取布局id
     */
    protected abstract fun getContentViewLayoutID(): Int
    /**
     * 初始化控件
     */
    protected abstract fun initView(savedInstanceState: Bundle?)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (getContentViewLayoutID()!=0)
        {
            setContentView(getContentViewLayoutID())
            initView(savedInstanceState)
        }
    }
    protected fun showToast(desc: String) {
        Toast.makeText(this,desc,Toast.LENGTH_SHORT).show()
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值