本节重点介绍一下Android架构设计实践开发中Activity相关内容:废话不多说,先来一张思维导图帮助大家系统的对Activity有一个大概的了解。
Android架构设置实践之Activity.png
Activity定义:
Activity
是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。每个Activity
都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。
Activity生命周期:
activity生命周期.png如图所示,想要理解Activity的生命周期其实主要抓住三个关键循环,四种基本状态以及七个生命周期的方法即可。
Activity生命周期的三个关键循环:
1、 Activity
完整的生命周期:
从onCreate(Bundle)
开始到onDestroy()
结束。Activity
在onCreate()
设置所有的全局状态,在onDestory()
释放所有的资源。例如:某个Activity
有一个在后台运行的线程,用于从网络下载数据,则该Activity
可以在onCreate()
中创建线程,在onDestory()
中停止线程。
2、 Activity
可见的生命周期:
从onStart()
开始到onStop()
结束。在这段时间,可以看到Activity
在屏幕上,尽管有可能不在前台,不能和用户交互。在这两个接口之间,需要保持显示给用户的UI数据和资源等,例如:可以在onStart()
中注册一个IntentReceiver
来监听数据变化导致UI的变动,当不再需要显示的时候,可以在onStop()
中注销它,onStart(),onStop()
都可以被多次调用,因为Activity
随时可以在可见和隐藏之间转换。
3、 Activity
前台的生命周期:
从onResume()
开始到onPause()
结束。在这段时间里,该Activity
处于所有Activity
的最前面,和用户进行交互。Activity
可以经常性的在resumed
和paused
状态之间切换,例如:当设备准备休眠时,当一个Activity
处理结果被分发时,当一个新的Intent
被分发时。所有在这些接口方法中的代码应该属于非常轻量级的。
Activity四种基本状态:
1、 Active/Running
(活动状态)
当Activity
运行在屏幕前台(处于当前任务活动栈的最上面),此时它获取了焦点能响应用户的操作,属于运行状态,同一个时刻只有一个Activity
处于活动(Active)
或运行(Running)
状态。
2、 Paused
(暂停状态)
当Activity
失去焦点但仍对用户可见(如在它之上有另一个透明的Activity
或Toast
、AlertDialog
等弹出窗口时)它处于暂停状态。暂停的Activity
仍然是存活状态(它保留着所有的状态和成员信息并保持和窗口管理器的连接),但是当系统内存极小时可以被系统杀掉。
3、 Stopped
(停止状态)
完全被另一个Activity
遮挡时处于停止状态,它仍然保留着所有的状态和成员信息。只是对用户不可见,当其他地方需要内存时它往往被系统杀掉。
4、 Dead
(非活动状态)
Activity
尚未被启动、已经被手动终止,或已经被系统回收时处于非活动状态,要手动终止Activity
,可以在程序中调用finish()
方法。如果是(按根据内存不足时的回收机制)被系统回收,可能是因为内存不足了,内存不足时,Dalvak
虚拟机会根据其内存回收规则来回收内存:
(1). 先回收与其他Activity
或Service/Intent Receiver
无关的进程(即优先回收独立的Activity
)因此建议,我们的一些耗时后台操作,最好是放在Service
中进行。
(2). 不可见(处于Stopped
状态的Activity
)。
(3). Service
进程(除非真的没有内存可用时会被销毁)。
(4). 非活动的可见的(处于Paused
状态的Activity
)。
(5). 当前正在运行(处于Active/Running
状态的Activity
)。
Activity生命周期的七个方法:
i. protected void onCreate(Bundle savedInstanceState)
一个Activity
的实例被启动时调用的第一个方法。一般情况下,我们都覆盖该方法作为应用程序的一个入口点,在这里做一些初始化数据、设置用户界面等工作。大多数情况下,我们都要在这里从 xml 中加载设计好的用户界面。例如:
setContentView(R.layout.main);
当然,也可从savedInstanceState
中读我们保存到存储设备中的数据,但是需要判断 savedInstanceState
是否为 null,因为 Activity
第一次启动时并没有数据被存贮在设备中:
if(savedInstanceState!=null){
savedInstanceState.get("Key");
}
ii. protected void onStart()
该方法在onCreate()
方法之后被调用,或者在 Activity
从 Stop
状态转换为 Active
状态时被调用。
iii. protected void onResume()
在 Activity
从 Pause
状态转换到 Active
状态时被调用。
iv. protected void onPause()
在 Activity
从 Active
状态转换到 Pause
状态时被调用。
v. protected void onStop()
在 Activity
从Active
状态转换到 Stop
状态时被调用。一般我们在这里保存 Activity
的状态信息。
vi. protected void onDestroy()
在 Active
被结束时调用,它是被结束时调用的最后一个方法,在这里一般做些释放资源,清理内存等工作。
以上方法的调用时机如图所示:
Activity的启动模式:
Activity
的启用模式也较为简单,它会在活动切换时用到。Activity
的启动模式分为四种,standard
、singleTop
、singleTask
、singleInstance
模式。接下来将为大家详细的介绍一下这几种加载模式。
Activity
的加载模式可以在配置文件AndroidManifest.xml
中进行配置,配置项为android:launchMode
具体如下图所示:
1、
standard
模式在
Activity
的栈中无论该活动有没有加入栈,活动就会被创建。测试方式是把MainActivity
的launchMode
设置成standard
, 在MainActivity
中添加一个按钮,点击按钮使用Intent
跳转到当前Activity
,看onCreate
方法中打印的Log
。点击按钮的方法如下:
Button launchModelButton = (Button) findViewById(R.id.launch_model_button);
launchModelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MainActivity.class);
startActivity(intent);
}
});
standard
加载模式的栈如下所示,无论栈中有没有该对象的实例,都会被创建。
2、 singleTop
模式
只要被创建的活动不位于栈的顶部,该活动就会被创建入栈。如果将要被创建的活动位于栈的顶部,该活动的实例就不会被创建。测试方法,把上面的模式直接改成singleTop
模式,MainActivty
往自己身上跳转就不会从新创建一个新的实例,会重用之前在栈顶中的实例。如果是MainActivty
跳转到SecondActivty
, 然后SecondActivity
再次跳转到MainActivty
, 那么此时的MainActivity
将会被创建,因为栈顶是SecondActivity
。如下所示:
3、 singleTask
模式
单任务模式,这个也不难理解,如果从MainActivty
跳转到SecondActivity
, 如果再从SecondActivty
跳转到MainActivity
, 在单任务模式下MainActivity
已经在栈中,就会把它之前的Activity
出栈,使其处于在栈顶活跃的位置。原理如下图所示:
4、 singleInstance
模式
可以看成单例模式,这个比较特殊,被设置成 singleInstance
的 Activity
将会放入另一个栈中,因为这样为了便于共用。上面3中模式位于同一个栈中。下方 ThirdActivity
跳转到一个加载模式为 singleInstance
的 Activity
中。
Activity的启动过程:
可以将Activity
的启动过程简单总结为以下六个过程
1、使用代理模式启动到ActivityManagerService
中执行。
2、创建ActivityRecord
到mHistory
记录中。
3、通过socket
通信到Zygote
相关类创建process
。
4、通过ApplicaitonThread
与ActivityManagerService
建立通信。
5、ActivityManagerService
通知ActivityThread
启动Activity
的创建。
6、ActivityThread
创建Activity
加入到mActivities
中并开始调度Activity
执行。
Activity的通信方式:
1、 使用Intent
通信
在Android
中,不同的 Activity
实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在 Activity
之间传递消息。Android
中通过 Intent
对象来表示一条消息,一个 Intent
对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封Email
,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象
,消息“目的地”是必须的,而内容则是可选项。
在上面的实例中通过 Activity. startActivity(intent)
启动另外一个 Activity
的时候,我们在 Intent
类的构造器中指定了“收件人地址”。
如果我们想要给“收件人”Activity
说点什么的话,那么可以通过下面这封“e-mail”
来将我们消息传递出去:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);
// 创建一个带“收件人地址”的 email
Bundle bundle =new Bundle();// 创建 email 内容
bundle.putBoolean("boolean_key", true);// 编写内容
bundle.putString("string_key", "string_value");
intent.putExtra("key", bundle);// 封装 email
startActivity(intent);// 启动新的 Activity
那么“收件人”该如何收信呢?在 OtherActivity
类的 onCreate()
或者其它任何地方使用下面的代码就可以打开这封“e-mail”
阅读其中的信息:
Intent intent =getIntent();// 收取 email
Bundle bundle =intent.getBundleExtra("key");// 打开 email
bundle.getBoolean("boolean_key");// 读取内容
bundle.getString("string_key");
上面我们通过bundle
对象来传递信息,bundle
维护了一个 HashMap<String, Object>
对象,将我们的数据存贮在这个HashMap
中来进行传递。但是像上面这样的代码稍显复杂,因为Intent
内部为我们准备好了一个 bundle
,所以我们也可以使用这种更为简便的方法:
Intent intent =new Intent(EX06.this,OtherActivity.class);
intent.putExtra("boolean_key", true);
intent.putExtra("string_key", "string_value");
startActivity(intent);
接收:
Intent intent=getIntent();
intent.getBooleanExtra("boolean_key",false);
intent.getStringExtra("string_key");
2、 使用 SharedPreferences
SharedPreferences
使用 xml
格式为 Android
应用提供一种永久的数据存贮方式。对于一个Android
应用,它存贮在文件系统的/data/ data/your_app_package_name/shared_prefs/
目录下,可以被处在同一个应用中的所有 Activity
访问。Android
提供了相关的 API
来处理这些数据而不需要程序员直接操作这些文件或者考虑数据同步问题。
// 写入 SharedPreferences
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
Editor editor = preferences.edit();
editor.putBoolean("boolean_key", true);
editor.putString("string_key", "string_value");
editor.commit();
// 读取 SharedPreferences
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
preferences.getBoolean("boolean_key", false);
preferences.getString("string_key", "default_value");
3、 其它方式
Android
提供了包括 SharedPreferences
在内的很多种数据存贮方式,比如 SQLite
,文件等,程序员可以通过这些 API
实现 Activity
之间的数据交换。如果必要,我们还可以使用IPC
方式。
Activity的Intent Filter:
Intent Filter
描述了一个组件愿意接收什么样的 Intent
对象,Android
将其抽象为 android.content.IntentFilter
类。在 Android
的 AndroidManifest.xml
配置文件中可以通过 <intent-filter >
节点为一个 Activity
指定其Intent Filter
,以便告诉系统该 Activity
可以响应什么类型的 Intent
。
当程序员使用 startActivity(intent)
来启动另外一个 Activity
时,如果直接指定intent
了对象的 Component
属性,那么Activity Manager
将试图启动其 Component
属性指定的Activity
。否则Android
将通过 Intent
的其它属性从安装在系统中的所有 Activity
中查找与之最匹配的一个启动,如果没有找到合适的Activity
,应用程序会得到一个系统抛出的异常。这个匹配的过程如下:
Action匹配:
Action
是一个用户定义的字符串,用于描述一个 Android
应用程序组件,一个 Intent Filter
可以包含多个 Action
。在 AndroidManifest.xml
的 Activity
定义时可以在其 <intent-filter >
节点指定一个 Action
列表用于标示 Activity
所能接受的“动作”,例如:
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<action android:name="com.zy.myaction" />
……
</intent-filter>
如果我们在启动一个 Activity
时使用这样的 Intent
对象:
Intent intent =new Intent();
intent.setAction("com.zy.myaction");
那么所有的Action
列表中包含了“com.zy.myaction”
的Activity
都将会匹配成功。
Android
预定义了一系列的 Action
分别表示特定的系统动作。这些 Action
通过常量的方式定义在 android.content. Intent
中,以“ACTION_”
开头。我们可以在Android
提供的文档中找到它们的详细说明。
URI 数据匹配:
一个Intent
可以通过 URI
携带外部数据给目标组件。在<intent-filter >
节点中,通过<data/>
节点匹配外部数据。
mimeType
属性指定携带外部数据的数据类型,scheme
指定协议,host、port、path
指定数据的位置、端口、和路径。如下:
<data android:mimeType="mimeType" android:scheme="scheme"
android:host="host" android:port="port" android:path="path"/>
如果在Intent Filter
中指定了这些属性,那么只有所有的属性都匹配成功时URI
数据匹配才会成功。
Category 类别匹配:
<intent-filter >
节点中可以为组件定义一个 Category
类别列表,当 Intent
中包含这个列表的所有项目时 Category
类别匹配才会成功。
其他:
Activity被回收了怎么办
当我们的临时数据还存放在活动中,可活动被回收了怎么办?这样的情况明显是常常会遇见的。查阅文档可以发现Activity
提供了一个onSaveInstanceState()
回调方法,这个方法可以保证活动在回收前一定可以调用。所以我们可以利用这个方法,在活动被销毁前,将一些我们期望保留的数据保存下来。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you want to save";
outState.putString("data key", tempData);
}
可以看到onSaveInstanceState()
携带了一个Bundle
类型的参数,Bundle
类提供了一系列的方法用于保存数据,每个保存方法需要传入两个参数:取出数据时用到的键值和数据对象。
然后取出的数据可以在onCreate
中去恢复,因为它也有一个Bundle
类型参数,这个参数在一般情况下都是 null
,但是当活动被系统回收之前有通过 onSaveInstanceState()
方法来保存数据的话,这个参数就会带有之前所保存的全部数据,我们只需对Bundle对象调用getString()
方法并传入保存时写入的键值即可,当然我们在取出数据之前最好先判断一下Bundle
对象是否为空。
Activity全屏设置
方式1:AndroidManifest.xml
<activity android:name="MainActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
方式2:代码实现
requestWindowFeature(Window.FEATURE_NO_TITLE); // 隐藏标题栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); // 隐藏状态栏
注意:设置全屏的代码必须在setContentView(R.layout.main)
之前,不然会报错。
Activity横竖屏设置
方式1:AndroidManifest.xml
<activity android:name="MainActivity" android:screenOrientation="landscape" /> // 或者 “portrait”
方式2:代码实现
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Activity获取横竖屏方向
int orientation = this.getResources().getConfiguration().orientation;
orientation 的常用取值可以为 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE(横屏) 或 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT(竖屏)
Activity屏幕一直显示
1、AndroidManifest.xml
添加权限
<uses-permission android:name="android.permission.WAKE_LOCK" />
2、代码实现
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
网友评论