04-Android中的Intent

631 阅读9分钟

使用的工具

现在android项目使用的 android studio 来进行开发,开发的语言现在推荐的是 kotlin, 不过这里还是先使用 Java 来开发

  • AndroidStudio 版本: Android Studio Iguana | 2023.2.1

  • 项目语言: Java

  • JDK版本: 17

  • 对应的android版本:12

    整体demon代码地址

前置文章

Intent的作用

activity 和 intent  |  Android Developers (google.cn)

到目前为止,您所处理的应用只有一个 activity。实际上,许多 Android 应用都需要多个 activity,并在它们之间进行导航。 在此 Codelab 中,您将构建一个字典应用,使其使用多个 activity、通过 intent 在各 activity 之间导航,并向其他应用传递数据。

从官网的解释来看,Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景。

Intent的类型

显式Intent

显示 Intent 的意思就是通过代码明确写出来我要跳转到哪个 Activity 中去,比如当前项目结构如下 两个 Activity 的代码如下

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity");  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
    }  
}

public class SecondActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_second);  
    }  
}

对应的两个布局文件如下

  • res/layout/activity_main.xml
<?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/cus_button"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="MainActivityButton"></Button>  
    <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="Hello World test!"  
        app:layout_constraintBottom_toBottomOf="parent"  
        app:layout_constraintEnd_toEndOf="parent"  
        app:layout_constraintStart_toStartOf="parent"  
        app:layout_constraintTop_toTopOf="parent" />  
  
</androidx.constraintlayout.widget.ConstraintLayout>
  • res/layout/activity_second.xml
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
  
    <!--  
        android:id="@+id/button_1" 表示给该控件添加了一个id  
        layout_width=match_parent 表示该button控件的宽度和父组件一致  
        layout_height=wrap_content 表示该button控件的高度是按照实际内容来的  
        android:text="ButtonOne" 是button中显示的内容  
    -->  
    <Button  
        android:id="@+id/button_1"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="ButtonOne"/>  
</LinearLayout>

现在要做的事情就是在 MainActivity 中点击 button(对应的 id 是 cus_button) 的跳转到 SecondActivity 中,所以接下来修改 MainActivity 类的代码,修改后的 MainActivity 内容如下

package com.example.ademo;  
  
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) {  
        Log.d("MainActivity", "开始创建主Activity");  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);
        // 通过 findViewById 方法获取 Button 实例
        Button button = (Button)findViewById(R.id.cus_button);  
        
        button.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
	            // 点击之后跳转到新的 Activity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);  
                startActivity(intent);  
            }  
        });  
    }  
}
  • 核心代码就是 startActivity(Intent);
  • 当然上面还有一些代码,比如 findViewById(R.id.cus_button) 可以获取一个 Button 类,这个可以参考 [[Android中View详解]] 此时运行项目,刚进入项目的主页面如下(对应的是 activity_main.xml 布局内容) 因为在 MainActivity 的 onCreate 方法中有给 Button 添加点击事件的监听器,所以点击这个按钮之后就会跳转到 SecondActivity 中(对应的布局是 activity_second.xml),所以点击按钮会变成如下形式

隐式Intent

  • 上面显示的的 Intent 是通过代码明确的知道需要跳转的 Activity 是哪一个
  • 而隐式 Intent 则不能直接通过代码跳转,而是通过一系列的匹配规则来实现跳转,这一系列的匹配规则是通过 action 和 category 来实现的
    • 这里的 action 是指 action 标签
    • 这里的 category 也是指 category 标签

action和category

action说明

对于 <action> 标签可以参考官网说明 <动作>  |  Android 开发者  |  Android Developers (google.cn) 上面的截图就是来自官网,上面的说明中能体现出以下几点

  • <action> 标签是需要放在 <intent-filter> 中的
  • <action> 中 name 属性(也就是操作的名称) 是有一些标准值的,不过接下来我们演示的代码会是自定义的 action,对于标准的 action 在后面会演示 接下来先给出一个示例,上面使用显示Intent时 MainActivity 中的跳转代码核心逻辑是
// MainActivity 中的 onCreate 方法
protected void onCreate(Bundle savedInstanceState) {  
    Log.d("MainActivity", "开始创建主Activity");  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    // 通过 findViewById 方法获取 Button 实例  
    Button button = (Button)findViewById(R.id.cus_button);  
    // 给 button 设置点击监听器  
    button.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            // 点击之后跳转到新的 Activity            
            Intent intent = new Intent(MainActivity.this, SecondActivity.class);  
            startActivity(intent);
        }  
    });  
}

这里使用的 Intent 构造函数是

public class Intent implements Parcelable, Cloneable {
	public Intent(Context packageContext, Class<?> cls) {  
	    mComponent = new ComponentName(packageContext, cls);  
	}
}

现在我们换一个构造函数,使用下面这个构造函数

public class Intent implements Parcelable, Cloneable {
	public Intent(String action) {  
	    setAction(action);  
	}
}

在这个构造函数中创建的 Intent 只是给定了一个 action,此时将 MainActivity 中的 onCreate 方法修改成如下方式

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity");  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        // 通过 findViewById 方法获取 Button 实例  
        Button button = (Button)findViewById(R.id.cus_button);  
        // 给 button 设置点击监听器  
        button.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                // 点击之后跳转到新的 Activity                
                //Intent intent = new Intent(MainActivity.this, SecondActivity.class);       
                Intent intent = new Intent("com.example.ademo.MainActivity.MAIN_ACTION");  
                startActivity(intent);  
            }  
        });  
    }  
}

好了,现在修改了 Intent 创建的方式,点击之后还能跳转到 SecondActivity 中么? 答案是不行的,因为在 AndroidManifest.xml 中注册的 SecondActivity 中并没有在 inten-filter 标签中配置相应的 action,看看现在的 AndroidManifest.xml 中关于 SecondActivity 的配置

<activity  android:name=".SecondActivity" android:exported="true" />

intent-filter

接下来在 activity 标签中配置 intent-filter,然后在 intent-filter 标签中配置 action 和 category

<activity android:name=".SecondActivity"  
    android:exported="true">  
    <intent-filter>  
        <action android:name="com.example.ademo.MainActivity.MAIN_ACTION"/>  
        <!-- category 如果没有配置的话就是 android.intent.category.DEFAULT -->    
        <category android:name="android.intent.category.DEFAULT"/>  
    </intent-filter>  
</activity>

现在已经给 SecondActivity 添加了 action 标签,现在再去重新编译并启动项目,点击 MainActivity 中的 button 也能跳转到 SecondActivity

action的注意点

<intent-filter> 标签中可以配置一个或多个 <action> 标签,比如下面这种形式

<activity android:name=".SecondActivity"  
    android:exported="true">  
    <intent-filter>  
        <action android:name="com.example.ademo.MainActivity.MAIN_ACTION"/>  
        <action android:name="com.example.ademo.MainActivity.MAIN_ACTION2"/>  
        <!-- category 如果没有配置的话就是 android.intent.category.DEFAULT -->     
        <category android:name="android.intent.category.DEFAULT"/>  
    </intent-filter>  
</activity>

这两个 action 是一个或的关系,即匹配到了 Intent 中的任何一个 action 都可以,比如此时 MainActivity 中的代码修改成如下形式都可以(即 Intent 中的 action 还是会有作用)

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity");  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        // 通过 findViewById 方法获取 Button 实例  
        Button button = (Button)findViewById(R.id.cus_button);  
        // 给 button 设置点击监听器  
        button.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                // Intent 对象中的 action 只能有一个,以下两行代码都能正常工作,即只要匹配了其中一个 action 就可以  
                //Intent intent = new Intent("com.example.ademo.MainActivity.MAIN_ACTION");  
                Intent intent = new Intent("com.example.ademo.MainActivity.MAIN_ACTION2");  
                startActivity(intent);  
            }  
        });  
    }  
}

category

注意点:如果使用了 action,那么 category 必须填写,无论有没有特殊的 category,默认的那个 category是必须填写上的 <intent-filter> 中除了可以配置 <action> 标签之外还可以配置 <category> 标签进行过滤,如果没有配置 <category> 标签,则相当于使用的是默认的 <category> , 如下所示

 <activity android:name=".SecondActivity"  
    android:exported="true">  
    <intent-filter>  
        <action android:name="com.example.ademo.MainActivity.MAIN_ACTION"/>  
        <category android:name="android.intent.category.DEFAULT"/>  
    </intent-filter>  
</activity>

而 Intent 对象中是可以设置多个 category 的,比如现在在 <intent-filter> 中不使用默认的 category,而是自己设置几个 category,设置后如下所示

<activity android:name=".SecondActivity"  
    android:exported="true">  
    <intent-filter>  
        <action android:name="com.example.ademo.MainActivity.MAIN_ACTION"/>  
        <action android:name="com.example.ademo.MainActivity.MAIN_ACTION2"/>  
        <!--这里需要注意的是,即使有其他的 category,默认的category还是需要加上-->
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="com.example.ademo.MainActivity.CATEGORY_ONE"/>   
    </intent-filter>  
</activity>

现在如果不修改 MainActivity 的逻辑点击 MainActivity 中的按钮还可以跳转到 SecondActivity 么? 这里先给出未修改前的 MainActivity 代码

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity");  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        // 通过 findViewById 方法获取 Button 实例  
        Button button = (Button)findViewById(R.id.cus_button);  
        // 给 button 设置点击监听器  
        button.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                // Intent 对象中的 action 只能有一个,以下两行代码都能正常工作,即只要匹配了其中一个 action 就可以  
                Intent intent = new Intent("com.example.ademo.MainActivity.MAIN_ACTION");  
                startActivity(intent);  
            }  
        });  
    }  
}

直接使用的上面的代码是无法跳转的,因为这个 Intent 对象中只匹配到了 <intent-filter> 标签中的 action,但是没有匹配到 category,所以还需要给 Intent 对象添加 category,修改后的代码如下

button.setOnClickListener(new View.OnClickListener() {  
    @Override  
    public void onClick(View v) {  
        //Intent intent = new Intent(MainActivity.this, SecondActivity.class);  
        Intent intent = new Intent("com.example.ademo.MainActivity.MAIN_ACTION");  
        intent.addCategory("com.example.ademo.MainActivity.CATEGORY_ONE");  
        startActivity(intent);  
    }  
});

这样才可以正常的跳转

隐式Intent的高级作用

使用 intent 的不仅仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得 Android 多个应用程序之间的功能共享成为了可能。所以在 android 系统中只要 intent-filter 中的 action 和 category 条件可以匹配的上,就可以响应对应的活动。 注意点:在 android 12 中 activity 的标签配置中增加了android:exported="true" 或者 android:exported="false",如果该属性配置的是false,那么就不会响应外部的intent

使用intent在活动间传递数据

Intent 类提供了一系列的保存数据的方法,接着上面的代码示例(所有的代码在 整体demon代码地址中可以获取),现在可以在 MainActivity 中添加数据,当跳转到 SecondActivity 中的时候再将这些数据打印出来

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity");  
        super.onCreate(savedInstanceState);  
        //EdgeToEdge.enable(this);  
        setContentView(R.layout.activity_main);  
        // 通过 findViewById 方法获取 Button 实例  
        Button button = (Button)findViewById(R.id.cus_button);  
        // 给 button 设置点击监听器  
        button.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Intent intent = new Intent("com.example.ademo.MainActivity.MAIN_ACTION");  
                intent.addCategory("com.example.ademo.MainActivity.CATEGORY_ONE");  
                // 这里是忘 Intent 中添加数据,第一个参数是key,第二个参数是 值,这里是一系列的重载方法
                intent.putExtra("main_key_str", "main_value1");  
                intent.putExtra("main_key_int", 1);  
                startActivity(intent);  
            }  
        });  
    }  
}

然后在 SecondActivity 中从 Intent 获取数据的逻辑是

public class SecondActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_second);  
        Intent intent = getIntent();  
        String mainKeyStr = intent.getStringExtra("main_key_str");  
        // 这里是获取int类型的 参数,第二个参数是默认值,是没有这个key的时候返回的值
        int mainKeyInt = intent.getIntExtra("main_key_int", 0);  
        Log.d("SecondActivity", mainKeyStr);  
        Log.d("SecondActivity", mainKeyInt + "");  
    }  
}

如何将当前活动的数据传递给上一个活动

前面讲解了在跳转 activity 的时候如何将数据传递给即将要跳转的 activity,那么在当前的 activity 销毁的时候如何将一些结果传递给上一个 activity 呢? 这个可以参考 [[05-Android中在activity销毁后将数据传递给上一个活动]]