Python ElementTree操作AndroidManifest.xml的若干个关键点

1、在调用ElementTree.parse之前一定要想调用以下代码:

   uri = "http://schemas.android.com/apk/res/android"
   ElementTree.register_namespace("android", uri)

要是没有调用上述代码注册namespace,在调用 tree.write将xml写回去时候,android:会被替换为ns0:
2、获取某个标签的属性时候,可以用以下两种方式:(以获取包名为例)
1)packageName = root.get('package')
2) packageName = root.attrib['package']
3、添加某个节点时,注意属性与namespace的设置:
以添加以下meta-data为例:

  <meta-data android:name="android.max_aspect" android:value="2.1"/>

ElementTree的实现代码:

 namespace = "{http://schemas.android.com/apk/res/android}"
 tree = ElementTree.parse(androidManifestPath)
 root = tree.getroot()
 application = root.find("application")
 max_aspect = Element("meta-data", attrib={namespace + 'name': "android.max_aspect", namespace + 'value': "2.1"})
 max_aspect.tail = "\n\t"
 application.append(max_aspect)
 tree.write(androidManifestPath,encoding="UTF-8")

4、替换某个字符串(注意:xml读出来再写回去会少了<?xml version="1.0" encoding="utf-8"?>)

newContents = []
newContents.append('<?xml version="1.0" encoding="utf-8"?>')
newContents.append("\n")
with open(androidManifestPath, "r", errors='ignore') as file:
       contents = file.read()
       newContents.append(contents.replace("{applicationId}", packageName).replace("singleTask", "singleTop"))
with open(androidManifestPath, "w", errors='ignore') as f:
       f.writelines(newContents)
       f.flush()

官方文档:https://docs.python.org/zh-cn/3/library/xml.etree.elementtree.html
示例代码:

# 修改AndroidManifest.xml对应配置
def changeCommonAndroidManifest(androidManifestPath,appName,packageName,versionCode,versionName,sswlAppId,urlHost):
    printLog("修改AndroidManifest.xml公共配置...")
    # 注意声明register_namespace,要不write写回去时候android会被替换为ns0
    uri = "http://schemas.android.com/apk/res/android"
    namespace = "{http://schemas.android.com/apk/res/android}"
    key = namespace + 'name'

    # 替换跟sdk包名相关的字符串,以及将有些游戏设置为singleTask的启动模式改为singleTop
    if packageName:
        newContents = []
        newContents.append('<?xml version="1.0" encoding="utf-8"?>')
        newContents.append("\n")
        with open(androidManifestPath, "r", errors='ignore') as file:
            contents = file.read()
            newContents.append(
                contents.replace("{applicationId}", packageName).replace("singleTask", "singleTop"))

        with open(androidManifestPath, "w", errors='ignore') as f:
            f.writelines(newContents)
            f.flush()


    ElementTree.register_namespace("android", uri)
    tree = ElementTree.parse(androidManifestPath)
    root = tree.getroot()
    oldPackage = root.get('package') #或者 package = root.attrib['package']
    # 设置包名
    if packageName:
        root.set('package', packageName)
    # 设置版本号
    if versionCode:
        root.set(namespace+'versionCode',versionCode)
    # 设置版本名
    if versionName:
        root.set(namespace+'versionName', versionName)

    application = root.find("application")
    applicationClassName = application.get(namespace+"name")
    # 获取icon名 android:icon="@drawable/icon"
    iconStr = application.get(namespace + "icon")
    iconName =None
    if iconStr:
        iconName = iconStr.split('/')[1]
    # 获取应用名 android:label="@string/app_name"
    if appName:
        appNameStr = application.get(namespace + "label")
        if appNameStr:
            # 通过字符串引用方式声明应用名
            label = appNameStr.split('/')
            if len(label) >= 2:
                label = appNameStr.split('/')[1]
                # 到res/values/strings.xml查找应用名
                stringsXml = os.path.dirname(androidManifestPath) + "/res/values/strings.xml"
                stringsTree = ElementTree.parse(stringsXml)
                sroot = stringsTree.getroot()
                appNameE = sroot.find("./string[@name='" + label + "']")
                if appNameE is not None:
                    appNameE.text = appName
            else:
                # 直接设置的应用名
                application.set(namespace + "label", appName)

        stringsTree.write(stringsXml)



    # 添加 meta 适配全面屏
    if contents.find("android.max_aspect") == -1:
        max_aspect = Element("meta-data", attrib={namespace + 'name': "android.max_aspect", namespace + 'value': "2.1"})
        max_aspect.tail = "\n\t"
        application.append(max_aspect)


    # 添加meta appid
    if contents.find("SSWL_APPID") == -1:
        if sswlAppId:
            sswlAppIdE = Element("meta-data", attrib={key: "SSWL_APPID", namespace + 'value': sswlAppId})
            sswlAppIdE.tail = "\n\t"
            application.append(sswlAppIdE)
    else:
        # 修改appid
        metadataNodes = application.findall('meta-data')
        for child in metadataNodes:
            if child.get(key) == 'SSWL_APPID':
                child.set(namespace + 'value',sswlAppId)
                break

    # 添加meta scheme
    if contents.find("URL_SCHEME") == -1:
        if packageName:
            urlSchema = Element("meta-data",attrib={namespace + 'name': "URL_SCHEME", namespace + 'value': 'sswl://' + urlHost})
            urlSchema.tail = "\n\t"
            application.append(urlSchema)

    # 给主 activity添加url scheme
    activityNodes = application.findall('activity')

    hasFind = False
    # 判断是否已经添加过
    if contents.find('android:host=\"' + urlHost + '\"') == -1:
        if activityNodes is not None and len(activityNodes) > 0:
            for child in activityNodes:
                if hasFind:
                    break
                actionNodes = child.findall(".//*[@" + namespace + "name='android.intent.action.MAIN']")
                if actionNodes is not None and len(actionNodes) > 0:
                    for action in actionNodes:
                        if action.get(key) == 'android.intent.action.MAIN':
                            # 添加浏览器打开应用的scheme
                            filterE = Element("intent-filter")
                            filterE.tail = "\n\t"
                            filterE.text = "\n\t\t"
                            actionE = Element("action", attrib={key: "android.intent.action.VIEW"})
                            actionE.tail = "\n\t\t"
                            categoryE = Element("category", attrib={key: "android.intent.category.DEFAULT"})
                            categoryE.tail = "\n\t\t"
                            category1E = Element("category", attrib={key: "android.intent.category.BROWSABLE"})
                            category1E.tail = "\n\t\t"
                            dataE = Element("data", attrib={namespace + 'scheme': "sswl", namespace + 'host': urlHost})
                            dataE.tail = "\n\t\t"
                            child.append(filterE)
                            filterE.append(actionE)
                            filterE.append(categoryE)
                            filterE.append(category1E)
                            filterE.append(dataE)
                            hasFind = True
                            break

    # 将修改写回去
    tree.write(androidManifestPath,encoding="UTF-8")

    # 替换跟game包名相关的字符串,以及将有些游戏设置为singleTask的启动模式改为singleTop
    if packageName:
        newContents = []
        newContents.append('<?xml version="1.0" encoding="utf-8"?>')
        newContents.append("\n")
        with open(androidManifestPath, "r", errors='ignore') as file:
            contents = file.read()
            newContents.append(contents.replace(oldPackage, packageName))

        with open(androidManifestPath, "w", errors='ignore') as f:
            f.writelines(newContents)
            f.flush()
    # 移动旧包名目录下的文件到新包名,并修改文件的中包名相关的字符串
    gamePath = os.path.dirname(androidManifestPath)
    searchPath = []
    for d in os.listdir(gamePath):
        subPath = os.path.join(gamePath,d)
        if d.__contains__("smali") and os.path.isdir(subPath):
            searchPath.append(subPath)
    for path in searchPath:
        oldStr = oldPackage.replace(".","/")
        newStr = packageName.replace(".","/")
        oldPath = path
        newPath = path
        for p in oldStr.split("/"):
            oldPath = os.path.join(oldPath, p)
        for p in newStr.split("/"):
            newPath = os.path.join(newPath,p)
        # 替换字符串
        replaceDirSpecifiedFileContent(oldPath,oldStr,newStr)
        # 复制文件到新目录
        copy_files(oldPath,newPath)
        # 删除文件
        shutil.rmtree(oldPath)

    return iconName,applicationClassName

发布了36 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43278826/article/details/90212562