# Activity 必知必会

Activity 是 Android 的四大组件之一,Activity 是一种能够显示用户界面的组件,用户通过和 Activity 交互完成相关操作。

一个应用中可以包含 0 个或多个 Activity,但不包含任何 Activity 的应用程序是无法被用户看见的。

Tips

1. Activity 用于显示用户界面,用户通过 Activity 交互完成相关操作

2. 一个 App 允许有多个 Activity

Activity必知必会

# Activity 生命周期

android_activity_lifecycle

Activity 类中定义了 7 个回调方法,覆盖了 Activity 生命周期的每一个环节,下面就来介绍一下这 7 个方法。

# onCreate()

该方法会在 Activity 第一次创建时进行调用,在这个方法中通常会做 Activity 初始化相关的操作,例如:加载布局、绑定事件等。

# onStart()

这个方法会在 Activity 由不可见变为可见的时候调用,但是还不能和用户进行交互。

# onResume()

表示 Activity 已经启动完成,进入到了前台,可以同用户进行交互了。

# onPause()

这个方法在系统准备去启动另一个 Activity 的时候调用。可以在这里释放系统资源,动画的停止,不宜在此做耗时操作。

# onStop()

当 Activity 不可见的时候回调此方法。需要在这里释放全部用户使用不到的资源。可以做较重量级的工作,如对注册广播的解注册,对一些状态数据的存储。此时 Activity 还不会被销毁掉,而是保持在内存中,但随时都会被回收。通常发生在启动另一个 Activity 或切换到后台时

# onDestroy()

Activity 即将被销毁。此时必须主动释放掉所有占用的资源。

# onRestart()

这个方法在 Activity 由停止状态变为运行状态之前调用,也就是 Activity 被重新启动了(APP 切到后台会进入 onStop (), 再切换到前台时会触发 onRestart () 方法)

# Activity 组件注册

四大组件需要在 AndroidManifest 文件中配置否则无法使用,类似 Activity 无法启动,

一般情况下: 在新建一个 activity 后,为了使 intent 可以调用此活动,我们要在 androidManifest.xml 文件中添加一个标签,标签的一般格式如下:

<activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
  </activity>
  • android:name 是对应 Activity 的类名称
  • android:label 是 Activity 标题栏显示的内容。现已不推荐使用
  • 是意图过滤器。常用语隐式跳转
  • 是动作名称,是指 intent 要执行的动作
  • 是过滤器的类别 一般情况下,每个 中都要显示指定一个默认的类别名称,即 <category android:name="android.intent.category.DEFAULT" />

但是上面的代码中没有指定默认类别名称,这是一个例外情况,因为其 中的是 "android.intent.action.MAIN",意思是这个 Activity 是应用程序的入口点,这种情况下可以不加默认类别名称。

# Activity 启动与参数传递

Tips

在 Android 中我们可以通过下面两种方式来启动一个新的 Activity, 注意这里是怎么启动,分为显示启动和隐式启动!

1. 显式启动:通过包名来启动,写法如下:

** 最常见的:**startActivity

:常规跳转
val intent = Intent(MainActivity.this,SecondActivity.class)
startActivity(intent);
:携带参数启动新Activity
val intent = Intent(MainActivity.this,SecondActivity.class)
intent.putExtra("extra_data", "extra_data")
intent.putExtra("extra_int_data", 100)
startActivity(intent);

期待从目标页获取数据:startActivityForResult----> 比如启动相册获取图片

①假设从A--->B页面,以startActivityForResult方式启动
val intent = Intent(MainActivity.this,SecondActivity.class)
startActivityForResult(intent,100);
②如果B页面返回时,调用了 
setResult(Activity.RESULT_OK,resultIntent)
finish()
③则A页面会回调下面的方法,在该回调里可以拿到返回的数据data
onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)

2. 隐式启动

隐式 Intent 要比显示 Intent 含蓄的多,他并不明确指定要启动哪个 Activity,而是通过指定 actioncategory 的信息,让系统去分析这个 Intent ,并找出合适的 Activity 去启动。

<activity android:name=".SecondActivity">
     <intent-filter>
         <action android:name="com.example.firstapp.action.SecondActivity" />
         <category android:name="com.example.firstapp.category.SecondActivity" />
         <category android:name="android.intent.category.DEFAULT" /> // 一定要有
     </intent-filter>
</activity>
val intent = Intent()
intent.setAction("com.example.firstapp.action.SecondActivity")
intent.setCategory("com.example.firstapp.category.SecondActivity")
intent.putExtra("extra_data", "extra_data")
intent.putExtra("extra_int_data", 100)
startActivity(intent);
startActivityForResult(intent,100);

# 系统给我们提供的一些常见的 Activtiy

# 拨打电话

给移动客服 10086 拨打电话

Uri uri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);

# 发送短信

给 10086 发送内容为 “Hello” 的短信

Uri uri = Uri.parse("smsto:10086");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_body", "Hello");
startActivity(intent);

# 打开浏览器:

打开 baidu 主页

Uri uri = Uri.parse("http://www.baidu.com");
Intent intent  = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

# 多媒体播放:

Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/foo.mp3");
intent.setDataAndType(uri, "audio/mp3");
startActivity(intent);

# 打开摄像头拍照:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
startActivityForResult(intent, 0);
>>> 在Activity的onActivityResult方法回调中取出照片数据
Bundle extras = intent.getExtras(); 
Bitmap bitmap = (Bitmap) extras.get("data");

# 从图库选图并剪切

// 获取并剪切图片
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.putExtra("crop", "true"); // 开启剪切
intent.putExtra("aspectX", 1); // 剪切的宽高比为 1:2
intent.putExtra("aspectY", 2);
intent.putExtra("outputX", 20); // 保存图片的宽和高
intent.putExtra("outputY", 40); 
intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路径
intent.putExtra("outputFormat", "JPEG");// 返回格式
startActivityForResult(intent, 0);
>>>>  在Activity的onActivityResult方法中去读取保存的文件

# 剪切指定图片文件

Intent intent = new Intent("com.android.camera.action.CROP"); 
intent.setClassName("com.android.camera", "com.android.camera.CropImage"); 
intent.setData(Uri.fromFile(new File("/mnt/sdcard/temp"))); 
intent.putExtra("outputX", 1); // 剪切的宽高比为 1:2
intent.putExtra("outputY", 2);
intent.putExtra("aspectX", 20); // 保存图片的宽和高
intent.putExtra("aspectY", 40);
intent.putExtra("scale", true);
intent.putExtra("noFaceDetection", true); 
intent.putExtra("output", Uri.parse("file:///mnt/sdcard/temp")); 
startActivityForResult(intent, 0);
>>>>  在Activity的onActivityResult方法中去读取保存的文件

# 进入手机的无线网络设置页面

// 进入无线网络设置界面(其它可以举一反三)  
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);  
startActivityForResult(intent, 0);

# Activity 四种启动模式

# standard

默认值,多实例模式。每启动一次,都会创建一个新的 Activity 实例。

启动的生命周期为:onCreate ()->onStart ()->onResume ()

standard

# singleTop

栈顶复用模式

如果任务栈顶已经存在需要启动的目标 Activity,则直接启动,并会回调 onNewIntent () 方法,生命周期顺序为: onPause () ->onNewIntent ()->onResume ()

如果任务栈上顶没有需要启动的目标 Activity,则创建新的实例,此时生命周期顺序为: onCreate ()->onStart ()->onResume ()

两种情况如下图,从图中可以看出,此模式下还是会出现多实例,只要启动的目标 Activity 不在栈顶的话。

img

# singleTask

栈内复用模式,一个任务栈只能有一个实例。

有几种情况:

  • 当启动的 Activity 目标任务栈不存在时,则以此启动 Activity 为根 Activity 创建目标任务栈,并切换到前面

  • D 为 singleTask 模式

    img

  • 当启动的 Activity 存在时,则会直接切换到 Activity 所在的任务栈,并且任务栈中在 Activity 上面的所有其他 Activity 都出栈(调用 destroy ()),此时启动的 Activity 位于任务栈顶,并且会回调 onNewIntent () 方法。

    img

# singleInstance

singleInstance 名称是单例模式,即 App 运行时,该 Activity 只有一个实例。既然只有一个,那么也就说明很重要、很特殊,我们需要将其 “保护起来”。单例模式的 “保护措施” 是将其单独放到一个任务栈中。。

img

# Intent FLag 设定启动模式

除了可以在 manifest 中设置 Activity 的启动模式,也可以通过设置 Intent 的 flag 标识来设定 Activity 的启动模式。

常用的有:FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_SINGLE_TOP,FLAG_ACTIVITY_CLEAR_TOP

# FLAG_ACTIVITY_NEW_TASK

启动 Activity 时,如果不存在 Activity 的实例,则会以此 Activity 为根 Activity 创建新的任务栈,如果存在的话则直接切换到对应的 Activity 实例,并回调 onNewIntent () 方法。相当于 “singleTask” 启动模式。

# FLAG_ACTIVITY_SINGLE_TOP

相当于 “singleTop” 模式

# FLAG_ACTIVITY_CLEAR_TOP

设置此标识的 Activity 在启动时,如果当前的任务栈内存在此 Activity 实例,则跳转到此实例,并清除掉在此实例上面的所有 Activity 实例,此时此 Activity 实例位于任务栈的栈顶