使用的工具
现在android项目使用的 android studio 来进行开发,开发的语言现在推荐的是 kotlin, 不过这里还是先使用 Java 来开发
-
AndroidStudio 版本: Android Studio Iguana | 2023.2.1
-
项目语言: Java
-
JDK版本: 17
-
对应的android版本:12
前置文章
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销毁后将数据传递给上一个活动]]