Android Aidl跨进程通讯的简单使用

学更好的别人,

做更好的自己。

——《微卡智享》

c45f249fac3987b4e5894eacb7a1a7cb.jpeg

本文长度为3130,预计阅读7分钟

前言

多进程其实在大的APP中越来越多,像微信里面就是,消息接收是单独的进程服务,所以AIDL的跨进程通讯少不了是需要掌握的技能,本篇就是实现一个AIDL跨进程通讯的简单事例,做为一个入门的了解。

7fb7debb73f52572f9455e24c17138da.png

AIDL简介

90d390d5812996053a23d735f74ad738.png

微卡智享

AIDL全名Android Interface Definition Language,目的是为了实现进程间通信,由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,所以我们必须将要传输的数据转化为能够在内存之间流通的形式,通过AIDL进行跨进程通信的时候,选择的序列化方式是实现 Parcelable 接口。

AIDL默认支持的数据类型包括:

  • 基本数据类型:(byte,short,int,long,float,double,boolean,char)、String 类型、CharSequence类型。

  • List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。

  • Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。

AIDL中还有定向的Tag,包括了in、out、inout。其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。

代码实现

882f853193669890c47ea41cfb4aec97.png

微卡智享

AIDL服务端

01

创建AIDL服务

在Android Studio中新建一个应用后,我们先创建一个AIDL的Service,File--New--New Module

928cb65db061eab5d2b30e8958a033f0.png

f73def46af285103c86e955b961f886e.png

02

创建数据类实现Parcelable接口

前面简介中提到过,AIDL数据类通讯需要实现Parcelable接口,为了省去接口实现的代码,Kotlin中通过kotlin-parcelize即可实现了。

01ab5247faade4561207489f4d7bccf7.png

在build.gradle的plugins中加入id("kotlin-parcelize")

创建TestData数据类

096358a047646eb39a9c75f404e5039d.png

package vac.test.aidlservice


import android.os.Parcelable
import kotlinx.parcelize.Parcelize




@Parcelize
data class TestData(var code: String, var name: String, var price: Float, var qty: Int) : Parcelable

通过引入kotlinx.parcelize.Parcelize,然后加入标注@Parcelize,即可直接实现Parcelable接口,省去了很多代码。

03

创建AIDL文件

3c34c330294dcd30503358301aafea7d.png

首先先创建一个AIDL的目录,在New--Folder--AIDL Folder中直接创建即可。

然后新建一个ITestDataAidlInterface的AIDL文件接口,New--AIDL--AIDL File,这里要注意,默认的AIDL File是灰色的不能创建,需要在build.gradle中加入一个修改项后才能正常显示。

e4008bc1b6abcc4e995f6188c55a3c60.png

android {
    buildFeatures {
        aidl = true
    }
}

加入上面代码后,即可正常创建了,所以在app和aidlservice两个module中都加入了这一项。

// ITestDataAidlInterface.aidl
package vac.test.aidlservice;


// Declare any non-default types here with import statements


parcelable TestData;


interface ITestDataAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);




    //根据编码返回测试类
    TestData getTestData(String code);


    //返回测试列表
    List<TestData> getTestDatas();


    //修改测试类
    boolean updateTestData(in TestData data);
}

加入了三个实现方法,分别的返回列表数据,返回第一条数据和修改数据三个方法。

57b75aa3c8e64815ed1222e1ac2a136a.png

在aidl中使用了数据类TestData,所以Aidl文件和数据类的文件必须保证在同一包名下,并不是说放在同一文件夹下,实体类TestData文件在主Code文件夹下(java目录下),包名和aidl文件夹中放置.aidl文件的包名一致。保证这样后再重新Rebuild就不会报错了。

04

创建服务

ef418305c122faa3181de99eff49ec42.png

7aa25fabbb4bd440e293832c60be4337.png

新建了AidlService服务,在onbind中实现ITestDataAidlInterface方法。

package vac.test.aidlservice


import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.util.Log


class AidlService : Service() {


    val CHANNEL_STRING = "vac.test.aidlservice"
    val CHANNEL_ID = 0x11


    val mTestDatas: MutableList<TestData> = mutableListOf()


    fun initList() {
        for (i in 1..5) {
            val price = ((0..10).random()).toFloat()
            val qty = (10..50).random()
            val item = TestData("0000${i}", "测试数据${i}", price, qty)
            mTestDatas.add(item)
        }
    }


    fun startServiceForeground() {
        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channel = NotificationChannel(
            CHANNEL_STRING, "AidlServer",
            NotificationManager.IMPORTANCE_LOW
        )
        notificationManager.createNotificationChannel(channel)
        val notification = Notification.Builder(applicationContext, CHANNEL_STRING).build()
        startForeground(CHANNEL_ID, notification)
    }


    override fun onCreate() {
        super.onCreate()
        /*Debug版本时调试使用 */
        // Debug.waitForDebugger()
        startServiceForeground()
        //初始化数据
        initList()
    }


    override fun onBind(intent: Intent): IBinder {


        return object : ITestDataAidlInterface.Stub() {
            override fun basicTypes(
                anInt: Int,
                aLong: Long,
                aBoolean: Boolean,
                aFloat: Float,
                aDouble: Double,
                aString: String?
            ) {
                TODO("Not yet implemented")
            }


            override fun getTestData(code: String?): TestData? {
                return mTestDatas.firstOrNull { t -> t.code == code }
            }


            override fun getTestDatas(): MutableList<TestData> {
                return mTestDatas
            }


            override fun updateTestData(data: TestData?): Boolean {
                data?.let {
                    var item: TestData? =
                        mTestDatas.firstOrNull { t -> t.code == it.code } ?: return false
                    item?.let { t ->
                        t.code = it.code
                        t.name = it.name
                        t.price = it.price
                        t.qty = it.qty
                    }
                    return true
                } ?: return false
            }


        }
    }
}

为了使其他进程可以被调用,所以在AndroidManifest.xml需要加入Action,并且服务启动时要改为前台服务,所以前台服务的权限也要加入 

b0ae7997cffb113c665ed7970f5dcb19.png

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="vac.test.aidlservice">


    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AidlRoomDemo">
        <service
            android:name=".AidlService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="AIDL_SERVICE" />
            </intent-filter>
        </service>
    </application>


</manifest>

一个简单的AIDL服务端这样就完成了。

a7ae8512d35a9af4dcebc882907b1d76.png

AIDL客户端

01

加入AIDL和数据类

因为客户端和服务端是两个不同的进程,所以客户端也要像服务端一样创建AIDL文件夹,复制对应的 aidl 文件和自定义的数据类,请保证包名保持一致,然后编译一下。

ebafe7c17e9899779980c1e309c35ef0.png

02

客户端布局

主页面一个Recyclerview,两个按钮,一个为获取列表,一个获取第一条数据,Adapter中布局就是显示数据信息。

9c7d231bc5fc7e099636be0094658ceb.png

2182a7a8cec01d092b7c3555c223f0e3.png

03

绑定服务

绑定服务最主要的就是创建ServiceConnection,通过ServiceConnecttion返回Aidl的接口数据,再通过Aidl的接口调用里面的接口方法来实现数据对交互。

9343e2480b9efe93fa520eff5cebd201.png

这块单独放在一个类中,方便后续别的页面调用接口,所以单独摘了出来,放在了AidlProcessUtil类中。

package vac.test.aidldemo


import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import vac.test.aidlservice.ITestDataAidlInterface


object AidlProcessUtil {


    private var aidlService: ITestDataAidlInterface? = null
    private var mAidlServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
            Log.i("aidlpkg", "onServiceConnected")
            aidlService = ITestDataAidlInterface.Stub.asInterface(p1)
        }


        override fun onServiceDisconnected(p0: ComponentName?) {
            Log.i("aidlpkg", "onServiceDisconnected")
            aidlService = null
        }


    }


    fun getAidlService():ITestDataAidlInterface?{
        return aidlService
    }


    fun getAidlServiceConnection():ServiceConnection{
        return mAidlServiceConnection
    }








    //获取当前进程名
    fun getCurrentProcessName(context:Context):String?{
        val pid = android.os.Process.myPid()
        val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager


        val runApps = am.runningAppProcesses
        if(runApps.isEmpty()) return null


        for(procinfo in runApps){
            if(procinfo.pid == pid){
                return procinfo.processName
            }
        }
        return null
    }
}

而MainActivity中的核心主要是绑定Seivice,实现调用,核心的方法如下:

f7460b947cc14d29044a9ba543c3bad8.png

private fun bindAidlService() {
        val intent = Intent()
        // AIDLService中的包名
        val pkg = "vac.test.aidlservice"
        // AIDLService中定义的action
        val act = "AIDL_SERVICE"
        val cls = "vac.test.aidlservice.AidlService"
        intent.action = act
        intent.setClassName(pkg, cls)


        val aidlserviceconnect = AidlProcessUtil.getAidlServiceConnection()


        val bl = bindService(intent, aidlserviceconnect, BIND_AUTO_CREATE)
        Log.i("aidlpkg", "bindservice ${bl}")
        if (!bl) {
            Snackbar.make(
                binding.recyclerView,
                "AIDL_SERVICEE服务绑定失败,请检查是否安装服务程序",
                Snackbar.LENGTH_INDEFINITE
            )
                .setAction("关闭") {
                    Toast.makeText(this, "点击关闭按钮", Toast.LENGTH_SHORT).show()
                }
                .show()
        }
    }

调用接口方法返回数据

55880906e2ebcbaf68b72200f2a1a028.png

Adapter的实现这里就不再列出来的了,源码会在最后列出地址来。

04

AndroidManifest及build.gradle设置

高版本的Android使用AIDL通信,需要在AndroidManifest中加入queries请求,否则无法实现

adffe5e048ca4850a66bf8ddcb0fe568.png

<queries>
        <!--如果想要与其他的应用进行AIDL通信的话,需要在这里注册包名的信息-->
        <!--谷歌的文档说明 https://developer.android.google.cn/guide/topics/manifest/queries-element?hl=en-->
        <package android:name="vac.test.aidlservice" />
    </queries>

在Build.gradle中也要加入aidl的设置,用到了viewbinding,这两个设置是在一想的,同时引用了basequickadapter。

66c81c1b824317445ddf2fd4df1b9e6b.png

这样,使用AIDL多进程通讯的Demo就实现了。

实现效果

b33cfda14536bae033be51003cbd9d94.gif

7e1cd0db6db0b0e41cc165a75c2ff802.png

源码地址

https://github.com/Vaccae/AndroidAIDLDemo.git

点击原文链接可以看到“码云”的源码地址

07cfbae7d71416f641b1314e76065f2b.png

b87c79c89f36a6d7dded6ec96dca2830.png

往期精彩回顾

 

43d47b2af72270590d26eb71bfcda7c6.jpeg

Android BlueToothBLE入门(三)——数据的分包发送和接收(源码已更新)

 

 

3be927823684626ddb2af70c6f7eb9a4.jpeg

Android BlueToothBLE入门(二)——设备的连接和通讯(附Demo源码地址)

 

 

cf0eb33685665dafcff09b41a078500c.jpeg

Android BlueToothBLE入门(一)——低功耗蓝牙介绍

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值