初识 Activity

一.Activity介绍及其用法

(一). 基本介绍

Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务

Activity中所有操作都与用户密切相关,是一个负责与用户交互的组件,可以通过setContentView(View)显示指定控件

在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信

(二). 基本使用

1. 手动创建Activity

  • 创建No Activity项目,命名为 CreateAty 。随后创建一个 Empty Activity,命名为 FirstActivity
  • 创建时不要勾选 Generate a Layout FileLauncher 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();
        }
    });
}
  1. 通过 findViewById() 方法获取到在布局文件中定义的元素,程序中传入 R.id.button_1 ,得到按钮的实例。
  2. findViewById() 方法返回一个 View 对象,需要向下转成 Button 对象
  3. 得到实例后,通过调用 setOnClickListener() 方法为按钮注册一个监听器,点击按钮时就会执行监听器中的 onClick() 方法。将 Toast 功能在 onClick() 方法中编写。
  4. 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

  1. 新建一个 Empty Activity,命名为 SecondActivity ,勾选 Generate Layout File ,创建为 second_layout ,不要勾选 Launcher Activity

  2. 创建后,在 layout 中创建 Button2 组件, id 设置为 button_2

  3. AndroidManifest.xml 文件中默认配置了 SecondActivity 相关内容。

  4. 利用 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 则含蓄很多,并不指明想要启动的活动,而是指定一系列更为抽象的 actioncategory 等信息,然后交由系统去分析这个 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 中指定的 actioncategory 时,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() 方法获取到用于启动 SecondActivityIntent ,然后利用 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

ActivitystartActivityForResult() 方法也是用于启动活动的,这个方法期望在活动销毁时返回一个结果给上一个活动。这就是所需要的功能。

  • 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_OKRESULT_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 生命周期体验

创建两个活动 FirstActivitySecondActivity ,在 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 #完全销毁

三. 活动的启动模式

在实际项目中应该根据特定的需求为每个活动指定恰当的启动模式。启动模式一共有四种,分别为: standardsingleTopsingleTasksingleInstance ,可以在 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() 方法和 SecondActivityonDestroy() 方法会得到执行。

(五). 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 键才会退出程序,原理如下图所示:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值