在Android中使用native程序(非Java)来广播intent

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

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

    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> 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<String>();
            int i;
            for (i=0; i<N; i++) {
                mCategories.add(in.readString().intern());
            }
        } else {
            mCategories = null;
        }

        if (in.readInt() != 0) {
            mSelector = new Intent(in);
        }

        if (in.readInt() != 0) {
            mClipData = new ClipData(in);
        }

        mExtras = in.readBundle();

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

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

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

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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值