开篇废话
最近公司忙着搬迁,没有太多的精力去分享一些比较耗时间分析的知识点,随之就跟着之前的计划,开始回顾一下Android相关的基础知识。
今天我们一起来回顾一下我们接触最多,相对来说最熟悉的四大组件之一,Activity。
其实,当我在想什么是Activity的时候,确实有那么一会儿懵逼的感觉,因为不知道该如何组织语言去表达。
在这里,我大概说一下我的理解,到时候与别人交谈的时候,可以用这个思路来表达:
在我们日常应用中,Android是与用户交互的接口,它提供了一个界面让用户进行点击、各种滑动操作,这就是Activity的意义
技术详情
这次的内容比较简单,看起来会比较轻松,我主要分四个角度来进行讲述:
1. Activity的生命周期
2. Android系统中的任务栈
3. Activity的启动模式
4. scheme跳转协议
1. Activity的生命周期
1.1 Activity的四种状态
running:
表明activity处于活动状态,用户点击屏幕,屏幕可以作出响应,它是一个处于activity栈顶的状态
paused:
表明activity失去焦点的时候,也许是被一个非全屏的activity占据,或者被一个透明的activity放置在activity栈顶,这种情况下,就处于paused状态,用户操作对它不会有响应,但并不代表它被销毁了,这时候,他所有的状态信息和成员变量都还在,除非系统内存非常紧张的时候,就有可能被销毁掉。
stopped:
当activity被另一个Activity完全覆盖的时候,被覆盖的那个activity就会处于stopped状态,当内存不紧张的时候,状态信息和成员变量也是还在的。
killed:
表明activity已经被系统回收掉了,状态信息和成员变量都已经不存在了。
1.2 Activity的生命周期分析
首先,我们都需要看懂下面这张图:
大概分析几个场景的调用流程:
1.Activity启动 --> onCreate() --> onStart() --> onResume()
2.点击Home键回到系统主界面的时候 --> onPause() --> onStop()
3.当我们再次回到原来的那个Activity的时候 --> onRestart() --> onStart() --> onResume()
4.退出当前的Activity的时候 --> onPause() --> onStop() --> onDestory()
5.A Activity 调用弹出B Activity的时候(完全覆盖) --> A onPause() --> B onCreate() --> B onStart() --> B onResume --> A onStop()
6.Activity异常退出的时候 --> onPause() --> onSaveInstanceState() --> onStop() --> onDestory(),
需要注意的是onSaveInstanceState() 方法与onPause并没有严格的先后关系,有可能在onPause之前,也有可能在其后面调用,但会在onStop()方法之前调用
7.异常退出后又重新启动该Activity --> onCreate() --> onStart() --> onRestoreInstanceState() --> onResume()
1.3 Android中的进程优先级
Android系统中,主要有这样五个进程优先级(按优先级高到低排列):
1. 前台进程:一般情况下处于与用户进行交互的Activity或者与前台Activity绑定的Service
2. 可见进程:如果一个Activity处于可见但是不是处于前台,就是用户不能点击的情况下就是可见进程
3. 服务进程:就是在后台开启了一个Service服务,这就是服务进程
4. 后台进程:假设我们当前的Activity是前台进程,然后我们按下Home键,前台进程就变成了后台进程
5. 空进程:五个进程里面,优先级是最低的,如果我们的进程不属于上面四个进程的进程,那就是空进程,空进程系统可以随时杀死
2. Android系统中的任务栈
我们的Android系统为了记录用户开启了哪些Activity,记录这些Activity开启的先后顺序,所以引入了任务栈(Task Stack)的概念,可以增强用户的体验。
任务栈与下面讲的启动模式其实密切相关,关于任务栈相关的知识点如下:
1. 任务栈,Task Stack,别名Back Stack(后退栈),记录存放用户开启的Activity
2. 我们的App开启的时候,Android系统就给这个App分配了一个任务栈,当所有的Activity都退出的时候,任务栈就清空了
3. 我们的App的任务栈并不是唯一的,可能不止一个任务栈,一个Activity也可以独享一个任务栈
4. 清空了任务栈,我们的App的进程并不会销毁,还是会保留
5. 任务栈可以移动到后台,并且保留了每一个Activity的状态信息和成员变量
6. 只有在栈顶的Activity才能与用户进行交互
3. Activity的启动模式
Android系统中Activity的启动模式主要有四种:
1. standard
2. singleTop
3. singleTask
4. singleInstance
3.1 standard
这个模式,我们平时开发中使用得比较多,属于默认模式,可以指定,也可以不用配置。
在这个模式下,都会默认创建一个新的实例。也就意味着在一个任务栈中,可能存在多个相同的实例,也可以多个相同的Activity进行叠加
可以在清单文件里面进行指定该启动模式:
当前栈内顺序是A B C D,这个时候,在栈顶的D Activity启动A Activity,由于是A是Standard模式,那么就会在当前任务栈重新创建一个新的实例A,那么任务栈就变成了A B C D A。
3.2 singleTop
我们一般都称此模式为栈顶复用,表明与Standard不同的是,并不是每次都无脑的进行重新创建一个实例,需要进行判断。当被启动的Activity已经处于栈顶,就不会进行创建新的实例,而是调用栈顶Activity的onNewIntent方法,当然,如果栈顶的Activity并不是被启动的但是同一个任务栈的,依然会重新创建一个实例
3.3 singleTask
这个模式,我们中文称作栈内复用,与栈顶复用差不太多,只是,判断的是当前Activity处于的整个任务栈。如果在整个任务栈中,已经有当前Activity的实例,那就是调用该Activity的onNewIntent方法,而不会重新创建实例。
这个模式值得注意的是,当需要复用当前任务栈中的某一个Activity实例的时候,会直接把该实例上面的Activity进行出栈进行销毁,从而将该Activity实例放于栈顶。
3.3 singleInstance
这个模式比较特殊,当使用该模式的时候,会创建一个新的任务栈,而且,这个任务栈有且仅有这一个Activity实例。
如果再一次调用这个Activity实例,就会发生发用,调用它的onNewIntent方法,反正不会再重新创建一个了。
值得注意的是,按返回退出的时候,是一个任务栈都出栈完了之后,再操作另一个任务栈。
4. scheme跳转协议
Android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转到我们App中的各个页面;
通过scheme协议,服务器也可以定制化告诉App跳转到那个页面,可以通过通知栏消息定制化跳转页面,也可以通过H5页面跳转到页面等。
下面简单讲一下scheme的使用。
第一步,将满足scheme协议的Activity在清单文件里面进行注册:
<activity
android:name=".SecondActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="senduo" />
</intent-filter>
</activity>
第二步,在需要调用的地方,定义URI格式(也就是我们自定义的协议),其中格式URI格式如下:
[scheme:][//domain][path][?query][#fragment]
代码举例说明如下:
tvDomain = (TextView) findViewById(R.id.tvDomain);
tvDomainWithParams = (TextView) findViewById(R.id.tvDomainWithParams);
tvDomain.setText(Html.fromHtml("<a href='senduo://domain/path?params'>无参示例</a>"));
tvDomain.setMovementMethod(LinkMovementMethod.getInstance());
tvDomainWithParams.setText(Html.fromHtml("<a href='senduo://second_activity?id=123456&name=senduo'>有参示例</a>"));
tvDomainWithParams.setMovementMethod(LinkMovementMethod.getInstance());
第三步,在onCreate方法或者onNewIntent方法中进行URI的解析,根据实际的业务逻辑进行处理
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null) {
dispatchUri(uri);
} else {
Log.e(TAG, "Uri is null");
}
}
//URI的解析
private void dispatchUri(Uri uri) {
try {
final String domain = uri.getAuthority();
if (TextUtils.equals(SECOND_DOMAIN, domain)) {
final int id = Integer.valueOf(uri.getQueryParameter("id"));
final String name = uri.getQueryParameter("name");
Toast.makeText(this, id + " " + name, Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Log.e(TAG, "Uri Parse Error");
}
}
关于scheme的调用,可以根据具体的实际业务场景进行自行设计,像通知栏的跳转,网页的跳转啥的,具体的内容可以查看相关博客进行更深一步的学习。
干货总结
以上,是这一次关于Activity知识的一个整理,其实,Activity的知识点可以讲的很简单,也可以深入Framework层进行分析,那样理解起来会比较吃力,但收获也将不少。奈何最近各种需要干苦力,这些分析就留到Android源码分析的时候再深入了解吧。
我们作为一个Android开发人员来讲,Activity是四大组件中离我们最近,接触得最多的一个,需要对它的一些原理,机制有一定的了解,希望通过这一篇文章,能够提供一个清晰的思路来回顾Activity,能够回答以下问题:
1. 什么是Activity
2. activity有哪些状态
3. 手绘Activity的生命周期图
4. Android系统中的进程优先级
5. Activity的四种启动模式简述
6. scheme跳转协议是什么