Android AIDL的简单使用

文章目录

    • 一、引言
    • 二、正文

一、引言

最近在学习巩固Framework的一些东西,学完BInder后接触到AIDL,尝试简单使用一下。期间也遇到一些问题,但都解决了,在此记录一下。
本篇博客可以了解到:
1.AIDL IPC的简单使用,本demo使用同一项目下的两个Module代表C端和S端,C端简单调用S端服务
2.了解AIDL生成的java文件内容
3.解决C端bindService失败的问题

二、正文

前提:
创建两个模块作为两个进程:
Android AIDL的简单使用_第1张图片

1、S端创建AIDL文件

// IMyAidlInterface.aidl
package cn.edu.swu.server;

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

interface IMyAidlInterface {

     //业务代码
     String getName(int seq);

}

2、编译生成java文件

在目录build\generated\aidl_source_output_dir\debug\out\cn\edu\swu\kttest\IMyAidlInterface.java

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package cn.edu.swu.server;
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * Default implementation for IMyAidlInterface.
     */
    public static class Default implements cn.edu.swu.server.IMyAidlInterface {
        //业务代码

        @Override
        public java.lang.String getName(int seq) 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 cn.edu.swu.server.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "cn.edu.swu.server.IMyAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an cn.edu.swu.server.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static cn.edu.swu.server.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof cn.edu.swu.server.IMyAidlInterface))) {
                return ((cn.edu.swu.server.IMyAidlInterface) iin);
            }
            return new cn.edu.swu.server.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_getName: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    java.lang.String _result = this.getName(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements cn.edu.swu.server.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 getName(int seq) 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);
                    _data.writeInt(seq);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getName(seq);
                    }
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static cn.edu.swu.server.IMyAidlInterface sDefaultImpl;
        }

        static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        public static boolean setDefaultImpl(cn.edu.swu.server.IMyAidlInterface impl) {
            // Only one user of this interface can use this function
            // at a time. This is a heuristic to detect if two different
            // users in the same process use this function.
            if (Stub.Proxy.sDefaultImpl != null) {
                throw new IllegalStateException("setDefaultImpl() called twice");
            }
            if (impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static cn.edu.swu.server.IMyAidlInterface getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }
    //业务代码

    public java.lang.String getName(int seq) throws android.os.RemoteException;
}

1、IMyAidlInterface接口

它声明了IMyAidlInterface.aidl定义的getName方法,继承自IInterface。

2、IMyAidlInterface.Stub抽象类

IMyAidlInterface的静态内部类,它继承自Binder,说明它是一个 Binder 本地对象,它虽然实现了IMyAidlInterface接口,但是它继续声明为一个抽象类,并没有实现IMyAidlInterface接口中的getName方法,表明子类需要实现getName方法,返回具体的信息,而服务端将会实现这个Stub抽象类

3、IMyAidlInterface.Stub.Proxy代理类

IMyAidlInterface.stub的静态内部类,它实现了IMyAidlInterface接口,并且实现了getName方法,但是里面只是把数据装进data这个Parcel对象,通过mRemote的transact方法发送给服务端,接着用reply这个Parcel对象等待服务端数据的返回,这一切都是通过mRemote这个IBinder对象进行,mRemote代表着Binder对象的本地代理,mRemote会通过Binder驱动来完成与远程服务端的Stub的通信。

可以看到Stub类和Stub.Proxy类都实现了IMyAidlInterface接口,这就是一个典型的代理模式,它们的getName方法有着不同的实现,Stub类它将会在远程的服务端完成getName方法的具体实现,而Stub.Proxy类是本地客户端的一个代理类,它已经替我们默认的实现了getName方法,该方法里面通过mRemote这个Binder引用的transact方法把请求通过驱动发送给服务端,我们注意到mRemote发送请求时还传进了TRANSACTION_getName这个代表着getName方法的标识名,这表示客户端告诉服务端我要调用getName这个方法,当驱动把请求转发给服务端后,服务端的Stub类的onTransact方法就会回调,它里面有一个switch语句,根据code来调用不同的方法,这时它就会走到case TRANSACTION_getName这个分支,然后调用getName方法的在服务端的具体实现,如果有返回值的话,还会通过reply返回给客户端,这样就通过Binder驱动完成了一次远程方法调用(RPC)。

这里要注意的是客户端通过mRemote的transact方法把请求发送给客户端之后,这时会阻塞UI线程等待服务端的返回,而服务端的onTransact方法回调时,服务端的getUser方法会被回调,这时服务端的getName方法是运行在服务端Binder线程池中,所以如果此时有UI操作需要回到UI线程再进行UI操作。

IInterface

这是一个接口,用来表示服务端提供了哪些服务,如果服务端需要暴露调用服务的方法给客户端使用,就一定要继承这个接口,它里面有个asBinder方法,用于返回当前的Binder对象。

IBinder

这时一个跨进程通信的Base接口,它声明了跨进程通信需要实现的一系列抽象方法,实现了这个接口就说明可以进行跨进程通信,Binder和BinderProxy都继承了这个接口。

Binder

代表的是 Binder 本地对象(Binder实体),它继承自IBinder,所有本地对象都要继承Binder,Binder中有一个内部类BinderProxy,它也继承自IBinder,它代表着Binder对象的本地代理(Binder引用),Binder实体只存在于服务端,而Binder引用则遍布于各个客户端。

3、配置服务端

package cn.edu.swu.server

import android.app.Service
import android.content.Intent
import android.os.IBinder

class RemoteService : Service() {

    private val mBinder = object :IMyAidlInterface.Stub() {
        override fun getName(seq: Int): String {
            return "server"
        }
    }

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

}
<service android:name=".RemoteService">
    <intent-filter>
        <action android:name="cn.edu.swu.server.remote"/>
    intent-filter>
service>

4、客户端调用

复制S端AIDL目录下内容到C端,注意包名一样

package cn.edu.swu.aidltest

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.widget.TextView
import cn.edu.swu.server.IMyAidlInterface

class MainActivity : AppCompatActivity() {

    private lateinit var mService : IMyAidlInterface

    //1、创建ServiceConnection
    private val serviceConnection = object :ServiceConnection {
        override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
            Log.d("tag", "connect server success!")

            //把服务端返回的Binder引用,通过Stub.asInterface方法包装成本地代理类IMyAidlInterface.Stub.Proxy
            mService = IMyAidlInterface.Stub.asInterface(p1)

            //通过本地代理对象远程调用服务端的方法
            Log.d("tag", mService.getName(0))

        }

        override fun onServiceDisconnected(p0: ComponentName?) {
            Log.d("tag", "connect server failed!")
        }
    }


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

        //2、启动并通过ServiceConnection绑定远程服务
        val intent = Intent("cn.edu.swu.server.remote")
        intent.setPackage("cn.edu.swu.server")
        val res = applicationContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)

        Log.d("tag", res.toString())

    }

    override fun onDestroy() {
        super.onDestroy()

        //3、解绑服务
        unbindService(serviceConnection)

    }

}

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.edu.swu.aidltest">

    <queries>
        
        
        <package android:name="cn.edu.swu.server" />
    queries>

    <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.AidlTest">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>
        activity>
    application>

manifest>

Log:

2022-05-23 11:22:44.749 12084-12084/cn.edu.swu.aidltest D/tag: true
2022-05-23 11:22:45.479 12084-12084/cn.edu.swu.aidltest D/tag: connect server success!
2022-05-23 11:22:45.482 12084-12084/cn.edu.swu.aidltest D/tag: server

ps:终于连上了,因为SdkVersion问题,bindService返回false,找了半天的bug

注意:

  1. S端aidl目录需要和C端aidl目录相同(直接找到目录复制即可)
  2. C端调用时注意包名和ApplicationID区别

感谢:Android 解决AIDL bindService异常_看我大华夏的博客-CSDN博客_aidl bindservice失败

  1. targetSdkVersion高于30时,需要在C端manifest中注册使用的包名

感谢:AIDL报错,bindService一直连接不上、不起作用。_马占柱的博客-CSDN博客_aidl bindservice失败

你可能感兴趣的:(Android,android,java,android,studio)