之所以写下这篇文章,是因为我最近在Android应用开发过程中常会产生很多疑问,有些甚至是很简单很基础的问题。平时我也买了一些Android开发相关的书,虽然每本书只看了一部分的内容(这个是我个人的问题,不能怨作者,所以说大家不要自己买书,而要劝别人买,买完后借过来读,这样效率还高些),不过遇到问题时还是会重新翻翻。
不过应该是我脑袋不灵光的原因,读这些书对我的问题没有实质性的帮助。看着它们就好像看着作者对着我露出深深地鄙视:这种问题还要说明?你照着我写的做,能用就行了。
好吧,幸好有Android API文档以及网上各路大神们的解答,这才为我的智商充了一点值。我想现在可能也有一小撮和我一样的弱智分子,我们对Android了解得越多,越发现自己对它不了解。
因此才有了这种将我对Android系统所了解内容进行梳理的想法,如果真的有哪位大神无意中看见了这篇文章或者后续文章中存在的错误,还请随手指出,不才感激万分。
好了,不再废话了,下面针对AndroidManifest.xml文件进行说明。
AndroidManifest.xml是一个应用中的配置文件,我们在手机上应用信息中能够查看的内容多数是在这个文件中定义的。比如:应用的名称、应用的版本、应用中有开启了哪些服务、应用申请了哪些权限等等。同时它还包含了一些用户看不到但是对应用来说至关重要的信息,比如用来唯一区别应用的标识、应用适配的Android系统版本等。
下面是一个从Android API官方文档中扒下来的结构说明:<?xml version="1.0" encoding="utf-8"?>
<manifest>
<uses-permission />
<permission />
<permission-tree />
<permission-group />
<instrumentation />
<uses-sdk />
<uses-configuration />
<uses-feature />
<supports-screens />
<compatible-screens />
<supports-gl-texture />
<application>
<activity>
<intent-filter>
<action />
<category />
<data />
</intent-filter>
<meta-data />
</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>
</manifest>
从上面的结构中可以看出,这是一个典型的xml文件,每个xml标签表示一项具体信息,下面我们来对每个标签内容进行说明。
先放上一个最简单的AndroidManifest.xml文件:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.oldsun.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.oldsun.test.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<manifest>标签说明
首先xmlns:android="http://schemas.android.com/apk/res/android"定义了一个xml的命名空间,也就是说它表示该标签包括的内容可以使用该schemas文件进行约束。其中xmlns是xmlnamespace的缩写,而android表示命名空间的别名,后续引用这个命名空间时可以用android:+属性名来表示,例如android:versionCode,而后面的http://schemas.android.com/apk/res/android是android系统本身为我们创建的命名空间,如果使用eclipse等开发工具,可以查看到该命名空间的定义。关于命名空间,后续再进行讨论。
接下来的属性:package="com.oldsun.test",顾名思义指的是应用开发时的包信息,由于Java中为了防止包名重复,开始时将域名颠倒过来作为包名信息,因此沿袭过来的Android同样采用该方式,并且指定该值为应用的唯一标识,它同样还是应用进程的默认名称。
也就是说,如果有两个应用存在同样的包名,在Android系统中就会认为这是同一个应用,顶多存在前后版本的关系。当然了,大家也不用害怕另外一个人开发的应用会将我们的应用替换掉,因为在覆盖安装时,除了包名一致外,还需要安装文件的证书签名一致,关于这部分以后再讨论。
工作上的关系,有时候别人经常向我问一个apk安装文件的标识是什么,有没有一种方法可以不通过反编译文件直接得到包名。实际上,还真有方法。由于apk本质上就是一个zip格式的压缩文件(因此有的手机上下载apk时会自动转换为zip),我们可以通过winrar或者7zip等工具将其进行解压缩,得到AndroidManifest.xml文件。注意,大家不要高兴得太早,这个文件可是一个二进制文件,里面并非xml格式。这样做当然是为了能够让Android系统更有效率地进行操作,不过却给我们造成了部分困扰。
不过没关系,如果只是要查看包名是什么,还是比较容易的。即使将一个文本格式的xml进行二进制转换,里面也不会对数据内容进行编码转换,用UE打开这个文件,可以看到如下内容:
实际上这就是包名信息,只不过每个字节间都插入了00字节,如果使用winrar进行查看,由于00为不可见字符,显示信息就更明显了:
当然了,还有一种最准确的方式是将xml文件重新转换为文档文件,这种反编译的处理后续再进行讨论。
android:versionCode="1"与android:versionName="1.0"用来表示应用的版本信息,其中android:versionCode是用来真正进行版本识别的,也可以给一些应用商店用来进行版本比较,它的值是int类型,越大的值表示版本越高;而android:versionName只是单纯的用于显示使用其中可以为任意字符,一般是数字的点分格式,也有一些包含beta等内容。
其它manifest标签下内容还包括android:sharedUserId、android:sharedUserLabel、android:installLocation。
其中sharedUserId是给应用分配的用户空间,如果设置该项,默认每个应用都在不同的用户空间,这样可以防止不同应用间互相干扰;sharedUserLabel是给sharedUserId设置一个用户可读的标签,只有sharedUserId设置时才有效。关于用户空间的内容后续再讨论。
installLocation是Android2.2系统引入的,它用来设置程序的默认安装位置,按照API文档中的描述,有三个选项可供选择:
android:installLocation=["auto"| "internalOnly" | "preferExternal"]
"internalOnly"应用只能安装在内存中,如果手机内存为空,将无法安装这个应用,应用永远无法安装到外部存储卡中,如果没有installLocation属性,则默认设置该值。
"auto"这种时候应用可能会安装到外部存储中。不过默认情况下还是优先安装到手机内存中,一旦手机内存满了,系统会自动安装到存储卡中。这种应用即使在手机内存中安装了,也可以由用户手工转移到外部存储卡中。
"preferExternal"这种应用优先安装到外部存储卡中,当存储卡不可用时,系统会自动安装到手机内存中。同样的,应用安装后,用户可以手工将应用在手机内存与外部存储卡中互相转移。
<uses-permission>标签说明
这个标签主要是为了声明应用中需要使用的权限,比如网络访问的权限,存储卡访问的权限等,一个应用中所有需要的权限声明信息可以在安装过程中看见(我怀疑大家从来不看吧,下次一定要看看,否则不知不觉中被扣款了就不好玩了)。
假定应用未声明这些权限,但是使用过程中有相应操作,这时会怎么样?这种情况下,我会负责任地告诉他,操作过程中会失败。这时发现Permission denied错误,该知道怎么办了吧?
API文档中定义语法如下:
<uses-permission android:name="string" android:maxSdkVersion="integer" />
< permission >标签说明
这个是权限的定义标签,其中声明了<uses-permission>需要的权限信息。这个标签通常情况下用处不多,主要是为了为其他应用提供可以使用的代码或者数据信息。它还可以与<permission-group>以及<permission-tree>配合使用来构造更有层次的、更有针对性权限系统。
使用举例:如果我们需要一个邮箱进行邮件操作,但是还有一个应用希望能够查询到部分邮件,但是又不能干扰邮箱的处理过程,就可以在邮箱中定义这个权限,然后在其它应用中申请这个权限。当然这种需求在地图应用中也是比较常见。
API文档中定义语法如下:
<permissionandroid:description="string resource"
android:icon="drawable resource"
android:label="stringresource"
android:name="string"
android:permissionGroup="string"
android:protectionLevel=["normal" | "dangerous" |"signature" | "signatureOrSystem"] />
关于权限定义更完善的处理,我们后续再进行讨论。
<instrumentation >标签说明
按照描述信息来说,这个标签是用来声明Instrumentation测试类来监控Android应用的行为并应用到相关的功能测试中。看起来是一个挺有意思的用法,平时对这个标签没有使用过,需要仔细地研究下这个标签的使用方法,下次对这个标签进行详细说明。