两个独立 APP 通过 AIDL 进行通讯的简单例子

新建个 AIDLService 项目,用于做服务端。

图1

在项目中选择新建 AIDL 文件时可能会如下(图2)所示:

图2

 为了能够正常的创建aidl文件,我们只需要在 build.gradle 的 android 中增加以下代码即可,记得执行同步gradle操作。

buildFeatures {
    aidl true
}

此时通过 AS 的快捷菜单我们就可以快速新建 aidl 文件了。如下图我选择新建一个 IMyServer.aidl。

图3

点击完成后在项目的aidl文件夹中自动生成一个aidl文件。

图4

 在 IMyServer.aidl 中定义了两个方法。注意。aidl 目前只支持java语法,暂不支持kotlin。跟普通java接口差不多。

// IMyServer.aidl
package com.wen.aidlserver;

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

interface IMyServer {
    // 接收一个字符串参数
    void setData(String value);
    // 返回一个字符串
    String getData();
}

 在 java 包名下我们新增一个  services 包路径。新增一个 MyService.kt

图4

在清单文件中记得加上,因为要给到外部应用使用,所以 exported= true。

<service
    android:name=".services.MyService"
    android:enabled="true"
    android:exported="true" />

MyService 中的代码如下,此时服务端 APPService 项目的代码就完成了。这个APP我没写其他东西。运行起来也只是个demo。

package com.wen.aidlserver.services

import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.wen.aidlserver.IMyServer

class MyService : Service() {

    private val mBinder by lazy { MyBinder() }

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

    private inner class MyBinder : IMyServer.Stub() {

        private var mData: String = ""

        override fun setData(value: String) {
            mData = value
        }

        override fun getData() = mData
    }
}

 接下来新建一个  AIDLClient 项目 用作客户端。

图6

 在 AIDLClient 项目中,我们需要把 AIDLServer 中 aidl 生成的java文件(图7)拷贝到项目中(图8)来,注意包名也要一致。

图7
图8

 此时就可以通过拷贝过来的 IMyServer 去连接 AIDLServer 项目了。如下

private inner class MyServiceConnection : ServiceConnection {
    var mServer: IMyServer? = null
        private set

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

    override fun onServiceDisconnected(name: ComponentName?) {
        mServer = null
    }
}

 在 AIDLClient 项目中 通过以下代码绑定 AIDLServer 项目中定义的服务。

private fun setConnection() {
    try {
        val intent = Intent()
        // AIDLServer 项目的包名(applicationId)
        // AIDLServer 项目中注册的服务的全路径
        intent.component =
            ComponentName("com.wen.aidlserver", "com.wen.aidlserver.services.MyService")

        bindService(intent, mMyServiceConnection, Context.BIND_AUTO_CREATE)
    } catch (e: SecurityException) {
        e.printStackTrace()
    }
}

当然解绑也是必不可少的。

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

在 Android11 版本以上 bindService 可能会返回 false。我们需要在 AIDLClient 中的清单文件(<manifest>下面)加上以下代码:

    <queries>
        <!--如果想要与其他的应用进行AIDL通信的话,需要在这里注册对应包名的信息-->
        <package android:name="com.wen.aidlserver" />
    </queries>

至此,两个APP之间就已经可以进行通讯了。在 AIDLClient 项目中,我简单的写了两个点击事件。一个用于设置数据到 AIDLServer 服务端。一个从服务端获取数据:

private var mNumber = 0

private fun initView() {
    mViewBinding.tvGetData.setOnClickListener {
        Log.d("MyLog ======>", "getData: ${mMyServiceConnection.mServer?.data}")
    }
    mViewBinding.tvSetData.setOnClickListener {
        mNumber++
        mMyServiceConnection.mServer?.data = mNumber.toString()
        Log.d("MyLog ======>", "setData: $mNumber")
    }
}

此时点击对应的按钮,我们可以把 mNumber  自增后的数据设置到服务端,然后又从服务端拿回数据。

MyLog ======>           com.wen.aidlclient                   D  setData: 1
MyLog ======>           com.wen.aidlclient                   D  getData: 1
MyLog ======>           com.wen.aidlclient                   D  setData: 2
MyLog ======>           com.wen.aidlclient                   D  getData: 2
MyLog ======>           com.wen.aidlclient                   D  setData: 3
MyLog ======>           com.wen.aidlclient                   D  getData: 3
MyLog ======>           com.wen.aidlclient                   D  setData: 4
MyLog ======>           com.wen.aidlclient                   D  setData: 5
MyLog ======>           com.wen.aidlclient                   D  getData: 5

接下来,如果你想往服务端传自定义对象的话。需要在服务端增加一个实现了 Parcelable接口的对象(只支持Parcelable,不支持Serializable)。(定义在main/java代码中而不是在main/aidl)如下我定义了个 User 类:

package com.wen.aidlserver.model

import android.os.Parcel
import android.os.Parcelable

class User(var name: String?, var age: Int?) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readValue(Int::class.java.classLoader) as? Int
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeValue(age)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<User> {
        override fun createFromParcel(parcel: Parcel): User {
            return User(parcel)
        }

        override fun newArray(size: Int): Array<User?> {
            return arrayOfNulls(size)
        }
    }

    override fun toString(): String {
        return "name = $name , age = $age"
    }
}

然后根据User我们还需要在main/aidl文件夹下生成对应的aidl文件,注意包名要一致如(图9)

// User.aidl
package com.wen.aidlserver.model;
parcelable User;
图9

 IMyServer.aidl 增加两个方法。注意导包和参数前面加 in、out、inout。

// IMyServer.aidl
package com.wen.aidlserver;

// 自定义对象需要单独导包
import com.wen.aidlserver.model.User;

interface IMyServer {
    // 接收一个字符串参数
    void setData(String value);
    // 返回一个字符串
    String getData();

    // 接收一个 User 对象
    // 跟 java 参数不同的是前面加了个 in。有三个标识:in、out、inout。in:服务端可以接收参数,不可修改。out:服务端不可接收参数,可以修改。
    // inout:服务端可接收参数,也可修改。
    void setUser(in User user);

    // 返回一个 User 对象
    User getUser();
}
MyBinder 增加新增的两个方法实现:
private inner class MyBinder : IMyServer.Stub() {

    private var mData: String? = ""
    private var mUser: User? = null

    override fun setData(value: String?) {
        mData = value
    }

    override fun getData() = mData

    override fun setUser(user: User?) {
        mUser = user
    }

    override fun getUser() = mUser
}

运行项目,重新把 aidl 生成的 Java 文件同步到客户端。同时把 User 文件也拷贝到客户端。注意包名要一致!

稍微改下客户端的代码测试下新增的接口:

    private fun initView() {
        mViewBinding.tvGetData.setOnClickListener {
            Log.d("MyLog ======>", "getData: ${mMyServiceConnection.mServer?.user?.toString()}")
        }
        mViewBinding.tvSetData.setOnClickListener {
            val user = User("张三", 28)
            mMyServiceConnection.mServer?.user = user
            Log.d("MyLog ======>", "setData: ${user.toString()}")
        }
    }

执行结果如下:

MyLog ======>           com.wen.aidlclient                   D  getData: null
MyLog ======>           com.wen.aidlclient                   D  setData: name = 张三 , age = 28
MyLog ======>           com.wen.aidlclient                   D  getData: name = 张三 , age = 28

至此。服务端与客户端可实现自定义数据传输了。

当然 aidl 也是可以传递接口回调的。接下来我们简单定义一个 IMyServerlistener.aidl 文件。

// IMyServerlistener.aidl
package com.wen.aidlserver;

interface IMyServerlistener {
    void onCallback(String str);
}

然后在 IMyServer.aidl 中增加一个 setListener方法。记得导包(import com.wen.aidlserver.IMyServerlistener;)。如下:

// IMyServer.aidl
package com.wen.aidlserver;

// 自定义对象需要单独导包
import com.wen.aidlserver.model.User;
import com.wen.aidlserver.IMyServerlistener;

interface IMyServer {
    // 接收一个字符串参数
    void setData(String value);
    // 返回一个字符串
    String getData();

    // 接收一个 User 对象
    // 跟 java 参数不同的是前面加了个 in。有三个标识:in、out、inout。in:服务端可以接收参数,不可修改。out:服务端不可接收参数,可以修改。
    // inout:服务端可接收参数,也可修改。
    void setUser(in User user);

    // 返回一个 User 对象
    User getUser();

    // 接收一个 aidl 接口
    void setListener(in IMyServerlistener listener);
}

在 MyBinder 中增加 setListener 的实现。为了方便演示,我直接回调了个字符串。

    private inner class MyBinder : IMyServer.Stub() {

        private var mData: String? = ""
        private var mUser: User? = null

        override fun setData(value: String?) {
            mData = value
        }

        override fun getData() = mData

        override fun setUser(user: User?) {
            mUser = user
        }

        override fun getUser() = mUser

        override fun setListener(listener: IMyServerlistener?) {
            listener?.onCallback("MyService callback")
        }
    }

编译项目,再次把(build/generated)下 aidl 生成的java文件拷贝同步到客户端。

在客户端界面中增加一个按钮点击事件:

mViewBinding.tvSetListener.setOnClickListener {
    mMyServiceConnection.mServer?.setListener(object : IMyServerlistener.Stub() {
        override fun onCallback(str: String?) {
            Log.d("MyLog ======>", "onCallback: $str")
        }
    })
}

客户端中点击结果:

MyLog ======>           com.wen.aidlclient                   D  onCallback: MyService callback

至此,一个简单的AIDL跨APP进程双向通讯已经完成了。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要调用AIDL接口,需要完成以下步骤: 1.定义aidl文件:首先需要定义一个aidl文件来描述服务提供的接口。该文件应该定义在服务和客户端都可以访问的位置。 2.实现aidl接口:在服务端中实现aidl接口,实现aidl接口中的方法,并将其添加到服务类中。 3.绑定服务:在客户端中,通过bindService()方法绑定服务,以获取服务的接口。 4.调用aidl接口:一旦客户端获取了服务的接口,就可以使用该接口来调用服务中的方法。 以下是一个简单的示例: 1.定义aidl文件 在服务端中创建一个名为"IMyService.aidl"的文件,其中包含以下代码: ``` interface IMyService { void doSomething(); } ``` 2.实现aidl接口 在服务端中创建一个名为"MyService"的类,并实现该接口: ``` public class MyService extends Service { private final IMyService.Stub mBinder = new IMyService.Stub() { @Override public void doSomething() { // 实现具体的功能 } }; @Override public IBinder onBind(Intent intent) { return mBinder; } } ``` 3.绑定服务 在客户端中,通过bindService()方法绑定服务: ``` private IMyService mService; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IMyService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; } }; Intent intent = new Intent(this, MyService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); ``` 4.调用aidl接口 一旦客户端获取了服务的接口,就可以使用该接口来调用服务中的方法: ``` mService.doSomething(); ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值