首页 > 代码库 > Android四大组件完全解析(一)---Activity
Android四大组件完全解析(一)---Activity
本文参考\android\android\frameworks\base\core\java\android\app\Activity.java文件中的类注释,以及android/frameworks/base/docs/html/guide/components/activities.jd文件
One Activity简介:
Activity是一个单独的、可以和用户交互的东西。几乎所有的activities都要与用户交互,所以activity承担着创建window的重任,你可以通过setContentView的方法往window里填充view。通过一个主题属性android.R.attr#windowIsFloating来设置activities是全屏显示full-screen还是悬浮窗isfloat(比如dialog,或者是一个悬浮的view),当然这里所说的全屏显示不包括状态栏。
<!-- 全屏显示 -->
<item name="windowIsFloating">false</item>
在对应的主题中有dialog主题样式的
<!-- 悬浮窗显示 -->
<style name="Theme.Material.BaseDialog">
‘‘‘‘‘‘
<item name="windowIsFloating">true</item>
‘‘‘‘‘‘
</style>
在继承Activity时需要实现两个方法
- onCreate: 在该方法中初始化activity。更重要的是,通常需要在该方法中调用setContentView方法来加载layout文件,并且用findViewById来检索需要和你进行交互的layout文件中的控件。
- onPause :在用户离开该activity时调用该方法处理。用户做出的任何修改都应该在该方法中提交commit(通常保存提交的数据使用android.content.ContentProvider)。
为了能够使用 android.content.Context#startActivity Context.startActivity()打开一个activity,我们需要将activity在对应包下的androidmanifest文件中使用activity节点进行声明
<application android:icon="@drawable/icon"
‘‘‘‘‘‘
<!-- 声明activity -->
<activity android:name=".MainMenuActivity"
‘‘‘‘‘‘
</activity>
‘‘‘‘‘‘
</application>
Two 与activity相关的主题有
- #Fragments
- #ActivityLifecycle
- #ConfigurationChanges
- #StartingActivities
- #SavingPersistentState
- #Permissions
- #ProcessLifecycle
一,Fragments:
fragment简介
Fragment开始于Android3.0,是应用程序组件碎片的意思,可以被放在activity内部。通过FragmentManager来管理与fragment的交互。fragmentmanager的对象可以通过两种方式获取到
- Activity#getFragmentManager() Activity.getFragmentManager()
- Fragment#getFragmentManager() Fragment.getFragmentManager()
fragment类可以用来获取各种各样的结果,在它内部,它代表一个普通的操作或者是交互接口。fragment与包含他的activity紧密相连,fragment依赖于activity存在。虽然fragment有它自己的生命周期,但fragment的生命周期与activity的生命周期息息相关。fragment生命周期图如下:
fragment依附于activity存在,当activity被stopped之后,activity中的fragment不能够started。当activity销毁后,位于activity中的fragment也随之销毁。
所有Fragment的子类必须包含一个无参的构造方法。当有需要时尤其是在状态恢复期,framework层会经常重新初始化fragment,framework会去找无参构造器去初始化fragment。如果fragment中无参构造器不可用的话就会在状态恢复时抛出 runtime exception 。
fragment相关主题有:
- #OlderPlatforms旧版本
- #Lifecycle生命周期
- #Layout布局
- #BackStack
OlderPlatforms:
fragment 是在Android3.0才加入进来的,所以在Android3.0之前如果想要使用fragment可以使用android.support.v4.app.FragmentActivity具体可以参考
fragments for all
Lifecycle
- #onAttach 当fragment与activity绑定时调用
- #onCreate 当fragment初始化创建时调用
- #onCreateView创建并返回与fragment相关的view视图
- #onActivityCreated 通知fragment它所绑定的activity已经oncreate
- #onViewStateRestored 通知fragment所保存的所有view的状态已经被恢复
- #onStart fragment对用户可见
- #onResume fragment对用户既可见又获取焦点
- #onPause fragment不再能和用户交互(activity被paused或者fragment之间的操作)
- #onStop fragment对用户不可见(activity被stopped或者fragment之间的操作)
- #onDestroyView 清除与fragment相关的所有views相关资源
- #onDestroy 清除fragment状态
- #onDetach 当fragment与activity解绑时调用
layout
fragment可以作为应用程序布局的一部分,借助fragment,activity可以更好的 模块化,为更大的屏幕创建更复杂的用户交互,帮助应用实现小屏和大屏之间的尺寸的切换。例如,在一个activity上可以编写一个有item列表的fragment,然后再组合一个fragment去显示每个item的详细信息。
<fragment
class="com.example.android.wifidirect.DeviceListFragment"
android:id="@+id/frag_list"
android:layout_width="match_parent"
android:layout_height="@dimen/phone_list_height">
<!-- Preview: layout=@layout/row_devices -->
</fragment>
fragment相关可以参考:
fragment嵌套
fragment与activity
二,ActivityLifeCycle
在系统中,activity被一个称为activity栈activity stack的东西在管理。当新创建一个activity时,就被被放在栈顶,并且成为正在运行的activity—-先前的activity会被保留在activity的下方,当位于栈的activity退出后,位于该activity下方的activity就会运行到前台,activity栈遵循栈的原则:后进先出。
一个activity基本上有四种状态
- active/running:当activity位于屏幕前台时(此时位于栈顶)我们称他为处于active或者是running的状态。
- paused:当activity失去焦点但仍对用户可见时,称之为paused的状态。在成这种原因可能是有一个透明的activity或者是size较小的activity覆盖在该activity之上。处于paused的activity也处于存活状态(该activity保留了所有的状态和成员信息,并仍旧和wiindowmanager绑定),但是当内存不足时,处于paused状态的activity很有可能被系统杀死
- stopped:当一个activity完全的被另一个activity覆盖时就处于stopped状态。此时activity对用户不可见且没有焦点,但是仍旧保存有所有状态和成员信息。当其他地方需要内存时,系统会将处于stopped状态的activity杀死。
- 如果用户处于paused或者stopped状态,系统很可能通过finish该activity或者是kill进程的方式将该activity从内存中移除。当再次加载给用户时,必须重新开始并且恢复他先前的状态。
接下来看一张activity生命周期的流程图:(来自源码)
写的很是清楚啊
在这个流程图中可以看到有三个关键的循环
- entire lifetime:activity的完整周期:从activity第一次调用onCreate开始到最后调用ondestroy为止。activity会在oncreate方法中创建所有的全局global状态,并且在onDestroy()方法中释放所有剩余的资源。例如,如果activity在后台开启一个网络下载进程,activity会在onCreate方法中开启该线程,并在onDestroy方法中停止该线程。
- visible lifetime可见周期:发生在activity调用onStart和onStop之间。在可见期间activity对用户可见,但有可能不在前台无法与用户交互。在这两个方法之间,你可以保存activity需要展现给用户的资源。例如,可以在onStart中注册一个BroadcastReceiver来监控影响UI的改变,并在onstop方法中注销该广播。onStart和onstop方法可以被多次调用,来让activity可见或者隐藏 。
- foreground lifetime前台时期:activity调用onResume开始,到调用onPause为止。在这期间activity位于所有activity之上并且可以和用户进行交互。一个activity可以频繁的在resumed状态和paused状态之间切换—例如当设备休眠时activity处于paused,所以activity的onResume和onPause方法中应该是一些轻量级的代码。
public class MyActivity extends Activity{
protected void onStart() {
//注册广播
mContext.registerReceiver(mReceiver, mIntentFilter);
}
protect void onStop(){
//注销广播
mContext.unregisterReceiver(mReceiver);
}
}
activity的完整生命时期包括以下所有activity的方法。你可以实现这些方法来完成工作。所有activity会实现onCreate方法来完成初始化操作;好多activity也会实现onPause方法来提交数据的改变并准备停止用户交互。在覆写方法时应该调用父类的方法
public class Activity extends ApplicationContext{
protected void onCreate(Bundle savedInstanceState)
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onPause();
protected void onDestroy();
}
activity生命周期方法之间的切换如下表所示
Method | Description | killable | Next |
---|---|---|---|
onCreate() | 当activity第一次创建时调用。在onCreate方法中需要做一些静态初始化的操作:创建views,绑定列表数据等等。如果onCreate传入的bundle参数不为null的话,可以从bundle中获取到activity先前的状态 | No | onStart |
onRestart() | 当activity已经被stopped,但又重新加载时调用 | No | onStart |
onStart() | 当activity对用户可见visible时调用 | No | 如果activity成为前台activity,则接下来会调用onResume方法。如果activity被隐藏hidden则接下来会调用onStop方法 |
onResume() | 当activity可以开始于用户交互即activity获取到焦点时会调用该方法。随着用户的输入,activity会处在栈顶。 | No | onPause |
onPause() | 当系统想要让一个先前的activity获取焦点时调用。该方法通常用来提交一些未保存的数据,停止动画以及其他一些消耗cpu内存的事情。方法的实现体必须快速,因为下一个activity只有在onPause方法执行返回之后才会resumed所以会一直处于阻塞状态 | 当activity重新返回到前台to the front时会调用onResume,当activity对用户不可见invisible时会调用onStop | |
onStop() | 当activity不再对用户可见时会调用该方法,因为其他activity已经resumed并且覆盖了该activity。当一个新的activity开始时会被放在该activity的前面时会调用onStop,或者该activity被销毁会调用onStop | yes | 如果activity重新加载出来与用户交互,则会调用onRestart方法。如果activity被销毁则会调用onDestroy方法 |
onDestroy() | 当activity被销毁时会调用该方法。Activity被销毁有两种情况,一种是用户调用了activity的finish方法结束了activity,一种是系统为节省空间销毁了activity。可以调用isFinishing方法来区分是哪一种情况 | yes | Nothing |
表格中killable这一列值得注意一下:
对于被标记成可以被killable的方法来说,当activity执行完这些方法返回时,持有该activity的进程《在任何时候》都可能被系统杀死,不再执行该activity中的任何一行代码。也因为如此,你应该在onPause方法中去保存数据(例如,用户的编辑)。另外,当将activity运行到后台状态时可以调用onSaveInstanceState(Bundle)方法来将activity的动态数据保存到一个bundle对象中,如果activity需要重新create的话,可以在onCreate中获取到Bundle数据。
注:在Android3.0以前保存数据应该在onPause中进行,因为onSaveInstanceState不是activity的生命周期的一部分,在进程生命周期相关中不会被调用。从Android3.0开始发生了改变。应用只有在onStop方法返回后才能被killable。这也就导致了在activity被杀死之前运行完onPause之后可能会调用onSaveInsatanceState(Bundle),并且可以让应用一直等待去保存数据直到运行了onStop方法。对于那些没有标记为可以被killable的方法表示,在方法开始调用直至方法返回这一段时间系统都不会去杀死activity的进程。因而一个activity是在调用onPause之后调用onResume之前才处于可以被kill的状态。
三, Configuration
如果一个设备的配置Resources.Configuration发生改变,显示给用户的交互界面也应该随之更新来配合格局发生的改变(横竖屏)。因为activity是与用户交互的主要机制,它包含用来处理设备配置改变的一些函数。
除非你有其他的指定,否则当设备的configuration发生改变时(例如,屏幕方向,语言,输入设备等等)会引起你当前activity的destroyed,经历activity的正常的生命周期过程onPause–>onStop–>onDestroy。如果activity已经加载到前台in the foreground或者对用户可见visible了,该activity实例一旦调用了onDestroy方法,就会去创建一个新的activity的实例,该activity实例可以获取到通过onSaveInstanceState所保存的先前的状态savedInstanceState.
应用中任何资源,包含layout文件都会基于configuration值的改变而发生变化。因而,处理configuration改变最安全的方法就是去检索所有资源文件,包括layouts,drawable,strings。因为activity必须知道如何如保存他们的状态以及如何根据保存的状态重新创建他们,所以,快捷的方法就是提供一个新的配置来restart一个activity。
在某些情况下,你可能想在configuration发生改变是不去重启activity,这需要借助配置文件中的属性android.R.attr#configChanges android:configChanges来完成。
<!-- 当屏幕方向或者键盘方向发生改变时不去调用activity的oncreate-->
android:configChanges="keyboardHidden|orientation"
当对该属性进行了配置后,就代表你可以进行监控,当所规定的配置发生变化时就会去调用onConfigurationChanged方法,而不是重启activity。
四, StartingActivities
开启一个activity并且获取到结果
android.app.Activity#startActivity方法用来打开一个activity,打开之后activity会被放在栈顶。在调用startActivity方法时需要用intent指明要打开的activity。
有时你可能想在activity结束时获取到一个返回结果。比如,你可能开启一个activity来让用户从联系人列表中选择一个联系人,当activity结束时把选择的结果返回回来,那就可以用android.app.Activity#startActivityForResult(Intent, int)打开activity,结果可以在activity的onActivityResult方法中获取
在一个存在的activity中调用setResult(int)方法把结果返回给它的父activity。在返回结果时必须提供一个结果码,可以是RESULT_CANCELED、RESULT_OK或者是其他自定义的代号。另外,也可以选择返回一个带有额外数据的intent返回回去。借助结果码,所有的信息都可以在parent的onActivityResult获取到。如果子activity发生了崩溃,父activity接受到的结果码就是RESULT_CANCELED
public class MyActivity extends Activity {
...
static final int PICK_CONTACT_REQUEST = 0;
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
new Intent(Intent.ACTION_PICK,
new Uri("content://contacts")),
PICK_CONTACT_REQUEST);
return true;
}
return false;
}
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Here we will just display it
// to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
}
}
}
}
五,SavingPersistentState
保存永久状态:
activity通常会保存两种持久的状态
- 共享文档 :数据(借助content provider保存在数据库中的数据)
- 内部状态:比如用户的偏好设置
对于第一类共享数据,activity应该使用“edit in place”编辑到位用户模型。也就是说,用户编辑之后可以立即保存,不需要其他的步骤。在使用这个模型时必须遵循以下两条规则
- 创建一个新文档时,立即创建他所依赖的数据库条目或者是文件。例如,如果用户选择写一个新的email,与该email相关的新的条目也必须要创建起来,来保证如果用户去了其他任何的activity这个email不会在草稿中消失。
- 当activity调用onPause方法时,他应该将用户针对数据所作出的修改进行commit提交。这个操作可以保证用户的修改可以被其他将要运行的activity所知道。你也有可能想要在activity生命周期的关键时刻去主动提交修改的数据:例如,在新打开一个activity之前,在activity被finish之前,当用户切换输入字段等等
这个模型的设计是用来防止当用户在activity之间进行切换时数据的丢失,并且允许system在activity被paused之后的任何时间可以安全的把activity杀死(因为其它地方需要系统资源,该activity被杀死)。这也就是说着用户按下“BACK”键并不意味”cancel”—它意味着保存他当前的内容并离开。在一个activity中取消编辑必须通过其它的机制来提供,例如,一个明确的“revert(还原)”或者是“undo(撤销)”选项。
Activity也提供了一个API来管理与activity相关的内部状态。例如,可以用来记录用户的偏好设置,并对用户的日历接卖弄进行一个初始化,或者是在使用浏览器时为用户显示一个默认的主页。
Activity的持久的状态通过getPreferences方法管理,允许检索或者是修改与activity相关的一组name/value键值对。为了让preferences可以应用程序多个组件(activities,receivers,services,providers)之间共享,你可以使用已有的方法
Context#getSharedPreferences Context.getSharedPreferences()来检索对应某个特殊name的preferences对象。(跨进程时不能通过preferences分享数据—只能通过contentProvider)
。以下是日历中的一个代码片段
public class CalendarActivity extends Activity {
...
static final int DAY_VIEW_MODE = 0;
static final int WEEK_VIEW_MODE = 1;
private SharedPreferences mPrefs;
private int mCurViewMode;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences mPrefs = getSharedPreferences();
mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
}
protected void onPause() {
super.onPause();
SharedPreferences.Editor ed = mPrefs.edit();
ed.putInt("view_mode", mCurViewMode);
ed.commit();
}
}
六,Permissions
在Androidmanifest中注册activity时可以给activity写明一个权限,这样其他应用在打开该activity时就需要拥有这个权限。
当打开一个activity时,你可以设置在intent上设置标志位Intent.FLAG_GRANT_READ_URI_PERMISSION或者 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。
这将授予activity对intent中特定uri的访问权限。访问权限会被保留到activity结束(他将保持到主机进程被杀死并且其他暂时性损坏)。Android2.0来说,如果activity已经被created并且一个新的intent也被发送给onNewIntent(intent),任何新授予的uri权限都会被添加到所现有的uri中。
七,ProcessLifecycle
Android系统试图将应用进程尽可能的保留更长时间,但是当内存较少时最终需要杀死旧的进程。正如在activity的生命周期ActivityLifecycle中所描述的那样,关于哪一个进程应该被移除由与用户交互状态决定。通常来说,根据运行在进程中的activity的状态可以看出进程有四种状态,按照重要性的顺序排列。系统在重新排序杀死重要进程之前会优先杀死最不重要的进程。
- foreground activity前台activity:(activity位于屏幕上方,与用户进行交互)是最重要的。如果占用的内存超过了用户的可用内存,该进程会在最后被杀死。通常,从这方面来说设备已经到达一个内存分页状态,所以为了保证用户输入的流畅必须要杀死一些进程
- visible activityactivity(对用户可见,但是没在前台即不能和用户交互,例如activity上方弹出一个dialog)也是非常重要的,只有在系统要求保持前台activity运行时才会杀死该进程
- background activity(后台进程,activity对用户不可见,并且已经被paused的activity)不再是危急的activity,所以系统可以很安全的杀死它的进程回收内存运行foreground 进程和visible 进程。如果它的进程需要被杀死,当用户再次切换回该activity时(再次显示到屏幕上),会调用oncreate方法并且从savedInstanceState中取出先前onSaveInstanceState保存的数据,以保证它在重新打开时获取到用户最后离开时的状态
- empty process(空进程)是一个没有任何activity或者其他应用组件的进程。当内存不足时会很快被杀死。因为这个原因,任何你在activity之外的后台操作都必须在service和broadcastreceiver的上下文中执行,以保证系统可以保持你的进程运行。
有时候一个activity可能需要去做一个与activity生命周期无关的长时间运行的一个操作。举个例子,相机应用允许你从网络下载图片。下载需要花很长事件,当进行下载操作时应用允许用户离开相机应用。为了实现这个目的,你的activity就应该开一个service来处理下载操作。这个操作可以让系统优先考虑你的进程(考虑到它比其他不可见的应用程序更重要),与原始的activity处于何种状态无关
Android四大组件完全解析(一)---Activity