title: 原生扩展 - 清单合并工具
一些平台上需要提供清单片段(或称存根)来为扩展提供支持。
可以是部分 AndroidManifest.xml
、Info.plist
或者 engine_template.html
。
从应用基础清单开始,每个扩展清单存根依次被应用。
基础清单可以是默认的(位于 builtins\manifests\<platforms>\...
),也可以是由用户自定义的。
扩展清单必须被放置在适当的指定位置才能生效。
/myextension
ext.manifest
/manifests
/android
AndroidManifest.xml
/ios
Info.plist
/osx
Info.plist
/web
engine_template.html
Android 平台提供了清单合并工具(基于 ManifestMerger2
),bob.jar
中使用此工具合并清单。
关于 Android 清单的详细信息,请参见官方文档
::: important
如果扩展清单中没有设置应用的 android:targetSdkVersion
,下列权限会被自动加入:WRITE_EXTERNAL_STORAGE
、READ_PHONE_STATE
、READ_EXTERNAL_STORAGE
。详情请见此文档。
我们推荐这样设置:<uses-sdk android:targetSdkVersion="{{android.target_sdk_version}}" />
:::
基础 manifest
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android'
package='com.defold.testmerge'
android:versionCode='14'
android:versionName='1.0'
android:installLocation='auto'>
<uses-feature android:required='true' android:glEsVersion='0x00020000' />
<uses-sdk android:minSdkVersion='9' android:targetSdkVersion='26' />
<application android:label='Test Project' android:hasCode='true'>
</application>
<uses-permission android:name='android.permission.VIBRATE' />
</manifest>
扩展 manifest:
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android' package='com.defold.testmerge'>
<uses-sdk android:targetSdkVersion=“{{android.target_sdk_version}}” />
<uses-feature android:required='true' android:glEsVersion='0x00030000' />
<application>
<meta-data android:name='com.facebook.sdk.ApplicationName'
android:value='Test Project' />
<activity android:name='com.facebook.FacebookActivity'
android:theme='@android:style/Theme.Translucent.NoTitleBar'
android:configChanges='keyboard|keyboardHidden|screenLayout|screenSize|orientation'
android:label='Test Project' />
</application>
</manifest>
合并结果
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android'
package='com.defold.testmerge'
android:installLocation='auto'
android:versionCode='14'
android:versionName='1.0' >
<uses-sdk
android:minSdkVersion='9'
android:targetSdkVersion='26' />
<uses-permission android:name='android.permission.VIBRATE' />
<uses-feature
android:glEsVersion='0x00030000'
android:required='true' />
<application
android:hasCode='true'
android:label='Test Project' >
<meta-data
android:name='com.facebook.sdk.ApplicationName'
android:value='Test Project' />
<activity
android:name='com.facebook.FacebookActivity'
android:configChanges='keyboard|keyboardHidden|screenLayout|screenSize|orientation'
android:label='Test Project'
android:theme='@android:style/Theme.Translucent.NoTitleBar' />
</application>
</manifest>
对于 Info.plist
,我们使用自己的实现来合并列表和字典。可以在键上指定合并标记属性 merge
、keep
或 replace
,默认是 merge
。
基础 Manifest
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
<plist version='1.0'>
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>foobar.net</key>
<dict>
<key>testproperty</key>
<true/>
</dict>
</dict>
</dict>
<key>INT</key>
<integer>8</integer>
<key>REAL</key>
<real>8.0</real>
<!-- 即使扩展清单包含相同的键,也要保留此值 -->
<key merge='keep'>BASE64</key>
<data>SEVMTE8gV09STEQ=</data>
<!-- 如果扩展清单也包含此键的数组,则任何字典值将与基础数组的第一个字典值合并 -->
<key>Array1</key>
<array>
<dict>
<key>Foobar</key>
<array>
<string>a</string>
</array>
</dict>
</array>
<!-- 不要尝试合并此数组的值,而是应将扩展清单的值添加到数组末尾 -->
<key merge='keep'>Array2</key>
<array>
<dict>
<key>Foobar</key>
<array>
<string>a</string>
</array>
</dict>
</array>
</dict>
</plist>
扩展 manifest:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
<plist version='1.0'>
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>facebook.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
<key>INT</key>
<integer>42</integer>
<!-- 替换基础清单中的现有值 -->
<key merge='replace'>REAL</key>
<integer>16.0</integer>
<key>BASE64</key>
<data>Rk9PQkFS</data>
<key>Array1</key>
<array>
<dict>
<key>Foobar</key>
<array>
<string>b</string>
</array>
</dict>
</array>
<key>Array2</key>
<array>
<dict>
<key>Foobar</key>
<array>
<string>b</string>
</array>
</dict>
</array>
</dict>
</plist>
合并结果:
<?xml version='1.0'?>
<!DOCTYPE plist SYSTEM 'file://localhost/System/Library/DTDs/PropertyList.dtd'>
<plist version='1.0'>
<!-- 基础清单和扩展清单的字典的嵌套合并 -->
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>foobar.net</key>
<dict>
<key>testproperty</key>
<true/>
</dict>
<key>facebook.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
<!-- 来自基础清单 -->
<key>INT</key>
<integer>8</integer>
<!-- 基础清单的值被替换,因为扩展清单中的合并标记设置为 "replace" -->
<key>REAL</key>
<real>16.0</real>
<!-- 使用基础清单的值,因为基础清单中的合并标记设置为 "keep" -->
<key>BASE64</key>
<data>SEVMTE8gV09STEQ=</data>
<!-- 添加扩展清单的值,因为没有指定合并标记 -->
<key>INT</key>
<integer>42</integer>
<!-- 数组的字典值被合并,因为基础清单默认使用 "merge" -->
<key>Array1</key>
<array>
<dict>
<key>Foobar</key>
<array>
<string>a</string>
<string>b</string>
</array>
</dict>
</array>
<!-- 字典值被添加到数组,因为基础清单使用了 "keep" -->
<key>Array2</key>
<array>
<dict>
<key>Foobar</key>
<array>
<string>a</string>
</array>
</dict>
<dict>
<key>Foobar</key>
<array>
<string>b</string>
</array>
</dict>
</array>
</dict>
</plist>
对于html模板,我们为每个部分命名,以便能够匹配(例如"engine-start")。
然后可以指定属性 merge
或 keep
。merge
是默认值。
基础 manifest
<!DOCTYPE html>
<html>
<body>
<script id='engine-loader' type='text/javascript' src='dmloader.js'></script>
<script id='engine-setup' type='text/javascript'>
function load_engine() {
var engineJS = document.createElement('script');
engineJS.type = 'text/javascript';
engineJS.src = '{{exe-name}}_wasm.js';
document.head.appendChild(engineJS);
}
</script>
<script id='engine-start' type='text/javascript'>
load_engine();
</script>
</body>
</html>
扩展 manifest
<html>
<body>
<script id='engine-loader' type='text/javascript' src='mydmloader.js'></script>
<script id='engine-start' type='text/javascript' merge='keep'>
my_load_engine();
</script>
</body>
</html>
合并结果
<!doctype html>
<html>
<head></head>
<body>
<script id='engine-loader' type='text/javascript' src='mydmloader.js'></script>
<script id='engine-setup' type='text/javascript'>
function load_engine() {
var engineJS = document.createElement('script');
engineJS.type = 'text/javascript';
engineJS.src = '{{exe-name}}_wasm.js';
document.head.appendChild(engineJS);
}
</script>
<script id='engine-start' type='text/javascript' merge='keep'>
my_load_engine();
</script>
</body>
</html>