前言
前阵子复习 AIDL
相关的知识,知道 AIDL
不支持 short
类型,但是我却发现同样使用 AIDL
的 Messenger
却可以。
这是怎么回事,本文将探索这其中原因。
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
}
}
|
这就奇怪了,为什么同样使用 AIDL
, Messenger
就可以使用 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
这个 Set
和 kJavaLikeTypeToAidlType
这个 Map
。显然这里就没有 short
,所以返回 false
。
接着调用 TryGetDefinedType
,这个函数是用于处理自定义类型的,显然也是没有 short
的,所以这里返回 nullptr
,最后整个函数返回 pair(type_name, false)
,所以最后会输出 Failed to resolve 'short'
分析到这里我们就知道了在把 .aidl
转换为 .java
的时候不支持 short
,所以在 .aidl
中不能使用的原因在于这里。
总结
今天我们发现同样基于 AIDL
的 Messenger
可以支持 short
类型,而 AIDL
却不可以。
接着我们从源码中找到了 AIDL
不支持的原因。