Activity 是一个利用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。每个 Activity 都会取得一个用于绘制其用户界面的窗口。窗口通常会充斥屏幕,但也可小于屏幕并浮动在其余窗口之上。
Activity
1. Activity 的应用
咱们新建的工程中带有一个根底 activity。
新建工程中,须要留神 3 个文件。
- MainActivity.java 在 src/main/java 里,对应的包名目录下。
- activity_main.xml 在 res/layout 里。
- AndroidManifest.xml 在 src/main 里。这里叫做“清单文件”。
这 3 个文件散布在不同的中央。简略来说,java 文件能够管制界面逻辑。
layout 文件(这里指的是 activity_main.xml)预设了 UI 如何摆放。
清单文件通知零碎,我这个 app 有哪些组件,申请了什么权限。
2. layout – 界面布局
新建的 layout 中,as 个别会默认给一个ConstraintLayout
。比方 activity_main.xml。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 省略默认的 TextView -->
</androidx.constraintlayout.widget.ConstraintLayout>
这里为了用起来不便,咱们把它换成 LinearLayout
有的敌人会问,都 2021 年了,为什么不间接用 ConstraintLayout?
当初不做什么性能,先用 LinearLayout,就是为了不便。换成 LinearLayout 后,layout 文件长这样。
换成 LinearLayout 后的 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 省略默认的 TextView -->
</LinearLayout>
能够看到,标签的结尾和结尾都换成了LinearLayout
。其余中央临时不批改。
as 功能强大,十分便当。咱们能够用鼠标选中标签开始的 androidx...Layout
,而后间接键盘输入LinearLayout
的前几位字母。
as 会自动弹出抉择框,在外面双击抉择 LinearLayout
或者回车抉择,标签就替换实现了。
3. Java – 管制界面
layout 文件设计的是界面的初始布局。它决定了初始界面上放着什么 UI 组件以及组件是怎么组织安顿的。
这里咱们说的是「初始界面」或者「初始布局」。也就是说,咱们能够管制界面上的 UI 元素。
先看默认的 MainActivity.java。在 onCreate
办法里,R.layout.activity_main
指的就是activity_main.xml。
当初 layout 中有一个 TextView,它能够用来显示文字。咱们想在 MainActivity
中管制它,该怎么做呢?
当初改一下这个 TextView。删掉原来 ConstraintLayout 用到的那些属性。
给它增加一个 id。这个 id 在这个 layout 文件中必须是举世无双的。给它调配一个 id 叫做tv1
,就像上面。
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
当初 TextView 有了身份证,咱们在 activity 中就能够找到它。用的是 findViewById
办法。
TextView tv1 = findViewById(R.id.tv1);
当初咱们就拿到了界面上的这个 TextView 对象。能够对它进行操作了。
比方扭转它显示的文字。
TextView tv1 = findViewById(R.id.tv1); // 拿到 textView 的对象
tv1.setText("Today is a good day."); // 扭转文字
4. AndroidManifest.xml – 清单文件
也能够简称为「manifest 文件」。清单文件十分重要,它通知零碎咱们的 app 有哪些 activity,用到了什么权限等等信息。
如果要新建 activity,须要在清单中注册。
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
从这个默认的清单文件中咱们能够得悉,activity 是属于 application 的。application 就是咱们的利用。
application 标签中也指定了各种元素,例如利用的图标,名字,主题等等。
MainActivity 是利用启动的第一个 activity。能够察看到它设置了 action 和 category 属性。
android.intent.action.MAIN
决定应用程序最先启动的 Activity。android.intent.category.LAUNCHER
示意能够在手机“桌面”上看到利用图标。
设置了这 2 个标签,决定了这个 activity 是用户点击利用图标时第一个启动的界面。
小结
activity 是利用重要的组件之一。纷繁复杂的内容须要 activity 来承载。
之后咱们会在 activity 中管制各种各样的 UI 组件,解决用户的操作,申请权限等等。还要理解 activity 的生命周期,启动形式和跳转办法。
Activity 生命周期
生命周期图示
1. 生命周期变动
执行一些常见的操作,打 log 看一下生命周期的变动。测试机型:RedMi。
启动而后退出
onCreate
onStart
onResume
onWindowFocusChanged: hasFocus: true
onWindowFocusChanged: hasFocus: false
onPause
onStop
onDestroy
启动后按 home 键
Act1: onCreate
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
// 按 home 键
Act1: onWindowFocusChanged: hasFocus: false
Act1: onPause
Act1: onStop
// 再回来
Act1: onRestart
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
// 按返回键退出 act
Act1: onWindowFocusChanged: hasFocus: false
Act1: onPause
Act1: onStop
Act1: onDestroy
旋转手机
activity 在切换横竖屏的时候的生命周期。
[Life]: onCreate
[Life]: onStart
[Life]: onResume
[Life]: onWindowFocusChanged: hasFocus: true
// 横屏
[Life]: onPause
[Life]: onStop
[Life]: onDestroy
[Life]: onCreate
[Life]: onStart
[Life]: onResume
[Life]: onWindowFocusChanged: hasFocus: true
// 竖屏
[Life]: onPause
[Life]: onStop
[Life]: onDestroy
[Life]: onCreate
[Life]: onStart
[Life]: onResume
[Life]: onWindowFocusChanged: hasFocus: true
// 返回
[Life]: onWindowFocusChanged: hasFocus: false
[Life]: onPause
[Life]: onStop
[Life]: onDestroy
来回切换的生命周期变动
以 2 个 Activity 启动为例。
Act1: onCreate
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
Act1: onPause
Act1: onWindowFocusChanged: hasFocus: false
Act2: onCreate
Act2: onStart
Act2: onResume
Act2: onWindowFocusChanged: hasFocus: true
Act1: onStop
Act2: onWindowFocusChanged: hasFocus: false
Act2: onPause
Act1: onRestart
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
Act2: onStop
Act2: onDestroy
Act1: onWindowFocusChanged: hasFocus: false
Act1: onPause
Act1: onStop
Act1: onDestroy
弹出 AlertDialog
点击按钮弹出一个AlertDialog
。察看发现调用 onWindowFocusChanged
。
onWindowFocusChanged: hasFocus: false
onWindowFocusChanged: hasFocus: true
这里也能够用 DialogFragment 来做测试。
recreate
调用 recreate() 办法
[Life]: onCreate
[Life]: onStart
[Life]: onResume
[Life]: onWindowFocusChanged: hasFocus: true
[Life]: click [recreate]
[Life]: onPause
[Life]: onStop
[Life]: onDestroy
[Life]: onCreate
[Life]: onStart
[Life]: onResume
能够看到,调用 recreate()
办法后并没有走 onWindowFocusChanged
回调。
2. onCreate 和 onStart 的区别
activity 的状态区别
onCreate
在零碎首次创立 Activity 时触发。Activity 会在创立后进入 已创立 状态。- 当 Activity 进入“已开始”状态时,零碎会调用此回调。
onStart()
调用使 Activity 对用户可见,因为利用会为 Activity 进入前台并反对交互做筹备。
onStart()
办法会十分疾速地实现,并且与“已创立”状态一样,Activity 不会始终处于“已开始”状态。一旦此回调完结,Activity 便会进入已复原状态,零碎将调用 onResume()
办法。
3. onPause 和 onStop 的区别
onPause()
执行非常简单,而且不肯定要有足够的工夫来执行保留操作。因而,您不应应用 onPause()
来保留利用或用户数据、进行网络调用,或执行数据库事务。因为在该办法实现之前,此类工作可能无奈实现。
已进入已进行状态,因而零碎将调用 onStop()
回调。举例而言,如果新启动的 Activity 笼罩整个屏幕,就可能会产生这种状况。
在 onStop()
办法中,利用应开释或调整利用对用户不可见时的无用资源。例如,利用能够暂停动画成果,或从细粒度地位更新切换到粗粒度地位更新。应用 onStop()
而非 onPause()
可确保与界面相干的工作持续进行,即便用户在多窗口模式下查看您的 Activity 也能如此。您还应该应用 onStop()
执行 CPU 绝对密集的敞开操作。
Activity 启动,携带参数启动
后面大抵理解了 Activity 是一个利用组件,能为用户提供一个界面。以及如何新增 activity。一个 App 中,通常有多个界面。假如每一个界面对应一个 activity,不同界面之间怎么跳转呢?
1. Intent
通常 activity 之间的跳转离不开 Intent 这个类。Intent,直译为“用意”。咱们把信息包裹在 intent 对象中,而后执行。比方启动 RelativeLayoutGuideAct
这个 activity。
startActivity(new Intent(getApplicationContext(), RelativeLayoutGuideAct.class));
这里用到一个很常见的办法 startActivity (Intent intent)
。startActivity
属于 Context 类,Activity 是 Context 的子类。
java.lang.Object
android.content.Context
android.content.ContextWrapper
android.view.ContextThemeWrapper
android.app.Activity
当初咱们晓得了,启动 activity 须要应用 Intent,调用 startActivity
办法。
2. 带参数的跳转
在跳转去下一个页面时,咱们可能会想携带一些信息到下一个界面去。例如携带一些文本,数字等等。或者是一个对象。这些信息咱们能够交给 Intent,传递到下一个 activity 去。下一个 activity 中拿到咱们传入的 Intent。
携带根本类型和 String
咱们间接看 intent 的办法。
Intent intent = new Intent(getApplicationContext(), SendParamsDemo.class);
intent.putExtra(SendParamsDemo.K_INT, 100);
intent.putExtra(SendParamsDemo.K_BOOL, true);
intent.putExtra(SendParamsDemo.K_STR, "Input string");
startActivity(intent);
intent 的 putExtra
办法,能够传入参数。它承受 1 个 String 作为 key,而后是具体参数。例子中咱们跳转去了 SendParamsDemo。
public class SendParamsDemo extends AbsActivity {
public static final String K_INT = "k_int";
public static final String K_BOOL = "k_bool";
public static final String K_STR = "k_str";
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
gotInput();}
private void gotInput() {Intent intent = getIntent();
if (intent != null) {int i = intent.getIntExtra(K_INT, -1);
boolean b = intent.getBooleanExtra(K_BOOL, false);
String str = intent.getStringExtra(K_STR);
Log.d(TAG, "gotInput: i:" + i + ", b:" + b + ", str:" + str);
} else {Log.d(TAG, "gotInput: input null.");
}
}
}
// log:
// com.rustfisher.tutorial2020 D/rustAppSendParamsDemo: gotInput: i:100, b: true, str: Input string
在这个 activity 中咱们接管到了传入的参数。
察看 intent 的 putExtra
办法,咱们发现它反对传入很多种参数。
int,byte, char, float, double, long, boolean,string,CharSequence 或是它们的数组。也能够传入 Parcelable,Serializable 对象或是对象数组。
传入 Serializable 对象
除了根本类型和 String,能够传送对象吗?答案是必定的。Intent 能够携带 Serializable
对象。Serializable
自身是一个接口,自定义的对象实现这个接口后,就能够被 Intent 携带。比方咱们革新一下 DataTest 类,让它实现 Serializable
接口。
public class DataTest implements Serializable {// 实现接口
而后将对象送给 intent,再启动 activity。
Intent intent = new Intent(getApplicationContext(), RecyclerViewDemo2Act.class);
DataTest out = new DataTest("input time", 233, 666, 999);
Log.d(TAG, "startInputData: sending object:" + out);
intent.putExtra(RecyclerViewDemo2Act.K_INPUT_DATA, out);
startActivity(intent);
被启动的 activity 承受传入的 intent 并取出对象。
Intent intent = getIntent();
if (intent != null) {DataTest d = (DataTest) intent.getSerializableExtra(K_INPUT_DATA);
// 取出了对象,拿去显示
}
Serializable
接口不含任何办法。实现了这个接口的类,零碎会主动将其序列化。
咱们打印登程送和接管到的对象。
startInputData: sending object: com.rustfisher.tutorial2020.recycler.data.DataTest@fb43df5
getInputData: input data object: com.rustfisher.tutorial2020.recycler.data.DataTest@a588b5c
能够发现这 2 个对象并不是同一个援用。但它们的“内容”是一样的。对象经验了序列化和反序列化的过程。
值得注意的是,Intent 能携带的对象大小并不是无限度的。理论开发中,须要开发者本人预估传输的数据大小。
传送 Parcelable 对象和传送 Serializable 对象相似,用同样的存入和取出操作。
Activity 相干面试题
1. 谈一下返回栈
首先了解 android 是应用 Task 来治理流动,一个 Task 就是一组寄存在栈里的流动的汇合,这个栈就叫做返回栈,每启动一个新的流动,就会将其放入栈顶,当咱们点击 back 回退或调用 activity 的 finish 函数处于栈顶的流动就会出栈,前一个入栈的流动就会到栈顶,零碎总是显示处于栈顶的流动。
2. 说下 Activity 的生命周期?
- onCreate()办法:流动第一次创立的时候被调用,常做初始化的操作,比方加载布局(setContentView),绑定事件(findViewById)。示意 Activity 正在创立。
- onStart()办法:流动由不可见到可见的时候被调用,示意 Activity 正在启动,此时 Activity 可见但不在前台。
- onResume()办法:流动筹备好和用户进行交互时调用。示意 Acitivity 取得焦点,此时 Activity 可见且在前台。
- onPause()办法:零碎筹备去启动或复原另一个流动时调用。示意 Activity 正在进行,此时可做存储数据,进行动画等操作。
- onStop()办法:在流动齐全不可见的时候调用。示意 Activity 行将进行。
- onDestory()办法:在流动被销毁之前调用,示意 Activity 行将销毁,常做回收工作、资源开释。
- onRestart()办法:在流动由进行状态变为运行状态之前调用。示意 Activity 行将重启。
3. 说下流动的生存期
流动的生存期分为三个:
- 残缺生存期
- 可见生存期
- 前台生存期
残缺生存期:onCreate()办法与 onDestory()都处于残缺生存期,个别状况下,Activity 会在 onCreate()办法中实现各种初始化操作,而在 onDestory()办法中实现开释内存的操作。
可见生存期:onStart()办法与 onStop()办法就是可见生存期,Activity 对于用户是可见的,但无奈与用户交互。onStart()办法中对资源进行加载,onStop()办法中对资源进行开释。
前台生存期:onResume 办法与 onPause 办法就是前台生存期,在前台生存期内,流动处于运行状态,此时能够与用户交互。
4. 说下 Activity 处于 onPasue()下能够执行那些操作?
- 用户返回该 Activity,调用 onResume()办法,从新 running
- 用户关上了其余 Activity,就会调用 onStop()办法
- 零碎内存不足,领有更高权限的利用须要内存,该 Activity 就会被零碎回收
-
如果用户返回到 onStop()的 Activity 又显示在前台了,零碎会调用
onRestart() -> onStart() -> onResume() 而后从新 running
当 Activity 完结(调用 finish()办法)就会调用 onDestory()办法开释所有占用的资源。
生命周期的切换过程
- 启动一个 Activity onCreate->onStart->onResume
- 当一个 Activity 关上另一个 Activity 都会回调哪些办法,如果 ActivityB 是齐全通明的呢,如果启动的是一个对话框 Activity 呢?A:onPause->B:onCreate->B:onStart->B:onResume->A:onStop 如果 ActivityB 是齐全通明的或对话框 Activity 则不会调用 onStop。
- 启动新 Activity 后,又返回到旧的 Activity B:onPause->A:onRestart->A:onStart->A:onResume->B:onStop->B:onDestory
- 敞开屏幕 / 按 Home 键:onPause->onStop
- 当一个 Activity 按 Home 键切换到桌面后又回到该 Activity 回调哪些办法。onPause->onStop->onRestart->onStart->onResume
- 当一个 Activity 按 back 键回退时回调哪些办法 onPause->onStop->onDestory
Activity 的优先级
- 可见且能够交互(前台 Acitivity):正在和用户交互,优先级最高。
- 可见但不能够交互(可见但非前台 Activity):比方以后 Activity 启动了一个对话框 Activity,以后 Activity 就是可见但不能够交互。
- 后盾 Activity:曾经被暂停的 Activity,比方执行了 onStop,优先级最低。当零碎内存不足,会依照优先级程序从低到高去杀死指标 Activity 所在的过程。
5. 优先级低的 Activity 在内存不足被回收后怎么做能够复原到销毁前状态?
优先级低的 Activity 在内存不足被回收后从新关上(横竖屏切换的过程中)会引发 Activity 重建。
在 Activity 因为异常情况被终止时,零碎会调用 onSaveInstanceState
办法来保留以后 Activity 的状态,该办法调用于 onStop 之前,与 onPause 办法没有时序关系。
当异样终止的 Activity 被重建时,会调用 onRestoreInstanceState
办法(该办法在 onStart 之后),并且把 Activity 销毁时 onSaveInstanceState
保留的 Bundle 对象参数同时传递给 onCreate
办法和onRestoreInstanceState
办法。该办法的调用是在 onStart
之前。
因而可通过 onRestoreInstanceState(Bundle savedInstanceState)
和 onCreate((Bundle savedInstanceState)
来判断 Activity
是否被重建,并取出数据进行复原。但须要留神的是,在 onCreate
取出数据时肯定要先判断savedInstanceState
是否为空。
补充:其中 onCreate 和 onRestoreInstanceState
办法来复原 Activity 的状态的区别:onRestoreInstanceState
办法回调则阐明 bundle 对象非空,不须要加非空判断,而 onCreate 须要非空判断。
6. 谈谈 onSaveInstanceState()
与 onRestoreIntanceState()
onSaveInstanceState()
这两个办法并不是生命周期办法,它们并不一定会被触发。当利用遇到意外状况(如:内存不足、用户间接按 Home 键)由零碎销毁一个 Activity 时,onSaveInstanceState() 会被调用,该办法的调用在 onStop 之前,与 onPause 没有时序关系。然而当用户被动去销毁一个 Activity 时,例如在利用中按返回键,onSaveInstanceState()就不会被调用。因为在这种状况下,用户的行为决定了不须要保留 Activity 的状态。
onSaveInstanceState()
机会:
(1)用户按下 Home 键
(2)横竖屏切换
(3)按下电源按钮(敞开屏幕显示)
(4)内存不足导致优先级的 Activity 被杀死
onRestoreIntanceState()
当被零碎异样销毁的 Activity 被重建时,会调用 onRestoreIntanceState 或 onCreate 办法来复原,而 onRestoreInstance 与 Oncreate 办法中传入的 Bundle 对象是销毁时 onSaveInstanceState 保留的,onRestoreIntanceState 在 onStart 之后。
7. onSaveInstanceState()与 onPause()的区别?
onSaveInstanceState()
只适宜用于保留一些临时性的状态,而 onPause()
适宜用于数据的长久化保留。
8. 谈谈横竖屏切换过程中调用的函数
要切记这里流动曾经被销毁了。
onPause->onSaveInstanceState->onStop->onDestory()->onCreate->onStart->onRestoreIntanceState->onResume
9. 如何避免横竖屏切换(配置扭转)时 Activity 销毁并切换
通过对 AndroidManifest 文件的 Activity 中指定(configChanges)属性:
android:configChanges =“orientation| screensize”
来防止横竖屏切换时,Activity 的销毁和重建,而是回调了 onCofigurationChanged()办法
@Override
public void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);
}
这里附上 android configChanges 的所有属性解释
“mcc“挪动国家号码,由三位数字组成,每个国家都有本人独立的 MCC,能够辨认手机用户所属国家。“mnc“移动网号,在一个国家或者地区中,用于辨别手机用户的服务商。“locale“所在地区发生变化。“touchscreen“触摸屏曾经扭转。(这不应该常产生。)“keyboard“键盘模式发生变化,例如:用户接入内部键盘输入。“keyboardHidden“用户关上手机硬件键盘“navigation“导航型产生了变动。(这不应该常产生。)“orientation“设施旋转,横向显示和竖向显示模式切换。“fontScale“全局字体大小缩放产生扭转
10. 说下 Activity 的四种启动模式?
- standard 模式(规范模式):一般启动模式,每次启动 Activity 时,就会创立一个实例。
- singletop 模式(栈顶模式):当启动 Activity 时,会判断工作栈的栈顶是否为该 Activity,如果是该 Activity 则不会创立实例,去回调 onNewIntent(intent)办法,否则会创立实例
- singletask 模式(栈内模式):当启动 Activity 时,只有该 Activity 在指定的栈中,就不会创立实例,去回调 onNewIntent(intent)** 办法。如果不存在,会判断是否指定的栈不存在,就创立一个栈并将 Activity 的实例压入,如果指定的栈存在,就间接压入该栈中。
- singleInstance 模式(单实例模式):该模式下,创立 Activity 实例时,间接创立一个栈,栈中只有该 Activity 实例。之后无论哪个应用程序启动该 Activity,都只会调用栈中该实例。
11. 谈谈 singleTop 和 singleTask 的区别以及利用场景
singleTop 模式的含意是(参考下面问题),singleTask 模式的含意是(参考下面问题)
因而二者的差异为:
- singleTop 模式:该模式下,工作栈中可能有多个雷同 Activity 实例,因为它只是判断以后启动的 Activity 是否在栈顶。该模式的 Activity 会默认进入启动它所属的工作栈,不波及工作栈的转换。罕用于避免疾速间断点击而创立多个 Activity 实例。
- singleTask 模式:该模式向,工作栈中只会有一个 Activity 实例,因为它会判断以后启动的 Activity 是否在以后指定的栈中。该模式下 Activity 能够通过 taskAffinity 去指定须要的工作栈,可能波及工作栈的转换,罕用于首页或登录页。因为不管咱们在进入首页后进入了多少个 Activity,当咱们返回首页后,还是心愿退出首页间接能够退出利用。该模式下会把栈中位于要启动的 Activity 下面的 Activity 都出栈。
12. onNewIntent()调用机会?
有两个调用机会,别离是 singleTop 模式下与 singleTask 模式下启动 Activity。
singleTop 模式:当启动的 Activity 是在工作栈的栈顶时,会回调 onNewIntent 办法。
singleTask 模式:当启动的 Activity 存在于工作栈中,会回调 onNewIntent 办法。
13. 理解哪些 Activity 启动模式的标记位?
- FLAG_ACTIVITY_SINGLE_TOP: 对应 singleTop 启动模式
- FLAG_ACTIVITY_NEW_TASK:对应 singleTask 模式
更多 Android 零根底入门教程学习参考