_di_JXmlResourceParser缺失方法及属性的技术咨询
我之前也碰到过一模一样的情况——C++Builder生成的JNI包装类确实没有完全暴露Android原生XmlResourceParser的所有方法,这是因为Embarcadero的封装策略有时候会省略一些他们认为不常用的接口。不过有几个可行的解决办法:
1. 直接通过JNI调用原生方法
这是最直接的方案,绕过C++Builder的封装,直接调用Android SDK底层的方法。你可以借助JNIEnv来调用XmlResourceParser的原生方法,比如获取事件类型、节点名称这些:
#include <Androidapi.JNI.JavaTypes.hpp> #include <Androidapi.Helpers.hpp> // 假设你已经成功获取到_di_JXmlResourceParser对象 _di_JXmlResourceParser parser = ...; JNIEnv* env = AndroidHelper::JNIEnv; jobject parserObj = parser->GetObjectID(); jclass parserClass = env->GetObjectClass(parserObj); // 获取getEventType()方法并调用 jmethodID midEventType = env->GetMethodID(parserClass, "getEventType", "()I"); int eventType = env->CallIntMethod(parserObj, midEventType); // 获取getName()方法并调用,转换为C++字符串 jmethodID midGetName = env->GetMethodID(parserClass, "getName", "()Ljava/lang/String;"); jstring jNodeName = static_cast<jstring>(env->CallObjectMethod(parserObj, midGetName)); UnicodeString nodeName = JStringToString(jNodeName); env->DeleteLocalRef(jNodeName); // 获取getText()方法并调用 jmethodID midGetText = env->GetMethodID(parserClass, "getText", "()Ljava/lang/String;"); jstring jText = static_cast<jstring>(env->CallObjectMethod(parserObj, midGetText)); UnicodeString nodeText = JStringToString(jText); env->DeleteLocalRef(jText); // 用完记得释放局部引用 env->DeleteLocalRef(parserClass);
需要注意的是,方法签名必须和Android SDK里的一致(比如getEventType的签名是()I,表示无参数返回int),如果不确定签名,可以对照Android官方文档里的方法定义来写。
2. 检查是否遗漏了头文件或使用了正确的接口
有时候可能是因为没有包含完整的Android头文件,导致部分方法没有被声明。确保你已经包含了:
#include <Androidapi.JNI.ContentRes.hpp>
另外,试试直接使用JXmlResourceParser类而不是_di_JXmlResourceParser智能指针,看是否能访问到更多方法——不过大部分情况下智能指针和原始类的接口是一致的。
3. 替代方案:读取XML文件为字符串后用C++解析
如果JNI调用觉得麻烦,也可以换个思路:把res/xml下的文件读取成字符串,然后用C++Builder自带的TXMLDocument或者其他第三方XML库(比如TinyXML)来解析。示例代码:
#include <Androidapi.JNI.ContentRes.hpp> #include <Androidapi.JNI.IO.hpp> #include <Xml.XMLDoc.hpp> // 读取XML文件到字符串 UnicodeString ReadXmlToString(int resId) { _di_JResources res = AndroidHelper::Context->getResources(); _di_JInputStream stream = res->openRawResource(resId); _di_JByteArrayOutputStream baos = TJByteArrayOutputStream::JavaClass->init(1024); jbyte buffer[1024]; int bytesRead; while ((bytesRead = stream->read(buffer, 0, 1024)) != -1) { baos->write(buffer, 0, bytesRead); } jstring jXmlStr = TJString::JavaClass->init(baos->toByteArray(), "UTF-8"); UnicodeString xmlStr = JStringToString(jXmlStr); stream->close(); baos->close(); env->DeleteLocalRef(jXmlStr); return xmlStr; } // 用TXMLDocument解析 void ParseXmlString(const UnicodeString& xmlStr) { TXMLDocument* xmlDoc = new TXMLDocument(NULL); try { xmlDoc->LoadFromXML(xmlStr); // 这里就可以用TXMLDocument的接口遍历节点、获取属性了 // 比如 xmlDoc->DocumentElement->NodeName, xmlDoc->DocumentElement->Attributes["xxx"] } __finally { delete xmlDoc; } }
关于getAttribute方法的问题
你提到用getAttribute没得到预期结果,大概率是参数不对。Android原生的getAttribute需要传入命名空间和属性名,或者属性索引,而C++Builder的getAttributeXXXX系列方法(比如getAttributeString)是封装后的版本,需要传入属性的索引值。你可以先通过getAttributeCount()获取属性总数,再遍历索引来获取对应的属性值。
内容的提问来源于stack exchange,提问作者IDS

