在Android中使用native程序(非Java)来广播intent
2014-02-07 14:02
281 查看
首先在看之前必须确定你已经部分了解广播intent的原理(从Java层到native层)。如果一窍不通的话,请先百度看完。
进入正题,广播intent从Java层最终会调用binder机制来触发native层的发送,即发送消息BROADCAST_INTENT_TRANSACTION,而这个消息是通过IActivityManager接口处理的,所以我们在程序中必须先获得这个接口,即如下:
然后我们就得到了am,然后可以继续使用transact函数来通过消息发送intent。
当然这里我们还需要一个intent,但是怎么在native程序中创建intent呢?
首先我们需要创建一个Parcel,这是binder经常使用的一个数据包类。
创建完成后,进行填充,如下所示:
第一行:当发送消息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获得。
这里我们先写了int值来表示有多少extras,但是只是-1,我们会在之后进行计算来重写这个值。然后我们写入了一个magic值(BNDL),这个值在解析extras,即Bundle类中readFromParcelInner函数中会先读取这个magic值,然后才继续处理。这里我们会在写入前保存当前data位置,然后写入map的数量,即一组”key”=”1234”,然后写完后使用新的data位置减去老的位置即获得了表示长度的data位置,然后写入新的长度值。这里关于map表里的描述符(如VAL_STRING)可以在Parcel.java中readValue函数中找到。
OK,填充完这14行后,如果我们还需要添加一些末尾信息:
这里包括了一些其他需要的属性,可以看注释了解。
但是为什么会需要这样的顺序进行添加呢?
我们可以看ActivityManagerNative.java中对BROADCAST_INTENT_TRANSACTION的处理:
我们可以看到第一行就是判断刚才说的interface的。
第二行会获得caller即调用者。
之后会创建intent,这里会进入Intent.java中newIntent,继而调用readFromParcel
这里就会调用Parcel的readString,readInt等一系列函数,每次调用后data位置就会后移。
这里会创建mData,即我们刚刚说的uri地址,如果你给他的是NULL_TYPE_ID,那么他直接返回null,如果是string类型,那么他还会readString一遍,所以必须要配对好写入一个字符串,不然如果多读一次会影响data的位置,导致之后的读取错误。接下来一系列的Parcel读取就会按照刚刚创建时的顺序进行。
完成intent创建后,继续读取Parcel中的尾部,即我们最后添加的内容。然后所有这些完成后就会调用broadcastIntent发送及处理。
所以发送成功与否关键在于intent创建是否正确。
进入正题,广播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创建是否正确。
相关文章推荐
- Android,java发送intent,native c/c++发送intent, am命令行执行程序。
- Java和Android Http连接程序:使用java.net.URL 下载服务器图片到客户端
- android开发(26) 和其他应用交换数据方式一,使用intent指定自定义action调用其他程序里的activity,并获得其返回的结果
- android语音识别方法一:使用intent调用语音识别程序
- 使用native程序来守护android进程
- Android使用百度地图SDK调用百度地图"java.lang.UnsatisfiedLinkError: Native method not found: com.baidu.platform."
- Java如何操作Android的adb shell 之 我自己在程序中的使用方法
- 使用valgrind检测Android native程序的内存
- Java和Android Http连接程序:使用java.net.URL 下载服务器图片到客户端
- 使用native方法扩展Java程序的功能
- 【Android】使用Intent调用系统其它程序,使用onKeyDown对音量键的监听,长按事件
- 在Android程序中使用Modbus协议时报 java.net.SocketException: recvfrom failed: ECONNRESET解决办法
- 关于使用google map程序中报java.lang.NoClassDefFoundError: android.net.NetworkConnectivityListener的问题
- Appium :Windows 平台上的使用 Java 语言实现 appium 自动化程序 for Android
- 使用native程序来守护android进程
- 使用valgrind检测Android native程序的内存
- Java和Android Http连接程序:使用java.net.URL 下载服务器图片到客户端
- android开发(26) 和其他应用交换数据方式一,使用intent指定自定义action调用其他程序里的activity,并获得其返回的结果
- 在Android系统使用socket在Java层和native之间数据通信
- android语音识别方法一:使用intent调用语音识别程序