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。

启动而后退出

onCreateonStartonResumeonWindowFocusChanged: hasFocus: trueonWindowFocusChanged: hasFocus: falseonPauseonStoponDestroy

启动后按home键

Act1: onCreateAct1: onStartAct1: onResumeAct1: onWindowFocusChanged: hasFocus: true// 按home键Act1: onWindowFocusChanged: hasFocus: falseAct1: onPauseAct1: onStop// 再回来Act1: onRestartAct1: onStartAct1: onResumeAct1: onWindowFocusChanged: hasFocus: true// 按返回键退出actAct1: onWindowFocusChanged: hasFocus: falseAct1: onPauseAct1: onStopAct1: 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: onCreateAct1: onStartAct1: onResumeAct1: onWindowFocusChanged: hasFocus: trueAct1: onPauseAct1: onWindowFocusChanged: hasFocus: falseAct2: onCreateAct2: onStartAct2: onResumeAct2: onWindowFocusChanged: hasFocus: trueAct1: onStopAct2: onWindowFocusChanged: hasFocus: falseAct2: onPauseAct1: onRestartAct1: onStartAct1: onResumeAct1: onWindowFocusChanged: hasFocus: trueAct2: onStopAct2: onDestroyAct1: onWindowFocusChanged: hasFocus: falseAct1: onPauseAct1: onStopAct1: onDestroy

弹出 AlertDialog
点击按钮弹出一个AlertDialog。察看发现调用 onWindowFocusChanged

onWindowFocusChanged: hasFocus: falseonWindowFocusChanged: 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.Objectandroid.content.Contextandroid.content.ContextWrapperandroid.view.ContextThemeWrapperandroid.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@fb43df5getInputData: 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()办法开释所有占用的资源。

生命周期的切换过程

  1. 启动一个Activity onCreate->onStart->onResume
  2. 当一个Activity关上另一个Activity都会回调哪些办法,如果ActivityB是齐全通明的呢,如果启动的是一个对话框Activity呢? A:onPause->B:onCreate->B:onStart->B:onResume->A:onStop 如果ActivityB是齐全通明的或对话框Activity则不会调用onStop。
  3. 启动新Activity后,又返回到旧的Activity B:onPause->A:onRestart->A:onStart->A:onResume->B:onStop->B:onDestory
  4. 敞开屏幕/按Home键: onPause->onStop
  5. 当一个Activity按Home键切换到桌面后又回到该Activity回调哪些办法。 onPause->onStop->onRestart->onStart->onResume
  6. 当一个Activity按back键回退时回调哪些办法 onPause->onStop->onDestory

Activity的优先级

  1. 可见且能够交互(前台Acitivity):正在和用户交互,优先级最高。
  2. 可见但不能够交互(可见但非前台Activity):比方以后Activity启动了一个对话框Activity,以后Activity就是可见但不能够交互。
  3. 后盾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的四种启动模式?

  1. standard模式(规范模式):一般启动模式,每次启动Activity时,就会创立一个实例。
  2. singletop模式(栈顶模式):当启动Activity时,会判断工作栈的栈顶是否为该Activity,如果是该Activity则不会创立实例,去回调onNewIntent(intent)办法,否则会创立实例
  3. singletask模式(栈内模式):当启动Activity时,只有该Activity在指定的栈中,就不会创立实例,去回调onNewIntent(intent)**办法。如果不存在,会判断是否指定的栈不存在,就创立一个栈并将Activity的实例压入,如果指定的栈存在,就间接压入该栈中。
  4. 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零根底入门教程学习参考