Android Service--AIDL

本文介绍了AIDL(Android接口定义语言)实现跨进程通信的方法。先说明了传统方法无法实现跨进程数据共享,引出AIDL。接着阐述创建相关AIDL文件及生成对应Java文件的步骤,还分析了Stub类及代理类Proxy,最后总结AIDL可跨进程通信,要注意语法参数、线程安全和避免耗时操作。

项目地址:项目地址包含之前的内容

上个文章介绍了IBinder和Messenger的使用。而且也说了,Messenger底层也是使用了AIDL。下面笔记一下AIDL。
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。相信这个解释,可以在很多文章中看到。所以也就知道为啥不在家.java文件,而是.aidl格式的文件。其中语法呢,大家可以去了解一下,不过和java大同小异。

下面有一个场景,咱们现在有很多的书,Book类,书有名字。我现在需要一个BookManager类来管理这些书,如:获取所有的书籍、添加一本书、根据一个名字找到一本书。
正常情况下,会分为两步

  1. 建一个Book类
public class Book  {
    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "book name:" + name;
    }
}
  1. 建一个BookManager类
public class BookManager{
{
        private List<Book> bookList=new ArrayList();
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

        
        public void addBook(Book book) throws RemoteException {
            bookList.add(book);
        }

       
        public Book getBookByName(String name) throws RemoteException {
            if (bookList.size()==0){
                return null;
            }

            for (Book b : bookList) {
                if (b.getName().equals(name)){
                    return b;
                }
            }
            return null;
        }

    };
}

通过这两步咱们就可以进行控制,Book的一些内容了。
但是,这种传统的方法并不能提供夸进程的数据共享。所以呢就需要借助AIDL了。好,这个时候我们认为,AIDL能够实现夸进程。那我们把上面两个文件变成对应的AIDL文件不就好了。

  1. 新建一个要处理的数据类,如:Book.java。
  2. 新建一个AIDL,接口名字和要处理的数据类保持一致。如:Book.aidl。
  3. 新建一个操作的AIDL,会定义一些具体的操作方法。如:BookManager.aidl。
    通过上面三步,我们建好了,对应的文件,但是真正运行的时候应用只能加载java文件,怎么办呢,所以我们会有第一步,先创建好要处理的数据模型类。Book.aidl会和Book.java对应。
    现在有了,程序可以加载的Book.java,但是没有BookManager.java,接下来就要看BookManager.aidl的了,AS会自动帮我们生成对应的java文件。
    看代码:
    Book类
package com.example.study.entity;

import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "book name:" + name;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
    }

    public void readFromParcel(Parcel dest) {
        name = dest.readString();
    }

    protected Book(Parcel in) {
        this.name = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };


}

进程间传递数据需要序列化,所以这里实现了Parcelable 接口。

Book.aidl

// com.example.com.example.study.entity.Book.aidl

package com.example.study.entity;
parcelable Book;
// Declare any non-default types here with import statements

指定了序列化的类。切记,这里面就这么多内容,其它自动生成部分删除掉。

BookManager.aidl

// BookManager.aidl
package com.example.study;

// Declare any non-default types here with import statements
import com.example.study.entity.Book;
interface BookManager {

        List<com.example.study.entity.Book> getBookList();

        void addBook(in com.example.study.entity.Book book);
        com.example.study.entity.Book getBookByName(in String name);

}

aidl默认的目录和java同级,包名就是项目的包名。需要注意,Book.java和Book.aidl的他们的包名必须保持一致

当build一下,就会出现一个BookManager.java文件,目录在
…\app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out\com\example\study\BookManager.java

package com.example.study;
public interface BookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
    ....
    }

    public java.util.List<com.example.study.entity.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.example.study.entity.Book book) throws android.os.RemoteException;

    public com.example.study.entity.Book getBookByName(String name) throws android.os.RemoteException;
}

可以看到生成了一个对应的BookManager接口,定义的方法都在,多了一个Stub类,这个类继承了Binder,实现了BookManager接口。至于Stub类,先不管它,根据方法再去看。

  public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
        private static final String DESCRIPTOR = "com.example.study.BookManager";

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

        /**
         * Cast an IBinder object into an com.example.study.BookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.study.BookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.study.BookManager))) {
                return ((com.example.study.BookManager) iin);
            }
            return new com.example.study.BookManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
        ....
        private static class Proxy implements com.example.study.BookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            ......
         }
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_getBookByName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

建一个AIDLIService

public class AIDLService extends BaseService {

    private List<Book> bookList;

    @Override
    public void onCreate() {
        super.onCreate();
        bookList = new ArrayList<>();
    }

    @Override
    public IBinder onBind(Intent intent) {
        super.onBind(intent);
        return stub.asBinder();
    }

    final BookManager.Stub stub = new BookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            bookList.add(book);
        }

        @Override
        public Book getBookByName(String name) throws RemoteException {
            if (bookList.size()==0){
                return null;
            }

            for (Book b : bookList) {
                if (b.getName().equals(name)){
                    return b;
                }
            }
            return null;
        }

    };

}

Service中我们实例化了一个stub对象。在onBind的时候返回了stub.asBinder(),这个方法有没有很熟悉的感觉,Messenger.getBinder(),最终调用的是IMessenger的asBinder方法。所以说Messenger是AIDL的一种封装,这里也可以返回stub因为他继承了Binder。

Activity中:

	/**
     *     书本管理对象
     */
    private BookManager manager;
    ...
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        //建立连接
        conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                isBind = true;
                Log.i("life", "ServiceConnection-----onServiceConnected");
                //得到接口对象
               manager= BookManager.Stub.asInterface(service);

            }
            ...
        };
    }
    		...
    		//点击事件
    		case R.id.bt_1:
    			try {
                    List<Book> bookList = manager.getBookList();
                    tv.setText(bookList.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                break;
            case R.id.bt_2:
                try {
                    manager.addBook(new Book("归隐"));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.bt_3:
                try {
                    Book book = manager.getBookByName("归隐");
                    tv.setText(book.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
                ....

ok,到这里就完成了夸进程通信了,当然不夸进程这种方式也能进行通信。
这里是在本应用内直接使用的,所以没有进行拷贝之类的操作。
如果新建一个应用的话,只需要将Book.java,和生成后的BookManager.java拷贝过去就行了。切记包名和原来保持一致。

Stub浅析

首先我们看一下,调用了Stub的那些方法:

  • stub.asBinder(),(其实就是stub自己)
  • BookManager.Stub.asInterface(service)
    我们只用了上面两个方法,先看一下第一个
  public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
        ...
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
        ...

可以看出,返回的就是它本身。而且Stub是一个抽象类,并没有实现BookManager的方法。
第二个方法

 public static abstract class Stub extends android.os.Binder implements com.example.study.BookManager {
 		....
        /**
         * Cast an IBinder object into an com.example.study.BookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.study.BookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //得到IBinder上的接口对象,这里是BookManager
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.study.BookManager))) {
                return ((com.example.study.BookManager) iin);
            }
            //重新实例化一个
            return new com.example.study.BookManager.Stub.Proxy(obj);
        }

先通过IBinder去得到BookManager接口对象,如果没有实例化一个Proxy对象,看下Proxy

private static class Proxy implements com.example.study.BookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            ....

代理类Proxy实现了BookManager接口,里面有个成员变量IBinder。并且实现了BookManager的所有方法。也就是说真正的方法实现还是Proxy中,那么Proxy的方法是怎么被回调的呢?
getBookList方法为例

			@Override
            public java.util.List<com.example.study.entity.Book> getBookList() throws android.os.RemoteException {
            	//客户端请求参数
                android.os.Parcel _data = android.os.Parcel.obtain();
                //服务端返回的数据
                android.os.Parcel _reply = android.os.Parcel.obtain();
                //返回结果
                java.util.List<com.example.study.entity.Book> _result;
                try {
                	//设置Token确实是哪个接口
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //发送RPC请求
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    //获取返回结果
                    _result = _reply.createTypedArrayList(com.example.study.entity.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

IBinder机制发送RPC请求会调用到服务端的Stub 的onTransact

 		@Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
   				...
                case TRANSACTION_getBookList: {
                    data.enforceInterface(descriptor);
                    //调用service的方法,并返回结果
                    java.util.List<com.example.study.entity.Book> _result = this.getBookList();
                    reply.writeNoException();
                    //序列化结果
                    reply.writeTypedList(_result);
                    return true;
                }
                ....

Stub的this.getBookList()方法,我们在AIDLService中已经实现了。返回结果后交给reply进行序列化。
其中code是用来确定调用哪个方法的,毕竟方法有很多。

总结 :
AIDL可以实现夸进程通信。
语法参数有in 、out、inout,三种。
多任务并发执行,需要考虑线程安全问题。
避免耗时操作,阻塞UI线程。

<think>好的,我现在需要解决用户提到的Android硬件GNSS AIDL服务崩溃的问题,具体错误是“qti agnssServiceDied IAGnss AIDL service died”。首先,我需要了解这个错误发生的背景和可能的原因。 根据用户提供的引用信息,尤其是引用[1]中提到的关于HIDL和VINTF清单的内容,我推测这个问题可能与HIDL服务的管理或VINTF清单的配置有关。可能涉及到服务注册、Binder通信或者依赖库的问题。此外,引用[2]提到了GnssLocationProvider的native方法,可能涉及到GNSS服务的底层实现或JNI调用问题。 接下来,我需要考虑常见的导致AIDL服务崩溃的原因,比如服务未正确注册、Binder通信超时、权限问题、资源泄漏或底层驱动故障。同时,用户提到的错误信息中提到了“qti agnssServiceDied”,这可能表明服务在运行时发生了未处理的异常,或者依赖的组件未能正确初始化。 根据引用[1]中的VINTF清单相关错误,还需要检查Android.bp或相关构建文件是否正确引用了VNDK库,避免类似“extends属性引用非VNDK库”的错误。此外,查看日志中的堆栈跟踪信息可以帮助定位崩溃的具体位置,比如是否是Binder事务失败、内存溢出或权限不足。 可能的解决步骤包括: 1. 检查服务注册:确认AIDL服务在manifest中正确声明,并且服务类正确继承自生成的Stub类。 2. 查看日志:通过adb logcat获取详细错误日志,过滤关键字如“IAGnss”、“AIDL”、“Died”等,确定崩溃点。 3. 权限检查:确保服务需要的权限(如位置权限、Binder权限)已在AndroidManifest.xml中声明。 4. 依赖库验证:确保所有依赖的HIDL或AIDL接口库版本兼容,VINTF清单正确配置。 5. 资源管理:检查是否有内存泄漏或资源未释放的情况,尤其是在JNI层或原生代码中。 6. 更新驱动或固件:如果是硬件相关的问题,可能需要更新GNSS模块的驱动或固件。 另外,用户提到的错误可能涉及到供应商特定的实现(如qti),因此需要参考供应商提供的文档或支持资源,确认是否有已知的问题或补丁。如果服务崩溃发生在回调方法中,可能需要确保回调接口的实现是线程安全的,并且正确处理了异常情况。 最后,建议用户尝试复现问题,并在崩溃时收集完整的logcat输出,以便更精确地定位问题根源。如果问题依然存在,可能需要深入调试服务的生命周期或与底层HAL层的交互。</think>### IAGnss AIDL服务崩溃的解决方案 #### 1. **检查服务注册与绑定逻辑** - **服务声明**:确保AIDL服务在`AndroidManifest.xml`中正确注册,例如: ```xml <service android:name=".QtiAGnssAidlService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.hardware.gnss.IAGnss/default"/> </intent-filter> </service> ``` 若未正确声明服务路径或接口名称,会导致服务无法被系统发现[^1]。 - **Binder连接**:在客户端绑定服务时,需检查`ServiceConnection`是否正确处理绑定状态: ```java private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iAGnss = IAGnss.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { iAGnss = null; // 触发重连或错误处理 } }; ``` #### 2. **分析日志定位崩溃点** - 通过`adb logcat`过滤关键字(如`IAGnss`、`AIDL`、`Died`),例如: ``` adb logcat | grep -E 'IAGnss|AIDL|Binder|Died' ``` - **常见错误类型**: - **Binder事务失败**:若日志显示`DeadObjectException`,表明Binder通信中断,可能是服务端崩溃或未响应。 - **权限不足**:检查是否缺少`android.permission.ACCESS_FINE_LOCATION`或`android.permission.LOCATION_HARDWARE`权限[^2]。 - **原生层崩溃**:若日志包含`signal 11 (SIGSEGV)`,可能是JNI或HAL层内存越界或空指针。 #### 3. **验证VINTF清单与依赖库** - 在`Android.bp`或`Android.mk`中,确保依赖的库(如`libhidltransport`)标记为VNDK,避免构建错误: ```makefile vndk: { enabled: true, } ``` 引用非VNDK库会导致服务无法在供应商分区加载。 #### 4. **服务端代码健壮性检查** - **异常捕获**:在AIDL接口实现中,添加`try-catch`块处理潜在异常: ```java @Override public void setCallback(IAGnssCallback callback) throws RemoteException { try { // 业务逻辑 } catch (Exception e) { Log.e(TAG, "setCallback failed", e); } } ``` - **资源释放**:若涉及JNI层操作(如GNSS天线信息获取),需确保及时释放内存: ```cpp extern "C" JNIEXPORT void JNICALL Java_com_example_GnssNative_releaseResources(JNIEnv* env, jobject thiz) { if (gnssAntennaInfo != nullptr) { delete gnssAntennaInfo; gnssAntennaInfo = nullptr; } } ``` #### 5. **更新驱动与固件** - 联系硬件厂商确认GNSS模块固件版本,并更新到最新版本。部分崩溃可能由芯片驱动缺陷引起。 #### 6. **调试工具辅助** - 使用`ndk-stack`分析原生层崩溃: ``` adb logcat | ndk-stack -sym /path/to/so ``` - 启用AIDL详细日志: ``` adb shell setprop log.tag.Aidl V ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值