每个APP都泡在一个单独的DALVIK虚拟机实例上使用一个单独的进程。
安卓应用程序由一系列松散耦合的组件构成,application manifest描述了这些组件之间的关系。app metadata包含了所需的硬件和平台要求。
以下六大组件组成了安卓应用程序:
- Activities 应用程序的展示层,应用程序的每一屏都是一个Activity类的扩展。Activity使用Views来建立图形UI
- Services: 应用程序的幕后功臣,Service组件在后台运行,会更新数据源和可见的Activities,出发Notifications.用来执行那些即使Activity不处于活动状态仍然要继续运行的任务。
- Content Providers 共享的数据存储,用来管理和共享应用程序数据库。可以创建自己的Content Provider用来将数据和其他APP共享,也可以调用其他Content Provider来获取信息。Android的原生Content Provider有媒体存储和联系人存储等。
- Intents 一个用于APP内部传递消息的框架。使用Intent,可以将消息广播给系统范围也可以直接传递给某个特定的Activity或者Service.
- Broadcast Receivers 作为广播intent的接收者,如果你创建了一个Broadcast Receiver,你的应用程序就可以侦听到满足要求的广播intent,接着Broadcast Receivers将自动启动你的应用程序作出适当的反馈,这种机制非常适合于事件驱动的应用程序
- Widgets 可以加到主页的可视组件,Broadcast receiver的一个特殊变种
- Notifications 用来通知用户而不强行获得焦点。
Application manifest介绍
每个安卓应用程序都包括一个manifest文件:AndroidManifest.xml,存放在项目目录的根目录中,用来定义程序结构和metadata。
对于应用程序的每一个组件(activites, services, content providers, broadcast receivers),manifest都有一个node与之对应。通过加入intent filters和permissions来决定各个组件之间的关系
每个manifest文件都有一个<
manifest>根节点,该节点通过
package属性来制定项目的包名,通过xmlns:android属性来提供系统参数,android:versionCode用来定义当前应用程序的
整数版本号,android:versionName参数用来指定显示给用户的一个公共版本号。
<manifest>标签内部的节点定义了应用程序的组件结构,安全设定,测试类,应用程序的基本需求,以下列出了<manifest>标签的内部节点:
- uses-sdk 用来定义一个用于执行该应用程序的最小,最大以及目标SDK版本号。最小版本号用来防止运行在不兼容的低版本SDK上,最大版本号用来在应用程序市场上按相应的版本显示。推荐的做法是不设置最大版本号。需要注意的是,这里使用的版本号数字和通常所知的版本号不是一致的,具体需要查询以下表格http://developer.android.com/guide/appendix/api-levels.html
- user-configuration 指定支持的输入方式,可以支持以下几种:
reqFiveWayNav 如果需要上下左右方向键盘输入,设置为true.
reqHardKeyboard 如果需要实体键盘,设置为true.
reqKeyboardType 指定键盘类型:nokeys, qwerty, twelvekey, 或者undefined.
reqNavigation 可以设置为nonav, dpad, trackball, wheel, 或者undefined.
reqTouchScreen 可设置为notouch, stylus, finger, undefined.
这种设定的目的是,在安装时,如果发现所安装的机器不支持这些设定,那么安装将会失败。 - user-feature 要求特殊的硬件支持,比如需要具有摄像头
android.hardware.camera 需要摄像头
android.hardware.camera.autofocus 需要自动对焦摄像头 - supports-screens smallScreens, normalScreens, largeScreens, anyDensity(设置为true,如果可以适应任何分辨率)
- application 每个manifest只能包含一个application节点。它用来指定应用程序的metadata。在开发时,需要将debuggable属性置为true,在release时再去掉。application可以包含以下节点:
activity 每个activity需要一个activity节点与之对应, 属性android:name指定了类名。启动一个没有在manifest中定义的activity会抛出运行时异常,每个actitivty节点可以包含<intent-filter>子节点来指出哪种intent来启动这个activity. - service 类似于activity节点
- provider 用来指定content-provider
- receiver 注册一个broadcast receiver
- uses-permission 作为安全模型的重要组成,uses-permission,用来声明适用的特殊服务,比如位置服务等,该声明会在安装时向用户显示
- permission 第三方程序可以通过这个标签来定义自己的权限控制
- instrumentation 为应用程序提供一个测试框架,在运行时可以监视到应用程序组件的运行情况
安卓应用程序生命周期
默认情况下,每个安卓应用程序
运行在一个独立的进程中,
使用一个独立的dalvik虚拟机,而内存和进程管理被统一放在外部由运行时处理。在某些情况下,为了和其他的应用程序共享某组件的功能,可以将该组件单独运行一个进程,适用android:process标签。android系统会霸道的管理资源,使用一切手段来保证设备处于高响应状态,这意味着进程可能随时被杀死以释放内存而不会有任何警告信息。
应用程序优先级和进程状态
进程被销毁的顺序由其所在的应用程序的优先级决定,
一个应用程序的优先级等于其最高优先级的组件。
如果两个应用程序拥有相同的优先级,处于相对较低的优先级相对较长时间的进程将被优先销毁。
如果A应用程序通过Service/Content Provider向B应用程序提供支持,则A应用程序的优先级高于B。
所有的Android应用程序都会一直运行在内存中直到系统需要为其他应用程序的运行分配资源的时候。
Android程序的状态有以下几种:
- Active processes 活动的(前景)进程是正在和用户进行交互的进程,Android系统会通过释放其他进程的资源来保证这些进程与用户的交互,只有在万不得已的情况下才会销毁这些进程。这类进程包括:在前景对用户事件进行反馈的进程;正在执行onReceive的BroadCast Receiver进程;正在执行onStart, onCreate, onDestroy的Service进程;被标记为运行在前景的Service进程。
- Visible Processes 位于显示但是不处于活动状态的进程。虽然它们正被现实,但是不处于最前面,或者并不对用户事件进行响应。这种情况出现在并没有占据整个屏幕而只被部分显示的应用程序。只有在极端情况下,为使Active进程继续工作才会销毁这类进程
- Started Service processes 承担Service角色的进程。这类进程用于Service,所以没有可视界面。
- Background processes 这类进程承担Activity的角色,它不可见并且不包含任何正在运行的Service。Android系统会使用last-seen-first-killed模式来销毁这类进程
- Empty processes 为了增加系统效率,Android会在某个应用程序结束其生命周期后仍在内存中保留它。这样下次它启动时,速度就会加快。这类进程将被定时销毁
外部化资源
将图片,字符串等非代码资源放置在代码之外一直是一种优良的设计实践。Android支持将很多此类资源如颜色,字符串,图像,动画,主题等外部化处理。
应用程序的资源都放置在res/目录下,次目录包括;values, drawable-ldpi, drawable-mdpi, drawable-hdpi,和layout资源。
这些资源将在编译时以尽量有效的方式加入到应用程序包中,这个过程会生成一个名为R的类文件,此文件包含所有用到的资源的引用,这样就可以在代码中引用到这些资源了。
Strings
按惯例,将字符串放在res/values/strings.xml中
<string name="stop_message">Stop.</string>
支持简单的HTML标签,如<b><i><u>
可以使用String.format方法,但是该方法不支持上面的含HTML标签的字符串,需要用逃逸符进行替换。
Colors
有以下格式:
#RGB, #RRGGBB #ARGB #AARRGGBB
<color name="opaque_blue">#00F</color>
Dimensions
px, in, pt, mm, dp, sp
<dimen name="standard_border">5dp</dimen>
Styles and Themes
维护一个键值对,来记录风格
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="StyleName">
<item name="attributeName">value</item>
</style>
</resources>
Drawables
单独放置在res/drawable目录中
Layouts
用于将界面设计和布局解耦
Animations
支持两类动画Tweened动画,用于旋转,移动,拉伸,渐变一个View.
帧动画,是一个Drawable对象的序列。
tweened动画存储在一个单独的XML文件中,位于res/anim目录之下,文件名作为这个动画的ID
帧动画是一个Drawables序列,通过引用res/drawable目录中的文件来表示一个动画序列。
Menus
将菜单结构用XML表示
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_refresh"
android:title="Refresh"/>
<item android:id="@+id/menu_settings"
android:title="Settings"/>
</menu>
使用资源
不要直接修改R文件,而是通过修改XML文件再重新编译
R的静态成员将会返回一个引用ID,而如果要获得该资源本身,就需要使用工具类
Resources myResources = getResources();
Drawable icon = myResources.getDrawable(R.drawable.appicon);
帧动画将被包装成AnimationResources,可以通过getDrawable方法将其转为AnimationDrawable.
可以在XML资源文件中引用其他XML资源文件定义的资源
格式是:
attribute="@[packagename:]resourcetype/resourceID"
使用系统资源
android系统本身自带一些资源,引用它们的方式是使用android.R.xxxx,
而用于XML文件中时,加上android的包名:
android:text="@android:string/httpErrorBadUrl"
使用当前系统风格
使用?android:而非@作为资源前缀,就可以使用当前系统默认的风格
比如:
<EditText
android:id="@+id/myEditText"
android:layout_width="fill_parent"
android;layout_height="wrap_content"
android:text="@string/stop_message"
android:textColor="?android:textColor"
/>
为不同语言和硬件创建不同的资源
通过目录名加上“-”符号后跟不同的条件,来为不同的地区/硬件提供不同的资源方案。
比如:
values
values-fr
values-fr-rCA
可设置的类型有:
- MCC/MNC http://en.wikipedia.org/wiki/Mobile_Network_Code
- 语言,地区 (两个小写字母的ISO 639-1语言代码)-r(两个字母的ISO 3166-1-alpha-2地区代码)
- 屏幕尺寸 small medium large
- 屏幕宽度/长度 long notlong
- 屏幕方向 port land square
- 屏幕点密度 ldpi mdpi hdpi
- 触屏类型 notouch stylus finger
- 键盘可用性 keysexposed keyshidden keyssoft
- 键盘输入类型 nokeys qwerty 12keys
- 界面导航类型(轨迹球,方向键,滚轮)nonav dpad wheel trackball
如果以上配置都无法找到合适的,运行时将会抛出异常。运行时匹配的顺序是匹配满足最多条件的配置,如果两个目录名匹配条件相同,则它们的顺序将决定谁被采用。
运行时配置更改
安卓系统会通过结束并重启应用程序,重新加载资源文件,来处理运行时,语言,位置及硬件的改动
这种默认行为往往并不是开发者所希望的,为了让Activity监听到运行时配置更改,自定义这些事件发生时的行为,在manifest中加入一个android:configChanges属性,指明需要处理的更改事件:
- orientation
- keyboardHidden
- fontScale
- locale
- keyboard
- touchscreen/navigation
可以用|符号来连接多个事件:
android:configChanges="orientation | keyboardHidden"
很多时候,多个事件会同时发生,比如推出键盘时,keyboardHidden和orientation就都会发生
通过加入这个属性,默认的重启应用程序就不会发生,此时,需要重载Activity的onCOnfigurationChanged方法,该方法有个形参Configuration _newCOnfig, 这个形参就可以得到发生的是哪种改变,注意,在这个方法中
需要在第一行调用超类的onConfigurationChanged方法。
当onConfigurationChanged方法被调用时,Activity的资源已经被替换成相应的新的值,可以放心使用
如果没有显式的表明,默认的行为是重启Activity并且不会调用onConfigurationChanged
Application类的介绍
通过继承Application类,可以具有以下三种能力:
- 维护app状态
- 在app组件之间传送对象
- 管理,维护供多个组件使用的资源
当你继承的Application类在manifest中注册后,一旦你的app进程开始创建,这个类就会被自动初始化,这个类是一个天然的
Singleton实例,且也应该按照singleton的方式进行创建
public class MyApplication extends Application{private static MyApplication singleton;public static MyApplication getInstance(){return singleton;}@Overridepublic final void onCreate(){super.onCreate();singleton = this;}
}
在manifest中注册:
<application android:icon="@drawable/icon"android:name="MyApplication">..... </application>
在Application类中设置状态对象:
MyObject value = MyApplication.getInstance().getGlobalStateValue();
MyApplication.getInstance().setGlobalStateValue(myObjectValue);
Override application life cycle events
Application类提供了许多事件处理回调函数:
- onCreate 当应用程序被创建时调用,覆盖了这个方法后,用来初始化application singleton,创建并初始化应用程序状态对象等
- onTerminate 当应用程序对象被销毁时调用,但是,如果应用程序是由内核终止以释放资源,这个方法将不会被调用到。
- onLowMemory 提供一个提高可用性的接口,用来释放额外的内存。这个方法一般只有在后台进程已经终止,而前台程序还处于低内存状态时。这个方法的目的主要是为了清除缓存释放不必要的资源
- onCOnfigurationChanged 和Activity不同的是,Application对象在设置发生改变时不会被终止重启,覆盖这一方法可以在应用程序层面处理设置改变。
以上方法都必须先调用父类的同名方法。
A Closer look at android activities
每个Activity代表一屏,应用程序越复杂,Screen就越多
每个Screen需要创建一个单独的Activity。
通过继承Activity来创建一个新的Activity类,基本骨架是:
public class MyActivity extends Activity {@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);}
}
以上代码仅创建了一个空的UI界面,后面的工作就需要通过使用VIew和layout来建立自己的UI界面:
在onCreate方法中调用setContentView方法来建立UI。
要使用Activity,还需要在manifest中注册,这是通过在<application>节点中创建一个新的<activity>节点来实现的。
通过在<activity>节点中添加<intent-filter>来指定该Activity会接收的Intent
Activity生命周期
Activity Stacks
每个Activity的状态决定于它在Activity栈中的位置,Activity栈是一个后进先出的Activity的集合。启动一个新的Activity后,之前的Activity就会被压入栈顶。如果用户按了back键,或者前面的Activity被关闭,栈内的下一个元素就会变成活动状态。
Activity States
- Active 栈最上面的元素,可见,有焦点,接收用户输入,安卓系统会尽一切可能保证它的运行,当其他Activity变成Active状态后,之前的就变成Paused状态
- Paused 在某些情况下,Activity仍是可见状态,但失去了焦点,这时,它就处于paused状态。这种情况发生在一个透明的或者非全屏Activity变成Active状态后。处于paused状态的Activity除了不接收用户输入之外,和Active状态没有太多不同,只有在极端情况下,系统才会终止Paused Activity以保证Active Activity的运行
- Stopped 当一个Activity不处于可见状态时,它就Stop了。这个Activity会继续驻留在内存中,保持所有的状态信息。但是往往作为系统终止活动的候选者。当Activity变成Stopped状态时,应该保存数据和当前UI状态。一旦一个Activity退出或者被关闭,它就变成inactive状态。
- Inactive 在Activity被终止后,再下一次启动它之前,它都处于Inactive状态,Inactive状态的Activity将被移出Activity stack。
- onCreate 生命周期最开始,用于初始化Activity
- onRestoreInstanceState 在onCreate之后调用,用于重新加载UI,通过参数savedInstanceState来重新加载
- onRestart 在第二次可见周期前调用。用于加载Activity的改变
- onStart 在可见周期前调用,应用UI的改变
- onResume 在active周期前调用,在这个方法中唤醒该activity需要并且处于挂起状态的线程等。。
- onSaveInstanceState 在active周期结束时调用,以保存UI状态,将UI状态保存在参数savedInstanceState中,这个参数将会在onCreate时传入(如果这个Activity被重启)。
- onPause 在活动周期结束时调用,用来挂起线程,UI更新等等
- onStop 在可见周期结束时调用。挂起该Activity依赖的UI更新,线程。持久化所有数据。在这个方法结束后,该进程很可能被终止
- onDestroy 在生命周期终止时, 关闭数据库连接,清理释放资源
有时候,Activity进程也会在不调用onDestroy方法而直接被终止掉。
onCreate方法铺开UI,分配类变量引用,将数据和显示控件绑定,创建Service和线程。onCreate有一个传入参数,savedInstanceState,该参数保存了上一次onSaveInstanceState方法调用时的状态,使用这个参数就可以恢复之前的状态,这个恢复的动作可以放在onCreate中,也可以放在onRestoreINstanceState中。
作为Android的开发指导意见,建议尽量少创建临时对象,快速的创建并销毁对象会带来额外的GC负担
一个Activity的可见周期位于onStart和onStop之间,极端情况下,系统并不会调用onStop而直接终止Activity。
onStop方法应该用来终止,暂停动画,传感器侦听,GPS搜索,计时器,Service等。这些操作都会消耗CPU时间和网络带宽。
除了第一次onStart之外,
onRestart都是被唤醒时执行的第一个方法。
onStart/onStop方法还用来
注册/注销Broadcast Receiver
活动周期开始于对onResume方法的调用,结束于onPause
尽量将onResume/onPause方法中的代码保持轻量级,以便平滑切换过程
在onPause之前,onSaveInstanceState方法被调用,该方法提供了一个将UI状态保存得到Bundle中的机会,这个Bundle将会在之后传给onCreate和onRestoreInstanceState方法。使用onSavedInstanceState用来存储状态(比如,
checkbox状态,已键入但并没提交的用户输入)。
大多数Activity至少会覆盖onPause方法,以提交持久化数据,因为过了这个时间点后,Activity就很可能被未经警告的终止了。
一般onResume方法都是比较轻量级的,对于UI状态的重载已经在onCreate和onRestoreInstanceState方法中处理了,一般用onResume方法来重新注册Broadcast Receiver或者重启之前在onPause中挂起的进程。
Android Activity Classes
Android SDK已经提供了一些Activity的子类,封装了一些常用的UI widgets。 比如:
- MapActivity 封装了支持MapVIew widget的资源
- ListActivity
- ExpandableListActivity
- TabActivity