Android官方技术文档翻译 Manifest Merger

来源:互联网 发布:mysql添加字段 编辑:程序博客网 时间:2024/05/17 23:42

Android官方技术文档翻译——清单合并

本文译自Android官方技术文档《Manifest Merger》,

原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger。


清单合并

目录

  1. 清单文件顺序
  2. Android 清单文件合并
  3. 元素和属性的合并处理
    1. 3.1 隐含声明
    2. 3.2 自动升级
    3. 3.3 占位符支持
    4. 3.4 合并策略的常见描述
    5. 3.5 标记
      1. 3.5.1 tools:node 标记
      2. 3.5.2 tools:attr 标记
      3. 3.5.3 选择器(Selector)
      4. 3.5.4 tools:overrideLibrary 标记
    6. 3.6 日志
      1. 3.6.1 日志记录:
        1. 3.6.1.1 消息
        2. 3.6.1.2 NodeRecord
          1. 3.6.1.2.1 操作格式
          2. 3.6.1.2.2 示例:
    7. 3.7 构建错误
    8. 3.8 Blame
    9. 3.9 合并策略
      1. 3.9.1 合并
      2. 3.9.2 只合并子节点
      3. 3.9.3 总是合并
      4. 3.9.4 元素合并策略和key的列表
      5. 3.9.5 包名称智能替换
  4. 属性标记示例
    1. 4.1 重写来自库项目的属性
    2. 4.2 删除来自库项目的属性。
    3. 4.3 强制更新属性值
    4. 4.4 混合操作
  5. 元素标记示例
    1. 5.1 移除元素
    2. 5.2 移除所有元素
    3. 5.3 元素替换
    4. 5.4 选择器示例
      1. 5.4.1 Main Manifest



这个新的合并工具是gradle android 插件的 0.10 版中引入的。截至 0.11 版本,该 gradle 插件默认情况下都是使用此合并工具。

如果想恢复使用旧的清单合并工具,可以在你的 build.gradle 中添加以下配置:

android {
useOldManifestMerger true
}

Manifest 文件排序


一般情况下,有三种类型的清单文件需要合并成一个最终的应用程序清单,这里按照优先级顺序列出:

  1. Product flavors 和构建类型所指定的清单文件。

  2. 应用程序的主清单文件。

  3. 类库的清单文件。


第一种类型的清单文件通常会重写清单的内容,因为它专门提供用于特定的交付的应用程序。然后,第三种类型的清单文件通常会被合并入上一步产生的主清单中。合并的规则取决于每个节点类型,可以使用“tools:”命名空间属性来更改。


由于多个product flavors和构建类型,这些清单文件合并的结果的组合可能会是一个矩阵。然而,对于每一个组装的步骤,每一个flavor group和构建类型的值都只能选择一个,导致出现了潜伏的覆盖主清单文件的清单文件的排序列表。


例如,下面的 FlavorGroups: abi,density,API,Prod/Internal 导致形成了以下可能的flavors的矩阵:



ABI

Density

API

Prod/Internal

x86

mdpi

9

prod

arm

high

14

internal

mips

xhigh

15

  

xxhigh

  

这样就形成了3x4x3x2 种可能的组合。然而,对于每一次执行组装,只能是一个group里的falvor,定义在原始的 build.gradle 的flavor group 的属性形成了一个可能要合并的清单 文件的列表,并且这个列表由高优先级到低优先级排序。


例如,构建 x86-high-15-prod 的 variant ,会查找以下的清单文件以进行合并

  1. x86/AndroidManifest.xml

  2. high/AndroidManifest.xml

  3. 15/AndroidManifest.xml

  4. internal/AndroidManifest.xml


在这个有序列表中,每个文件根据其在列表中的顺序都有一个优先级,合并工具将使用这个优先级来决定哪个XML元素或属性将覆盖一个较低优先级的设置。


因此,合并工具的输入将如下:

  • 由flavor或编译类型的清单文件按优先级组成的有序列表,这些会被经常引用作为 flavors的清单。

  • 主清单文件

  • 由一些类库声明(或依赖传递而有的)的清单文件组成的有序列表。

  • 用于占位符和XML产生的注入值

Android 清单文件合并


在一个清单文件中的每个元素都可以根据它的元素类型(比如activity,intent-filter)和一个可选的键值(key value)来识别。一些元素,如“activity”,必须有一个key,因为在一个Andr​​oidManifest.xml中可能存在多个这样的元素。其他的元素,像“application”,就不要求一定要有一个key,因为只能有一个这样的元素。


元素的类型和键值对代表了一个清单元素的身份。


合并的activity的始终是两个相同类型的元素之间,一个来自更高优先级的清单文件,一个来自较低优先级的清单文件。每一个合并的activity都有一个默认的行为,这一点将在随后进行描述。此外,在节点或一个指定的属性上的每个默认合并的activity都可能会被including工具的指定标记所覆盖。


合并过程中还会把对每一个节点的合并结果记录下来,这将在“日志”章节描述。

元素和属性的合并过程

隐式声明

一些属性都会有默认值(默认定义在在线 文档中)。当一个较高优先级的元素没有定义默认值为X的属性时,如果一个较低优先级的元素也恰好定义了一个值同样是X的属性,那么这个属性仍然会被添加到合并的元素中 (当然它的值是X), 因为它表示了类库对这个属性的值的一个明确的选择,从而不去考虑把默认值作为它的值,而是为某特性设置一个正确的值(以防默认值发生改变)。


大多数有默认值的属性,如果在低优先级的属性中已经定义了值,那么默认值将被manifest合并工具忽略;定义的值会被合并,因为在默认值 和一个设置的值之间,并没有冲突。在生成的合并的元素中,这个属性会被设置为设定的值。


然而,下面列出的几种情况例外:


<uses-feature android:required>

默认值为 true。在与其他属性合并时,将使用“或”的合并策略。这时因为如果任何一个库需要该特性,那么生成的应用程序也将需要此特性。

<uses-library android:required>

同 uses-feature:required。

<uses-sdk android:minSdkVersion>

默认值为 1。

将使用更高优先级文件的版本,但导入一个较新版本的库时将会产生错误。      

<uses-sdk android:maxSdkVersion>

同 uses-sdk:minSdkVersion

<uses-sdk android:targetSdkVersion>

同 uses-sdk:minSdkVersion


自动升级

当导入一个target SDK 版本比项目低的库时,它可能需要显式声明地授予权限 (可能还需要进行其他更改),以使得类库在以后运行时能正常运行。这将由清单合并工具自动进行。

占位符支持

当属性值包含一个占位符 (见下面的格式)时,合并工具将把此占位符的值换成一个注入的值。注入的值是在build.gradle里面定义的。

占位符值的语法是 ${name},因为@符号已经预留给了链接。在最后的文件合并发生之后,并且生成合并后的 android 的清单文件输出之前,带有占位符的所有值将都会被替换为注入的值。如果变量名是未知的,将导致构建失败。


占位符字符串可以有一个前缀或后缀,以实现只替换部分的内容。


示例:


android:authority="${applicationId}.foo"

android:authority=”com.acme.${localApplicationId}”

android:authority=”com.acme.${localApplicationId}.foo”


隐式占位符 ${applicationId} 的值将由现有的build.gradle的 applicationId值自动提供。


示例:


<activity

android:name=".Main">

    <intent-filter>

    <action android:name="${applicationId}.foo">

        </action>

</intent-filter>

</activity>


通过以下的gradle的声明:


android {

   compileSdkVersion 19

   buildToolsVersion "19.0.2"


   productFlavors {

       flavor1 {

           applicationId = "com.android.tests.flavorlib.app.flavor1"

       }

}


一旦合并,<action android:name> 将会是

<action android:name=“com.android.tests.flavorlib.app.flavor1.foo”>


对于自定义的占位符替换,可以使用以下的 DSL 来配置占位符的值:


android { defaultConfig { manifestPlaceholders = [ activityLabel:"defaultName"] } productFlavors { free { } pro { manifestPlaceholders = [ activityLabel:"proName" ] } }


它将替换下面声明中的占位符:
<activity android:name=".MainActivity"android:label="${activityLabel}" >

合并策略的常见描述


XML的合并可能是在节点级别上的合并,也可能是在属性级别上的合并。


在节点级别上,默认的合并策略是,只要没有冲突就合并属性和子元素。当两个相同标识的元素具有相同的属性,并且属性的值不同时,就会出现冲突。


举个例子:

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@theme1”/>


与下面的声明进行合并时不会产生冲突:

<activity

 android:name="com.foo.bar.ActivityOne"

android:screenOrientation=”landscape/>


同样

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@theme1”/>


与下面的声明合并时也不会产生冲突,因为在这两个元素中都定义的"theme"属性具有相同的值。

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme="@theme1"

android:screenOrientation=”landscape/>


但是,

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@theme1”/>


与下面的声明合并时就会产生冲突,因为在这两个元素中定义的"theme"属性的值并不相同。

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@theme2”

android:screenOrientation=”landscape/>


现在,每个元素都可以有子元素和规则,用于匹配那些将遵循同样的基本原则的属性,它们具有相同标识的子元素将匹配在一起。如果一个子元素仅存在于其中一个父元素,并不会冲突。


标记


标记是在工具(tools)命名空间中的一个特别的属性,用来描述对如何解决冲突所采取的决定。


所有标记都属于 Android 工具命名空间,因此您必须包含至少一个标记的任何 AndroidManifest.xml 中声明该命名空间:

xmlns:tools="http://schemas.android.com/tools"


示例:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.android.tests.flavorlib.app"

   xmlns:tools="http://schemas.android.com/tools">


   <application

       android:icon="@drawable/icon"

       android:label="@string/app_name"

       tools:replace=”icon, label”/>

</manifest>


当要合并的元素之间存在冲突时,必须显式添加一些标记来指导清单合并工具(Manifest Marger)。在节点级别,应使用 tools:node 属性,在属性级别,应使用 tools:attr 属性。


tools:node 标记


当一个节点存在冲突并且需要解决时,就应该要有一个 tools:node="maker_value" 的属性存在。  


<tools:node> 属性值

清单合并工具的行为

<tools:node="merge">

这是节点合并的隐式的默认模式,节点只要不冲突就会被合并。

<tools:node="replace">

用注解的那一个替换低优先级的声明。

<tools:node="strict">

当另一个具有相同标识的节点存在并且不是严格相等时,将导致构建失败。

<tools:node="merge-only-attributes">

只合并较低优先级的声明中的属性。

<tools:node="remove">

从生成的 XML 中删除所注解的元素。不论是否可能冲突,低优先级的声明都不会被合并进去。

<tools:node="removeAll">

移除所有相同节点类型(不是关键必须的)的元素 。


tools:attr 标记


在任何特别的元素中,可能会有许多与标记相关的属性,用于解决所有属性的冲突问题。


<tools:strict=”x, y, z”>

属性默认的隐式模式,会在当尝试合并有不同值的低优先级属性声明时产生错误。

<tools:remove=”x, y, z”>

当合并时,从任何较低优先级的声明中删除 x、 y、 z 属性。

<tools:replace=”x, y, z”>

把任何低优先级声明的x,y,z属性的值替换为所提供的值(必须是在同一节点上)。


选择器


每一个 tools:node 或 tools:attr 声明都可以通过一个 tools:selector 的属性进行扩展,这个属性是合并策略是否应该被应用到当前的低优先级的 XML 的描述的上下文信息。例如,当仅在一个特定的库,而不是任何的库,才需要删除一个权限时,它会非常有用:


   <permission

         android:name="permissionOne"

         tools:node="remove"

         tools:selector="com.example.lib1">

tools:overrideLibrary 标记

这是一个特殊的标记,仅与use-sdk的声明一起使用,用于在导入的库的最小SDK版本比应用程序的最小SDK版本还要新时,对这个库是否导入进行重写。
如果没有这样的标志,清单合并就会失败。这个标记将允许用户忽略最低的SDK版本而选择哪些库可以被导入。 

例如,在main android 清单中: 
<uses-sdk android:targetSdkVersion="14" android:minSdkVersion="2"

tools:overrideLibrary="com.example.lib1, com.example.lib2"/>


将允许具有以下清单的库被导入而不出错: 
<manifestxmlns:android="http://schemas.android.com/apk/res/android"

        package="com.example.lib1">        <uses-sdk android:minSdkVersion="4" />    </manifest>

日志


在清单合并期间的每个操作或决策都需要是

  • 被记录的

  • 枨式化以让计算机能够解析的

  • 按节点排序的 (因为一个节点的多个属性可能会产生好几种合并决定)


日志记录不会被组织为最终产生输出文件的事件和决策的线性集合。相反,为了方便开发人员,日志文件将按输入文件中发生冲突的顶级XML节点来进行组织 (无论当我们想想文件的节点删除的时候,它是否存在 于输出文件中)。

日志记录:


一个日志记录既是一个节点记录 (描述在该特定节点上采取的所有操作),也是一个包含错误消息和警告的消息记录。


日志文件 = 日志记录*

日志记录 = 节点记录 | 消息

消息

消息=文件:行号:列号 严重性:\n描述

描述=(\t内容\n)*


名称

file

生成日志条目的输入文件

line-number

生成日志条目的输入的文件行号

column-number

生成日志条目的输入文件列号

严重性

Error, Warning, Info

description

日志条目有效载荷(译者注:有效载荷即记载着信息的那部分数据)


示例


/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:

Attribute activity@screenOrientation value=(portrait) from AndroidManifest.xml:3:9

is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)

Suggestion: add 'tools:replace="icon"' to <activity> element at AndroidManifest.xml:1:5 to override

节点记录


名称

node-type

XML 节点类型

node-key

节点的键的属性值

record-type

[Action |Log ] *


node-type#node-key\n

\t(node_action:Action)*

\t\t(attribute_action:Action)*

操作格式

名称

action-type

added | rejected | implied

target

node | attribute

target-name

节点的键名称或属性名称

origin

原始值的位置

示例:

application

ADDED from AndroidManifest.xml:10:5

MERGED from flavorlib:lib2:unspecified:3:5

android:label

ADDED from AndroidManifest.xml:12:9

REJECTED from flavorlib:lib2:unspecified:3:55

android:icon

ADDED from AndroidManifest.xml:11:9

REJECTED from flavorlib:lib2:unspecified:3:18



receiver#com.example.WidgetReceiver

ADDED from ManifestMerger2Test0_main.xml:60:9

android:labelADDED from ManifestMerger2Test0_main.xml:61:13

android:iconADDED from ManifestMerger2Test0_main.xml:62:13

android:nameADDED from ManifestMerger2Test0_main.xml:63:13

构建错误


当发生构建错误时,则应显示特定节点失败的日志,并在后面有一段向用户描述的错误消息。举个例子:


更高优先级的声明

<activity

 android:name="com.foo.bar.ActivityOne"

android:screenOrientation="portrait"

android:theme=”@theme1”/>


和一个较低优先级的声明:

<activity

 android:name="com.foo.bar.ActivityOne"

android:screenOrientation=”landscape/>


会同时产生日志文件和输出结果(能让人类,计算机以及IDE都能识别的确切格式还未确定)。


/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:

Attribute activity@screenOrientation value=(portrait) from AndroidManifest.xml:3:9

is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)

Suggestion: add 'tools:replace="icon"' to <activity> element at AndroidManifest.xml:1:5 to override


Blame

通过元素或属性所在的一些指示,可以获取一个“blame”类型的输出,以限制合并的XML中产生的每一个元素和属性。

合并策略


每个元素类型都有一特定的默认合并策略附属于它。例如,大部分的元素类型,像activity或application都有一个默认合并策略,这个策略是所有属性和子元素都会被合并(假设没有冲突)到所生产的元素当中。不过,其他元素,如顶级的manifest,默认合并策略是只合并子元素。这意味着较低优先级的 AndroidManifest.xml 的manifest 元素的属性都没有资格能够合并进去。


每个元素也可以拥有或不拥有一个与它关联的键。例如,application没有键,在每一个 AndroidManifest.xml 中只能有一个 <application>元素。 大部分带键的元素都使用“ android:name 属性”来表示它们的键值,或其他内容等

合并

没有冲突的属性会被合并,子元素也会依据它们各自的合并策略进行合并。

只合并子元素

属性不会被合并,只有子元素会根据它们各自的合并政策进行合并。

总是合并

始终保持元素的“原样”,并添加到生成的合并文件里的共同的父元素中。


元素合并策略和键的列表


节点类型

合并策略

action

合并

android:name 属性

activity

合并

android:name 属性

application

合并

没有键

category

合并

android:name 属性

data

合并

没有键

grant-uri-permission

合并

没有键

instrumentation

合并

android:name 属性

intent-filter

总是合并

子元素的action和categories android: name attribute。允许相同的键有多个声明。

manifest

只合并子元素

没有键

meta-data

合并

android:name 属性

path-permission

合并

没有键

permission-group

合并

android:name 属性

permission

合并

android:name 属性

permission-tree

合并

android:name 属性

provider

合并

android:name 属性

receiver

合并

android:name 属性

screen

合并

属性 screenSize

service

合并

android:name 属性

supports-gl-texture

合并

android:name 属性

supports-screen

合并

没有键

uses-configuration

合并

没有键

uses-feature

合并

首先是属性名称,如果不存在,则接着是 glEsVersion 属性

uses-library

合并

android:name 属性

uses-permission

合并

android:name 属性

uses-sdk

合并

没有键

自定义元素

合并

没有键


包名称智能替换

有些属性是包依赖属性,意思就是说这些属性支持通过由清单节点中的package属性提供的包设置,对部分完全限定的类名称的智能替换。


下面描述的每个属性都可以有一个局部的类名称,这个类名称是以一个点或不包含任何点开头的。


示例:


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.app1">


   <application>

       <activity android:name=".Main" />

   </application>

</manifest>


将被扩充成:


   <application>

       <activity android:name="com.example.app1.Main" />

   </application>


这是独立于build.gradle里的任何包设置的(译者注,关于package和applicationId可以看一下我的系列博客中的另一篇文章的介绍)。例如,你build.gradle 包含以下内容:


android {

   compileSdkVersion 19

   buildToolsVersion "19.0.2"


   productFlavors {

       flavor1 {

           applicationId = "com.android.tests.flavorlib.app.flavor1"

       }

}


扩充的结果仍然是

<activity android:name=”com.example.app1.Main”>


如果你需要让注入的值作为扩充的属性值,可以使用 ${applicationId} 占位符,例如:


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.app1">


   <application>

       <activity android:name="${applicationId}.Main" />

   </application>

</manifest>


下面是可以使用这种智能替换的能力包独立属性的列表:

节点类型

属性的本地名称

activity

name, parentActivityName

activity-alias

name, targetActivity

application

name, backupAgent

instrumentation

name

provider

name

receiver

name

service

name


属性标记示例

重写来自库的属性


使用 tools:replace="x, y, z" 将会重写从外部库的activity 的XML声明中导入的 x,y,z 属性。


更高级别的声明

<activity

 android:name="com.foo.bar.ActivityOne"

android:screenOrientation="portrait"

android:theme="@theme1"

tools:replace=”theme”/>


和一个较低优先级的声明:

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme="@olddogtheme"

android:windowSoftInputMode="stateUnchanged"

android:exported="true" >


将产生:

<activity

 android:name="com.foo.bar.ActivityOne"

android:screenOrientation="portrait"

android:theme="@theme1"

android:windowSoftInputMode="stateUnchanged"

android:exported="true"/>

删除来自库的属性。


使用 tools:remove="x, y, z" 将会在产生的XML中删除 x,y,z 属性的声明。


更高优先级的声明

<activity

 android:name="com.foo.bar.ActivityOne"

android:hardwareAccelerated="true"

tools:remove="android:theme,android:screenOrientation" />


和一个较低优先级的声明:

<activity

 android:name="com.foo.bar.ActivityOne"

android:screenOrientation="landscape"

android:theme="@olddogtheme"

android:windowSoftInputMode="stateUnchanged"

android:exported="true"/>


将产生:

<activity

 android:name="com.foo.bar.ActivityOne"

android:hardwareAccelerated="true"

android:windowSoftInputMode="stateUnchanged"

android:exported="true"/>


强制更新属性值


毫无疑问,所有声明属性几乎都带有“strict”的合并策略,所以如果两个要合并的元素都有一个同样名称的属性但值却不同,就是一个需要明确解决的冲突。


所以,一个较高优先级的声明

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@newdogtheme”/>


和一个较低优先级的声明:

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@olddogtheme”/>


与一个较高优先级的声明

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@newdogtheme”

tools:strict=”theme”/>


和一个较低优先级的声明:

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme=”@olddogtheme”/>


是完全等价的,并且都将不能正确地合并,除非添加一个 tools:replace="theme" 的属性。

混合操作


如果用户想要删除某些属性且重写其他属性同时保存另一组的原始属性,只需依次添加所有标记。


例如:

<activity

 android:name="com.foo.bar.ActivityOne"

android:windowSoftInputMode="stateUnchanged"

android:theme="@theme1"

tools:remove="android:exported, android:screenOrientation"

tools:replace="android:theme"/>


和一个较低优先级的声明:

<activity

 android:name="com.foo.bar.ActivityOne"

android:screenOrientation="landscape"

android:theme="@olddogtheme"

android:exported="true"/>


将产生:

<activity

 android:name="com.foo.bar.ActivityOne"

android:theme="@theme1"

android:windowSoftInputMode="stateUnchanged" />


需要注意的是,如果低优先级的声明中包含 android:windowSoftInputMode或者未明确标记为删除或替换的任何属性,将生成一个构建错误。

元素标记示例

移除元素

如果要删除任何一个库的某个元素,需要在更高优先级的文件中声明

<activity-alias

android:name="foo.bar.alias">

<meta-data

android:name="zoo"

tools:node="remove"/>

</activity-alias>


与下面进行合并

<activity-alias

android:name="foo.bar.alias">

<meta-data

android:name="zoo"

android:value="@string/bear"/>

</activity-alias>


将产生:

<activity-alias

android:name="foo.bar.alias">

</activity-alias>


移除所有元素

如果要作任何一个库的一个特定类型的所有元素,需要在更高优先级的文件中声明

<activity-alias

android:name="foo.bar.alias">

<meta-data  

tools:node="removeAll" />

</activity-alias>


与下面进行合并

<activity-alias

android:name="foo.bar.alias">

<meta-data

android:name="zoo"

android:value="@string/bear"/>

<meta-data

android:name="cage"

android:value="@string/iron"/>

</activity-alias>


将产生:

<activity-alias

android:name="foo.bar.alias"

</activity-alias>


元素替换

<activity-alias

android:name="foo.bar.alias"

tools:node="replace">

<meta-data

android:name="zoo"/>

</activity-alias>


与下面进行合并

<activity-alias

android:name="foo.bar.alias">

<meta-data

android:name="cage"

android:value="@string/iron"/>

</activity-alias>


将产生:

<activity-alias

android:name="foo.bar.alias">

<meta-data

android:name="zoo"

tools:node="remove"/>

</activity-alias>

选择器示例

使用包名称来选择库这里,我们有三个库要合并进一个主清单文件中。

主清单

<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   xmlns:tools="http://schemas.android.com/tools"

   package="com.example.main">


   <permission

         android:name="permissionOne"

         tools:node="remove"

         tools:selector="com.example.lib1">

   </permission>

   <permission

         tools:node="removeAll"

         tools:selector="com.example.lib3">

   </permission>

   <permission

            android:name="permissionThree"

            android:protectionLevel="signature"

            tools:node="replace">

   </permission>


</manifest>


与库1 


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.lib1">


   <permission android:name="permissionOne"

            android:protectionLevel="signature">

   </permission>

   <permission android:name="permissionTwo"

            android:protectionLevel="signature">

   </permission>

</manifest>


和库2 


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.lib2">


   <permission android:name="permissionThree"

            android:protectionLevel="normal">

   </permission>

   <permission android:name="permissionFour"

            android:protectionLevel="normal">

   </permission>

</manifest>


及库3 


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.lib2">


   <permission android:name="permissionFive"

            android:protectionLevel="normal">

   </permission>

</manifest>


将产生:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.main" >


   <permission

       android:name="permissionThree"

       android:protectionLevel="signature" >

   </permission>

   <permission

       android:name="permissionTwo"

       android:protectionLevel="signature" >

   </permission>

   <permission

       android:name="permissionFour"

       android:protectionLevel="normal" >

   </permission>


</manifest>



---------------------------------------------------------------

关于AndroidManifest.xml详解

AndroidManifest.xml 是每个android程序中必须的文件。它位于整个项目的根目录,描述了package中暴露的组件(activities, services, 等等),他们各自的实现类,各种能被处理的数据和启动位置。 除了能声明程序中的Activities, ContentProviders, Services, 和Intent Receivers,还能指定permissions和instrumentation(安全控制和测试)

二、AndroidManifest.xml结构

<?xmlversion="1.0"encoding="utf-8"?>

<manifest>

    <application>

       <activity>

           <intent-filter>

               <action/>

               <category/>

           </intent-filter>

      </activity>

       <activity-alias>

           <intent-filter></intent-filter>

           <meta-data/>

      </activity-alias>

       <service>

           <intent-filter></intent-filter>

           <meta-data/>

       </service>

       <receiver>

           <intent-filter></intent-filter>

           <meta-data/>

       </receiver>

       <provider>

           <grant-uri-permission/>

           <meta-data/>

       </provider>

       <uses-library/>

    </application>

    <uses-permission/>

    <permission/>

    <permission-tree/>

    <permission-group/>

    <instrumentation/>

    <uses-sdk/>

    <uses-configuration/> 

    <uses-feature/> 

    <supports-screens/>

</manifest>

三、各个节点的详细介绍

 上面就是整个am(androidManifest).xml的结构,下面以外向内开始阐述~~

1、第一层(<Manifest>):(属性)

<manifest  xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.woody.test"
          android:sharedUserId="string"
          android:sharedUserLabel="string resource"
          android:versionCode="integer"
          android:versionName="string"
          android:installLocation=["auto" | "internalOnly" | "preferExternal"] >
</manifest>

A、xmlns:android

定义android命名空间,一般为http://schemas.android.com/apk/res/android,这样使得Android中各种标准属性能在文件中使用,提供了大部分元素中的数据。


B、package

指定本应用内java主程序包的包名,它也是一个应用进程的默认名称


C、sharedUserId

表明数据权限,因为默认情况下,Android给每个APK分配一个唯一的UserID,所以是默认禁止不同APK访问共享数据的。若要共享数据,第一可以采用Share Preference方法,第二种就可以采用sharedUserId了,将不同APK的sharedUserId都设为一样,则这些APK之间就可以互相共享数据了。详见:http://wallage.blog.163.com/blog/static/17389624201011010539408/

D、sharedUserLabel

一个共享的用户名,它只有在设置了sharedUserId属性的前提下才会有意义


E、versionCode

是给设备程序识别版本(升级)用的必须是一个interger值代表app更新过多少次,比如第一版一般为1,之后若要更新版本就设置为2,3等等。。。


F、versionName

这个名称是给用户看的,你可以将你的APP版本号设置为1.1版,后续更新版本设置为1.2、2.0版本等等。。。


G、installLocation

安装参数,是Android2.2中的一个新特性,installLocation有三个值可以选择:internalOnly、auto、preferExternal

选择preferExternal,系统会优先考虑将APK安装到SD卡上(当然最终用户可以选择为内部ROM存储上,如果SD存储已满,也会安装到内部存储上)

选择auto,系统将会根据存储空间自己去适应

选择internalOnly是指必须安装到内部才能运行

(注:需要进行后台类监控的APP最好安装在内部,而一些较大的游戏APP最好安装在SD卡上。现默认为安装在内部,如果把APP安装在SD卡上,首先得设置你的level为8,并且要配置android:installLocation这个参数的属性为preferExternal)


2、第二层(<Application>):属性

一个AndroidManifest.xml中必须含有一个Application标签,这个标签声明了每一个应用程序的组件及其属性(如icon,label,permission等)

<application  android:allowClearUserData=["true" | "false"]
             android:allowTaskReparenting=["true" | "false"]
             android:backupAgent="string"
             android:debuggable=["true" | "false"]
             android:description="string resource"
             android:enabled=["true" | "false"]
             android:hasCode=["true" | "false"]
             android:icon="drawable resource"
             android:killAfterRestore=["true" | "false"]
             android:label="string resource"
             android:manageSpaceActivity="string"
             android:name="string"
             android:permission="string"
             android:persistent=["true" | "false"]
             android:process="string"
             android:restoreAnyVersion=["true" | "false"]
             android:taskAffinity="string"
             android:theme="resource or theme" >
</application>

A、android:allowClearUserData('true' or 'false')

用户是否能选择自行清除数据,默认为true,程序管理器包含一个选择允许用户清除数据。当为true时,用户可自己清理用户数据,反之亦然


B、android:allowTaskReparenting('true' or 'false')

是否允许activity更换从属的任务,比如从短信息任务切换到浏览器任务


C、android:backupAgent

这也是Android2.2中的一个新特性,设置该APP的备份,属性值应该是一个完整的类名,如com.project.TestCase,此属性并没有默认值,并且类名必须得指定(就是个备份工具,将数据备份到云端的操作)


D、android:debuggable

这个从字面上就可以看出是什么作用的,当设置为true时,表明该APP在手机上可以被调试。默认为false,在false的情况下调试该APP,就会报以下错误:

Device XXX requires that applications explicitely declare themselves as debuggable in their manifest.

 Application XXX does not have the attribute 'debuggable' set to TRUE in its manifest and cannot be debugged.


E、android:description/android:label

此两个属性都是为许可提供的,均为字符串资源,当用户去看许可列表(android:label)或者某个许可的详细信息(android:description)时,这些字符串资源就可以显示给用户。label应当尽量简短,之需要告知用户该许可是在保护什么功能就行。而description可以用于具体描述获取该许可的程序可以做哪些事情,实际上让用户可以知道如果他们同意程序获取该权限的话,该程序可以做什么。我们通常用两句话来描述许可,第一句描述该许可,第二句警告用户如果批准该权限会可能有什么不好的事情发生


F、android:enabled

Android系统是否能够实例化该应用程序的组件,如果为true,每个组件的enabled属性决定那个组件是否可以被 enabled。如果为false,它覆盖组件指定的值;所有组件都是disabled。


G、android:hasCode('true' or 'false')

表示此APP是否包含任何的代码,默认为true,若为false,则系统在运行组件时,不会去尝试加载任何的APP代码

一个应用程序自身不会含有任何的代码,除非内置组件类,比如Activity类,此类使用了AliasActivity类,当然这是个罕见的现象

(在Android2.3可以用标准C来开发应用程序,可在androidManifest.xml中将此属性设置为false,因为这个APP本身已经不含有任何的JAVA代码了)

H、android:icon

这个很简单,就是声明整个APP的图标,图片一般都放在drawable文件夹下

I、android:killAfterRestore

J、android:manageSpaceActivity

K、android:name

为应用程序所实现的Application子类的全名。当应用程序进程开始时,该类在所有应用程序组件之前被实例化。

若该类(比方androidMain类)是在声明的package下,则可以直接声明android:name="androidMain",但此类是在package下面的子包的话,就必须声明为全路径或android:name="package名称.子包名成.androidMain"

L、android:permission

设置许可名,这个属性若在<application>上定义的话,是一个给应用程序的所有组件设置许可的便捷方式,当然它是被各组件设置的许可名所覆盖的

M、android:presistent

该应用程序是否应该在任何时候都保持运行状态,默认为false。因为应用程序通常不应该设置本标识,持续模式仅仅应该设置给某些系统应用程序才是有意义的。

N、android:process

应用程序运行的进程名,它的默认值为<manifest>元素里设置的包名,当然每个组件都可以通过设置该属性来覆盖默认值。如果你想两个应用程序共用一个进程的话,你可以设置他们的android:process相同,但前提条件是他们共享一个用户ID及被赋予了相同证书的时候

O、android:restoreAnyVersion

同样也是android2.2的一个新特性,用来表明应用是否准备尝试恢复所有的备份,甚至该备份是比当前设备上更要新的版本,默认是false

P、android:taskAffinity

拥有相同的affinity的Activity理论上属于相同的Task,应用程序默认的affinity的名字是<manifest>元素中设定的package名


Q、android:theme

是一个资源的风格,它定义了一个默认的主题风格给所有的activity,当然也可以在自己的theme里面去设置它,有点类似style。

3、第三层(<Activity>):属性

<activity android:allowTaskReparenting=["true" | "false"]
          android:alwaysRetainTaskState=["true" | "false"]
          android:clearTaskOnLaunch=["true" | "false"]
          android:configChanges=["mcc", "mnc", "locale",
                                 "touchscreen", "keyboard", "keyboardHidden",
                                 "navigation", "orientation", "screenLayout",
                                 "fontScale", "uiMode"]
          android:enabled=["true" | "false"]
          android:excludeFromRecents=["true" | "false"]
          android:exported=["true" | "false"]
          android:finishOnTaskLaunch=["true" | "false"]
          android:icon="drawable resource"
          android:label="string resource"
          android:launchMode=["multiple" | "singleTop" |
                              "singleTask" | "singleInstance"]
          android:multiprocess=["true" | "false"]
          android:name="string"
          android:noHistory=["true" | "false"]  
          android:permission="string"
          android:process="string"
          android:screenOrientation=["unspecified" | "user" | "behind" |
                                     "landscape" | "portrait" |
                                     "sensor" | "nosensor"]
          android:stateNotNeeded=["true" | "false"]
          android:taskAffinity="string"
          android:theme="resource or theme"
          android:windowSoftInputMode=["stateUnspecified",
                                       "stateUnchanged", "stateHidden",
                                       "stateAlwaysHidden", "stateVisible",
                                       "stateAlwaysVisible", "adjustUnspecified",
                                       "adjustResize", "adjustPan"] >   
</activity>

(注:有些在application中重复的就不多阐述了)

1、android:alwaysRetainTaskState

 是否保留状态不变, 比如切换回home, 再从新打开,activity处于最后的状态。比如一个浏览器拥有很多状态(当打开了多个TAB的时候),用户并不希望丢失这些状态时,此时可将此属性设置为true

2、android:clearTaskOnLaunch 
比如 P 是 activity, Q 是被P 触发的 activity, 然后返回Home, 重新启动 P,是否显示 Q

3、android:configChanges

当配置list发生修改时, 是否调用 onConfigurationChanged() 方法  比如 "locale|navigation|orientation". 
这个我用过,主要用来看手机方向改变的. android手机在旋转后,layout会重新布局, 如何做到呢?
正常情况下. 如果手机旋转了.当前Activity后杀掉,然后根据方向重新加载这个Activity. 就会从onCreate开始重新加载.
如果你设置了 这个选项, 当手机旋转后,当前Activity之后调用onConfigurationChanged() 方法. 而不跑onCreate方法等.

4、android:excludeFromRecents

是否可被显示在最近打开的activity列表里,默认是false

5、android:finishOnTaskLaunch

当用户重新启动这个任务的时候,是否关闭已打开的activity,默认是false

如果这个属性和allowTaskReparenting都是true,这个属性就是王牌。Activity的亲和力将被忽略。该Activity已经被摧毁并非re-parented


6、android:launchMode(Activity加载模式)

在多Activity开发中,有可能是自己应用之间的Activity跳转,或者夹带其他应用的可复用Activity。可能会希望跳转到原来某个Activity实例,而不是产生大量重复的Activity。这需要为Activity配置特定的加载模式,而不是使用默认的加载模式

Activity有四种加载模式:

standard、singleTop、singleTask、singleInstance(其中前两个是一组、后两个是一组),默认为standard 
 

standard:就是intent将发送给新的实例,所以每次跳转都会生成新的activity。

singleTop:也是发送新的实例,但不同standard的一点是,在请求的Activity正好位于栈顶时(配置成singleTop的Activity),不会构造新的实例

singleTask:和后面的singleInstance都只创建一个实例,当intent到来,需要创建设置为singleTask的Activity的时候,系统会检查栈里面是否已经有该Activity的实例。如果有直接将intent发送给它。

singleInstance:

首先说明一下task这个概念,Task可以认为是一个栈,可放入多个Activity。比如启动一个应用,那么Android就创建了一个Task,然后启动这个应用的入口Activity,那在它的界面上调用其他的Activity也只是在这个task里面。那如果在多个task中共享一个Activity的话怎么办呢。举个例来说,如果开启一个导游服务类的应用程序,里面有个Activity是开启GOOGLE地图的,当按下home键退回到主菜单又启动GOOGLE地图的应用时,显示的就是刚才的地图,实际上是同一个Activity,实际上这就引入了singleInstance。singleInstance模式就是将该Activity单独放入一个栈中,这样这个栈中只有这一个Activity,不同应用的intent都由这个Activity接收和展示,这样就做到了共享。当然前提是这些应用都没有被销毁,所以刚才是按下的HOME键,如果按下了返回键,则无效

7、android:multiprocess

是否允许多进程,默认是false

具体可看该篇文章:http://www.bangchui.org/simple/?t3181.html

8、android:noHistory

当用户从Activity上离开并且它在屏幕上不再可见时,Activity是否从Activity stack中清除并结束。默认是false。Activity不会留下历史痕迹

9、android:screenOrientation

activity显示的模式

默认为unspecified:由系统自动判断显示方向

landscape横屏模式,宽度比高度大

portrait竖屏模式, 高度比宽度大

user模式,用户当前首选的方向

behind模式:和该Activity下面的那个Activity的方向一致(在Activity堆栈中的)

sensor模式:有物理的感应器来决定。如果用户旋转设备这屏幕会横竖屏切换

nosensor模式:忽略物理感应器,这样就不会随着用户旋转设备而更改了

10、android:stateNotNeeded

activity被销毁或者成功重启时是否保存状态

11、android:windowSoftInputMode

activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Android1.5后的一个新特性。

这个属性能影响两件事情:

【A】当有焦点产生时,软键盘是隐藏还是显示

【B】是否减少活动主窗口大小以便腾出空间放软键盘

各值的含义:

【A】stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置

【B】stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示

【C】stateHidden:用户选择activity时,软键盘总是被隐藏

【D】stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏的

【E】stateVisible:软键盘通常是可见的

【F】stateAlwaysVisible:用户选择activity时,软键盘总是显示的状态

【G】adjustUnspecified:默认设置,通常由系统自行决定是隐藏还是显示

【H】adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间

【I】adjustPan:当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分

4、第四层(<intent-filter>)

结构图:

<intent-filter  android:icon="drawable resource"
               android:label="string resource"
               android:priority="integer" >

      <action />

      <category />

      <data />

</intent-filter> 
 

intent-filter属性

android:priority(解释:有序广播主要是按照声明的优先级别,如A的级别高于B,那么,广播先传给A,再传给B。优先级别就是用设置priority属性来确定,范围是从-1000~1000,数越大优先级别越高)


Intent filter内会设定的资料包括action,data与category三种。也就是说filter只会与intent里的这三种资料作对比动作


action属性

action很简单,只有android:name这个属性。常见的android:name值为android.intent.action.MAIN,表明此activity是作为应用程序的入口。有关android:name具体有哪些值,可参照这个网址:http://hi.baidu.com/linghtway/blog/item/83713cc1c2d053170ff477a7.html

category属性

category也只有android:name属性。常见的android:name值为android.intent.category.LAUNCHER(决定应用程序是否显示在程序列表里)

有关android:name具体有哪些值,可参照这个网址:http://chroya.javaeye.com/blog/685871

data属性

<data  android:host="string"
      android:mimeType="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:port="string"
      android:scheme="string"/>

【1】每个<data>元素指定一个URI和数据类型(MIME类型)。它有四个属性scheme、host、port、path对应于URI的每个部分: 
scheme://host:port/path

scheme的值一般为"http",host为包名,port为端口号,path为具体地址。如:http://com.test.project:200/folder/etc

其中host和port合起来构成URI的凭据(authority),如果host没有指定,则port也会被忽略

要让authority有意义,scheme也必须要指定。要让path有意义,scheme+authority也必须要指定

【2】mimeType(指定数据类型),若mimeType为'Image',则会从content Provider的指定地址中获取image类型的数据。还有'video'啥的,若设置为video/mp4,则表示在指定地址中获取mp4格式的video文件

【3】而pathPattern和PathPrefix主要是为了格式化path所使用的

5、第四层<meta-data>

<meta-data android:name="string"
           android:resource="resource specification"
           android:value="string"/>

这是该元素的基本结构.可以包含在<activity> <activity-alias> <service> <receiver>四个元素中。

android:name(解释:元数据项的名字,为了保证这个名字是唯一的,采用java风格的命名规范,如com.woody.project.fried)

android:resource(解释:资源的一个引用,指定给这个项的值是该资源的id。该id可以通过方法Bundle.getInt()来从meta-data中找到。)

android:value(解释:指定给这一项的值。可以作为值来指定的数据类型并且组件用来找回那些值的Bundle方法:[getString],[getInt],[getFloat],[getString],[getBoolean])


6、第三层<activity-alias>属性

<activity-alias android:enabled=["true" | "false"]
                android:exported=["true" | "false"]
                android:icon="drawable resource"
                android:label="string resource"
                android:name="string"
                android:permission="string"
                android:targetActivity="string">

<intent-filter/> 
<meta-data/>
</activity-alias>

<activity-alias>是为activity创建快捷方式的,如下实例:


<activity android:name=".shortcut">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

            </intent-filter>

</activity>

 <activity-alias android:name=".CreateShortcuts" android:targetActivity=".shortcut" android:label="@string/shortcut">

    <intent-filter>

             <action android:name="android.intent.action.CREATE_SHORTCUT" />

             <category android:name="android.intent.category.DEFAULT" />

     </intent-filter>

 </activity-alias>

其中android.targetActivity是指向对应快捷方式的activity,如上述的shortcut(此Activity名)

android:label是指快捷方式的名称,而快捷方式的图标默认是给定的application图标


7、第三层<service>

【1】service与activity同级,与activity不同的是,它不能自己启动的,运行在后台的程序,如果我们退出应用时,Service进程并没有结束,它仍然在后台运行。比如听音乐,网络下载数据等,都是由service运行的

【2】service生命周期:Service只继承了onCreate(),onStart(),onDestroy()三个方法,第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法

【3】service与activity间的通信

Service后端的数据最终还是要呈现在前端Activity之上的,因为启动Service时,系统会重新开启一个新的进程,这就涉及到不同进程间通信的问题了(AIDL),Activity与service间的通信主要用IBinder负责。具体可参照:http://zhangyan1158.blog.51cto.com/2487362/491358

【4】

<service android:enabled=["true" | "false"]

         android:exported[="true" | "false"]

         android:icon="drawable resource"

         android:label="string resource"

         android:name="string"

         android:permission="string"

         android:process="string">

</service>

service标签内的属性之前已有描述,在此不重复了~

8、第三层<receiver>

receiver的属性与service一样,这里就不显示了

BroadcastReceiver:用于发送广播,broadcast是在应用程序之间传输信息的一种机制,而BroadcastReceiver是对发送出来的 Broadcast进行过滤接受并响应的一类组件,具体参照http://kevin2562.javaeye.com/blog/686787

9、第三层<provider>属性

<provider android:authorities="list"

          android:enabled=["true" | "false"]

          android:exported=["true" | "false"]

          android:grantUriPermissions=["true" | "false"]

          android:icon="drawable resource"

          android:initOrder="integer"

          android:label="string resource"

          android:multiprocess=["true" | "false"]

          android:name="string"

          android:permission="string"

          android:process="string"

          android:readPermission="string"

          android:syncable=["true" | "false"]

          android:writePermission="string">

           <grant-uri-permission/>

           <meta-data/>

</provider>

contentProvider(数据存储)

【1】android:authorities:

标识这个ContentProvider,调用者可以根据这个标识来找到它

【2】android:grantUriPermission:

对某个URI授予的权限

【3】android:initOrder

10、第三层<uses-library>

用户库,可自定义。所有android的包都可以引用

11、第一层<supports-screens>

<supports-screens  android:smallScreens=["true" | "false"] 
                  android:normalScreens=["true" | "false"] 
                  android:largeScreens=["true" | "false"] 
                  android:anyDensity=["true" | "false"] />

这是在android1.6以后的新特性,支持多屏幕机制

各属性含义:这四个属性,是否支持大屏,是否支持中屏,是否支持小屏,是否支持多种不同密度

12、第二层<uses-configuration />与<uses-feature>性能都差不多

<uses-configuration  android:reqFiveWayNav=["true" | "false"] 
                    android:reqHardKeyboard=["true" | "false"]
                    android:reqKeyboardType=["undefined" | "nokeys" | "qwerty" |   "twelvekey"]
                    android:reqNavigation=["undefined" | "nonav" | "dpad" |  "trackball" | "wheel"]
                    android:reqTouchScreen=["undefined" | "notouch" | "stylus" | "finger"] />

<uses-feature android:glEsVersion="integer"
              android:name="string"
              android:required=["true" | "false"] />

这两者都是在描述应用所需要的硬件和软件特性,以便防止应用在没有这些特性的设备上安装。


13、第二层<uses-sdk />

<uses-sdk android:minSdkVersion="integer"
          android:targetSdkVersion="integer"
          android:maxSdkVersion="integer"/>

描述应用所需的api level,就是版本,目前是android 2.2 = 8,android2.1 = 7,android1.6 = 4,android1.5=3

在此属性中可以指定支持的最小版本,目标版本以及最大版本

14、第二层<instrumentation />

<instrumentation android:functionalTest=["true" | "false"]
                 android:handleProfiling=["true" | "false"]
                 android:icon="drawable resource"
                 android:label="string resource"
                 android:name="string"
                 android:targetPackage="string"/>

 定义一些用于探测和分析应用性能等等相关的类,可以监控程序。在各个应用程序的组件之前instrumentation类被实例化

android:functionalTest(解释:instrumentation类是否能运行一个功能测试,默认为false)

15、<permission>、<uses-permission>、<permission-tree />、<permission-group />区别~

最常用的当属<uses-permission>,当我们需要获取某个权限的时候就必须在我们的manifest文件中声明,此<uses-permission>与<application>同级,具体权限列表请看此处

通常情况下我们不需要为自己的应用程序声明某个权限,除非你提供了供其他应用程序调用的代码或者数据。这个时候你才需要使用<permission> 这个标签。很显然这个标签可以让我们声明自己的权限。比如:

<permission android:name="com.teleca.project.MY_SECURITY" . . . />

那么在activity中就可以声明该自定义权限了,如:


<application . . .>

        <activity android:name="XXX" . . . >

                  android:permission="com.teleca.project.MY_SECURITY"> </activity>

 </application>

当然自己声明的permission也不能随意的使用,还是需要使用<uses-permission>来声明你需要该权限

<permission-group> 就是声明一个标签,该标签代表了一组permissions,而<permission-tree>是为一组permissions声明了一个namespace。这两个标签可以看之前的系列文章。


来源: http://blog.csdn.net/u011200604/article/details/52651146


0 0
原创粉丝点击