应用程序原理
Android应用程序是通过Java编程语言来写。Android软件开发工具把你的代码和其它数据、资源文件一起编译、打包成一个APK文件,这个文档以.apk为后缀,保存了一个Android应用程序全部的内容。Android设备通过它来安装相应的应用。
一旦安装到设备上。每一个Android应用程序就执行在各自独立的安全沙盒中:
- Android系统是一个多用户的Linux系统。每一个应用都是一个用户。
- Android系统默认会给每一个应用分配一个唯一的用户ID(这个ID仅仅被系统使用。应用并不了解)。
系统给每一个应用相应的全部文件都设置了权限。仅仅实用户ID正确的应用才干訪问。
- 每一个进程都有它独立的虚拟机,因此一个应用程序代码才干独立执行,不受其它应用干扰。
- 默认的,每一个应用执行在各自独立的Linux进程中。
当一个应用中有一个组件须要执行时,系统就会开启它的进程。当这个进程里没有活动或者系统内存不足须要关闭进程为其它应用回收内存时。会关闭这个进程。
每一个应用执行在各自独立的沙盒中,通过这样的方式Android系统实现了最少权限原则—-即每一个应用默认仅仅有訪问它工作须要使用的资源的权限(译者注:注意这里是默认。非常多情况下事实上我们须要訪问其它应用的资源。这个时候就须要用到另外一个关键技术“进程通信”)。这样就创建了一个非常安全的环境,在这个环境下一个应用不能訪问那些它没有权限的文件。
然而,系统还是提供了一些应用间共享数据和应用訪问系统服务的方式:
- 我们能够给两个应用共享相同的Linux用户ID,那样它俩就能够訪问彼此的文件了。
为了节省系统资源,有相同UID的应用能够执行在同一个Linux进程中、使用同一个虚拟机,前提是这些应用必须有相同的签名。
- 一个应用能够请求一些訪问设备数据的权限。比方说用户的联系人、短信、SD卡、相机、蓝牙等等。
用户能够决定是否授予应用这些权限。
上面概述了关于Android应用在系统中怎样存在的原理。以下将介绍:
- 构建应用的核心框架组件
- 在manifest文件中为你的应用声明组件和请求设备特性
- 与代码分开、让你的应用在很多设备配置下表现的尽可能优雅的资源
应用组件
应用组件是构建一个Android程序必备的模块。系统能够通过不同组件的组件来进入你的应用。不是全部的组件用户都能看得到、摸得着,有些组件须要依靠其它组件才干启动,可是它们又作为一个独立个体存在,同一时候扮演着不同的角色帮助你决定你的应用都有什么行为。
应用组件有四种类型,每种类型都有其独特的功能,同一时候还有决定其怎样创建、销毁的生命周期。
以下是四种不同类型的应用组件:
-
Activity
一个Activity代表了一个界面。比方说,一个电子邮件应用可能有一个现实新邮件列表的Activity,另一个创建邮件的Activity,另外另一个阅读邮件的Activity。这些Activities一起工作组成一个完整的电子邮件用户体验。同一时候每一个Activity又是相互独立的,因此其它应用能够调用上述几个(假设这个电子邮件应用同意)。比方说一个相机应用为了让用户分享相片。能够直接启动创建邮件的Activity。
Services
Service是一个执行在后台的组件。经常使用来进行长耗时操作或者执行跨进程操作。Service不提供用户界面。比方说。Service能够在用户使用别的应用的时候后台播放音乐。或者也能够在最好还是碍用户交互的情况下,在后台从server请求数据。其它组件,比方说Activity,能够開始Service。或者跟Service绑定从而能够发生交互。Content Providers
Content provider 管理着一套可共享的应用数据。你能够把数据存到文件系统、SQLite数据库,网络或者其它不论什么你应用能訪问的可持久存储的地方。通过content provider,其它应用能够查询甚至改动数据(假设该content provider同意的话)。比方说。Android系统 提供了一个管理用户联系人信息的内容提供者。通过它不论什么应用仅仅要有权限就能够查询内容提供者的部分,然后读或者写特定的联系人信息。
Content provider还能够用来读写应用私有的数据。-
Broadcast Receivers
Broadcast receivers能够对全部广播进行回应的组件。很多广播都是系统发送的,比方说当屏幕关闭时发送的广播通知、电池 电量低时的广播、或者拍照完成的广播。应用也能够创建广播。比方说发个广播让其它程序知道有数据下载到设备上。已经能够使用了。尽管广播接受者没有界面,可是当一个广播事件发生时它能够在状态栏创建一个通知来通知用户。通常广播接受者仅仅是一个做一个其它组件的入口,做的工作非常少。比方说当收到某广播时启动一个Service来执行一些操作。
Android系统设计的一个独特之处是不论什么应用都能启动其它应用的组件。
比方说你想让用户使用设备的摄像头拍个照。设备上有专门的应用做了这个功能,你不须要写一个相机应用。仅仅需简单的调用系统相机应用就能够拍照。当拍完后,会给你返回要使用 的照片的数据。
对用户来说,看起来就像相机是你应用的一部分。
当系统启动一个组件时,会为这个应用单独启动一个进程(假设这个应用还没启动)。然后实例化一些要用到的类。比方说,你的应用启动了拍照顾用的activity。那个activity执行在相机应用所在的进程,而不是你的应用的进程。因此,不像其它平台系统的应用,Android应用不仅有一个入口(没有Java里的main方法)。
因为系统把每一个应用执行在不同进程,同一时候限制訪问其它应用文件的权限,你的应用不能直接激活其它应用的组件。然而你能够直接调用Android系统的组件。想要激活其它应用的组件,你必须在你的intent里标明信息告诉系统要启动一个特定的组件。系统就会为你激活那个组件。
激活组件
四大组件里的三种:activities、services、broadcast receivers都能够通过异步调用intent来激活。
Intent在执行时(能够理解为当请求调用其它组件时)绑定调用和被调用的组件,不管组件是不是属于你的应用。
通过Intent对象来创建一个intent。这个intent能够决定激活一个特定的组件还是激活一类组件。Intent能够是分为2中,显式和隐式。
对activities和services来说,一个intent决定了要调用的行为(比方要显示或者发送一些东西),也可能标明要使用数据(被调用的组件可能须要输入一些数据)的URI。
比方说。一个intent可能发出一个请求让一个activity显示某张照片或者打开一个网页。有些时候你启动一个activity后可能还须要接收返回结果,在这样的时候,activity会在一个Intent里返回结果(比方说你能够发起一个intent让用户选择一个联系人,然后给你返回信息。返回的intent里可能就包括了指向被选联系人的URI)。
对broadcast receivers来说。intent仅仅需简单的定义要接收广播的通告就能够(比方想要接收设备电量低的广播仅仅需在intent里表明一个指示“电量低”的action)。
另外的组件 – content provider,不能被intent激活。
它是被针对Content Resolver发出的请求所激活。
这个content resolver处理content provider的全部直接事务。因此组件不须要调用provider而是直接调用content resolver的方法就能够了。这样就在content provider和组件请求间保留了一个中间层,比較安全。
以下是几种激活不同类型组件的不同方法:
- 当你要开启一个activity或者给一个已经开启activity传递新数据时。通过给startActivity()或者startActivityForResult()(当你想要接收返回结果时)方法传递一个Intent就能够了。
- 当你要开启一个service或者给一个正在执行的service新指令时。通过给starService()方法传递一个Intent或者给bindService()传入一个Intent来和service绑定就能够了。
- 你能够通过给sendBroadcast(),sendOrderedBroadcast(),sendStickyBroadcast()方法传入一个Intent来实例化一个broadcast。
- 通过调用ContentResolver的query()方法来执行对content provider的查询操作。
The Manifest 文件
想要让Android系统能启动一个应用组件。系统须要通过查看应用的AndroidManifest.xml文件来知道该组件的是否存在。你的应用必须在应用代码根目录下的这个文件中声明全部的组件。
The manifest 除了声明应用组件外还做了非常多事,比方:
- 识别应用要使用的用户权限,比方说訪问网络或者訪问用户的联系人。
- 声明应用要求的用户最低手机版本号;
- 声明应用要求的硬件、软件特性,比方摄像头、蓝牙或者多点触屏;
- 声明应用要使用的API库(不是Android框架接口)。比方说谷歌地图接口。
你使用的activities,services,和content providers假设没有在manifest里声明,对系统来说是找不到的,因此就无法执行。
然而broadcast receivers有两种选择。要么在manifest里声明,要么也能够在代码里动态创建(创建一个BroadcastReceiver对象。然后调用registerReceiver()注冊)。
声明组件能力的 intent filter
正如上面所说。你能够使用一个Intent来启动activities,services,broadcast receivers。你能够在intent中显式地使用组件的class名来指定要调用的组件。
然而intent真正厉害的地方是隐式调用的概念。
一个隐式的intent简单的描写叙述了要执行行为的类型(你也能够给想调用的行为传递数据),同意系统找到能够执行你要求的行为的组件然后开启它。假设有多个组件能够执行intent里描写叙述的行为,用户须要选择使用哪个。
系统是怎样找到你的intent调用的组件的呢?答案是通过比較设备上其它应用的manifest文件中组件的intent filters标签。
当你在应用的manifest里声明一个activity时,你还能够给这个activity里加入一个声明activity能力的intent filter标签,然后这个activity就能够给其它应用做出回应了。你能够通过给组件的标签下加入一个标签来为你的组件加入一个intent filter。
比方说假设你创建一个邮件应用,须要有一个activity来创建一个新邮件。你能够在该activity里声明一个intent filter。来对发送(新邮件)的intent做出回应。比方这样:
<manifest ... >
...
<application ... >
<activity android:name="com.example.project.ComposeEmailActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:type="*/*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
假设有其它应用创建一个带有ACTION_SEND的action的intent。然后通过startActivity()启动。系统就可能启动你的activity。然后用户就能够编辑、发送一篇电子邮件。
声明应用配置要求
市面上搭载Android系统的设备种类太多了。这些设备拥有的硬件配置都不一样。为了避免你的应用被安装到不具备你要求配置的手机上。你须要在manifest文件中标明你的应用要求的硬件、软件配置。通常这些生命仅仅是用来提示,手机系统不会浏览,可是第三方服务商比方谷歌应用市场会浏览他们。然后在用户下载应用时帮助用户过滤那些有额外配置要求的应用。
比方说。假设你的应用要求使用摄像头。而且要求设备最低配置为Android2.7(API Level7)。你须要在manifest里这样标明:
<manifest ... >
<uses-feature android:name="android.hardware.camera.any"
android:required="true" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
...
</manifest>
声明了以后那些没有摄像头或者Android版本号低于2.1的设备在谷歌应用市场上就不能够安装你的应用了。
你也能够在你的应用中声明要使用摄像头可是不是必须要求。
这样的情况下你的应用必须把上述标签里的required属性设置为false。然后在执行过程中检查设备是否支持摄像头,假设没有的话就禁止摄像头相关操作。
应用资源介绍
一个Android应用不仅仅是由代码组成,它还有非常多代码以外的资源文件,比方说图片、音频文件。或者其它跟应用显示有关的东西。
比方说你须要定义动画、菜单、样式、颜色还有交互界面的布局文件等等。使用应用资源文件使得你更新一些东西更简单,不用改动代码,直接替换相应的资源文件。这样你的应用就能尽可能多的适应不同的设备环境(比方不同语言或者不同屏幕大小等)。
对于你项目中的每一个资源。软件开发工具都会给它定义一个唯一的整型ID,通过这个ID你能够从代码中或者其它资源文件中引用相应的资源。
比方说,你的应用中包括一个名为logo.png的图片文件(保存在res/drawable/目录下),SDK会生成一个名为R.drawable.logo的ID,你通过这个ID就能够引用logo图片。插入到界面里。
提供和源码分离的资源文件最重要的优点之中的一个就是你能够为适配不同设备提供不同的资源文件。比方说在XML中定义界面中的字符,你能够为这些字符准备不同语言版本号。存储在不同的文件中。然后系统会依据用户设备设置中的语言。在文件所在目录的后缀名字来找合适的字符来显示(比方存储在res/values-fr/下的法语字符,当用户系统语言为法语时会显示这个目录下存储的字符)。
Android支持很多不同的资源选择方式。这个选择方式主要取决于你为了在不同配置下使用不同资源时、创建的资源目录名称中的字符后缀。再举个栗子。你应该习惯依据设备屏幕尺寸或者方向为activity创建不同的布局文件。当设备屏幕竖屏时,你的布局里有一个button,当屏幕转成横屏时,你希望这个button转到横屏。
为了实现随着屏幕方向改变布局,你能够定义2个不同的布局。然后给每一个布局放到适当的、能够被正确选择的、不同的目录里。这样系统就会依据屏幕方向自己主动载入合适的布局了。