一.Activity介绍及其用法
(一). 基本介绍
Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务
Activity中所有操作都与用户密切相关,是一个负责与用户交互的组件,可以通过setContentView(View)来显示指定控件
在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信
(二). 基本使用
1. 手动创建Activity
- 创建No Activity项目,命名为
CreateAty。随后创建一个 Empty Activity,命名为FirstActivity
- 创建时不要勾选
Generate a Layout File和Launcher Activity这两个选项Generate a Layout File表示自动为FirstActivity创建一个对应的布局文件。Launcher Activity表示自动将FirstActivity设置为当前项目的主活动。
2. 创建和加载布局
- 在
app/src/main/res目录下新建 Directory,命名为layout,随后创建Layout resource file文件,命名为first_layout,根元素设置为LinearLayout,如下图所示。
- 随后在
layout文件中添加一个Button元素:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button_1"
android:text="Button 1"
/>
android:id是唯一标识符,@+id表示在XML中对应一个id
- 在
FirstActivity类的onCreate()方法中添加方法加载布局:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//向 setContentView 传入布局文件的id
//只需调用 R.layout.first_layout 就可以得到 first_layout.xml 布局的id,传入函数中
setContentView(R.layout.first_layout);
}
}
3. 在AndroidManifest文件中注册
所有活动都需要在
AndroidManifest.xml中注册才能生效,Android studio默认会自动注册
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CreateAty"
tools:targetApi="31">
<activity
android:name=".FirstActivity"
android:exported="true"
/>
</application>
-
活动的声明放在
<application>标签中,并通过<activity>标签进行对活动的注册。android:name指定了具体注册哪一个活动,.FirstActivity指的是com.example.createaty.FirstActivity的缩写,上面package属性已经指明。
-
继续为程序配置活动 ,在
<activity>标签中加入<intent-filter>标签,并加入<activity android:name=".FirstActivity" android:exported="true" android:label="This is FirstActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>这样程序就知道首先启动哪个活动。
4.在活动中使用Toast
Toast是Android系统中提供的一种提醒方式,以一些短小的信息通知给用户,一段时间后自动消失。
- 定义一个弹出Toast的触发点,在点击按钮是进行触发:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(FirstActivity.this, "You clicked Button1",Toast.LENGTH_SHORT).show();
}
});
}
- 通过
findViewById()方法获取到在布局文件中定义的元素,程序中传入R.id.button_1,得到按钮的实例。findViewById()方法返回一个View对象,需要向下转成Button对象- 得到实例后,通过调用
setOnClickListener()方法为按钮注册一个监听器,点击按钮时就会执行监听器中的onClick()方法。将 Toast 功能在onClick()方法中编写。- Toast 通过静态方法
makeText()创建出 Toast 对象,然后调用show()方法将 Toast 显示出来即可。makeText()需要传入三个参数:
- 第一个参数为: Context,即 Toast 要求的上下文,由于活动本身是 Context 对象,直接传入
FirstActivity.this- 第二个参数为: Toast 文本内容。
- 第三个参数为: Toast显示的时长
5.销毁一个活动
Activity类提供了
finish()方法来销毁当前活动。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
(三). Activity间通信
Intent 是Android程序中各组件之间进行交互的重要方式,不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。一般可被用于启动活动、启动服务以及发送广播等场景。可以分为 显式intent 和 隐式 Intent 。
1. 显式 Intent
-
新建一个
Empty Activity,命名为SecondActivity,勾选Generate Layout File,创建为second_layout,不要勾选Launcher Activity。 -
创建后,在
layout中创建Button2组件,id设置为button_2。 -
AndroidManifest.xml文件中默认配置了SecondActivity相关内容。 -
利用
Intent将两个Activity相联系。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
});
Intent 有多个构造函数的重载,其中一个为 Intent(Context packageContext, Class<?> cls) ,而 Activity 类提供了 startActivity() 方法启动活动,可以接收 Intent 参数。
- 第一个参数
Context要求提供一个启动活动的上下文 - 第二个参数
Class则是指定想要启动的目标活动
将 FirstActivity.this 作为上下文参数, SecondActivity.class 作为目标活动,即在 FirstActivity 活动的基础上打开 SecondActivity 活动;然后通过 startActivity() 方法执行这个 Intent 。
由于
Intent的意图非常明显,因此称之为 显示 Intent 。
2. 隐式 Intent
相比于显式
Intent,隐式Intent则含蓄很多,并不指明想要启动的活动,而是指定一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并找到合适的Activity去启动 。
- 首先在
AndroidManifest.xml中.SecondActivity进行配置<intent-filter>内容:
<activity
android:name=".SecondActivity"
android:exported="true" >
<intent-filter>
<action android:name="com.example.createaty.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MY_CATEGORY" />
</intent-filter>
</activity>
- 当
<action>与<category>中的内容同时匹配时能够匹配上Intent中指定的action与category时,Activity会响应该Intent。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.createaty.ACTION_START");
intent.addCategory("android.intent.category.MY_CATEGORY");
startActivity(intent);
}
});
使用了 Intent 的另一个构造函数,直接将 action 的字符串传入,表明想要启动 com.example.createaty.ACTION_START 这个活动,然后通过 addCategory 方法添加 category 进行匹配。
3. 传递数据给下一个Activity
Intent中提供了一系列putExtra()方法的重载,可以把想要传递的数据暂存在Intent中,启动另一个活动时,只需把这些数据再从Intent中取出即可。
-
首先利用显式
Intent启动SecondActivity,通过putExtra()方法传递字符串,其中putExtra()方法接受两个参数:- 第一个为键,用于后面从
Intent取值 - 第二个为传递的数据
- 第一个为键,用于后面从
-
将
FirstActivity中的字符串传到SecondActivity中:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("extra_data","This is a word!");
startActivity(intent);
}
});
SecondActivity通过getIntent()方法获取到用于启动SecondActivity的Intent,然后利用getStringExtra()方法,传入相应的键值,获取传递的数据:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d("SecondActivity",data);
}
}
4. 返回数据给上一个Activity
Activity中startActivityForResult()方法也是用于启动活动的,这个方法期望在活动销毁时返回一个结果给上一个活动。这就是所需要的功能。
startActivityForResult()方法接收两个参数:- 第一个参数还是
Intent - 第二个参数是请求码,用于之后回调中判断数据的来源。
- 第一个参数还是
- 首先修改
FirstActivity中的按钮点击事件:
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);
}
});
使用
startActivityForResult方法启动SecondActivity,请求码只要是唯一值即可,这里设置为1。
- 在
SecondActivity中给按钮注册点击事件,并在其中添加返回数据的逻辑。 setResult()方法专门用于向上一个活动返回数据。接收两个参数:- 第一个参数用于向上一个活动返回处理结果,一般是
RESULT_OK或RESULT_CANCELED。 - 第二个参数则将
intent传递回去。
- 第一个参数用于向上一个活动返回处理结果,一般是
Button button = (Button) findViewById(R.id.button_2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.putExtra("data_return", "This is a return word");
setResult(RESULT_OK, intent);
finish();
}
});
这里构建
Intent只是为了传递数据。随后将数据存放至Intent中,随后调用setResult()方法。
- 由于使用
startActivityForResult()方法启动SecondActivity,在SecondActivity被销毁后会调用上一个活动的onActivityResult()方法,因此需要再FirstActivity中重写这个方法来回获取数据。 onActivityResult()方法有三个参数,通过这三个参数来筛选出传回的数据。requestCode为启动时传入的请求码resultCode为返回数据时传入的处理结果data为携带的数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
assert data != null;
String returnedData = data.getStringExtra("data_return");
Toast.makeText(FirstActivity.this, returnedData,Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
二.活动的生命周期
Android是使用任务(Task)来管理活动的,一个任务就是存放在栈里的活动的集合,栈也称为返回栈(Back Stack)。
在默认情况下,每当启动一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。每当按下Back键或者调用 finish() 方法去销毁一个活动,处于栈顶的活动会出栈,前一个入栈的活动会重新处于栈顶的位置。系统总是显示处于栈顶的活动给用户。
(一). 活动状态
每个活动在其生命周期中最多可能会有4种状态。
1. 运行状态
一个新 Activity 启动入栈后,它显示在屏幕最前端,处于栈的最顶端(Activity栈顶),此时它处于可见并可和用户交互的激活状态,叫做活动状态或者运行状态。
2. 暂停状态
当 Activity 失去焦点, 被一个新的 非全屏的 Activity 或者 一个透明的 Activity 被放置在栈顶,此时的状态叫做暂停状态(Paused)。此时它依然与 窗口管理器 保持连接,Activity 依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被强行终止掉。所以它仍然可见,但已经失去了焦点故不可与用户进行交互。
3. 停止状态
如果一个 Activity 是 Paused 或者 Stopped 状态,系统可以将该 Activity 从内存中删除,Android 系统采用两种方式进行删除,要么要求该 Activity 结束,要么直接终止它的进程。当该 Activity 再次显示给用户时,它必须重新开始和重置前面的状态。
4. 销毁状态
当活动从返回栈中移除后就会变成销毁状态。系统会倾向于回收处于这种状态的活动,从而保证手机内存充足。
(二). Activity 生命周期简介
Activity 类中定义了七种回调方法,覆盖了生命周期的每一个环节。
onCreate()方法
在活动第一次创建时调用,在这个方法中完成活动的初始化操作,如加载布局、绑定事件等。
onStart()方法
在活动由不可见变为可见的时候调用。
onResume()方法
在活动准备好和用户进行交互的时候调用。此时活动一定位于返回栈的栈顶,并且处于运行状态。
onPause()方法
在系统准备去启动或者恢复另一个活动的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
onStop()方法
在活动完全不可见的时候调用。
与
onPause()方法主要区别在于: 如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行。
onDestory()方法
在活动被销毁之前调用,之后活动的状态将变为销毁状态。
onRestart()方法
在活动由停止状态变为运行状态之前调用,也就是活动被重新启动。
以上七个方法除了 onRestart() 方法外,其他都是两两相对的,从而可以将活动分为3种生存期。
- 完整生存期
活动在 onCreate() 方法和 onDestroy() 方法之间所经历的,就是完整生存期。
一般情况下,一个活动在 onCreate() 方法中完成各种初始化操作,在 onDestroy() 方法中完成释放内存的操作。
- 可见生存期
活动在 onStart() 方法和 onStop() 方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用户总是可见的,通过这两个方法,可以合理的管理那些对用户可见的资源。
比如在 onStart() 方法中对资源进行加载,在 onStop() 方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
- 前台生存期
活动在 onResume() 方法和 onPause() 方法之间所经历的就是前台生存期。
在前台生存期内,活动总是处于运行状态,此时的活动可以与用户进行交互,平时接触最多的也是这个状态下的活动。
(三). Activity 生命周期体验
创建两个活动 FirstActivity 与 SecondActivity ,在 FirstActivity 中开启 SecondActivity ,并重写七个生命周期函数,则会有:
FirstActivity onCreate
FirstActivity onStart
FirstActivity onResume
FirstActivity onPause
# 打开SecondActivity
SecondActivity onCreate
SecondActivity onStart
SecondActivity onResume
# 此时SecondActivity完全覆盖了FirstActivity
FirstActivity onStop
如果活动 SecondActivity 没有完全覆盖 FirstActivity
FirstActivity onCreate
FirstActivity onStart
FirstActivity onResume
FirstActivity onPause
# 打开SecondActivity
SecondActivity onCreate
SecondActivity onStart
SecondActivity onResume
# 此时SecondActivity没有完全覆盖了FirstActivity
# 点击空白处,退出SecondActivity
SecondActivity onPause
FirstActivity onResume #重新出现
SecondActivity onStop
SecondActivity onDestory #完全销毁
三. 活动的启动模式
在实际项目中应该根据特定的需求为每个活动指定恰当的启动模式。启动模式一共有四种,分别为: standard 、 singleTop 、 singleTask 、 singleInstance ,可以在 AndroidManifest.xml 中通过给 <activity> 标签指定 android:launchMode 属性来选择启动的模式。
(一). Activity 栈
Android 是通过一种 Activity 栈的方式来管理 Activity 的,一个 Activity 的实例的状态决定它在栈中的位置。处于前台的 Activity 总是在栈的顶端,当前台的 Activity 因为异常或其它原因被销毁时,处于栈第二层的 Activity 将被激活,上浮到栈顶。当新的 Activity 启动入栈时,原 Activity 会被压入到栈的第二层。一个 Activity 在栈中的位置变化反映了它在不同状态间的转换。
(二). standard
standard 是活动默认的启动模式,在此模式下,每当启动一个新活动,会在返回栈中入栈,并处于栈顶位置。对于 standard 模式的活动, 系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新实例。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
Log.d("FirstActivity", this.toString());
Button button1 = findViewById(R.id.button_1);
button1.setOnClickListener(view -> {
String data = "Hello SecondActivity.";
Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
startActivity(intent);
});
}
上述代码中,会在 FirstActivity 的基础上启动 FirstActivity ,如果连续通过按钮创建三个 FirstActivity 实例,则需要连按三次 Back 键才能退回程序。原理示意图如下图所示:
(三). singleTop
singleTop 模式下,启动活动时如果发现返回栈的栈顶已经是该活动,则直接使用,不会再创建新的活动实例。
<activity
android:name=".FirstActivity"
android:launchMode="singleTop"
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
但是如果从 FirstActivity 活动中启动 SecondActivity ,然后再从 SecondActivity 中启动 FirstActivity ,此时这两个 FirstActivity 实例是不同的。原理示意图如下图所示:
(四). singleTask
singleTask 模式下,启动活动时系统首先会在返回栈中检查是否存在该活动的实例,
- 如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动全部出栈;
- 如果不存在该活动的实例,则创建一个新的活动实例。
<activity
android:name=".FirstActivity"
android:launchMode="singleTask"
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
但是如果从 FirstActivity 活动中启动 SecondActivity ,然后再从 SecondActivity 中启动 FirstActivity ,则 SecondActivity 会从返回栈中出栈, FirstActivity 重新成为栈顶活动,因此 FirstActivity 的 onRestart() 方法和 SecondActivity 的 onDestroy() 方法会得到执行。
(五). singleInstance
singleInstance 模式下活动会启动一个新的返回栈来管理这个活动。当程序中有一个活动是允许其他进程调用的,如果想要实现其他程序和这个程序可以共享这个活动的实例,则可以使用 singleInstance 模式进行实现。
<!-- 修改 SecondActivity 的模式-->
<activity
android:name=".SecondActivity"
android:launchMode="singleInstance"
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
但是如果从 FirstActivity 活动中启动 SecondActivity ,然后再从 SecondActivity 中启动 ThirdActivity ,如果利用 getTaskId() 方法来获取栈 Id 的话,会发现 SecondActivity 与另外两个不同,即 SecondActivity 在其他的返回栈中。
当利用 Back 键返回时, ThirdActivity 会直接返回到 FirstActivity ,再按下 Back 键又会返回到 SecondActivity ,再按下 Back 键才会退出程序,原理如下图所示:

4032

被折叠的 条评论
为什么被折叠?



