2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > android基础 [超级详细android Activity组件解析(Activity综述 Activity生命周期

android基础 [超级详细android Activity组件解析(Activity综述 Activity生命周期

时间:2024-04-07 04:39:47

相关推荐

android基础 [超级详细android Activity组件解析(Activity综述 Activity生命周期

1 章节目录

2 Android Activity综述

2.1 Activity怎么用

2.2 layout - 界面布局

2.3 Java - 控制界面

2.4 AndroidManifest.xml - 清单文件

2.5 activity总览小结

3 Activity 生命周期

3.1 生命周期变化

3.2 onCreate和onStart的区别

3.3 onPause和onStop的区别

4 Activity 启动,携带参数启动

4.1 Intent

4.2 带参数的跳转

5 Activity 传递Parcelable对象

5.1 Parcelable对象简介

5.2 使用例子

6 Android Activity 返回时携带参数(更新)

6.1 启动新的Activity

6.2 装载回传数据

6.3 获取回传数据

6.4 requestCode的限制

2 Android Activity综述

用户看得见摸的着的是手机屏幕。我们要在手机屏幕上显示文字图像等信息,并对用户的点击滑动等等操作作出不同的反应。 App中与用户交互的大任由Activity来承担。当用户手指点击手机屏幕时,Android系统检测到屏幕发生的事情,将这一事件分发对应的App处理。 这里要注意,activity接收到的是系统给的信息。系统会判断这些交互消息该给哪个app来处理。

或者换个说法

Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。

2.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.2 layout - 界面布局

新建的layout中,as一般会默认给一个 ConstraintLayout 。比如activity_main.xml

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><!-- 省略默认的TextView --></androidx.constraintlayout.widget.ConstraintLayout>

这里为了用起来方便,我们把它换成LinearLayout。

有的朋友会问,都了,为什么不直接用ConstraintLayout?

现在不做什么功能,先用LinearLayout,就是为了方便。 换成LinearLayout后,layout文件长这样。

换成LinearLayout后的activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><!-- 省略默认的TextView --></LinearLayout>

可以看到,标签的开头和结尾都换成了 LinearLayout 。其他地方暂时不修改。

as功能强大,非常便利。我们可以用鼠标选中标签开始的 androidx...Layout ,然后直接键盘输入LinearLayout 的前几位字母。

as会自动弹出选择框,在里面双击选择 LinearLayout 或者回车选择,标签就替换完成了。

2.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 ,就像下面。

<TextViewandroid: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."); // 改变文字

2.4 AndroidManifest.xml - 清单文件

也可以简称为「manifest文件」。清单文件非常重要,它告诉系统我们的app有哪些activity,用到了什么权限等等信息。

如果要新建activity,需要在清单中注册。

AndroidManifest.xml

<applicationandroid: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是用户点击应用图标时「第一个启动的界面」。

2.5 activity总览小结

至此,我们知道了一个activity通常需要一个java文件,一个layout文件,并且要在清单中注册。

与网页开发类比的话,layout文件就像html,Java和js有类似的作用。style(样式)可以起到css的作用。

activity是应用重要的组件之一。纷繁复杂的内容需要activity来承载。

之后我们会在activity中控制各种各样的UI组件,处理用户的操作,申请权限等等。还要了解activity的生命周期,启动方式和跳转方法。

3 Activity 生命周期

当用户浏览、退出和返回到您的应用时,您应用中的Activity实例会在其生命周期的不同状态间转换。Activity类会提供许多回调,这些回调会让 Activity 知晓某个状态已经更改:系统正在创建、停止或恢复某个 Activity,或者正在销毁该 Activity 所在的进程。

在生命周期回调方法中,您可以声明用户离开和再次进入 Activity 时 Activity 的行为方式。例如,如果您正构建流媒体视频播放器,当用户切换至另一应用时,您可能要暂停视频或终止网络连接。当用户返回时,您可以重新连接网络并允许用户从同一位置继续播放视频。换言之,每个回调都支持您执行适合给定状态变更的特定作业。在合适的时间执行正确的作业,并妥善处理转换,这将提升应用的稳健性和性能。例如,良好的生命周期回调实现有助于防止应用出现以下问题:

当用户在使用应用时接听来电,或切换至另一应用时崩溃。

当用户未主动使用它时,消耗宝贵的系统资源。

当用户离开应用并在稍后返回时,丢失用户的进度。

当屏幕在横向和纵向之间旋转时,崩溃或丢失用户的进度。

生命周期图示

3.1 生命周期变化

执行一些常见的操作,打log看一下生命周期的变化。

生命周期情况示例

案例:

package com.hopu.demo;​import android.content.Intent;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;​public class MainActivity extends AppCompatActivity {private Button button;​@Overrideprotected void onCreate(Bundle savedInstanceState) {Log.d("MainActivity","onCreate()被调用");super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button=(Button)findViewById(R.id.button1);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Intent intent=new Intent(MainActivity.this,SecondActivity.class);startActivity(intent);}});}​@Overrideprotected void onStart() {super.onStart();Log.d("MainActivity","onStart()被调用");}​​@Overrideprotected void onResume() {super.onResume();Log.d("MainActivity","onResume()被调用");}​@Overrideprotected void onPause() {super.onPause();Log.d("MainActivity","onPause()被调用");}​@Overrideprotected void onStop() {super.onStop();Log.d("MainActivity","onStop()被调用");}​@Overrideprotected void onDestroy() {super.onDestroy();Log.d("MainActivity","onDestroy()被调用");}​@Overrideprotected void onRestart() {super.onRestart();Log.d("MainActivity","onRestart()被调用");}}

结论:

Activity 第一次启动,回调如下:onCreate -> onStart -> onResume

当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause -> onStop。特殊情况:如果新Activity采用了透明主题,那么当前Activity不会回调onStop

当用户再次回到原Activity时,回调如下:onRestart ->onStart -> onResume

当用户按back键回退时,回调如下:onPause ->onStop -> onDestroy

3.2 onCreate和onStart的区别

activity的状态区别

onCreate()在系统首次创建 Activity 时触发。Activity会在创建后进入已创建状态。

当 Activity 进入“已开始”状态时,系统会调用此回调。onStart()调用使 Activity 对用户可见,因为应用会为 Activity 进入前台并支持交互做准备。

onStart()方法会非常快速地完成,并且与“已创建”状态一样,Activity 不会一直处于“已开始”状态。一旦此回调结束,Activity 便会进入已恢复状态,系统将调用onResume()方法。

3.3 onPause和onStop的区别

onPause() 执行非常简单,而且不一定要有足够的时间来执行保存操作。 因此,不应使用onPause() 来保存应用或用户数据、进行网络调用,或执行数据库事务。因为在该方法完成之前,此类工作可能无法完成。

已进入已停止状态,因此系统将调用 onStop() 回调。举例而言,如果新启动的 Activity 覆盖整个屏幕,就可能会发生这种情况。

在 onStop() 方法中,应用应释放或调整应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从细粒度位置更新切换到粗粒度位置更新。 使用 onStop() 而非 onPause() 可确保与界面相关的工作继续进行,即使用户在多窗口模式下查看您的 Activity 也能如此。 您还应该使用 onStop() 执行CPU 相对密集的关闭操作。

4 Activity 启动,携带参数启动

前面我们大致了解了Activity是一个应用组件,能为用户提供一个界面。以及如何新增activity。 一个App中,通常有多个界面。假设每一个界面对应一个activity,不同界面之间怎么跳转呢?

4.1 Intent

通常activity之间的跳转离不开Intent这个类。 Intent,直译为“意图”。我们把信息包裹在intent对象中,然后执行。 比如启动 RelativeLayoutGuideAct 这个activity。

显式Intent跳转

Intent intent = new Intent(getApplicationContext(),RelativeLayoutGuideAct.class);startActivity(intent);

这里用到一个很常见的方法 startActivity (Intent intent) 。 startActivity 属于Context类,Activity是Context的子类。

现在我们知道了,启动activity需要使用Intent,调用startActivity方法。

4.2 带参数的跳转

在跳转去下一个页面时,我们可能会想携带一些信息到下一个界面去。例如携带一些文本,数字等等。或者是一个对象。 这些信息我们可以交给Intent,传递到下一个activity去。下一个activity中拿到我们传入的Intent。

4.2.1 携带基本类型和String

我们直接看intent的方法。

Intent intent = new Intent(getApplicationContext(), SendParamsDemo.class);intent.putExtra("id", 100);intent.putExtra("isShow", true);intent.putExtra("Msg", "Input string");startActivity(intent);

intent的 putExtra 方法,可以传入参数。它接受1个String作为key,然后是具体参数。

观察intent的 putExtra 方法,我们发现它支持传入很多种参数。int,byte, char, float, double, long, boolean,string,CharSequence或是它们的数组。 也可以传入Parcelable,Serializable对象或是对象数组。

如何在接收上个页面传递过来的值?

Intent intent = getIntent();int i = intent.getIntExtra("isShow", 0);boolean b = intent.getBooleanExtra("isShow", false);String str = intent.getStringExtra("Msg");

4.2.2传入Serializable对象

除了基本类型和String,可以传送对象吗? 答案是肯定的。Intent可以携带 Serializable 对象。Serializable 本身是一个接口,自定义的对象实现这个接口后,就可以被Intent携带。 比如我们创建一个DataTest类,让它实现 Serializable 接口。

public class Person implements Serializable { // 实现接口

Serializable 接口不含任何方法。实现了这个接口的类,系统会自动将其序列化。

然后将对象送给intent,再启动activity。

Intent intent = new Intent(getApplicationContext(), RecyclerViewDemo2Act.class);Person out = new Person("maoyz", '男', 32);Log.d(TAG, "startInputData: sending object: " + out);intent.putExtra("person", out);startActivity(intent);

被启动的activity接受传入的intent并取出对象。

Intent intent = getIntent();if (intent != null) {Person person = (Person) intent.getSerializableExtra("Person");// 取出了对象,拿去显示}

值得注意的是,Intent能携带的对象大小并不是无限制的。实际开发中,需要开发者自己预估传输的数据大小。

5 Activity 传递Parcelable对象

前面我们知道了启动activity的时候可以传递一些参数。Activity的跳转时可以传递Parcelable对象。

5.1 Parcelable对象简介

进行Android开发的时候,无法将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递。简单来说就是将对象转换为可以传输的二进制流(二进制序列)的过程,这样我们就可以通过序列化,转化为可以在网络传输或者保存到本地的流(序列),从而进行传输数据 ,那反序列化就是从二进制流(序列)转化为对象的过程.

Parcelable是Android为我们提供的序列化的接口,Parcelable相对于Serializable的使用相对复杂一些,但Parcelable的效率相对Serializable也高很多,这一直是Google工程师引以为傲的,有时间的可以看一下Parcelable和Serializable的效率对比 Parcelable vs Serializable 号称快10倍的效率

Parcelable对象和Serializable不一样。实现了Parcelable接口的类并不会被系统序列化。 接下来我们用一个例子看看如何使用这一接口。

5.2 使用例子

先准备数据,然后传送Parcelable对象。

5.2.1 数据准备

先设计一个类实现Parcelable接口。前面我们使用Serializable的时候,类只要实现Serializable接口即可,不需要额外的操作。但用Parcelable接口会需要开发者多做一些工作。

Parcelable接口在 android.os 包里,与Serializable不同。我们必须明确认识这一点。 官方给出的一个使用例子。

public class MyParcelable implements Parcelable {private int mData;public int describeContents() {return 0;}public void writeToParcel(Parcel out, int flags) {out.writeInt(mData);}public static final Parcelable.Creator<MyParcelable> CREATOR= new Parcelable.Creator<MyParcelable>() {public MyParcelable createFromParcel(Parcel in) {return new MyParcelable(in);}public MyParcelable[] newArray(int size) {return new MyParcelable[size];}};private MyParcelable(Parcel in) {mData = in.readInt();}}

可以看到,强制使用了一个 Parcelable.Creator 对象。里面的方法我们暂时不管也不修改。 重点关注私有构造器 MyParcelable(Parcel in 和 writeToParcel 方法。

官方例子中,私有构造器接收一个Parcel对象,然后从中读出一个int。而 writeToParcel 方法中,把mData写入Parcel对象中。 这写入和读出操作就是我们开发者需要特别关心的地方。

之前使用intent.putExtra方法的时候会传入一个String类型的key(键),用于标示传入的数据。 但在官方的例子中,我们只看到了一个int。而writeInt方法也没有指定key。系统如何区分出各个参数呢?

我们自定义一个类 DataParcel ,实现 Parcelable 接口。as自动在里面生成了CREATOR。

import android.os.Parcel;import android.os.Parcelable;​public class DataParcel implements Parcelable {private int number;private String str1;private String str2;private String noSave = "[不传送的字符串]";// getter setter ...public String info() {return "number: "+number+", str1: "+str1+", str2: "+str2+", noSave:"+noSave;}protected DataParcel(Parcel in) {number = in.readInt();str1 = in.readString();str2 = in.readString();}public DataParcel(int number, String str1, String str2, String noSave) {this.number = number;this.str1 = str1;this.str2 = str2;this.noSave = noSave;}public static final Creator<DataParcel> CREATOR = new Creator<DataParcel>(){@Overridepublic DataParcel createFromParcel(Parcel in) {return new DataParcel(in);}@Overridepublic DataParcel[] newArray(int size) {return new DataParcel[size];}};@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(number);dest.writeString(str1);dest.writeString(str2);}}

可以看到我们有1个int和3个String。公开构造器需要传入这4个属性。

writeToParcel方法中,按顺序写入了int和2个String。私有构造器中,按顺序读出了int和2个String。

noSave 并没有被写入和读出。拿来做对比。info()方法是拿来打印信息的。

5.2.2 传送Parcelable对象

现在我们的类已经设计好了,传送对象试试。

把DataParcel对象交给intent。

DataParcel dataParcel = new DataParcel(100, "s1", "s2", "改变这个字符串看看能否被传递");intent.putExtra(SendParamsDemo.K_PARCEL, dataParcel);

被打开的Activity接收传入的对象。

DataParcel dataParcel = intent.getParcelableExtra(K_PARCEL);

5.2.3 总结

至此,我们了解了如何使用Parcelable这个接口。 Parcel和Parcelable是Android IPC中使用到的容器和工具。大家可以了解一下Binder机制。

一般认为,普通情况下Parcelable性能上会优于SerializableSerializable涉及到序列化,系统会通过反射的方法来获取信息。相对而言比较耗资源。Parcel并不涉及序列化机制。它是为了高性能IPC传输设计的。因此,Parcel并不适合用来永久化存储数据。

实际工作中,我们可以根据业务需要,综合开发时间成本和应用性能要求,来选择使用Parcelable或者Serializable

6 Android Activity 返回时携带参数(更新)

我们已经知道启动activity的时候可以传递一些参数。那么在关闭activity时,能不能将一些数据传回上一个activity呢?

本文给出1个例子,实现关闭activity时,将一些数据传回给上一个activity。

例子中会有2个Activity来做示范,ForResultFirstActForResultSecondAct

6.1 启动新的Activity

ForResultFirstAct是第一个Activity。启动第二个activity时,要调用startActivityForResult方法。

private static final int REQ_CODE = 10;​startActivityForResult(new Intent(getApplicationContext(), ForResultSecondAct.class), REQ_CODE);

相当于给这次启动加上了一个标示。

6.2 装载回传数据

ForResultSecondAct是第二个activity。它可以设置返回时携带的数据。

Intent resultIntent = new Intent();resultIntent.putExtra(K_TITLE, mTitleEt.getText().toString());resultIntent.putExtra(K_SUB_TITLE, mSubTitleEt.getText().toString());setResult(1, resultIntent);finish();

6.3 获取回传数据

ForResultFirstAct中需要复写方法,获取返回的数据。

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case REQ_CODE:if (resultCode == 1) {if (data != null) {mTitleTv.setText(data.getStringExtra(ForResultSecondAct.K_TITLE));mSubTitleTv.setText(data.getStringExtra(ForResultSecondAct.K_SUB_TITLE));}} else {Toast.makeText(getApplicationContext(), "未保存修改", Toast.LENGTH_SHORT).show();}break;}}

以上是activity回传数据的一个例子。 我们着重注意

startActivityForResult 方法,启动下一个activity。

setResult 方法,装载回传数据。

onActivityResult 方法,获取回传结果。

6.4 requestCode的限制

startActivityForResult(Intent intent, int requestCode)requestCode 有限制; 以下是报错log

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?at android.app.ContextImpl.startActivity(ContextImpl.java:765)at android.app.ContextImpl.startActivity(ContextImpl.java:745)at android.content.ContextWrapper.startActivity(ContextWrapper.java:331)

报出的AndroidRuntimeException告诉我们,可以考虑启动一个新的task

即设置new task标记intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

android基础 [超级详细android Activity组件解析(Activity综述 Activity生命周期 Activity启动--携带参数启动)]

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。