Android系统中的Binder通信机制分析(7)- Java 层的 Binder 机制

news2024/12/26 23:30:53

声明

  • 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾…
  • 文中参考了很多书籍及博客内容,可能涉及的比较多先不具体列出来了;
  • 本文使用的代码是LineageOS的cm-14.1,对应Android 7.1.2,可以参考我的另一篇博客:cm-14.1 Android系统启动过程分析(1)-如何下载Nexus5的LineageOS14.1(cm-14.1)系统源码并编译、刷机

1 Java 层的 Binder 框架

  Java框架库中提供了一套 Binder 相关的类,让Java 代码可以直接使用 Binder 构建IPC。其中主要的类在 core/java/android/os/ 目录中,是 android.os 包的一部分。除此之外 core/java/com/android/internal/os/ 目录提供了 com.android.internal.os 包中的 BinderIntermal 类,它是 Binder 的一个支持类。
  Java层 Binder 支持的内容来自本地,通过JNI封装到 Java 层。Java 层的 Binder 和本地层的 Binder 实现的基础相同。

1.1 Binder 机制相关的类

  Binder 主要的 API 在 android.os 包中,它们是:IInterface 接口、IBinder 接口、Binder 类、Parcel 类。
lInterface 是一个接口,由实现者赋予它实际的方法。其中只有方法:

             static sp<IBinder>  asBinder(const IInterface*);
             static sp<IBinder>  asBinder(const sp<IInterface>&);

  asBinder() 方法用于将一个 IInterface 转化成一个 IBinder 类型。IBinder 也是一个接口,用于充当 IPC 两端通信的句柄,其中主要的内容是 transact() 方法,如下所示:

    virtual status_t        transact(   uint32_t code,
                                        const Parcel& data,
                                        Parcel* reply,
                                        uint32_t flags = 0) = 0;

  transact() 用于和远程的一个 Binder 通信,它包含4个参数为整数类型的通信码 code,Parcel 类型的参数data 和 返回值 reply,整数类型 flags。IBinder 中的其他几个重要的方法如下所示:

	virtual const String16& getInterfaceDescriptor() const = 0;
    virtual sp<IInterface>  queryLocalInterface(const String16& descriptor);
    virtual status_t        pingBinder() = 0;

  getInterfaceDescriptor() 用于获得此 Binder 对应的接口,queryLocalInterface() 用于获得这个Binder 对象所对应的 IInterface 的实现句柄。pingBinder() 用于查询这个 Binder 是否还存在。Binder 是实现 IBinder 接口的一个类,这个类依然是一个抽象类。主要在于 Binder 类中的onTransact() 方法是没有实现的,如下所示:

protected:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);

  IBinder.transact() 和 Binder.onTransact() 是具有对应关系的,在 IBinder.transact() 调用的方法,将从Binder.onTransact() 中返回,并且这是一个同步调用的方式。
在程序中,主要可以使用 IInterface 接口的实现者来表示一个具体的接口;不会直接继承 IBinder 接口,继承 Binder 抽象类。

  Parcel 是一个用于封装传输数据的类。Parcel 类中支持的内容,也就是使用 Binder 在进程间通信的时候能够使用的参数和返回值的类型。Parcel 中支持基本类型,字符串类型Binder 对象类型,Parcelable 类型,还有各种类型的数组。关于Parcel细节可参考:Android系统中的Binder通信机制分析(5)- 进程间数据传递载体 Parcel
  对于一个IInterface 的实现者,可以调用其中的 asBinder(),将其转化成 IBinder 类型就可以通过 Parcel 传递了。实际上 Parcel 中一个名称为 writeStrongInterface() 就是利用这种方法,并通过调用 writeStrongBinder() 来实现的

1.2 aidl描述文件

  AIDL 的全称是 Android Interface Definition Language (Android 接口描述语言)。IDL的作用就是使用特殊的语法来描述接口,接口是方法(函数)的集合。
  Android 中的 aidl 工具,可以根据 aidl 文件来自动生成 Java 代码文件,内容为一个具体使用 Binder 进行 IPC 的框架
在Java层使用 Binder 机制构建一个具体的通信框架的基本过程为:

  1. 使用aidl文件声明接口 IXXX,并加编译;
  2. 在服务段通过继承 IXXX.Stub 来实现真正的功能;
  3. 在客户端通过 IXXXStub.asInterface() 来获得 IXXX 类型的接口,并调用。

  在程序中,具体实现需定义接口 IXXX.aidl 。IXXX.aidl 加入编译之后,将会被 Android 中的 aidl 工具处理,自动生成一个名称为 XXX.java 的文件。其中包括了 IXXX 接口、IXXXStub 抽象类,它们就是具体 Binder 结构的框架。
  由于具有自动生成机制,具体 Binder 的框架只需要写一个比较简单的 aidl 文件,而不需要再编写通过 Binder IPC 进行通信的代码细节。对于程序的开发者,需要做的事情有三件:aidl文件、服务端继承 IXXX.Stub、实现客户端通过 IXX.Stub.asInterface() 得到 IXXX 类型调用。自动生成的 IXXX.Stub 是一个 Binder 类的继承者。

Binder 和 aidl 结合的使用方式如下图所示:
在这里插入图片描述
  它表示 aidl 文件中语法格式和一个 Java 接口的定义。但是它表示的不是一个简单的接口,而是具有很多附加的内容。例如,一个名称为 IAidITest.aidl 的文件的片断如下所示:

package android.os;				//接口所属于的包
import android.os.AidlTest;		//需要导入的类
interface IAidlTest {
//各个方法的集合
	int intMethod(int a);
	boolean[] booleanArray(in boolean[] a0, out boolean[] al, inout boolean[] a2);
	char[] charArray(in char[] a0, out char[] al, inout char[] a2);
	String[] stringArray(in String[] a0, out String[] al, inout Stringl] a2);
	void voidSecurityException();
	int intSecurityException();
//省略部分内容
}

  interface 是 aidl 文件中一个关键字,用于定义其表示接口的名称,主要内容是各个方法的集合。除了参数和返回值的类型有所限制之外,aidl 中的语法和 Java 中的接口定义基本相同。in、out 和 inout 表示参数传递的方向。
  aidl 文件中另有一个 oneway 的关键字,附加在 interface 前面使用。它表示调用者不会等接口中方法的返回结果,因此使用 oneway 修饰接口中的方法的返回类都是 void。oneway通常用于表示 “回调” 功能的接口。
  在Android 源代码的开发环境中,aidl 文件需要手动加入 LOCAL_SRC_FILES 中才会被进行处理。编译系统根据 aidl 扩展名,将使用特殊的 aidl 工具对其进行处理,自动生成 Java 代码文件。如下所示:

LOCAL SRC FILES += \
		{path}/*.aidl \

1.3 框架层和应用层对Binder的使用

  Java 层基于 Binder 的通信机制既可以在 Java 框架层使用,也可以在 Java 应用层使用者都需要使用aidl 文件定义接口,区别在于服务器端和客户端的结构不同。在Java 框架层使用 Binder 典型的情况是:服务器端完成一个IXXXStub类之后,需要使用ServiceManager类将其增加成某个名称的一个服务;客户端同样通过 ServiceManager根据名称获得这个服务,转换成IXXX类型的接口,进行调用。
  在Java 应用程序层中使用 Binder 典型的情况是:服务器端声明一个 Service 组件,在其onBind0方法中,将一个IXXXStub 的继承者返回成IBinder类型;客户端的组件中通过bindService0连接到Service 组件后,可以从 ServiceConnection 接口(android.content包)的实现者中得到IBinder 的句柄,将其转化成IXXX类型,然后进行调用。
  以上的两种情况是 Binder 的典型使用情况,服务器和客户端都在框架层,或者都在应用层。除此之外,还有一些特殊情况。
Java 框架层可以定义 Service 组件,在资源包的AndroidMenifest.xml 文件中对其声明即可。如果客户端调用框架层的 Service 组件,与典型应用程序层的调用方式基本相同。Java 框架层定义的aidl 方式的接口后,通常在 Java 服务包中实现,应用程序包中可以直接引用 aidl 文件,直接调用框架层中的服务实现。这种方式可以执行,但是并不常用。

2 Java框架层的Binder使用

2.1 ServiceManager和服务的注册

  Java 框架层使用 Binder 的特点是需要通过 ServiceManager 进行服务的注册和获取。其中服务的名字是客户端和服务器端联系的纽带。
ServiceManager 类是 android.os 中的一个内部类,代码位置:frameworks/base/core/java/android/os/ServiceManager.java

public final class ServiceManager {
    private static final String TAG = "ServiceManager";

    private static IServiceManager sServiceManager;
    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();

    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }
	//获得服务
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

	//增加服务
    public static void addService(String name, IBinder service) {
        try {
            getIServiceManager().addService(name, service, false);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

    public static void addService(String name, IBinder service, boolean allowIsolated) {
        try {
            getIServiceManager().addService(name, service, allowIsolated);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

    //检查服务
    public static IBinder checkService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().checkService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in checkService", e);
            return null;
        }
    }

    //列出服务
    public static String[] listServices() {
        try {
            return getIServiceManager().listServices();
        } catch (RemoteException e) {
            Log.e(TAG, "error in listServices", e);
            return null;
        }
    }

    public static void initServiceCache(Map<String, IBinder> cache) {
        if (sCache.size() != 0) {
            throw new IllegalStateException("setServiceCache may only be called once");
        }
        sCache.putAll(cache);
    }
}

  ServiceManager 类当中包含了一些静态的方法。其中所谓的服务是 IBinder 类型,通常情况下就是一个 IXXX.Stub 类型的继承者,表示一个服务的实现。
  通常情况下,服务器端使用 addService() 增加服务,客户端使用 getService() 得到服务并使用。服务端通常是服务库实现并注册的。一般情况下,一个服务也就是 Java 服务库 com.android.server 包中的一个类。
  Java 层的 ServiceManager 类实际上是基于本地层的服务管理器实现的,其中注册的内容统一被本地的 servicemanager 守护进程管理。

2.2 Binder的构建细节

  Java 框架层中使用 Binder 的主体内容是 aidl 文件、服务端和客户端3 个部分,再加上服务的增加和获得两个环境。
Binder的使用细节以及aidl 文件的处理和自动类的生成如图 7-4 所示。
在这里插入图片描述
  图中虚线中的内容是 Android 框架层已经有的类,IInterface 和 IBinder 是两个接口,Binder 是实现了IBinder 接口的抽象类。
  首先需要写一个名称为 IXXX.aidl 的接口描述文件,其中定义了一系列的方法。这个 aidl 文件被处理之后,将自动生成 IXXX.java 文件。IXXX.java 文件中的 IXXX 是一个接口,这个接口实现了 IInterface,并且包含了 IXXX 中定义的方法;IXXX.Stub 是一个抽象类,一方面它继承了 Binder 类,另一方面它实现了 IXXX 接口,但是 IXXX 中的方法并没有实现。
  服务器端需要提供一个类,继承 IXXX.Stub,并实现 IXXX 中定义的各个方法。因此这个类实际上也间接实现 IBinder 接口,可以通过 ServiceManager 的 addService() 方法将这个类的实例注册成某个名称的服务。
  客户端需要通过 ServiceManager 的 getService() 方法根据名称获得 IBinder 的句柄,然后通过IXXX.Stub 中的静态方法 asInterface() 将这个 IBinder 转化成 IXXX 类型,就可以调用其中的方法。
  以Java 框架层中的 AlarmManagerService 为例,服务器端是服务库的一部分,而客户端是框架层的一部分。其接口的定义在框架层中,文件的路径是 frameworks/base/core/java/android/app/IAlarmManager.aidl

package android.app;

import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.WorkSource;

/**
 * System private API for talking with the alarm manager service.
 *
 * {@hide}
 */
interface IAlarmManager {
    /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
    void set(String callingPackage, int type, long triggerAtTime, long windowLength,
            long interval, int flags, in PendingIntent operation, in IAlarmListener listener,
            String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock);
    boolean setTime(long millis);
    void setTimeZone(String zone);
    void remove(in PendingIntent operation, in IAlarmListener listener);
    long getNextWakeFromIdleTime();
    AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
    // update the uids being synchronized by network socket request manager
    void updateBlockedUids(int uid, boolean isBlocked);
}

  这里的内容定义了名称为 IAlarmManager 的接口,它位于android.app 包当中。注意!这不是一个简单的 Java 中的接口,它将生成复杂的内容。
服务端的实现在服务器包中,路径为:frameworks/base/services/core/java/com/android/server/AlarmManagerService.java

class AlarmManagerService extends SystemService {
......
    private final IBinder mService = new IAlarmManager.Stub() {
	......
	        @Override
        public boolean setTime(long millis) {
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME",
                    "setTime");

            if (mNativeData == 0) {
                Slog.w(TAG, "Not setting time since no alarm driver is available.");
                return false;
            }

            synchronized (mLock) {
                return setKernelTime(mNativeData, millis) == 0;
            }
        }

        @Override
        public void setTimeZone(String tz) {
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME_ZONE",
                    "setTimeZone");

            final long oldId = Binder.clearCallingIdentity();
            try {
                setTimeZoneImpl(tz);
            } finally {
                Binder.restoreCallingIdentity(oldId);
            }
        }
	......
	}
......
}

  AlarmManagerService 中private final IBinder mService实现了 IAlarmManager.Stub 类,其中具有 AlarmManager.aidl 文件中定义的几个方法,这些方法是在 AarmManagerService 类中实现的。
  AlarmManagerService 的注册在 frameworks/base/services/java/com/android/server/SystemServer.java 文件中完成,如下所示:

            traceBeginAndSlog("StartAlarmManagerService");
            mSystemServiceManager.startService(AlarmManagerService.class);
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

  客户端方面是 Java 框架库的一部分,根据服务名称得到接口过程在core/java/android/app/目录的ContextImpl.java文件中,其中的内容片断如下所示:

AlarmManager mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);

  客户端调用 Context 的 getSystemService() 方法,通过 Context.ALARM_SERVICE 参数得到一个 AlarmManager 类型的句柄。客户端得到AlarmManager 类型的句柄后就可以调用其中的方法了。

  AlarmManager 实际上只是对 IAlarmManager 的一个封装。其中具有同名的方法,并通过调用 IAlarmManager 中的内容实现,AlarmManager 也是警报器管理器对外的 API。各个部分调用 AlarmManager 的时候,实际上就和服务库中实现 AlarmManagerService 产生了远程调用关系。
  以上的几个部分内容就是 Java 架层中使用 Binder 机制的所需要手动构建的所有内容。除此之外,根据IAlarmManager.adil 文件还将自动生成 Java 源代码文件,其路径中如下所示:

out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app/IAlarmManager.java

IAlarmManager.java 文件的基本结构如下所示:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: frameworks/base/core/java/android/app/IAlarmManager.aidl
 */
package android.app;
/**
 * System private API for talking with the alarm manager service.
 *
 * {@hide}
 */
public interface IAlarmManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.app.IAlarmManager
{
private static final java.lang.String DESCRIPTOR = "android.app.IAlarmManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an android.app.IAlarmManager interface,
 * generating a proxy if needed.
 */
public static android.app.IAlarmManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.app.IAlarmManager))) {
return ((android.app.IAlarmManager)iin);
}
return new android.app.IAlarmManager.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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_set:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
long _arg2;
_arg2 = data.readLong();
long _arg3;
_arg3 = data.readLong();
long _arg4;
_arg4 = data.readLong();
int _arg5;
_arg5 = data.readInt();
android.app.PendingIntent _arg6;
if ((0!=data.readInt())) {
_arg6 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
_arg6 = null;
}
android.app.IAlarmListener _arg7;
_arg7 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
java.lang.String _arg8;
_arg8 = data.readString();
android.os.WorkSource _arg9;
if ((0!=data.readInt())) {
_arg9 = android.os.WorkSource.CREATOR.createFromParcel(data);
}
else {
_arg9 = null;
}
android.app.AlarmManager.AlarmClockInfo _arg10;
if ((0!=data.readInt())) {
_arg10 = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(data);
}
else {
_arg10 = null;
}
this.set(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);
reply.writeNoException();
return true;
}
case TRANSACTION_setTime:
{
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
boolean _result = this.setTime(_arg0);
reply.writeNoException();
reply.writeInt(((_result)?(1):(0)));
return true;
}
case TRANSACTION_setTimeZone:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setTimeZone(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_remove:
{
data.enforceInterface(DESCRIPTOR);
android.app.PendingIntent _arg0;
if ((0!=data.readInt())) {
_arg0 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
android.app.IAlarmListener _arg1;
_arg1 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
this.remove(_arg0, _arg1);
reply.writeNoException();
return true;
}
case TRANSACTION_getNextWakeFromIdleTime:
{
data.enforceInterface(DESCRIPTOR);
long _result = this.getNextWakeFromIdleTime();
reply.writeNoException();
reply.writeLong(_result);
return true;
}
case TRANSACTION_getNextAlarmClock:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
android.app.AlarmManager.AlarmClockInfo _result = this.getNextAlarmClock(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.app.IAlarmManager
{
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;
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
@Override public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(callingPackage);
_data.writeInt(type);
_data.writeLong(triggerAtTime);
_data.writeLong(windowLength);
_data.writeLong(interval);
_data.writeInt(flags);
if ((operation!=null)) {
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
_data.writeString(listenerTag);
if ((workSource!=null)) {
_data.writeInt(1);
workSource.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
if ((alarmClock!=null)) {
_data.writeInt(1);
alarmClock.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_set, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public boolean setTime(long millis) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(millis);
mRemote.transact(Stub.TRANSACTION_setTime, _data, _reply, 0);
_reply.readException();
_result = (0!=_reply.readInt());
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setTimeZone(java.lang.String zone) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(zone);
mRemote.transact(Stub.TRANSACTION_setTimeZone, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void remove(android.app.PendingIntent operation, android.app.IAlarmListener listener) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((operation!=null)) {
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_remove, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public long getNextWakeFromIdleTime() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
long _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getNextWakeFromIdleTime, _data, _reply, 0);
_reply.readException();
_result = _reply.readLong();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.app.AlarmManager.AlarmClockInfo _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(userId);
mRemote.transact(Stub.TRANSACTION_getNextAlarmClock, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_set = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setTimeZone = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_remove = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_getNextWakeFromIdleTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getNextAlarmClock = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) throws android.os.RemoteException;
public boolean setTime(long millis) throws android.os.RemoteException;
public void setTimeZone(java.lang.String zone) throws android.os.RemoteException;
public void remove(android.app.PendingIntent operation, android.app.IAlarmListener listener) throws android.os.RemoteException;
public long getNextWakeFromIdleTime() throws android.os.RemoteException;
public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException;
}

IAlarmManager.java 文件定义的内容主要有以下的部分:

  • IAlarmManager 接口实现了 android.os.IInterface,并且声明了 IAlarmManager.aidl 中定义的 set()、setRepeating() 等方法。
  • IAlarmManager.Stub 是一个抽象的静态类,它继承自 android.os.Binder,并实现了 IAlarmManager 接口本身。
  • IAlarmManager.Stub 中有一个固定方法 asInterface(),参数类型为 IBinder,返回类型为 IAlarmListener。
  • IAlarmManager.Stub 类的内部具有众多的方法,实现了基于 Binder 机制的通信。自动生成文件的过程,有工具来完成,并不需要开发者处理。因此在 Java 层中使用 Binder 机制不需要处理最为复杂的通信部分。

3 Binder 中参数和返回值的类型

3.1 类型概述

  由于 Java 层具体 Binder 接口通常使用 aidl 文件来描述。其中接口定义的方法涉及参数和返回值类型问题。aidl 接口的方法并不能适用于所有 Java 类型,本质上是受到 Parcel 中能传输类型的限制。

aidl 中能够使用的类型就是以下的几个方面:

  • Java 语言的基本类型 (boolean、byte、char、short、int、float 和 double)
  • java.lang 中的 CharSequence 和 String
  • java.util 中的 List 和 Map<K, V>
  • 其他 aidl 文件中定义的接口(需要import)
  • 通过 Parcelable 实现的类(需要import)

  Java 基本类型是 Parcel 中可以直接支持的,可以作为 aidl 接口的方法的参数和返回值类型,不需要 import 导入。显然,Java 也可以使用它们的数组类型。
  CharSequence 和 String 是表示字符串的类,可以作为 aidl 接口的方法的参数和返回值类型,不需要import 导入。List 和 Map<K, V> 是容器类型的模板类,可以使用这两个模板封装其他的类型,作为 aidl 接口的方法的参数和返回值类型,不需要 import 导入。
  除了以上的几种情况,还可以使用其他 aidl 文件中定义的接口和通过 Parcelable 实现的类,这两种情况都是需要 import 类才能够使用的。

3.2 其他 aidl 文件中定义的接口作为类型

  aidl 文件中定义的接口在本质上将形成一个 IInterface 接口的实现者,并可以转化成 IBinder 类型来使用。因此使用“其他 aidl 文件中定义的接口”作为 aidl 接口的方法的参数和返回值类型的本质是使用 IBinder 类型在 Parcel 中进行传递。
  在 Binder 的跨进程通信中,如果需要使用类似回调方面的机制,通常就需要使用另一个 aidl 文件表示回调接口,并在主接口的 aidl 文件中作为方法的参数类型。
  例如,在 Java 框架库的代码中的frameworks/core/java/android/app/目录中,具有两个和墙纸管理器相关的 aidl 文件,一个表示主接口(IWallpaperManager.aidl),另一个表示回调接口(IWallpaperManagerCallback.aidl)。

package android.app;
oneway interface IWallpaperManagerCallback {
    /**
     * Called when the wallpaper has changed
     */
    void onWallpaperChanged();
}

IWallpaperManager.aidl对这个回调类型的引用的片断如下所示:

package android.app;

import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.app.IWallpaperManagerCallback;
import android.app.WallpaperInfo;
import android.content.ComponentName;

/** @hide */
interface IWallpaperManager {
......
    /**
     * Get the wallpaper for a given user.
     */
    ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, int which,
            out Bundle outParams, int userId);
......
}

  由于使用 IWallpaperManagerCallback 作为参数类型,因此需要使用 import 导入这个接口,导入的名称是包名加 aidl 接口的名称。
  IWallpaperManager 接口实际上是由 Java 服务库中的一个类实现的,这点和框架层中其他的 aidl 内容相融。IWallpaperManagerCallback 则是在 Java 框架库中实现( WallpaperManager.java ),并作为参数传递给 IWallpaperManager 接口。在实际的运行过程中,Java 服务库中 IWallpaperManager 的实现是通过 IWallpaperManagerCallback 调用 Java 框架库中实现的 onWallpaperChanged() 方法,起到了通知的作用。

3.3 Parcelable 协议作为类型

  另外一种可以作为 aidl 接口的方法的参数和返回值类型的内容是 “基于 Parcelable 协议的类”。 Parcelable 协议表示可以使用 Parcel 写入和恢复,实现了 Parcelable 接口就实现了 Parcelable 协议。

Parcelable 是 android.os 包中 Parcelable 接口,其基本的结构如下所示:

public interface Parcelable {

    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
    public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
    public int describeContents();
    public void writeToParcel(Parcel dest, int flags);		//	写入Parcel的方法

    public interface Creator<T> {							//表示“创建者”的子类
        public T createFromParcel(Parcel source);
        public T[] newArray(int size);
    }

    public interface ClassLoaderCreator<T> extends Creator<T> {
        public T createFromParcel(Parcel source, ClassLoader loader);
    }
}

让一个类支持 Parcelable 协议,主要具有以下的步骤:

  1. 类实现 Parcelable 接口。
  2. 使用 void writeToParcel(Parcel out)方法完成参数写操作。
  3. 通过实现 Parcelable.Creator 接口构建一个 Creator,实现静态的创建功能
  4. 可以创建一个 aidl 文件作为接口(可选)。

在实际的应用过程中,基于 Parcelable 协议的类通常用于传递一个表示复杂数据结构的类。一个实现的实例如下所示:

public final class Rect implements Parcelable {
	public int left;		//各个类的域
	public int top;
	public int right;
	public int bottom;

	public static final Parcelable.Creator<Rect> CREATOR 
									= new Parcelable.Creator<Rect>() {
		public Rect createFromParcel(Parcel in)(	//实现创建方法
			return new Rect(in);
		}
		
		public Rect[] newArray(int size) {			//实现建立数组方法
			return new Rect[sizel;
		}
	};
	
	public Rect() {}
	public void writeToParcel(Parcelout) {
		//依次写入各个成员
		out.writeInt(top);
		out.writeInt(left);
		out.writeInt(right);
		out.writeInt(bottom);
	}
	public void readFromParcel(Parcel in) {
		//依次读出各个成员
		left = in.readInt();
		top = in.readInt();
		right = in.readInt();
		bottom = in.readInt();
	}
}

  Rect 类的本质是一个表示矩形类,其中包含了 4 个整数类型的域(属性)。这个类的结构虽然并不复杂。但是如果希望在 Binder 传递的过程中传递这样一个类,显然 Parcel 并不能直接知道它当中要传递的具体内容是什么。让 Rect 实现 Parcelable 接口的目的,就是为了建立一个 Parcel 的协议,根据这个协议传递具体的数据结构。Parcelabl 协议的本质就是通过调用 writeToParcel() 方法,让实现 Parcelable 接口的类自已决定应该传递的内容是什么。
  Android 框架层中具有众多实现了 Parcelable 接口的类,它们可以直接在 aidl 文件中作为参数和返回值的类型使用
android.os 包中的 Bundle 类就是一个 Parcelable 接口的实现者,其结构如下所示:

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
......
	//实现Creator<>
    public static final Parcelable.Creator<Bundle> CREATOR =
        new Parcelable.Creator<Bundle>() {
        @Override
        public Bundle createFromParcel(Parcel in) {
            return in.readBundle();
        }

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

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
        try {
            super.writeToParcelInner(parcel, flags);
        } finally {
            parcel.restoreAllowFds(oldAllowFds);
        }
    }
......
}

为了让其他的类引用,为 Bundle 类建立一个 frameworks/base/core/java/android/os/Bundle.aidl 文件,内容如下所示:

package android.os;
parcelable Bundle;

  以上的内容表示了 android.os 包中的 Bundle 类是 Parcelable 的实现者。在 aidl 文件中类可以通过以下语句来导入 Bundle 作为其参数和返回值的类型:

import android.os.Bundle;

  由此,可以直接在 aidl 文件中使用 Android 框架层中实现了 Parcelable 接口的类在实际的开发过程中,真正需要构建一个实现 Parcelable 协议的类的情况并不多见。主要的原因是:

  • 对于复杂数据类型的传递,可以拆散成方法中的若干个参数;
  • 使用 Bundle、Uri、Bitmap 等已经实现了 Parcelable 的类,尤其 Bundle,可以通过键-值的方式包含任意多的信息,可以完成对复杂数据结构的描述。只有当程序中的确有一个比较特殊的 “数据结构” 需要单独描述的时候,才需要实现 Parcelable 协议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/595012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络】无线路由器的AP、Client、WDS、WISP使用功能图解(清晰明了)

1、有线路由器 有线路由器组成:一个有一个 WAN 口和一个 LAN 口的路由器、一个有几个 LAN 口的网络交换机,一个接在 LAN 口的 DHCP 服务器。如下图所示 2、无线路由器:AP模式 在路由器的 LAN 口上,接了一个无线交换机。这个无线交换机的无线插接口,就是这个无线路由器…

es7.x Es常用核心知识快捷版2 各种查询

一 常用查询 1.1 term查询 term关键字查询&#xff0c;精确匹配&#xff0c;不会对查询条件进行分词。 1.2 match查询 匹配查询&#xff0c;会将查询条件进行分词&#xff0c;然后进行查询,多个分词后查询的关系是or elasticsearh中查询类型&#xff0c;term、match、match_…

golang 微服务中的断路器 hystrix 小案例

上次我们分享了 Hystrix 具体流程&#xff0c;作为断路器实现&#xff0c;我们如何将 hystrix 用在我们的项目代码中呢&#xff1f; 我们可以简单的将 hystrix-go 下载下来 go get github.com/afex/hystrix-go/hystrix 代码会放到我们的 GOPATH 中&#xff0c;的 pkg 下面&a…

用vue-full-calendar实现酒店预定管理展示

文章目录 前言一、关于vue-full-calendar二、使用步骤1. 引入库2. 使用库3. 开始编码4. 实际效果图展示5. 点击弹窗展示6. 弹窗展示效果图 总结 前言 近些天有位做酒店业务朋友问到我&#xff0c;有没有前端比较好用的预定日历查看插件&#xff0c;实际上我也没有研究过&#…

【板栗糖GIS】——如何良好的导出并批量管理kindle上的笔记

【板栗糖GIS】——如何良好的导出并批量管理kindle上的笔记 目录 1.将Kindle Mate安装到电脑上 2. 将kindle通过usb线连接到电脑。 3. 打开Kindle Mate软件&#xff0c;按F2自动导入笔记 4. 选择导出方式 5. 笔记修饰 1.将Kindle Mate安装到电脑上 下载地址&#xff1a;…

网络安全保姆级教程,别再说你学不会了

开始&#xff0c;在选择软件系统方面。很多人会纠结于是使用Linux还是Windows或者Mac系统。 虽然Linux系统看起来很酷&#xff0c;但对于新手来说并不是很友好。 同样&#xff0c;Windows系统也可以通过使用虚拟机装靶机进行学习。所以&#xff0c;直接用window&#xff0c;学…

【Windows安全】授权初探:访问控制基础及应用

▒ 目录 ▒ &#x1f6eb; 导读需求开发环境 1️⃣ 常见名词及缩写汇总主体对象权限&#xff08;规则&#xff09; 2️⃣ 常见概念访问控制及安全级别访问令牌&#xff08;Access tokens&#xff09;特权 (Privilege) - 线程相关的安全描述符(Security Descriptors,SD)访问控制…

批判马斯洛需求层次模型

再次批判马斯洛需求层次模型&#xff0c;有啥缺陷&#xff1f; 趣讲大白话&#xff1a;文化不同&#xff0c;心理不同 【趣讲信息科技182期】 **************************** 每个民族的文化心理结构都不一样 常常低估文化对人的影响 有一门心理学分支&#xff0c;文化心理学 专…

(转载)免疫优化算法在物流配送中心选址中的应用(matlab实现)

12.1 理论基础 12.1.1 物流中心选址问题 随着世界经济的快速发展以及现代科学技术的进步&#xff0c;物流业作为国民经济的一个新兴服务部门&#xff0c;正在全球范围内迅速发展。物流业的发展给社会的生产和管理、人们的生活和就业乃至政府的职能以及社会的法律制度等都带来…

Vue前端压缩图片后上传,拍照上传最佳实践

文章目录 一、前言二、使用shrinkpng进行图片压缩2.1 安装依赖包2.2 引入包2.3 开始编码 总结 一、前言 最近有一个需求&#xff0c;通过手机拍照后上传图片到服务器&#xff0c;大家应该都知道&#xff0c;现在的手机像素实在是太高了&#xff0c;随便拍一张都是10M以上&…

移动应用架构同React Native、Flutter有什么关系?

移动应用架构描述了设计和构建应用的模式与技术。 该架构可以提供构建应用时应遵循的路线图和最佳实践&#xff0c;构建一个结构合理的应用。 移动应用的常见层次结构包括用户界面层、业务逻辑层、数据访问层&#xff0c;但是随着跨平台开发框架的不断发展&#xff0c;以React…

从TCP协议到TCP通信的各种异常现象和分析

很多人总觉得学习TCP/IP协议没什么用&#xff0c;觉得日常编程开发只需要知道socket接口怎么用就可以了。如果大家定位过线上问题就会知道&#xff0c;实际上并非如此。如果应用在局域网内&#xff0c;且设备一切正常的情况下可能确实如此&#xff0c;但如果一旦出现诸如中间交…

Zigbee Install code的使用和CRC的计算

Zigbee Install code的使用和CRC的计算 前言什么是Install codes&#xff1f;Zigbee install codes的格式CRC算法信息&#xff1a;python演示&#xff1a;Install Code的使用&#xff1a;烧录Install codes到Silicon labs EFR32设备中去安装代码文件格式烧录Install codes:核查…

【学习笔记】【万字长文】linux三剑客学习笔记

前言 ​ 上班以后用到的服务器大多数是centos的&#xff0c;很多命令会用一部分但稍微复杂一点的只能问度娘了。 ​ 时间长了&#xff0c;还是没积攒下什么本事&#xff0c;每次都需要百度查找。 ​ 终于有时间整理一篇关于linux三剑客的笔记&#xff0c;作为记录方便以后查…

项目经验分享:LVGL编程举例

本文介绍如何在成功移植LVGL的基础之上&#xff0c;编写自己的LVGL GUI程序。 文章目录 1. LVGL组件简介与LVGL仿真1.1 LVGL组件1.2 LVGL仿真 2. 代码结构3. 编程目标4. 编程前的准备5. LVGL编程基础5.1 简单示例代码5.2 设置组件位置5.3 图片的显示5.4 组件的事件响应5.5 设置…

【P46】JMeter 响应断言(Response Assertion)

文章目录 一、响应断言&#xff08;Response Assertion&#xff09; 参数说明二、准备工作三、测试计划设计3.1、包括3.2、匹配3.3、相等3.4、字符串3.5、字符串3.6、或者 一、响应断言&#xff08;Response Assertion&#xff09; 参数说明 可以对 Jmeter 取样器的响应消息进…

0x10 会话服务 DiagnosticSessionControl

0x10 会话服务 相当是一种分类&#xff0c;通过控制会话模式&#xff0c;使用不同的服务。应始终只有一个诊断会话在服务器中处于活动状态。 服务器应在启动时始终启动默认的诊断会话。 如果没有启动其他诊断会话&#xff0c;则只要服务器通电&#xff0c;默认诊断会话就应该运…

Hudi(五)集成Flink(3)

12、离线compaction MOR表的compaction默认是自动打开的&#xff0c;策略是5个commits执行一次压缩。因为压缩操作比较耗费内存&#xff0c;和写流程放在同一个pipeline&#xff0c;在数据量比较大的时候&#xff08;10w/sqps&#xff09;&#xff0c;容易干扰写流程&#xff0…

LLM:LoRA: Low-Rank Adaptation of Large Language Models

随着模型规模的不断扩大&#xff0c;微调模型的所有参数&#xff08;所谓full fine-tuning&#xff09;的可行性变得越来越低。以GPT-3的175B参数为例&#xff0c;每增加一个新领域就需要完整微调一个新模型&#xff0c;代价和成本很高。 为解决微调大规模语言模型到不同领域和…

西南交通大学智能监测 培训课程练习3

2023.05.31培训 task1&#xff1a;MybatisPlus的使用 task2&#xff1a;SpringMVC常用接口开发 task3&#xff1a;JSON、接口测试 task4&#xff1a;SpringMVC拦截器与过滤器 目录 一、MybatisPlus 1.1DO类 1.2Mapper接口 1.3编写测试类测试 1.4Mybatis和MybatisPlus 二、…