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