程序活动单元Activity
Activity在安卓中负责与用户交互的逐渐,每个安卓应用都会用Activity来显示界面及处理界面上的一些控件的事件。
Activity你可以抽象的理解为一个页面,假如说这个页面有个按钮点击就可以跳转到另外一个页面,是不是像浏览网页一样?一个Activity就相当于一个页面;
Activity的生命周期状态
Activity有它的生命周期,就是从创建到将它销毁的这个过程就被成为生命周期,它在此过程中有5种状态,分别是启动、运行、暂停、停止这几个状态
1.启动状态
启动暂停很短暂,启动后就会进入运行状态
2.运行状态
Activity
在此时就是界面的最前端,它是可见的,有焦点的,而且还可以与用户进行交互,点击界面等操作
如果 Activity
进入这个状态,就会尽可能的保持这个状态,即使出现内存不足的情况,安卓也会先销毁栈底的 Activity
来确保当前的运行
3.暂停状态
当 Activity
处于暂停状态,但是 Activity
仍然可见,但是无法获取焦点,也无法进行操作,就比如弹出一个弹窗你必须先将弹窗关闭,然后才可以继续操作 Activity
4.停止状态
当 Activity
处于不可见的状态时,他就处于停止状态
5.销毁状态
Activity
处于销毁状态时,它会被清理出内存
启动和销毁状态都是过度状态,Activity不会在这两个状态停留
Activity生命周期方法
Activity的生命周期包括创建、可见、获取焦点、失去焦点、不可见、重修可见、销毁等环节,且每个环节都定义了响应的方法供调用
生命周期阶段 | 方法 | 描述 |
---|---|---|
创建 | onCreate() |
当Activity首次被创建时调用。用于进行基本的初始化工作,如设置布局、绑定事件监听器、初始化成员变量等。接收一个 Bundle 参数,可能包含之前保存的状态信息。 |
启动 | onStart() |
在Activity变得对用户可见(但尚未获得焦点)时调用。 |
运行 | onResume() |
当Activity开始与用户交互并获得焦点时调用。此时Activity处于前台,可接受用户输入。 |
暂停 | onPause() |
当系统准备启动或恢复另一个Activity(或其他UI组件)而导致当前Activity失去焦点时调用。应在此处保存关键数据,释放系统资源(如摄像头、传感器等),因为接下来Activity可能会被停止或销毁。 |
停止 | onStop() |
当Activity不再对用户可见(可能由于被另一个Activity完全覆盖或设备屏幕关闭)时调用。应在此处保存所有持久化数据,因为Activity可能被系统回收以释放内存。 |
重启 | onRestart() |
当已停止的Activity即将被重新启动并进入运行状态时调用。这是从停止状态返回到运行状态之间的过渡点,通常紧随 onStop() 之后。 |
销毁 | onDestroy() |
在Activity被销毁前最后调用。用于清理所有未释放的资源,如关闭数据库连接、注销广播接收器等。此方法并非每次都会被调用,例如当系统资源紧张直接回收Activity时可能跳过。 |
注意:生命周期方法的调用顺序遵循一定的逻辑链,即 onCreate()
→ onStart()
→ onResume()
→ (onPause()
→ onStop()
| onRestart()
→ onStart()
→ onResume()
) → onPause()
→ onStop()
→ onDestroy()
。括号内表示当Activity被暂停后,如果它随后被重新启动(如用户按“返回”键回到该Activity),则会经历 onRestart()
至 onResume()
的调用;否则,如果Activity被直接停止并可能进一步销毁,则会经历 onStop()
至 onDestroy()
的调用。
此外,横竖屏切换等配置更改可能导致Activity重建,若未在 AndroidManifest.xml
中指定相应的 configChanges
属性,那么会按照 onPause()
→ onSaveInstanceState()
→ onStop()
→ onDestroy()
→ onCreate()
→ onStart()
→ onRestoreInstanceState()
→ onResume()
的顺序调用生命周期方法。如果指定了适当的 configChanges
属性以处理旋转等事件,Activity可能不会被销毁重建,此时只会调用 onConfigurationChanged()
方法。
⭐️ 测试生命周期代码
运行这段代码在logcat中就会清楚的看到程序在调用的过程
package fun.tanc.testactivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
Log.i("MainActivity","调用onCreate()");
}
@Override
protected void onStart(){
super.onStart();
Log.i("MainActivity","调用onStart()");
}
@Override
protected void onResume(){
super.onResume();
Log.i("MainActivity","调用onResume()");
}
@Override
protected void onPause(){
super.onPause();
Log.i("MainActivity","调用onPause()");
}
@Override
protected void onStop(){
super.onStop();
Log.i("MainActivity","调用onStop()");
}
@Override
protected void onDestroy(){
super.onDestroy();
Log.i("MainActivity","调用onDestroy()");
}
@Override
protected void onRestart(){
super.onRestart();
Log.i("MainActivity","调用onStop()");
}
}
程序运行到 onResume()
时就不会在继续向下执行,等等与用户交互,点击模拟器的返回按钮可以看到下面的状态
为什么会没有调用 onRestart()
方法呢,因为此时 Activity
还只有一个,多个切换就需要了
⚠️安卓设备在横竖屏切换时,Activity的生命周期行为取决于应用程序对 AndroidManifest.xml
中 Activity
元素的 android:configChanges
属性的设置。以下是两种常见情况下的生命周期变化:
1️⃣未设置或未指定 orientation
的 android:configChanges
在这种情况下,当设备从竖屏切换到横屏或从横屏切换到竖屏时,系统默认会销毁当前的Activity,并重新创建一个新的Activity实例来适应新的屏幕方向。此时Activity的生命周期会经历如下过程:
-
保存状态:
onSaveInstanceState(Bundle outState)
:在Activity被销毁前,系统会调用此方法让应用有机会保存其瞬态状态。传递的Bundle
对象可用于存储需要保留的数据。
-
销毁旧Activity:
onPause()
:活动失去焦点,即将转入后台。onStop()
:活动对用户不可见。onDestroy()
:活动被销毁,资源被释放。
-
创建新Activity:
onCreate(Bundle savedInstanceState)
:新Activity实例被创建。savedInstanceState
参数包含了之前保存的状态信息。onStart()
:新Activity对用户可见。onRestoreInstanceState(Bundle savedInstanceState)
(可选):如果在onCreate()
中有需要,可以重写此方法来处理恢复特定UI状态。参数同样包含之前保存的状态。onResume()
:新Activity获得焦点,开始与用户交互。
2️⃣设置了 orientation
的 android:configChanges
如果在 AndroidManifest.xml
中为Activity指定了 android:configChanges="orientation"
(或包含 orientation
在内的多个配置项),则告诉系统当屏幕方向改变时,由应用程序自己处理配置变更,而非重新创建Activity。此时,横竖屏切换时Activity的生命周期变化如下:
- 配置变更通知:
onConfigurationChanged(Configuration newConfig)
:当屏幕方向改变时,系统会调用此方法。开发者可以在该方法中根据新的配置信息更新UI布局或资源。
注意: 在这种情况下,上述销毁和创建Activity的整个过程被省略,Activity保持原有的实例,不会经历 onSaveInstanceState()
、onPause()
、onStop()
、onDestroy()
、onCreate()
、onStart()
、onRestoreInstanceState()
这一系列生命周期方法的调用。只有 onConfigurationChanged()
会被触发,使得应用能够在不重新创建Activity的情况下适应新的屏幕方向。
总结来说,是否处理横竖屏切换导致的Activity重建,取决于开发者是否选择在 android:configChanges
中声明 orientation
。如果不声明,系统默认销毁并重新创建Activity,完整走一遍生命周期;如果声明了,那么系统仅调用 onConfigurationChanged()
,由开发者自行处理布局变化,避免了Activity的重建。
配置文件:
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="true">
如果你只希望某一个界面一直处于竖屏或者横屏状态,不随着手机方向改变就可以配置
android:screenOrientation="portrait" //竖
android:screenOrientation="landscape" //横
Activity的创建、配置、启动和关闭
创建Activity
创建的方法有两种:
1️⃣ 在存放Activity的文件夹下右键选择,这个创建就自动给你配置好 xml布局文件
和 Manifset.xml
文件了
可以看到 Activity
名和 xml
布局文件名
创建好自动创建的代码
package fun.tanc.testactivity;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity2 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main2);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
}
2️⃣ 第二种方法就是自己创建一个普通的 java
类,这个就需要自己配置和创建布局文件
这是创建好的样子
public class ActivityTwo {
}
其他部分和默认创建的MainActivity差不多,可以直接套用
package fun.tanc.testactivity;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
public class ActivityTwo extends Activity { //继承了 Activity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) { //需要一个创建方法
super.onCreate(savedInstanceState);
setContentView(R.layout.activityTwo_main); //这里会报错是因为没有创建xml布局文件
}
}
随后配置 AndroidManifest
//新增加在application中
<activity
android:name=".ActivityTwo" //这里配置Activity的名字
android:exported="true" /> //表示是否可以跳转到其他界面
还需要自己创建布局文件, 点击存储xml布局文件的文件夹创建
创建好的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
启动Activity
这里启动和关闭只是提一嘴,到后面才会具体实现,现在了解了解就好了
1️⃣ 启动
需要使用 startActivity()
来启动创建的 Activity
,使用方法如下
使用了Intent意图,意图是Android应用程序直接各个组件之间通信的桥梁,现在只需要了解一下就好了
第一个表示当前的 Activity
第二个参数表示你要启动哪个 Activity
Intent intent = new Intent();
intent.setClass(MainActivity.this,SecondActivity.class);
startActivity(intent);
//或者
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
2️⃣关闭
关闭需要使用到 finish()
,直接调用即可,以下代码演示了点击关闭的方法
Button btn = (Button)findViewById(R.id.btn1)
btn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
finish();
}
});
Intent与IntentFilter
现在只需要了解这个东西,后面案例做一下就明白了
在一个应用程序中一般都是由很多个
Activity
组成,每个 Activity
也表示了不同的功能,如果用户需要在两个Activity中切换,就必须使用到 Intent
,它用于同一个或者不同一个应用程序间的绑定;
Intent就是意图的意思,是程序中各组件之间进行交互的一种重要方式,它不仅仅可以指定当前组件要执行的动作,还可以在不同组件之间进行数据传递。
它分为两种模式
1️⃣ 显式Intent
用于明确自己需要跳转的目标的组件,可以直接指定需要跳转的
Activity
如下
Intent intent = new Intent();
intent.setClass(MainActivity.this,SecondActivity.class);
startActivity(intent);
⭐️ 案列
1.创建一个Activity和布局文件,还需要设置 Manifest
文件
package fun.tanc.testactivity;
import android.os.Bundle;
import android.util.Log;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
public class SecondActivity extends AppCompatActivity {
public final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_second);
}
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondActivity">
<TextView
android:id="@+id/second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第二个界面"
android:textSize="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<activity
android:name=".SecondActivity"
android:exported="true" />
2.编写主 Activity
的布局文件
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击进入"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
3.编写主 Activity的java
代码
public class MainActivity extends AppCompatActivity {
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
//监听button控件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击跳转
Intent intent = new Intent();
intent.setClass(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
});
}
启动点击按钮就可以跳转到第二个界面了
2️⃣ 隐式意图
隐式意图不会明确的指定目标组件,它广泛的应用在不同的应用程序之间进行消息传递,它和
IntentFilter
搭配使用使用它来匹配相应的组件
以下表格列出了 IntentFilter
匹配时涉及的主要属性及其用途:
属性 | 描述 |
---|---|
Action | 字符串标识,表示Intent所要执行的操作类型。可以是系统预定义的动作(如 ACTION_VIEW 、ACTION_SEND 等)或自定义动作。一个 Intent 可以包含一个动作,而 IntentFilter 可以声明多个动作,只要 Intent 中的动作与 IntentFilter 中任意一个动作完全匹配即可。 |
Category | 字符串标签,用来进一步描述 Intent 的上下文或目的。例如,CATEGORY_BROWSABLE 表示Intent可以通过浏览器访问。Intent 可以包含零个或多个类别,而 IntentFilter 中声明的所有类别都必须与 Intent 中对应的类别完全匹配。如果 Intent 没有指定类别,系统会默认添加 CATEGORY_DEFAULT 。 |
Data | 描述了 Intent 所操作的数据。包括以下子属性: |
- Scheme:URI的协议部分,如
http
、https
、content
、file
等。 - Host:对于某些scheme有意义,如网络地址中的域名。
- Port:与host相关联的端口号。
- Path:URI路径部分,用于定位特定资源。
- MimeType:指定数据类型的MIME字符串,如
text/plain
、image/jpeg
等。表明数据的内容格式。
IntentFilter
可以指定以上数据子属性的一个或多个组合,作为数据匹配条件。Intent
中的数据需与 IntentFilter
中指定的全部数据条件匹配(除非某些条件在 IntentFilter
中未指定)。
| Extra | 可选的附加信息,以键值对形式存在,通常用于传递额外的数据给目标组件。虽然 IntentFilter
本身不直接匹配 Extra
,但目标组件在接收到 Intent
后可以通过检查 Extra
来确定是否满足特定业务逻辑要求。 |
综上所述,IntentFilter
匹配主要关注 Action
、Category
和 Data
属性。在隐式Intent的场景下,只有当这些属性全部匹配成功时,系统才会认为 Intent
与某个 IntentFilter
相匹配,进而启动相应的目标组件(如Activity、Service或BroadcastReceiver)。Extra
虽然不影响 IntentFilter
的匹配过程,但它承载的具体数据对于目标组件接收并正确处理Intent至关重要。
⭐️ 这里直接上案列
修改 Manifest
文件
<activity
android:name=".SecondActivity"
android:exported="true" >
<intent-filter>
<action android:name="fun.tanc.START" /> //设置actio和category
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
修改监听代码
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击跳转
Intent intent = new Intent();
intent.setAction("fun.tanc.START");
startActivity(intent);
}
});
未完待续....