Android菜鸟必看:解析应用的基本原理

移动开发 Android
或许我们做过很多Android方面的应用,在很多时候,我们是为了实现其中的某一功能才去了解它相关的知识和基本的使用方法,但你真正了解Android的原理机制吗?下面,请跟随我的脚步,一起来探索Android的“秘密”。

应用基础-Application Fundamentals

Android应用程序以java作为开发语言。用Android SDK 提供的工具,可以将应用程序所需要的数据和资源文件打包到一个android包文件中,这个文件用.apk作为扩展名。所有代码都在单个.apk文件中,当成一个应用,这个文件就是通常安装在Android设备中的应用.

一旦安装到了一个设备,每个应用生存在它自己的安全沙箱中。

  • 一个Android系统是一个多用户的Linux系统,其中的每个应用都是一个不同的用户。
  • 默认情况下,系统给每个应用分配一个独立的Linux用户ID(这个ID只由系统使用并且对应用来说是不可知的),系统给在某个应用中的所有文件设置了权限,所以只有分配了那个用户ID的应用才能访问它们
  • 每个进程拥有它自己的虚拟机,所以一个应用代码的运行,与其他应用代码的运行是隔离的.
  • 默认情况下,每个应用程序均运行于它自己的Linux进程中。当应用程序中的任意代码开始执行时,Android启动一个进程,而当不再需要此进程而其它应用程序又需要系统资源时,则关闭这个进程。

通过这种方法,Android系统实现了最小特权原则。默认,每个应用仅仅访问需要工作的组件,并不多做其他的事。这样创建了一个非常安全的环境,应用不能访问系统没有授权的其他部分.

然而,应用可以有多种方法来与其他应用,共享数据及访问系统服务:

  • 有可能安排两个应用共用一个linux用户ID,在那种情况下,它们能互相访问相互的数据。为了节约系统资源,拥有相同用户ID的应用,可能也被安排运行在同一个Linux进程中并共享相同的VM(应用必须被签名成同样的认证)。
  • 所有应用能请求允许访问硬件数据,比如像用户通信录,SMS消息及可挂载的存储设备(SD card),摄像头,蓝牙等,所有应用的权限必须在用户安装时被许可。

上述了一个应用怎样存在于一个系统中的相关基本概念,这个文档的其他部分将向你介绍如下 内容:

  • 定义在你的应用中核心框架组件
  • 在manifest中,给你的应用,声明组件及设备特点请求
  • 独立于应用代码的资源,可以让你的应用极大的优化它在各种配置设备的表现

应用组件-Application Components


应用组件是构建Android应用程序的关键和基石。 每个组件是一个不同的入点,系统可以从这些点进入到你的应用。对于用户来说,并不是每个组件都是实际的入点,但它们之间有一些依赖.但是每一个存在的组件都有它自己的一个入点,并扮演一个特定的角色--每一个都是独一无二的构建块,帮助你定义你的应用的整体行为.

有四个不同类型的应用组件,每个类型服务于一个不同的目的,并有不同的生命周期,生命周期定义了如何创建和销毁它.

下面是四种应用组件:

Activities活动

  • 一个activity在一个屏幕,显示一个用户接口.比如,一个email应用可能有一个activity,这个activity用于显示新的email列表.而另一个activity用于写邮件,还有一个activity用于读取邮件.虽然这些activities一起工作于email应用中,形成一个完整的用户体验但每一个部分又是相互独立的.正因如此,不同的应用才能启动这些活动的任意一个(如个email应用允许它).比如,一个照相的应用,能开启一个email应用中写封新邮件的活动,让用户分享一张照片.
  • 一个activity被当作Activity的子类来实现的,在Activities开发指南中,你可以学到更多关于它的使用

Services服务

  • 一个service是长期运行在后台,执行操作的组件,甚至可以为远程进程工作.一个服务不提供用户界面.比如,当用户在其他应用中时,一个服务可能在后台播放音乐,或者在后台获取数据,这并不影响用户跟其他的活动进行交互操作.其他的组件,比如一个activity,可以启动一个服务,并可以让它运行或者邦定到这个activity,以便与其进行交互操作.
  • 一个服务是作为Service子类来实现的,在Services开发指南中,你能学到更多关于它的使用

Content providers 内容提供

  • 一个content provider管理共享的应用数据集.你可以把数据存在文件系统中,一个SQLite数据库中,网上,或你应用可以访问的永久存储器中.通过内容提供者,其他的应用可以查询甚至修改数据(如果内容提供者允许的话). 比如,Android系统提供一个内容提供者管理用户通信录信息.因此,任何拥用适当权限的应用,可以查询内容提供者的部分来(比如ContactsContract.Data)读取和写入关于某个人的信息.内容提供者对于读取和写入属于你的应用的私有的非共享数据也是非常有用的,比如Note Pad样例应用程序,就使用内容提供者来保存笔记的.
  • 一个内容提供者被当作ContentProvider的子类实现,并且必须实现一套标准的APIs,以让其他的应用能执行交换操作。
  • 参考Content Providers开发指南,以了解更多信息.

Broadcast receivers广播接收者

  • 广播接收者是一个响应系统范围广播公告(通知)的组件.许多广播信息,都是来源于系统,比如,通知屏幕关闭的公告,电量低,或抓取了一张图片.应用也能发起广播,比如,让其他的应用知道一些数据已下载到设备了,并且他们可以使用了。虽然广播接收者,不能显示用户界面,但当一个广播事件发生时,它们可以创建一个状态通知器,去提醒用户.但更多情况下,一个广播接收者只是一个其他组件,想要做极小量事件的一个"gateway”(途径).举例,它可能发起一个服务,去执行关于某个事件的一些工作.
  • 一个广播接收者,是当作BroadcastReceiver子类被实现的.每个广播接收者都是从Intent对象衍生出来的。更多信息,请参考BroadcastReceiver类

任何一个应用能启动另一个其他应用的组件,是Android系统设计独一无二的方面(aspect).比如,你想要用设备的照相机拍一张图片.其他的应用已经有了这个功能,并且你的应用可以使用它,而不需要你自己去开发一个拍照相的activity.你并不需要合并(包含)或者甚至是链接camera应中的代码; 而只是,简单的启动camera应用中的活动,来拍照就可以了.当拍照完成,甚至把照片返回给你的应用,所以你能使用它。对于用户来讲,camera像是你应用中一部分.当系统开启一个组件时,它会启动那个应用的进程(如果该应用没有运行),并实例化该组件所需要的类.举例,如果你的应用开启一个camera应用的activity,来拍照,这个activity将运行在属于camera应用的进程中,而不是在你的应用的进程中.因此,不像大多数其他的系统的应用,Android应用,没有单个的入点(比如没有main()函数).

因为系统运行的每个应用,在一个带有文件权限的,独立的进程中,这样限制了对其他应用的访问,你的应用不能直接访问其他应用中的组件.但时,Android系统也能激活其他应用的组件.你必须传一个消息给系统,指定你想要启动的组件,然后系统为你激活这个组件.

激活组件-Activating Components


4个组件中的其中三个组件---activities,serivces,和broadcast receivers----是被叫做intent的异步消息激活的.在运行时,Intents把某个的组件与其他的组件互相邦定,而不管这个组件是否属于你的应用还是其他的应用(你可以把它们想像成一个消息,用于请求一个其他组件的动作).

一个intent是一个由Intent创建的对象.该对象定义了一个激活某个特定组件或者某个组件类型的消息,一个intent可以是显示的,同样,也可以是隐式的.

对于activities和services,一个intent(意图)定义了一个要执行的动作(比如:to”view”或"send" 些什么),并指定了要采用的URI格式的数据(其中一些,是其他组件启动所需要知道的).比如,一个intent可能传送一个请求给一个activity,要显示一张图片或打开一个网页.在有些情况,你启动一个activity接收一个结果,这种情况下,activity将在Intent中返回一个结果.(比如,你可以指示一个intent,让用户取一个人的联系方式,并返回给你,返回的intent中会包含一个指向选定联系方式的URI.)

对于广播接收者,intent只是定义了一个做为广播的公告.(比如,一个广播指出,设备电池低,它只是包含了一个动作字串,表示”电池低”).

其他组件,内容提供者,不会被intents所激活.进一步讲,它是内容解释者(ContentResolver)所请求的目标所激活的.内容解释者,处理所有与内容提供者的直接交换.所以组件不需要执行与提供者交换,而是调用ContentResolver对象方法.(这一句不好理解。)为了安全起见,组件请求信息与内容提供者之间有一个抽象层.

下面是激活各种类型组件的几个方法:

  • 你可以通过传一个(或者一些要做新的事情)Intent参数给startActivity()或startActivityForResult()(当你想要activity返回一个参数)函数(),来启动一个activity.
  • 你可以传一个Intent给startService()方法,(或给一个新的指令给正在运行的服启),或者你可以传一个Intent给bindService()方法来邦定到服务.
  • 你可以通过使用sendBroadcast(), sendOrderedBroadcast(), 或者 sendStickyBroadcast()三种方法来广播一个intent。
  • 你可以对ContentResolver调用query()方法,对内容提供者进行查询

关于使用intents的详细信息,请看Intents and Intent Filters 文档。在后面的文档中,也有一些关于激活某个组件的信息Activities, Services, BroadcastReceiver and Content Providers.

清单文件-The Manifest File


在Android系统开启一个应用组件之前,系统必须通过读取AndroidManifest.xml文件来知道组件的存在.你的应用必须把它所有的组件声明在这个文件中,并且必须在应用工程的根目录下.

这个manifest文件除了声明组件外,还处理了许多其他的事情,比如:

  • 指定应用请求的其他权限,访问网络或访问用户的通信录
  • 声明应用要求的最小API Level,应用使用的是那个API
  • 声明应用请求和使用的软硬件特征,比如照相机,蓝牙服务,或多点触模屏
  • 应用需要链接的API库,比如Google Maps library
  • 等等

声明组件-Declaring components


manifest文件的主要任务是告诉系统,应用的组件,比如,一个manifest可以这样声明一个activity:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <manifest ... > 
  3.     <application android:icon="@drawable/app_icon.png" ... > 
  4.         <activity android:name="com.example.project.ExampleActivity" 
  5.                   android:label="@string/example_label" ... > 
  6.         </activity> 
  7.         ... 
  8.     </application> 
  9. </manifest> 
  10.   

在 元素中,android:icon指定应用的icon资源
在 元素中的,android:name 属性,指定Activity子类的完全类名,android:label 属性,为activity指定一个用户可以见的标签。
你必须这样声明 所有应用的组件:

  • 声明活动的元素
  • 声明服务的元素
  • 声明广播接收者元素
  • 声明内容提供者元素

在你代码中包含的,Activites,services和内容提供者,若没有在manifest中声明,对系统来说是不可见的,即将永远不会运行。但是,广播接收者即可以在manifest中声明,也可以在代码中动态创建(做为BroadcastReceiver对象)并且通过registerReceiver()方法向系统注册。

了解manifest文件的详细构建过程,请看The AndroidManifest.xml File文档

声明组件功能-Declaring component capabilities


就如在上面的Activating Components中所讨论的,你可以用一个Activating Components启动activities,services和broadcast 接收者.你也可以在intent中显式的指定目标组件(使用组件类名)。然而,intent真正强大的是它的intent action.(动作)。通过使用intent动作,你只须简单的描述你要执行的action类型,(并且,可选的与执行动作有关的数据),并且允许系统在设备上找到一个组件,这样就可以执行那个动作并启动它。如果有多个组件可以执行,intent指定的action,那么用户选择执行那一个.

通过比较设备上的其他应用的manifest文件上的intent filters与接收到的intent.系统确定那个组件可以响应一个intent.
当你在你的应用的manifest中声明一个组件时,你可以可选择包括intent filters(意图过滤器),来指定组件的功能,以让其能响应其他应用的intents.你可以加一个组件声明的元素的子元素,为你组件声明一个意图过滤器。

比如,一个email应用中,新建email的一个activity可能在它的manifest 中声明了一个意图过滤器,以便能响应”send”意图(为了发送邮件)。然后,在你的应用中的一个activity,创建了一个带有”send” ACTION_SEND的意图,.当你调用startActivity()方法,启动该意图过滤器时,系统将其匹配到email应用的“send”活动,并运行它。

关于创建意图过滤器的详细信息,参考Intents and Intent Filters 文档

声明应用需求-Declaring application requirements


有许多设备装了Android,但它们并不提供所有相同的特点和功能.为了避免你的应用,装在一个没有你应用所必特征的设备上.通过在你的manifest文件中声明软件硬件要求,明了的指出你的应用支持的硬件类型是非常重要的大多数声明仅仅只是信息,系统并不读取他们,但像Android市场这样的其他服务,将读取它们,以便让用户在为他们的设备寻找应用时,可以进行筛选.

比如,如果你的应用需要有照相机,并且使用的API是2.1(API Level 7),你应在你的manifest文件中声明这些要求.这样,那些没有照相机并且Android版本低于2.1的设备,就不能从Android市场上安装你的应用.

但,你也可以声明你的应用使用camera,但不必须要求。那种情况,你的应用必在运行时一个检查,以确定设备是否有一个照相机,如果没有照相机,并禁止与照相相关的功能。

下面是一些重要的设备特性,你在设计和开发应用时必须要考虑的..

  • creen size and density 屏幕尺寸与解释度
  • 为了能从它们的屏幕尺寸来分类设备,Android为每个设备定义了两个特性:屏幕尺寸(屏幕的物理尺寸)和解释度(在屏上的像素的物理密度,或者dpi--每英寸的点数).为了简化屏幕配置的所有不同类型,Android系统把它们分成可选的组,以便更容易定位

  • 屏幕大小:小,正常,大和极大

  • 屏幕解释度:低解释度,中解释度,高解释度,和极高解释度

  • 默认情况下,你的应用是兼容所有屏幕尺寸和解释度的,因为Android系统对此做了适当的调整,以使得它适合你的UI布局和图像资源

  • 然而,你应为某个屏幕尺寸创建特殊的布局,并为某些解释度提供特定的图像,使用可选的资源,并在你的manifest文件中用 元素声明,以明确指出你的应用支持的屏幕尺寸.

  • 更多信息,参考Supporting Multiple Screens文档

    • Input configurations 输入配置
  • 许多设备为用提供了一个不同类型输入装置,比如,硬件键盘,轨迹球,five-way导航pad.如果你的应用必须要一个特别的输入硬件,那么你应在你的应用中使用元素声明.但时,应用必须要一个特别的输入配置的情况是极少的.

    • Device features 设备特性
  • 在一个装有Android的设备中,有许多软硬件特性,有可能有,或有可能没有。比如照相机,光敏器件,蓝牙,或某个版本的OpenGL,或者触模屏的精度.你应该从不假设,在所有的装有Android的设备中某个特点是可用的(除了标准的Android库),所以你应该用 元素声明你的应用支持的特征.

    • Platform Version 平台版本
  • 不同的Android设备,经常运行不同的Android平台版本,比如Android1.6或者2.3. 每一个成功的版本通常包括在前一个版本中不可用的API。为了指出,那些APIs集是可用的,每个平台版本指定了一个API Level(比如, Android 1.0 is API Level 1 and Android 2.3 is API Level 9).如果你使用的APIs是在1.0版之后,加入到平台的,你应该用元素,声明最小API级别,这样就指出了那些API将被采用.

为你的应用声明所有必要性的要求非常重要.因为,当你把你的应用发布到Android市场.市场,将用这些声明信息来过滤出,那些应用在每个设备是可用的. 同样,你的应用应该只能在满足所有你应用需求的设备上才可用.

更多关于Android市场如何基于这些需求过滤的,请看Market Filters文档

应用资源-Application Resources


一个Android应用的组成不仅只是代码----它还有与代码独立的资源,比如图像,音频文件,及与应用可显图像任何其他相关的.比如,你应该定义动画,菜单,风格,颜色,和用XML文件定义活动的布局.使用应用资源,能让你的应用在不修改任何代码的情况下容易的升级各种特性---并且通过提供一套可选取的资源--能优化你的应用在各种配置不同的设备中的表现(比如不同的语言和屏幕尺寸).

对于每个包含在你的Android工程中的资源,SDK将其定义成一个唯一的整型ID,这样你就可以在你的代码中或在XML文件中定义的其他资源中引用它.如果你的应用包括一个图片名字是logo.png(保存在res/drawable/目录 ),SDK工具将生成一个资源ID命名成R.drawable.logo,你可以用它来引用图片,并插入你的用户界面中

提供与你的代码分开的资源的一个很重要的方面是,使得你能为不同的配置的设备提供可选资源.比如,在XML中定义UI字串,你可以把字串翻译成各种不同的语言并保存在不同的文件中.然后,以基于语言限定词,你可以追加资源目录名(比如res/values-fr/ 用语法语资源),和用户语言设置,Android系会将相应的资源应用到你的UI中.

Android为你的可选资源,支持许多不同的qualifiers (限定词).限定词是一个包括在你的目录名中的一个简短的字串,是为了定义那些资源将用在,该配置的设备上.再如,由于设备的屏幕的方向和尺寸不同,你通常需要为你的活动定义不同的布局.比如,若设备的屏幕是竖向(高),你可能要一个带有重直button 的布局,当屏幕是横向的(宽),按钮应是水平对齐的.要根据方向来改变布局,你要定义两个不同的布局,并在布局的目录名中使用相应的限定词(qualifier).然后,系统将自动根据当前的设备朝向来应用相应的布局.

责任编辑:张叶青 来源: eoe Android开发者社区
相关推荐

2013-04-07 14:09:55

Android应用基本

2012-01-12 14:37:34

jQuery

2010-09-26 17:13:31

2020-03-21 14:57:14

手机定位智能手机APP

2010-08-20 13:29:33

OFDM

2011-11-29 12:17:00

2009-02-24 09:43:00

IP电话原理

2013-09-22 14:02:09

内存数据库

2010-03-17 13:35:02

2016-08-17 23:53:29

网络爬虫抓取系统

2021-02-08 21:40:04

SockmapBPF存储

2016-08-18 00:04:09

网络爬虫抓取系统服务器

2019-11-28 10:45:28

ZooKeeper源码分布式

2020-11-26 13:54:03

容器LinuxDocker

2010-06-18 17:28:37

Linux Anacr

2011-07-07 14:46:10

Cocoa Xcode

2010-03-18 20:13:03

Java socket

2009-06-11 09:56:09

MySQL Repli原理

2020-12-29 16:55:44

ZooKeeper运维数据结构

2011-07-07 14:10:21

Cocoa 内省 hash
点赞
收藏

51CTO技术栈公众号