欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

Android官方文档之Introduction

来源: 开发者 投稿于  被查看 10676 次 评论:137

Android官方文档之Introduction


写在前面的话:接触Android的时间也不短了,听了视频、看了书、敲了代码,写了博客,做了demo。。。但是想做出一款优秀的APP(哪怕是封装一个不错的功能)还有很长的路要走。于是前些日子我打算更加深入地往底层、往源代码方向研究Android——我就买了一本《Android群英传》拜读一下,在刚读到前言的时候,我发现作者推荐了阅读官方的Training和Guide,我才意识到,其实之前我接触到的各个渠道的Android知识,都是来自官方文档,与其更加深入地了解Android,不如把官方文档的内容好好拜读一下,它才是最好的教材。从今天起,我将努力把Android官方文档翻译一遍,当然不是全部内容,而是我认为重要的知识点,一些很基础的内容就略过了。


本文将从官方文档中最开始的内容《Introduction》说起。
如需访问官方原文,请您点击这个链接:《Introduction to Android》。


Android介绍(Introduction to Android)


Android应用程序提供了多种访问入口
一个Android应用程序由不同的独立组件构成,并且可以独立调用,比如一个Activity可以提供一屏UI显示,一个Service可以用于在后台执行任务。
在一个组件中启动另一个组件,可以使用intent,甚至可以使用intent启动其他一般用程序,这使得一个应用程序可以有多个访问的入口,并且可以让其他应用程序的某个组件看起来好像自己应用的一部分。

一个应用程序可适配不同设备
Android提供了可适配的框架,一种资源可以适配不同的设备。比如一个layout布局会根据不同设备的屏幕自动匹配。开发者可以在应用运行时查看应用程序所需权限,开发者也可以在自己开发的应用中声明所需权限,Google Play可以自动筛选支持应用所需权限的设备,从而减少不必要的适配问题。


应用程序基础(Application Fundamentals)


Android应用程序主要由Java编写,Android SDK工具将代码、资源文件和数据内容打包成APK文件。APK文件包含了该应用程序的所有组件,该APK文件可以在支持Android的设备上安装并使用。
一旦安装成功,应用程序将受到沙箱机制的保护:

Android操作系统是一个多用户的Linux操作系统,每个应用程序就代表一个用户。

默认情况下,系统为每个APP分配一个用户ID(该用户ID对于APP来说不可见,仅系统可见),系统使用一个APP来管理所有文件的权限,这个APP也有用户ID,使用这个ID才能访问这些权限。

每个应用程序都有独立的虚拟机,这使得每个应用可以互不干扰地运行。

默认情况下,每个APP都有自己的Linux进程。一个应用的某个组件需要执行,Android就会启动这个应用的进程;相反,当应用不再使用或系统内存紧张时,该应用所在进程将被停止。


通过上述要求,Android是一个满足最小权限原则(principle of least privilege)的系统,也就是说,在默认情况下,每个APP只能启动该它所需的功能组件,系统未给予其权限的内容,APP将无法访问。
然而使用下列方式,应用程序之间可以共享数据,或者程序可以访问系统服务:

APP之间可以共享Linux用户ID,使用这种方式,APP之间可以共享数据,为了节省系统资源,共享相同Linux用户ID的APP将使用同一个进程,并运行在同一个虚拟机上(APP必须有相同签名)。 APP可以申请访问系统数据的权限,如用户联系人,短消息,SD卡,蓝牙,摄像头等。用户必须显式地授予应用这些权限,如需了解更多,请您访问这个链接:《Working with System Permissions》,或参考我翻译的博文《Android 6.0及以上版本的运行时权限介绍》。

下面将介绍APP的核心基本组件、manifest文件、与代码分离的资源文件及如何使用资源文件修饰符适配不同的设备。


APP组件(App Components)


也就是所谓的四大组件,每个组件都有自己独立的生命周期,管理者组件的创建和销毁。

Activities
Activity代表了与用户交互的UI(它并不是一个UI,而是一个用于呈现UI 的载体),您所看到的屏幕上的内容,都是以Activity作为载体的。如需访问有关Activity的官方文档,请您点击这个链接:《Activities》,当然我也将在后续博文中翻译该文档。

Services
Service是一个运行在后台的组件,它主要用于长连接和远程操作,它没有与用户交互的UI界面。比如说,Service可以与在后台运行音乐,或者从网上下载数据而不影响用户的UI操作。可以在Activity中启动或者绑定Service以便与其通信。

Content providers
content provider管理着一个APP中的数据信息。您可以将数据存储在任何可以持久化保存的地方,如SQLite数据库,文件系统,网络上等。其他应用可以通过content provider访问本应用的数据信息。例如,系统的联系人应用程序中的Content Provider提供了通讯录信息,使用ContactsContract.Data类(需配合Content Resolver)就可以访问系统的通讯录信息。
Content Provider对应用的数据隐私性做了较好的保护。

Broadcast receivers
Broadcast receivers用于接收系统发出的各式广播信息。系统广播有很多,如:点灭屏幕时,电量低时,获取图片时 等。应用程序也可以自定义广播,如当APP下载了一些信息需通知其他APP“信息已下载完毕可供使用”时,应用可发出一条广播,而其他应用可截获该广播。Broadcast receivers同样无法进UI展示,但显示一条Notification表示有广播发出。更简单地说,Broadcast receivers仅是发起了一个意图,可以指导对该意图感兴趣的应用做一些事情。


Android系统的一个独特设计就是任何一个APP都能其中另一APP的组件,例如,如果您打算使用系统摄像头照一张照片,使用自己的应用就能启动,而不必再从头编写照相功能的代码,您也无需将自己APP的代码与照相的应用程序的代码有什么关联。您只需使用自己的activity启动带有照相功能的activity并返回照相的结果就行了。这个带有照相功能的activity就好像是自己应用的一部分。
当您启动了一个APP的组件,就启动了该APP所在的进程,并初始化该组件的类,比如,您的APP启动了相机应用中的activity,该activity将运行在属于相机应用的进程中,而不是您的APP中。因此,Android应用并不存在唯一的入口(如没有main()方法)。


启动组件(Activating Components)

通过intent这个异步请求,可以启动activities, services, 和 broadcast receivers这三大组件,Intent在运行时将不同的组件做一绑定,不论这些组件是否来自同一应用。通过intent启动组件有两种方式:隐式intent和显式intent,这两种方式将在后续稳重中具体介绍。
对于启动activity或service,intent可以附加一些action,或是一些指向具体数据内容的URI。例如某些intent打算启动一个带有展示图片功能的activity,或是打算打开一个网页,在某些情况下,启动后的组件会通过intent返回一些数据(比如说您希望intent打开的是某个通讯录的联系人,并返回这个联系人的信息,返回的信息就包含在intent中,而且还包含了指向这个联系人的URI信息)。

对于broadcast receivers来说,可以在intent-filter中声明想要接收什么样的广播,比如说您打算在您的应用中收到系统发出的“电量低”的广播,那么只需要在您的broadcast receivers中配置有关“电量低”的消息就行了。

对于content provider来说,intent无法启动。Content Provider需要和ContentResolver配对使用。 content resolver负责处理与之对象的ContentProvider事务,所以ContentProvider不用显式地调用contentResolver中的方法,就能将数据传递出去。这种做法很好地将应用中的数据与访问该数据的方法分离,提高了应用的安全性。


可以使用下列方法启动相应组件:

启动Activity: startActivity() 或 startActivityForResult() 启动Service:startService();绑定Service:bindService() 启动Broadcast:sendBroadcast(), sendOrderedBroadcast(), 或 sendStickyBroadcast() 如需查询content provider中提供的数据信息,可调用ContentResolver.query()方法。

Manifest文件(The Manifest File)


在使用组件之前,需在Manifest文件中对组件进行注册。除了注册组件以外,Manifest文件还可以作如下事情:

为应用申请所需权限(如度取系统联系人、访问intent等),需配置 use-permissions 定义最小API ,需配置 minimum SDK Version 声明使用应用所需的硬件和软件配置(如蓝牙、相机、支持多点触控的屏幕),需use-features 应用所需链接的API库,如 Google Maps library,需配置meta-data 其他

声明组件(Declaring components)


配置activity的方法如下:



    
        
        
        ...
    

其中标签的icon属性用于配置启动图标。标签的name属性用于注册应用中的activity的子类,需使用全限定名注册。
broadcast receivers除了可以在ManiFest文件中静态注册,还可以在代码中使用registerReceiver()方法动态注册。有关ManiFest文件的更多内容,请您点击这个链接:《App Manifest》,我将在后续文章中翻译该文档。


声明组件的兼容性(Declaring component capabilities)


intent的真正强大之处在于它的隐式启动,隐式intent可以描述其想要启动的目标组件所具备的特性而不具体指明目标组件的名字。
通过为组件配置 intent filters,组件具有被隐式intent筛选的能力,只有匹配了intent filters中的条件,该组件才能被隐式intent启动。例如,您打算在应用中配置一个发送邮件的activity,使得通过隐式intent表明“发送邮件”的意图,那么您可以这样为activity配置 intent filters:


    ...
    
        
            
                
                
                
            
        
    

更多有关intent filter的官方介绍,请您访问这个链接:《Intents and Intent Filters》


声明APP所需的软件和硬件信息(Declaring app requirements)


支持Android的设备各不相同,它们支持的底层驱动也各有千秋,为了防止将需要某个底层驱动的应用(如带有照相功能的应用)安装在并不支持该功能的设备上(该设备没有摄像头),需在manifest文件中配置,这个配置信息在系统中仅是说明性质的(即Android系统并不读取这个配置说明),但将应用发布到某个平台上(如Google play)时,该平台会读取这些声明。比如说, 您的应用需要调用系统摄像头并运行在最小版本为Android 2.1(API 7)的设备上,您需要作如下配置:


    
    
    ...

低于android 2.1或不支持摄像头的设备将无法在Google play上搜索到该应用。
您也可以声明您的应用使用摄像头但不是必须的,这需要将use-features标签的android:required属性设置为false,并在代码中检测设备是否有摄像头、关闭所有与摄像头有关的功能。


APP资源(App Resources)


APP资源实现了与代码的分离,这些资源包括图片、影音等可视资源。例如,您可以定义animations,、menus、 styles、 colors或是activity的layout资源,使用资源配置,可以做到代码的解耦,并使其与更多地设备适配。
对于每一个资源,SDK工具都会为之设置一个唯一的整型ID,通过这个ID,您可以在代码中引用这个资源,
为各种不同的设备适配资源是APP资源的最大优势,您可以在资源文件名中追加修饰符(qualifier)以匹配不同的设备。如res/values-fr/文件夹中放置法语的字符串信息,而默认的res/values/目录下放置默认的字符串信息,系统会根据您安装的设备系统语言自动匹配不同文件的内容。
类似这种限定符还有很多,比如您还可以为适配横竖屏的不同布局而追加修饰符等。有关资源修饰符的官方文档,请您点击这个链接:《Providing Resources》。


设备的兼容性(Device Compatibility)


android设备千千万,这需要您的android应用程序具备很好的兼容性。为了更好的适配各式屏幕,android提供了修饰符。
关于兼容性,分为两种:设备的兼容性(device compatibility)和APP的兼容性(app compatibility)。
对于设备的兼容性,您不必担心,只要是能发布在Google play上的应用,都兼容android 设备。
但是对于APP的兼容性,您就得留意了,因为不同设备对硬件的支持不尽相同,比如有的应用需要指南针的底层支持,那么不支持指南针的设备将无法运行该应用。


以下是检测设备是否支持指南针功能,若不支持,应将其关闭:

PackageManager pm = getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS)) {
    // This device does not have a compass, turn off the compass feature
    disableCompassFeature();
}

每一个android版本都为应用做了向后兼容的适配,也就是说,您应该为应用程序适配新的版本以做到向前兼容(以我的经验来看,假设当前市面上占有率最高的版本是android 5.1,那么您应将应用的targetSDKVersion设置为 23 (android 6.0),这个设置表明您的应用程序在android6.0 的设备上可以正常运行,这可以保证占有率最高的android 5.1的设备升级到android6.0时,您的应用程序不会崩溃。所以一般将targetSDKVersion设置为比目前市场占有率最高的版本设备高1-2个版本即可。


系统权限(System Permissions)

Android的安全架构 (Security Architecture)

在默认情况下,每个应用都没有任何权限去访问其他应用、系统设置和用户的隐私数据。包括读写用户的隐私信息(通讯录和邮件),读写其他应用的数据,访问Internet、使设备保持唤醒状态等等。
基于android系统的沙箱机制,应用必须显式地分享数据和资源,这需要显式地申请权限:应用显式地在配置中申请权限,还需获得用户的同意。


应用的签名

所有apk文件都必须用证书签名,并由开发持有一个私有的key。这张签了名的证书就是对该应用程序开发者的认证,这张证书无需由权威机构认证,而是由开发工具进行自我签名,使用证书可以保护开发者的权益,并且系统可以允许或拒绝应用使用签名级别的权限(signature-level permissions);证书还可以允许或拒绝应用请求与其他应用持有相同Linux ID(request to be given the same Linux identity)的权利。


用户ID与文件访问(User IDs and File Access)


安装应用时,android赋予每个一包(android以包名区分不同应用)一个确定的Linux用户ID(Linux User ID),从该应用的安装到卸载,这个ID一直存在且保持不变。在不同的设备中,ID可能会不同;但这个ID在同一台设备上会保持唯一。


出于安全性的考虑,在一般情况下,两个不同的包(应用)不能处于同一进程中;但是,您可以在AndroidManifest.xml文件的manifest标签中的sharedUserId属性中为不同的包(应用)配置相同的用户ID(User ID),这时两个包(应用)会被看作是一个应用,它们具有相同的用户ID和文件权限(user ID and file permissions)。需特别注意的是,为了安全性的考虑,两个应用具有相同的签名,同时还配置了相同的sharedUserId属性,这两个应用才拥有相同的用户ID。


一个应用存储的数据都会被赋予该应用的用户ID,其他应用无法共享这些数据。


使用权限(Using Permissions)


在默认情况下,一个Android的应用程序为被赋予任何权限,这对用户的体验或数据的存取会产生不利影响。为了使用被设备保护的特性,您必须使用 标签添加权限。
比如说,您希望模拟器可以接收发送过来的短消息时,可是使用下列代码添加权限:


    
    ...

如果您申请的是基本权限(normal permission;基本权限意味着该权限不会对用户的隐私或设备的运行造成太多负面影响),系统会自动赋予这些权限;如果您申请的是危险权限(dangerous permission;危险权限意味着该权限可能对用户的隐私或设备的运行造成潜在的影响(potentially affect))【有关权限级别的官方介绍,请您点击这个链接:《Normal and Dangerous Permissions》】,系统会显式地告知并请求用户授权。设备的版本不同,告知和请求的方式也不同:

若您的设备是 Android 6.0 (API level 23)或以上版本,并且应用设定的targetSdkVersion是 23 或以上时,应用会在运行阶段向用户请求权限,用户也可以随时撤销赋予的权限,所以应用会在每次运行时查看自己的权限。更多有关运行时权限的介绍,你可以访问我的博文:《Android 6.0及以上版本的运行时权限介绍》。 若您的设备是Android 5.1 (API level 22)或以下版本,并且应用设定的targetSdkVersion是 22 或以下时,应用会在安装时请求用户赋予权限。若您在新的APP版本中加入了新的权限,安装新版本时,应用会请求用户赋予该新增的权限。一旦您安装了应用,就意味着您同意赋予所有请求的权限,若想取消任何权限,只能卸载该应用。

有些时候,应用请求权限失败时,会抛出SecurityException异常,但这个一场不什么时候都会抛出,大多是情况下,请求权限失败时只会打印Log。


一个特别的权限可能会在多处地方被使用:

调用系统内部的一些方法时:系统会阻止应用访问系统内部的一些核心方法; 当启动一个其它APP的Activity时; 当发送和接收一个广播时,该权限可以决定谁可以接收该广播,或者谁可以将该条广播发动给您; 当操作content provider时; 当启动或绑定服务时。

自适应权限(Automatic permission adjustments)


为了保证您的应用总能适配新的API版本增加的权限,建议将targetSdkVersion设置得尽可能的高(as high as possible)。


基本权限和危险权限(Normal and Dangerous Permissions)


系统为权限分成了若干个级别,其中最重要的两个级别即为基本权限和危险权限。

基本权限说的是您的应用需要访问沙箱以外的数据或资源,但是这对用户隐私和系统的运行几乎不会造成什么影响。比如说“设置时区”权限就是一个基本权限。若您在manifest文件中配置了基本权限,那么该权限会被自动赋予而无需请求用户。 危险权限说的是应用会访问用户的隐私信息、对用户存储的数据造成潜在的影响或是影响了其他APP的运行等。比如说“读取用户的联系人”就是危险权限,如果您在manifest文件中配置了危险权限,那么该权限需要在使用时由用户显式赋予。

权限组(Permission groups)


所有的危险权限都被分成了权限组,若设备的版本号是 Android 6.0 (API level 23)或以上,并且targetSdkVersion为23或更高。当您的APP请求危险权限时,下列行为将会发生:

若您的应用正在申请manifest中配置的危险权限,系统会弹出一个对话框,该对话框描述了该危险权限所属的权限组,而不仅仅是该权限。比如说,您的应用正在请求READ_CONTACTS权限时,系统仅仅会弹出该应用正在申请设备通讯录(device’s contacts)的对话框。若用户同意了该请求,系统仅会赋予请求的单个权限。 若您的应用正在申请manifest中配置的危险权限,并且用户之前已经赋予了该权限所属权限组的其他权限给应用,那么此时这个危险权限将自动被赋予无需用户同意。比如说,若应用之前已经获得了READ_CONTACTS权限,那么当应用请求WRITE _CONTACTS权限时,该权限将自动被赋予,而无需请求用户。

事实上,无论是基本权限还是危险权限都被分成了权限组,但是只有危险权限所在的权限组会发生上述的情形,所以您可以忽略权限组中的基本权限。


若设备的版本是Android 5.1 (API level 22)或以下,而且targetSdkVersion为22或以下,系统会在应用安装时,声明所需的所有权限,这些声明是以权限组展示给用户的而不是单个权限。
以下列出危险权限及其权限组:
这里写图片描述
这里写图片描述


自定义并执行权限(Defining and Enforcing Permissions)


若打算自定义权限,您需要在manifest文件中使用标签声明。
比如说,若打算控制某个Activity的启动权(谁可以启动该Activity),您可以配置如下代码:


    
    ...

其中

属性是不能缺省的,该属性表明了请求该权限的级别,或谁可以被允许持有该权限。 属性是可选的,该属性仅是用来以分组的形式将权限显示给用户,您可以将该属性设置到一个标准权限组标准权限组(android.Manifest.permission_group)里,也可以设置一个自定义的权限组。推荐使用前者,因为这样可以减少弹出对话框的次数。 label 和 description都是用户自定义的属性,当用户查看应用拥有的权限时,显示的将是label属性中的内容,而权限的具体介绍将是description中的内容。自定义的属性名称label应该简洁明了,能准确地表示该权限保护的内容,而description应该用几句话描述权限允许应用所做的事和可能带来的对用户隐私的窥探。下面是一个自定义的CALL_PHONE的label 和 description的例子:
directly call phone numbers
    Allows the application to call
        phone numbers without your intervention. Malicious applications may
        cause unexpected calls on your phone bill. Note that this does not
        allow the application to call emergency numbers.

在manifest文中强制实行的权限(Enforcing Permissions in AndroidManifest.xml)


若您在manifest文件中声明了高级权限(High-level permissions),那么这种权限不是所有组件都能获取的,若希望针对某个组件获取高级权限,您应当在该组件的标签中用android:permission 属性声明该高级权限。

Activity权限:在activity标签中声明的高级权限可以控制 谁可以启动该activity,您应当在调用Context.startActivity()和Activity.startActivityForResult()方法时判断是否能启动该activity。若未被赋予该权限,则这两个方法将抛出SecurityException 异常; Service权限:在service标签中声明的高级权限可以控制 谁可以启动或绑定该service,您需要在调用 Context.startService(), Context.stopService() 和 Context.bindService()方法时判断是否可以启动或绑定该service,若未被赋予该权限,则这三个方法将抛出SecurityException 异常; BroadcastReceiver权限:在receiver标签中声明的高级权限可以控制 谁可以发送广播给该BroadcastReceiver,您应当在Context.sendBroadcast()方法返回以后再判断权限是否被赋予,因为会尝试将该broadcast发送给其他满足条件的receiver,所以,如果声明了高级权限的receiver没有接受到broadcast,sendBroadcast()方法也不会抛出异常;若使用Context.registerReceiver() 方法动态注册receiver,同样可以赋予该receiver高级权限; ContentProvider权限:在provider标签中声明的高级权限可以控制 谁有权访问该provider提供的数据(contentprovider还提供了URI Permission,将在稍后介绍)。与其他的组件稍有不同,provider中可以声明两种权限属性: android:readPermission和android:writePermission,前者可以控制谁能读取该provider的数据,后者可以控制谁能写入该provider的数据。仅有写入该provider权限的组件不代表您也能读取,反之亦然。当您第一次检索该provider时,该高级权限将被检测(如果没有权限,SecurityException将被抛出),其中ContentResolver.query()方法需要android:readPermission属性, ContentResolver.insert(),ContentResolver.update(), ContentResolver.delete()这三者需要android:writePermission权限,若相应权限未设置,上述方法将抛出SecurityException异常。

发出广播时强制执行的权限(Enforcing Permissions when Sending Broadcasts)


除了上述提到的为broadcast receiver声明高级权限外,您还可以在调用Context.sendBroadcast()方式传入一个在String类型的权限。
由于Broadcasts和receiver都可以被赋予权限,两种权限都需要经过intent的筛选才能匹配。


URI权限(URI Permissions)

处于安全的考虑,对于content provider,可以为其声明保护自己的读写权限。比如说,获取邮件需要权限,但赋予的URI却指向了一个图片浏览器,该浏览器无法打开邮件,因为浏览器没有被赋予打开邮件的权限。
为了解决这个问题,可以在启动activity时为intent设置Intent.FLAG_GRANT_READ_ URI _PERMISSION 或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION字段。

用户评论