前言

前阵子复习 AIDL 相关的知识,知道 AIDL 不支持 short 类型,但是我却发现同样使用 AIDLMessenger 却可以。

这是怎么回事,本文将探索这其中原因。

short 的支持

我们知道 AIDL 是不支持 short 类型的,平常我们也不会去使用 short ,如果要用也可以用 int 来代替。

虽然 AIDL 不支持,但是在使用 Messenger 的时候,怀着好奇心在 Messenger 中的 data 里面放进去一个 short 类型数据,发现服务端可以收到,并正确打印。代码如下:

客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MainActivity : AppCompatActivity() {
        private val TAG = MainActivity::class.simpleName
        var mMessenger:Messenger? = null

        val conn = object : ServiceConnection {
            override fun onServiceDisconnected(name: ComponentName?) {
                Log.d(TAG,"onServiceDisconnected")
            }

            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mMessenger = Messenger(service)
                sendMsgToServer()
            }
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            bindService(Intent(this, MessengerService::class.java), conn, Context.BIND_AUTO_CREATE)

        }

        private fun sendMsgToServer() {
            val msg = Message.obtain(null, 1)
            msg.data = Bundle().also {
                it.putShort("shortTest", 2)
            }
            mMessenger?.send(msg)
        }
}

服务端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class MessengerService : Service() {
    val mMessenger = Messenger(object : Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            val data = msg.data
            println(data.getShort("shortTest"))
        }
    })

    override fun onBind(intent: Intent?): IBinder? {
        return mMessenger.binder
    }
}

这就奇怪了,为什么同样使用 AIDLMessenger 就可以使用 short ,反而 AIDL 不行?

这里我们发现 short 是放在 Bundle 中的, Bundle 实现了 Parcelable 。 而在 .aidl 中是直接写在文件中,基于这个差异于是猜想在 .aidl 文件中不能使用 short 类型,而自己写一个类,里面放 short 是可以的。

于是我写了如下代码,来验证一下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import android.os.Parcel;
import android.os.Parcelable;

class ShortMsg implements Parcelable {
    public short shortType;

    ShortMsg() {
        shortType = 2;
    }

    protected ShortMsg(Parcel in) {
        shortType = (short) in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt((int) shortType);
    }

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

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

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

对应的 AIDL 文件

1
2
3
package com.dev.bins.aidl;

parcelable ShortMsg;

写一个方法来传递数据

1
2
3
4
5
6
package com.dev.bins.aidl;
import com.dev.bins.aidl.ShortMsg;

interface IShortInterface {
    void send(in ShortMsg msg);
}

对应的 Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class ShortService : Service() {
    val binder = object: IShortInterface.Stub() {
        override fun send(msg: ShortMsg?) {
            println("short: ${msg?.shortType}")
        }
    }

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

Activity 中发送 ShortMsg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class MainActivity : AppCompatActivity() {

    companion object {
        val TAG = MainActivity.javaClass.simpleName
    }

    var server : IShortInterface? = null

    val conn = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
            Log.d(TAG,"onServiceDisconnected")
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d(TAG, "connect")
            server = IShortInterface.Stub.asInterface(service)
            val msg = ShortMsg()
            server?.send(msg)
        }

    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val intent = Intent(this, ShortService::class.java)
        bindService(intent, conn, Context.BIND_AUTO_CREATE)
    }
}

见证奇迹的时候,控制台打印了 short 的数据。

1
2021-05-28 23:39:42.845 16923-16936/com.dev.bins.aidl I/System.out: short: 2

到了这里就可以肯定了, short 不能在 .aidl 文件中使用,但是可以通过自定义类型来传递 short 类型的数据。

探秘 AIDL 不支持 short 的原因

如果我们在 aidl 文件中使用 short 会报如下错误

1
Failed to resolve 'short'

我们知道在编译的时候会把 .aidl 文件转成 .java 文件,转换使用的是 sdk/build-tools/30.0.1/aidl 工具来生成的,所以不能使用的原因在 aidl 这个工具里。 aidl 的源码在 /system/tools/aidl 中,我这里以 Android10 源码为例。

由于源码太多,我们从错误入手,使用 rg 搜索 Failed to resolve 幸运的发现只有一处,文件在 /system/tools/aidl/aidl_language.cpp 如下所示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
bool Parser::Resolve() {
    bool success = true;
    for (AidlTypeSpecifier* typespec : unresolved_typespecs_) {
        if (!typespec->Resolve(typenames_)) {
            AIDL_ERROR(typespec) << "Failed to resolve '" << typespec->GetUnresolvedName() << "'";
            success = false;
            // don't stop to show more errors if any
        }
    }
    return success;
}

bool AidlTypeSpecifier::Resolve(android::aidl::AidlTypenames& typenames) {
  assert(!IsResolved());
  pair<string, bool> result = typenames.ResolveTypename(unresolved_name_);
  if (result.second) {
    fully_qualified_name_ = result.first;
  }
  return result.second;
}

从上面的代码可以看出来,问题出现在 typespec->Resolve 中,接着发现在 Resolve 中调用了 AidlTypenames.ResolveTypename ,对应文件在 /system/tools/aidl/aidl_typenames.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// The built-in AIDL types..
// 没有发现 short 的身影
static const set<string> kBuiltinTypes = {
    "void", "boolean", "byte",           "char",         "int",
    "long", "float",   "double",         "String",       "List",
    "Map",  "IBinder", "FileDescriptor", "CharSequence", "ParcelFileDescriptor"};

static const map<string, string> kJavaLikeTypeToAidlType = {
    {"java.util.List", "List"},
    {"java.util.Map", "Map"},
    {"android.os.ParcelFileDescriptor", "ParcelFileDescriptor"},
};

bool AidlTypenames::IsBuiltinTypename(const string& type_name) {
    return kBuiltinTypes.find(type_name) != kBuiltinTypes.end() ||
        kJavaLikeTypeToAidlType.find(type_name) != kJavaLikeTypeToAidlType.end();
}

pair<string, bool> AidlTypenames::ResolveTypename(const string& type_name) const {
    if (IsBuiltinTypename(type_name)) {
        auto found = kJavaLikeTypeToAidlType.find(type_name);
        if (found != kJavaLikeTypeToAidlType.end()) {
            return make_pair(found->second, true);
        }
        return make_pair(type_name, true);
    }
    const AidlDefinedType* defined_type = TryGetDefinedType(type_name);
    if (defined_type != nullptr) {
        return make_pair(defined_type->GetCanonicalName(), true);
    } else {
        return make_pair(type_name, false);
    }
}

ResolveTypename 中,先调用了 IsBuiltinTypename 来判断是不是内置类型,而判断内置类型是通过查找 kBuiltinTypes 这个 SetkJavaLikeTypeToAidlType 这个 Map 。显然这里就没有 short ,所以返回 false

接着调用 TryGetDefinedType ,这个函数是用于处理自定义类型的,显然也是没有 short 的,所以这里返回 nullptr ,最后整个函数返回 pair(type_name, false) ,所以最后会输出 Failed to resolve 'short'

分析到这里我们就知道了在把 .aidl 转换为 .java 的时候不支持 short ,所以在 .aidl 中不能使用的原因在于这里。

总结

今天我们发现同样基于 AIDLMessenger 可以支持 short 类型,而 AIDL 却不可以。

接着我们从源码中找到了 AIDL 不支持的原因。