- 您想要操作 Android 应用清单 XML 文件。
-
为正确的工作使用正确的工具。
-
不要将 XML 文档作为文本处理:
- 这意味着您应该改用 XML 处理库。幸运的是,.NET至少有 2 个:
-
System.Xml - 这是 .NET Framework 1.x 天 (2000-2002) 中较旧的 XML 库。它以 W3C DOM 的早期标准为蓝本,使用起来非常困难。
-
System.Xml.Linq(又名“Linq to XML”) - 这是一个现代(相对而言)库,出现在 .NET Framework 3.5 中(与 Linq 和各种 我们今天仍然喜欢的美好事物)。与System.Xml 相比,API 设计非常不同,但也更加简洁和富有表现力。我注意到它主要是为查询 XML 而不是改变 XML 而设计的,但对于我们的目的来说它很好。
虽然(一般来说)Android manifest文件中元素的顺序does not matter, there are 2 documented exceptions where order does matter, and unfortunately for us the 2nd exception applies to us:
-
<activity-alias> 元素必须跟在作为别名的 <activity> 之后。
-
<application> 元素必须是 <manifest> 元素内的最后一个元素。
所以鸟瞰图是:
- 将 Android 应用清单 XML 文件加载到
XDocument 对象中。
- 要添加a new
<uses-permission> element 或a new <permission> element,您需要添加为根<manifest> 元素的直接子元素,但位于<application> 元素之前。
- 要添加 a new
<service> element,请将其作为新的直接子元素附加到 <application>。
像这样:
using System.Linq;
using System.Xml.Linq;
public static class Program
{
public static Int32 Main( String[] args )
{
const String FILE_NAME = "MyManifest.xml";
if( !File.Exists( FILE_NAME ) )
{
Console.WriteLine( "Couldn't find \"" + FILE_NAME + "\"." );
return 1;
}
// Load the XML from disk:
String manifestXmlText = File.ReadAllText( FILE_NAME );
// Parse it into an an XDocument:
XDocument manifestXmlDocument = XDocument.Parse( manifestXmlText );
// Add the new elements:
AddElementsToAndroidManifest( manifestXmlDocument );
// Then save the updated XML document back to disk, overwriting the original file:
using( FileStream fs = File.OpenWrite( FILE_NAME ) )
{
manifestXmlDocument.Save( fs );
}
return 0;
}
private static void AddElementsToAndroidManifest( XDocument manifestXmlDocument )
{
XElement manifestEl = manifestXmlDocument.Root ?? throw new InvalidOperationException( "Couldn't find root <manifest> element." ) ;
XElement applicationEl = manifestXmlDocument.Descendants("application").SingleOrDefault() ?? throw new InvalidOperationException( "Couldn't find <application> element." ); // Get the <application> element.
XNamespace androidNS = manifestEl.GetNamespaceOfPrefix(prefix: "android") ?? throw new InvalidOperationException( "Couldn't find xmlns:android" );; // "http://schemas.android.com/apk/res/android"
{
// <uses-permission>'s attributes use XML namespaces, so this complicates things:
// https://stackoverflow.com/questions/4985974/xelement-namespaces-how-to
// https://docs.microsoft.com/en-us/dotnet/standard/linq/namespaces-overview
// https://stackoverflow.com/questions/2874422/how-to-set-the-default-xml-namespace-for-an-xdocument
// https://stackoverflow.com/questions/1338517/how-can-i-write-xml-with-a-namespace-and-prefix-with-xelement
XElement newUsesPermissionEl = new XElement( "uses-permission",
new XAttribute( androidNS + "name" , "bar"),
new XAttribute( androidNS + "maxSdkVersion", "123")
);
applicationEl.AddBeforeSelf( newUsesPermissionEl ); // Add the new <uses-permission> element as a preceding sibling of <application>.
}
{
// <service>:
XElement newServiceEl = new XElement( "service",
new XAttribute( androidNS + "description", "Some day, some day, Some day, Dominion; Some day, some day, Some say prayers; I say mine"),
new XAttribute( androidNS + "name" , "Dominion service"),
new XAttribute( androidNS + "label" , "Andrew Eldritch fanclub <3"),
);
applicationEl.Add( newServiceEl ); // Add the new <service> element as an immediately child of <application>.
}
}
}
所以将the official example Manifest XML 作为输入(在MyManifest.xml 中)...:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.example.myapp">
<!-- Beware that these values are overridden by the build.gradle file -->
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="26" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
<!-- This name is resolved to com.example.myapp.MainActivity
based upon the package attribute -->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".DisplayMessageActivity" android:parentActivityName=".MainActivity" />
</application>
</manifest>
...我得到这个输出:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.example.myapp">
<!-- Beware that these values are overridden by the build.gradle file -->
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="26" />
<uses-permission android:name="bar" android:maxSdkVersion="123" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
<!-- This name is resolved to com.example.myapp.MainActivity
based upon the package attribute -->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".DisplayMessageActivity" android:parentActivityName=".MainActivity" />
<service android:description="Some day, some day, Some day, Dominion; Some day, some day, Some say prayers; I say mine" android:name="Dominion service" android:label="Andrew Eldritch fanclub <3" />
</application>
</manifest>
如您所见,<service> 和 <uses-permission> 元素已插入到正确的位置。