展会信息港展会大全

在Android中使用native程序(非Java)来广播intent
来源:互联网   发布日期:2016-01-14 09:28:59   浏览:2180次  

导读:首先在看之前必须确定你已经部分了解广播intent的原理(从Java层到native层)。如果一窍不通的话,请先百度看完。进入正题,广播intent从Java层最终会调用binder机制来触...

首先在看之前必须确定你已经部分了解广播intent的原理(从Java层到native层)。如果一窍不通的话,请先百度看完。

进入正题,广播intent从Java层最终会调用binder机制来触发native层的发送,即发送消息BROADCAST_INTENT_TRANSACTION,而这个消息是通过IActivityManager接口处理的,所以我们在程序中必须先获得这个接口,即如下:

sp sm = defaultServiceManager();

sp am = sm->checkService(String16("activity"));

然后我们就得到了am,然后可以继续使用transact函数来通过消息发送intent。

当然这里我们还需要一个intent,但是怎么在native程序中创建intent呢?

首先我们需要创建一个Parcel,这是binder经常使用的一个数据包类。

创建完成后,进行填充,如下所示:

1. data.writeInterfaceToken(String16("android.app.IActivityManager"));

2. data.writeStrongBinder(NULL); /* caller */

/* intent */

3. data.writeString16(String16(action_str)); /* action */

4. data.writeInt32(URI_TYPE_ID); /* Uri - type */

5. data.writeString16(String16(uri_str)); /* uri string if URI_TYPE_ID set */

6. data.writeString16(NULL, 0); /* type */

7. data.writeInt32(0); /* flags */

8. data.writeString16(NULL, 0); /* package name */

9. data.writeString16(NULL, 0); /* ComponentName */

10. data.writeInt32(0); /* source bound - size */

11. data.writeInt32(0); /* Categories - size */

12. data.writeInt32(0); /* selector - size */

13. data.writeInt32(0); /* ClipData */

14. data.writeInt32(-1); /* bundle(extras) size */

/* end of intent */

第一行:当发送消息BROADCAST_INTENT_TRANSACTION时,会检查token,如果不是IActivityManager.descriptor则会返回false,当然这里只会打出一段warning信息,不会发送失败。

第二行:这里是发送这个binder请求方。

第三行:这个是需要发送intent的action(android.intent.action.*)

第四行:这里写入了一个int值,可以有四个,其中三个分别对应三种uri类型:

NULL_TYPE_ID:0

StringUri.TYPE_ID:1

OpaqueUri.TYPE_ID:2

HierarchicalUri.TYPE_ID:3

第五行:这一行存在与否依赖于第四行,规则如下:

如果是NULL_TYPE_ID:该行不可存在

如果是StringUri.TYPE_ID:该行必须是writeString16写入uri地址

如果是OpaqueUri.TYPE_ID:可以看Uri.java中OpaqueUri类readFrom函数,由多个part组成。

如果是HierarchicalUri.TYPE_ID:可以看Uri.java中HierarchicalUri类readFrom函数,由多个part组成。

第六行-第十三行:在后面的注释都有解释。

第十四行:这一行是用来确定有多少extras(即映射参数),比如”key”=”1234”,可以使用getString获得。

{ /* Extras */

data.writeInt32(-1); /* length */

data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'

int oldPos = data.dataPosition();

{ /* writeMapInternal */

data.writeInt32(1); /* size */

data.writeInt32(VAL_STRING);

data.writeString16(String16("key"));

data.writeInt32(VAL_STRING);

data.writeString16(String16(“1234”));

}

int newPos = data.dataPosition();

data.setDataPosition(oldPos - 8);

data.writeInt32(newPos - oldPos); /* length */

data.setDataPosition(newPos);

}

这里我们先写了int值来表示有多少extras,但是只是-1,我们会在之后进行计算来重写这个值。然后我们写入了一个magic值(BNDL),这个值在解析extras,即Bundle类中readFromParcelInner函数中会先读取这个magic值,然后才继续处理。这里我们会在写入前保存当前data位置,然后写入map的数量,即一组”key”=”1234”,然后写完后使用新的data位置减去老的位置即获得了表示长度的data位置,然后写入新的长度值。这里关于map表里的描述符(如VAL_STRING)可以在Parcel.java中readValue函数中找到。

OK,填充完这14行后,如果我们还需要添加一些末尾信息:

data.writeString16(NULL, 0); /* resolvedType */

data.writeStrongBinder(NULL); /* resultTo */

data.writeInt32(-1); /* result code */

data.writeString16(NULL, 0); /* result data */

data.writeInt32(-1); /* no result extra */

data.writeString16(NULL, 0); /* permission */

data.writeInt32(false); /* app operation in AppOpsManager */

data.writeInt32(false); /* serialized */

data.writeInt32(false); /* sticky */

data.writeInt32(false); /* userid */

这里包括了一些其他需要的属性,可以看注释了解。

但是为什么会需要这样的顺序进行添加呢?

我们可以看ActivityManagerNative.java中对BROADCAST_INTENT_TRANSACTION的处理:

case BROADCAST_INTENT_TRANSACTION:

{

data.enforceInterface(IActivityManager.descriptor);

IBinder b = data.readStrongBinder();

IApplicationThread app =

b != null ? ApplicationThreadNative.asInterface(b) : null;

Intent intent = Intent.CREATOR.createFromParcel(data);

String resolvedType = data.readString();

b = data.readStrongBinder();

IIntentReceiver resultTo =

b != null ? IIntentReceiver.Stub.asInterface(b) : null;

int resultCode = data.readInt();

String resultData = data.readString();

Bundle resultExtras = data.readBundle();

String perm = data.readString();

int appOp = data.readInt();

boolean serialized = data.readInt() != 0;

boolean sticky = data.readInt() != 0;

int userId = data.readInt();

我们可以看到第一行就是判断刚才说的interface的。

第二行会获得caller即调用者。

之后会创建intent,这里会进入Intent.java中newIntent,继而调用readFromParcel

setAction(in.readString());

mData = Uri.CREATOR.createFromParcel(in);

mType = in.readString();

mFlags = in.readInt();

mPackage = in.readString();

mComponent = ComponentName.readFromParcel(in);

if (in.readInt() != 0) {

mSourceBounds = Rect.CREATOR.createFromParcel(in);

}

int N = in.readInt();

if (N > 0) {

mCategories = new ArraySet();

int i;

for (i=0; i

这里就会调用Parcel的readString,readInt等一系列函数,每次调用后data位置就会后移。

这里会创建mData,即我们刚刚说的uri地址,如果你给他的是NULL_TYPE_ID,那么他直接返回null,如果是string类型,那么他还会readString一遍,所以必须要配对好写入一个字符串,不然如果多读一次会影响data的位置,导致之后的读取错误。接下来一系列的Parcel读取就会按照刚刚创建时的顺序进行。

完成intent创建后,继续读取Parcel中的尾部,即我们最后添加的内容。然后所有这些完成后就会调用broadcastIntent发送及处理。

所以发送成功与否关键在于intent创建是否正确。

赞助本站

人工智能实验室

相关热词: android开发 教程

AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港