Activity和Intent

Activity和Intent学习

Activity

Activity定义

  • Activity,直译为活动,它是Android定义的四大应用组件之一,也是最重要、用得最多的组件
  • Activity用来提供一个能让用户操作并与之交互的界面
  • 一个应用有多个界面,也就是包含多个Activity
  • 打电话、发短信、拍照、发邮件等功能都是通过Activity来做的

Activity相关API

  • startActivity(Intent intent):一般启动Activity
  • startActivityForResult(int reqCode, Intent intent):带回调启动Activity
  • onActivityResult(int reqCode, int resultCode, Intent data):回调方法
  • setResult(int resultCode, Intent data):设置要返回的结果
  • finish():结束当前Activity
  • getIntent():得到启动Activity的意图

Activity的生命周期函数

  • onCreate():创建了一个界面
  • onStart():界面呈现出来,但仍然不可以交互
  • onResume():界面可见,可以交互
  • onPause():暂停,界面不可交互,可能还可见(比如弹出窗口,只能操作窗口而主界面不可交互,但主界面仍然可见)
  • onStop():界面不可见
  • onDestory():界面进行销毁
  • 生命周期函数的使用建议

    • onCreate():可以做一些控件、数据的初始化工作

    • onPause():可做一些小数据保存(不建议,因为会影响下一个界面打开的速度)

    • onDestroy():可以做一些收尾工作,数据保存工作

Intent

Intent的理解

  • Intent是Activity,Service和BroadcastReceiver这三个应用组件之间进行通信的信使
  • 例如:我要在Activity中启动另一个Actvity,就必须使用Intent对象
  • Intent对象还可以携带数据
  • 注意:Intent不是Android中的四大应用组件之一

Intent的分类

  • 显式意图:明确指定目标组件的意图

    • 创建对象:Intent(Context context, Class class)
    • 何时使用:当操作当前自己应用的组件时使用
  • 隐式意图:没有明确指定目标组件的意图

    • 创建对象:Intent(String action)
    • 何时使用:当操作其它应用的组件时使用

Intent相关API

Intent(Context packageContext, Class<?> cls):用于创建显示意图对象

Intent(String action):用于创建隐式意图对象

putExtra(String name, Xxx value):保存额外数据

Xxx getXxxExtra(String name):获取额外数据

setData(Uri data):设置有特定格式的uri数据

显式Intent

一共有三种跳转方式

  • 第一种方式:
Intent intent = new Intent(MainActivity.this, SettingActivity.class);
  • 第二种方式:(MainActivity.this只是比this的指向更加明了,本质上是相同的意思)
Intent intent = new Intent();
intent.setClass(this, SettingActivity.class);
  • 第三种方式:
Intent intent = new Intent();
intent.setClassName("com.example.activityjump","com.example.activityjump.SettingActivity");
  • 下面的方法与上面是等价的
Intent intent = new Intent();
intent.setClassName(MainActivity.this,"com.example.activityjump.SettingActivity");

隐式Intent

  • 隐式跳转不明确的指明要跳转到哪一个界面,而是通过条件筛选确定目的界面
筛选条件的规则
  • Action(必要):动作

  • Category(必要):类别,是一个执行动作Action的附加信息

  • Data(可选):数据,是执行动作的URI和MIME类型,不同的Action有不同的Data数据指定

  • Type(可选):类型,显式指定Intent的数据类型(MIME)

  • Component(可选):组件,指定Intent的的目标组件的类名称

  • Extra(可选):扩展信,是添加一些组件的附加信息

  • 如果你的Activity希望其它应用能访问到,需要在AndroidManifest中配置

  • 如果你想启动其它应用的界面,你必须用隐式intent,且目标界面Activty配置了

  • 可以在中为Activity设置三个规则,方便筛选和跳转

下图是一个示例:

<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
Action:动作
  • 描述intent的动作。
  • 方法是intent.setAction()
  • 必须有,只能set一个,必须和筛选条件里的action值一模一样才能匹配
  • 自己程序内部可以随意设置action的值,但是一般用独一无二的包名作为action的值
Category:类别
  • 对intent的一种额外描述
  • 方法是addCategory()
  • 设置intent时可选**(配置AndroidManifest时是必须的)**,可以add多个
  • intent里默认会添加一个Default,不写也会自带一条category:
intent.addCategory(Intent.CATEGORY_DEFAULT);
  • 每个都要和筛选条件里的category值一样才能匹配

如果想要当前Activity可以被隐式意图匹配,那么必须在AndroidManifest里面声明default的category

<activity
    android:name=".CustomActivity"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.CUSTOM" />

        //default这条必须声明,否则无法被隐式意图匹配
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
Data:数据
  • 目标界面需要有处理该数据的能力
  • 是intent条件的附加条件,是可选的

data类筛选条件主要有以下两种:

  • 数据地址:URI(统一资源标识符),比如:http://www.baidu.com,tel:110 等
  • URI的格式:
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
  • 类型:mimeType,比如:image/jpeg、image/*、video/mp4 等

  • Manifest文件的过滤条件data示例:

<data android:scheme="string"
      android:host="string"
      android:port="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:mimeType="string" />

我们需要做的:

  • 在Intent代码中设置的条件:

    • 自定义的条件:用来跳到自己写的界面
    • 系统提供的条件:用来跳到系统其他app的界面(常用)
  • 隐式跳转都是用来跳到其他app界面的

  • 显式跳转都是用来跳到app内部界面的

隐式跳转实战示例
跳转到浏览器

布局文件:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="jumpToBaidu"
        android:padding="10dp"
        android:text="跳转到浏览器百度"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

在MainActivity中写jumpToBaidu方法:

其中Uri uri = Uri.parse(path),就是将一个网址字符串解析成为一个uri对象

public void jumpToBaidu(View view) {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("http://www.baidu.com"));
    startActivity(intent);
}
跳转到相机

布局文件:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="jumpToCamera"
        android:padding="10dp"
        android:text="打开相机"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

在MainActivity中写jumpToCamera方法:

public void jumpToCamera(View view) {
    Intent intent = new Intent();
    intent.setAction("android.media.action.IMAGE_CAPTURE");
    startActivity(intent);
}

注意:上面的代码只是单纯的调用手机的相机进行拍摄,并没有对拍摄后照片进行保存的功能。如果想要实现打开手机的相机应用的功能,请参考跳转微信的代码,将包名和类名改为相机的包名和类名即可。

跳转到相册

布局文件:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="jumpToAlbum"
        android:padding="10dp"
        android:text="打开相册"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

在MainActivity中写jumpToAlbum方法:

public void jumpToAlbum(View view) {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_PICK);
    //让intent可以匹配图片类型
    intent.setType("image/*");
    startActivity(intent);
}
跳转到电话界面

布局文件:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="jumpToPhone"
        android:padding="10dp"
        android:text="打电话"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

在MainActivity中写jumpToPhone方法:

public void jumpToPhone(View view) {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_DIAL);
    //tel:后面可以写电话号码,跳转后的界面会把该号码输入好
    intent.setData(Uri.parse("tel:"));
    startActivity(intent);
}
跳转到发消息界面

布局文件:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="jumpToMessage"
        android:padding="10dp"
        android:text="发消息"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

在MainActivity中写jumpToMessage方法:

public void jumpToMessage(View view) {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_SENDTO);
    //smsto:后面可以写电话号码
    intent.setData(Uri.parse("smsto:"));
    //sms_body意思是消息主体,后面是要发送的消息
    intent.putExtra("sms_body","你好,这是给你的短信");
    startActivity(intent);
}
跳转到第三方界面(以微信举例)

布局文件:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="jumpToWeChat"
        android:padding="10dp"
        android:text="打开微信"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

在MainActivity中写jumpToWeChat方法(启动模式详见附录):

public void jumpToWeChat(View view) {
    try{
        Intent intent = new Intent();
        //ACTION_MAIN表示intent作为初始活动启动,没有输入和返回
        intent.setAction(Intent.ACTION_MAIN);
        //用setComponent指定微信的包名
        intent.setComponent(new ComponentName("com.tencent.mm","com.tencent.mm.ui.LauncherUI"));
        //CATEGORY_LAUNCHER表示intent指定的是应用的启动器
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        //等价于指定启动模式为singleTask,具体见附录
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }catch (Exception e){
        //无法顺利执行上述代码说明手机里找不到微信的包名,可能是没有安装微信
        ToastUtil toastutil = new ToastUtil();
        toastutil.ShowMsg(getApplicationContext(),"打开应用失败,可能没有安装此应用");
    }
}

ToastUtil是TextView章节里面的Toast集成类,用于解决多次点击Toast无法即时响应的问题,代码如下:

package com.example.activityjump2;

import android.content.Context;
import android.widget.Toast;

public class ToastUtil {
    public static Toast mToast;
    public static void ShowMsg (Context context, String string) {
        if (mToast != null) {
            mToast.cancel();
        }
        mToast = Toast.makeText(context, string, Toast.LENGTH_SHORT);
        mToast.show();
    }
}

多界面情况下的Activity生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OqGSHsCb-1691145009921)(…/pictures/Activity4.png)]

Home键返回桌面和Back键退出应用的不同

  • 当按下Home键,默认情况下stop前台的actiity,即activity设置成onstop,而不是ondestory

  • 如果再次启动该activity不是调用onCreate,而是调用onSavedInstanceState方法,保持上次Activity的状态则是从onRestart开始->onStart->onResume

  • 而当按下back键则不同,back键默认finish前台的activity,即activity的状态为onDestory为止

  • 再次启动该activity则从onCreate开始,不会调用onSavedInstanceState方法

从一个界面跳转到另一个界面

  • 从一个界面跳转到另一个界面时:
    • 先onPause暂停当前页面,然后走新页面的创建流程
    • 最后onStop之前那个页面。但它并没有被销毁。(因为他只是被别的盖住了,在任务栈的下一层,并没有退出)
  • 从另一个界面返回时:
    • 先onPause暂停当前页面,然后重新开始之前的页面(因为它没有销毁,所以不用创建)
    • 最后,停止当前页面、销毁当前页面。(因为它从任务栈中退出了)

附录(相关知识的补充)

在AndroidManifest.xml中指定启动模式

  • 在Android中,启动一个Activity有时需要总是创建一个新对象,有时需要复用已有的对象,可以在配置activity时通过launchMode属性指定。

  • launchMode属性值(4个)为:

    • standard:标准模式,每次调用startActivity()方法就会产生一个新的实例
    • singleTop(栈顶复用):如果已经有一个实例位于Activity任务栈的顶部时,就不产生新的实例;如果不位于栈顶,会产生一个新的实例
    • singleTask(栈内复用):如果有一个实例,则把实例上面的全部弹出,将实例暴露在最上面;如果没有实例,则新建一个,正常入栈
    • singleInstance:如果某任务栈有这个实例,直接复用:如果没有,则新建一个栈专门盛放该实例
  • activity的taskAffinity属性:为Activity指定所需任务栈,默认情况下,所有Activity所需任务栈的名字为应用的包名

在Intent中设置标志位指定启动模式

  • intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)这种方式也可以为Activity指定启动模式

  • 在Manifest文件中设置与在intent中设置的区别是:

    • 优先级上intent中设置的方式高于在AndroidManifest.xml中设置
    • 两种方式上限定范围上有所不同,比如:
      1. 第一种方式无法直接指定FLAG_ACTIVITY_CLEAR_TOP标示
      2. 第二种无法直接指定singleInstance模式
  • FLAG_ACTIVITY_CLEAR_TOP 加上 FLAG_ACTIVITY_NEW_TASK:相当于在xml中指定启动模式为singleTask

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  • FLAG_ACTIVITY_SINGLE_TOP:相当于在xml中指定启动模式为singleTop
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

弹出对话框

在给按钮设置监听时,如果选择不用toast弹出消息,那么可以考虑使用弹出对话框的方式。

如隐式跳转到第三方界面的代码可以做出如下修改:

public void jumpToWeChat(View view) {
    try{
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_MAIN);
        intent.setComponent(new ComponentName("com.tencent.mm","com.tencent.mm.ui.LauncherUI"));
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
        //可以更精确的抓取报错信息
    }catch (ActivityNotFoundException e){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        //设置对话框的标题
        builder.setTitle("Error");
        //设置对话框的内容
        builder.setMessage("打开应用失败,可能没有安装此应用");
        //设置退出按钮
        builder.setNegativeButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        AlertDialog alertDialog = builder.create();
        alertDialog.show();
    }
}

弹出对话框式的Activity

上面的示例是使用了系统自带的对话框样式,我们也可以弹出一个对话框样式的Activity

  • 首先在themes.xml文件中写下我们对话框样式的主题:
<style name="MyDialogTheme" parent="Theme.MaterialComponents.DayNight.Dialog.Alert" />
  • 然后在AndroidManifest.xml文件中为我们的Activity配置这个主题:
<activity
          android:name=".DialogActivity"
          android:theme="@style/MyDialogTheme"
          android:exported="false" />
  • 编写布局文件:
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    android:paddingTop="25dp"
    tools:context=".DialogActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="打开应用失败,可能没有安装此应用"
        android:textSize="20sp">

    </TextView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="DialogDestroy" //按钮的监听
            android:text="确定"
            android:textSize="20dp"
            android:layout_margin="20dp"/>

    </LinearLayout>


</LinearLayout>
  • 在DialogActivity中写个简单的退出:
public void DialogDestroy(View view) {
    this.finish();
}
  • 剩下就是在MainActivity布局中写一个按钮显式跳转到这个对话框式Activity:
<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="popDialog"
        android:padding="10dp"
        android:text="弹出界面"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
public void popDialog(View view) {
    Intent intent = new Intent(this,DialogActivity.class);
    startActivity(intent);
}
  • 这样就实现了弹出一个对话框式的Activity
  • 下图比较清晰的说明了在弹出一个对话框式的Activity时两个Activity的生命周期

弹出对话框和对话框式Activity的不同

  • 弹出对话框全程没有对ActiviyA产生任何影响,ActivityA没有onPause更没有onStop

  • 而弹出一个对话框式的ActivityB是先onPause暂停当前界面,然后走B的创建流程,并没有停止之前的界面

  • 在我们的视角,ActivityA虽然被遮住了一部分,但还是可见的,只是不能交互,所以是处于onPause状态(只有页面不可见时才是onStop)

  • 对话框式的ActivityB退出时也是先暂停B,再onResume活动A,再执行B的销毁任务(因为它从任务栈中退出了)

Of=“parent”
app:layout_constraintStart_toStartOf=“parent”
app:layout_constraintTop_toTopOf=“parent” />


```java
public void popDialog(View view) {
    Intent intent = new Intent(this,DialogActivity.class);
    startActivity(intent);
}
  • 这样就实现了弹出一个对话框式的Activity
  • 下图比较清晰的说明了在弹出一个对话框式的Activity时两个Activity的生命周期

弹出对话框和对话框式Activity的不同

  • 弹出对话框全程没有对ActiviyA产生任何影响,ActivityA没有onPause更没有onStop

  • 而弹出一个对话框式的ActivityB是先onPause暂停当前界面,然后走B的创建流程,并没有停止之前的界面

  • 在我们的视角,ActivityA虽然被遮住了一部分,但还是可见的,只是不能交互,所以是处于onPause状态(只有页面不可见时才是onStop)

  • 对话框式的ActivityB退出时也是先暂停B,再onResume活动A,再执行B的销毁任务(因为它从任务栈中退出了)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值