Android Activity - tenji/ks GitHub Wiki
活动代表了一个具有用户界面的单一屏幕,如 Java 的窗口或者帧。Android 的活动是 ContextThemeWrapper
类的子类。
如果你曾经用 C, C++ 或者 Java 语言编程,你应该知道这些程序从 main()
函数开始。很类似的,Android 系统初始化它的程序是通过活动中的 onCreate()
回调的调用开始的。存在有一序列的回调方法来启动一个活动,同时有一序列的方法来关闭活动。
活动(Activity)是最容易吸引到用户的地方了,它是一种可以包含用户界面的组件, 主要用于和用户进行交互。一个应用程序中可以包含零个或多个活动,但不包含任何活动的 应用程序很少见,谁也不想让自己的应用永远无法被用户看到吧?
New -> Activity -> Empty Activity
待更新...
说道 Activity 的生命周期就不得不看一张超级经典的图了:
- onCreate():创建时,这个方法是只在 Activity 被创建时调用一次,可以做一些数据的初始化工作;
- onStart():开始时,这个方法在每次启动 Activity 时调用,变成“用户可见不可交互”的状态;
- onResume():重启时,变成和用户可交互的状态,将当前 Activity 在放在栈的最上端;
- onPaues():暂停时,到这一步是可见但不可交互的,系统会停止动画等消耗CPU的事情。从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候你的程序的优先级降 低,有可能被系统收回。在这里保存的数据,应该在 onResume 里读出来;
- onStop():停止时,变得不可见 ,被下一个 Activity 覆盖了;
- onDestroy():销毁时,这里主要是做一些释放资源的操作。
在一个项目中,我们会用到很多的 Activity,因此我们需要一种特别的机制帮助我们在 Activity 之间传递消息。
Intent 是一种消息传递的机制,它负责对操作的动作、动作涉及数据、附加数据进行描述,Android 则根据此 Intent 的描述,负责找到对应的组件,将 Intent 传递给调用的组件,并完成组件的调用。我们有两种形式来使用 Intent:
通过指定具体的组件类,通知应用启动对应的组件。比如我们从 MainActivity 跳转到 SecondActivity,并传一个字符串。
MainActivity 中的代码:
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondAcvivity.class);
intent.putExtra("values", "传个值");
startActivity(intent);
SecondActivity 中的代码:
Intent intent = getIntent();
String values = intent.getStringExtra("values");
相比于显式 Intent,隐式 Intent 则含蓄了许多,它并不明确指出我们想要启动哪一个活动,而是指定了一系列更为抽象的 action和 category等信息,然后交由系统去分析这个 Intent,并帮我们找出合适的活动去启动。
什么叫做合适的活动呢?简单来说就是可以响应我们这个隐式 Intent 的活动,那么目前 Activity 可以响应什么样的隐式 Intent 呢?额,现在好像还什么都响应不了,不过很快就会有了。
我们还是要从 MainActivity 跳转到 SecondActivity,这时我们可以通过在 标签下配置 的内容,可以指定当前活动能够响应的 action 和 category,打开 AndroidManifest.xml,添加如下代码:
<activity android:name=".SecondActivity" >
<intent-filter>
<action android:name="com.example.administrator.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
在 标签中我们指明了当前活动可以响应 com.example.activitytest.ACTION_START 这个 action,而 标签则包含了一些附加信息,更精确地指明了当前的活动能够响应的 Intent 中还可能带有的 category。只有 和 中的内容同时能够匹配上 Intent 中指定的 action 和 category 时,这个活动才能响应该 Intent。
然后我们修改 MainActivity 中的 Intent 代码:
Intent intent = new Intent("com.example.administrator.ACTION_START");
startActivity(intent);
可以看到,我们使用了 Intent 的另一个构造函数,直接将 action 的字符串传了进去,表 明我们想要启动能够响应 com.example.activitytest.ACTION_START这个 action 的活动。那前面不是说要和同时匹配上才能响应的吗?怎么没看到哪里有指定category 呢?这是因为 android.intent.category.DEFAULT 是一种默认的 category,在调用startActivity()方法的时候会自动将这个 category 添加到 Intent 中。
这种方式同样可以启动 SecondActivity。不同的是,这次你是使用了隐式 Intent 的方式来启动的。
待更新...
-
Running(运行):在屏幕前台(位于当前任务堆栈的顶部)
-
Paused(暂停):失去焦点但仍然对用户可见(覆盖 Activity 可能是透明或未完全遮挡)
-
Stopped(停止):完全被另一个 Activity 覆盖
-
Destroyed(销毁):退出,完全销毁
Android 针对 Activity 的管理使用的是栈,就是说某一个时刻只有一个 Activity 处在栈顶,当这个 Activity 被销毁后,下面的 Activity 才有可能浮到栈顶,或者有一个新的 Activity 被创建出来,则旧的 Activity 就被压栈沉下去了。Activity 是 Android 程序的表现层。程序的每一个显示屏幕就是一个 Activity。正在运行的 Activity 处在栈的最顶端,它是运行状态的。
当在程序中调用 Activity.finish()方法时,结果和用户按下 BACK 键一样:它告诉 Activity Manager 该 Activity 实例可以被“回收”。随后 Activity Manager 激活处于栈第二层的 Activity ,把原 Activity 压入到栈的第二层,从 Running 状态转到 Paused 状态。
活动的启动模式对你来说应该是个全新的概念,在实际项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。启动模式一共有四种,分别是 standard、singleTop、singleTask和singleInstance,可以在 AndroidManifest.xml 中通 过给 标签 指 android:launchMode 属性来选择启动模式。下面我们来逐个进行学习。
standard 是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。经过上面的学习,你已经知道了 Android 是使用返回栈来管理活动的,在 standard 模式(即默认情况)下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用 standard 模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
singleTop 也是发送新的实例,但不同 standard 的一点是,在请求的 Activity 正好位于栈顶时(配置成 singleTop 的 Activity),不会构造新的实例
活动在整个应用程序的上下文中只存在一个实例,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
singleInstance 模式应该算是四种启动模式中最特殊也最复杂的一个了,你也需要多花点功夫来理解这个模式。不同于以上三种启动模式,指定为 singleInstance 模式的活动会启用一个新的返回栈来管理这个活动,这个模式主要解决了在不同 app 之间的共享活动实例的问题。
网上找了个例子,如果开启一个导游服务类的应用程序,里面有个 Activity 是开启 GOOGLE 地图的,当按下 home 键退回到主菜单又启动 GOOGLE 地图的应用时,显示的就是刚才的地图,实际上是同一个 Activity,实际上这就引入了 singleInstance。singleInstance 模式就是将该 Activity 单独放入一个栈中,这样这个栈中只有这一个 Activity,不同应用的intent 都由这个 Activity 接收和展示,这样就做到了共享。当然前提是这些应用都没有被销毁,所以刚才是按下的 HOME 键,如果按下了返回键,则无效。
你已经掌握了关于活动非常多的知识,但运用的技巧却是多种多样。所以,在这里我准备教你几种关于活动的最佳实践技巧,这些技巧在你以后的开发工作当中将会非常受用。
待更新...
如果目前你手机的界面还停留在 ThirdActivity,你会发现当前想退出程序是非常不方便的,需要连按三次 Back 键才行,如果我们的程序需要一个注销或者退出的功能该怎么办呢?必须要有一个随时随地都能退出程序的方案才行。
其实解决思路也很简单,只需要用一个专门的集合类对所有的活动进行管理就可以了,下面我们就来实现一下。
public class ActivityCollector {
public static List<AppCompatActivity> activities = new ArrayList<>();
/**
* 添加
*/
public static void addActivity(AppCompatActivity activity) {
activities.add(activity);
}
/**
* 移除
*/
public static void removeActivity(AppCompatActivity activity) {
activities.remove(activity);
}
/**
* 结束所有
*/
public static void finishAll() {
for (AppCompatActivity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}
在活动管理器中,我们通过一个 List 来暂存活动,然后提供了一个 addActivity() 方法用于向 List 中添加一个活动,提供了一个 removeActivity()方法用于从 List 中移除活动,最后提供了一个 finishAll() 方法用于将 List 中存储的活动全部都销毁掉。
接下来修改 BaseActivity 中的代码,如下所示:
public class BaseActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
Log.d("BaseActivity", getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
在 BaseActivity 的 onCreate() 方法中调用了 ActivityCollector 的 addActivity() 方法,表明将当前正在创建的活动添加到活动管理器里。然后在 BaseActivity 中重写 onDestroy() 方法,并调用了 ActivityCollector 的 removeActivity()方法,表明将一个马上要销毁的活动从活动管理器里移除。
以后不管在什么地方想要退出应用,都可以调用如下代码:
ActivityCollector.finishAll();
当然你还可以在销毁所有活动的代码后面再加上杀掉当前进程的代码,以保证程序完全退出。
待更新...