Android Intent基础

Intent 是 Android 一个常用的用于组件间互相通信的信息对象,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间进行数据传递。

基本用例主要包括以下三个:

启动 Activity

Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity(),您可以启动新的 Activity 实例。Intent 用于描述要启动的 Activity,并携带任何必要的数据。如果您希望在 Activity 完成后收到结果,请调用 startActivityForResult()。在 Activity 的onActivityResult() 回调中,您的 Activity 将结果作为单独的 Intent 对象接收。

启动服务

Service 是一个不使用用户界面而在后台执行操作的组件。使用 Android 5.0(API 级别 21)及更高版本,您可以启动包含 JobScheduler 的服务。

对于 Android 5.0(API 级别 21)之前的版本,您可以使用 Service 类的方法来启动服务。通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 用于描述要启动的服务,并携带任何必要的数据。

如果服务旨在使用客户端-服务器接口,则通过将 Intent 传递给 bindService(),您可以从其他组件绑定到此服务。

传递广播

广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast() 或 sendOrderedBroadcast(),您可以将广播传递给其他应用。

Intent大致可以分为两种:显式Intent和隐式Intent。

那么如何构建一个Intent呢?我们常见的代码中经常使用Intent以下的构造函数:

Intent(Context, Class) 
Context:应用和组件提供 Context 
Class:Class 对象

Intent 对象携带 Android 系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。

Intent 中包含的主要信息如下:

组件名称:要启动的组件名称。 这是可选项,但也是构建显式 Intent 的一项重要信息,这意味着 Intent应当仅传递给由组件名称定义的应用组件。如果没有组件名称,则 Intent 则为隐式,且系统将根据其他 Intent信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent

操作:指定要执行的通用操作(例如,查看或选取)的字符串。
对于广播 Intent,这是指已发生且正在报告的操作。操作会在很大程度上决定其余 Intent 的构成,特别是数据和 extra 中包含的内容。

数据: 引用待操作数据和/或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 Intent 的操作决定。例如,如果操作是ACTION_EDIT,则数据应包含待编辑文档的 URI。

类别:一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。

Extra:携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的 extra。
您可以使用各种 putExtra() 方法添加 extra 数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有 extra 数据的 Bundle 对象,然后使用 putExtras() 将 Bundle 插入 Intent 中。

标志 :标志在 Intent 类中定义,充当 Intent 的元数据。标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理

显式Intent:通过提供目标应用的软件包名称或完全限定的组件类名来指定可处理 Intent 的应用。通常,我们会在自己的应用中使用显式 Intent 来启动组件,这是因为我们知道要启动的 Activity 或服务的类名。

在我们上一篇的基础上,我们稍作修改并且添加第二个Activity(QIntentOne)用来展示Intent的使用。

修改第一个Activity的显示文字:
在这里插入图片描述
QIntentOne布局文件:
在这里插入图片描述

QFirstActivity是我们的启动页面,所以创建Intent的过程应该在这个页面中,然后在启动QIntentOne这个Activity.

QFirstActivity

kotlin代码

import kotlinx.android.synthetic.main.qfirst_layout.*;

class QFirstActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState);
        //加载布局文件
        setContentView(R.layout.qfirst_layout);

        firstBtn.setOnClickListener {
            val intent = Intent(this,QIntentOne::class.java);
            startActivity(intent);
        }
    }
}

这里有些东西需要坐下解释,避免有些朋友不太明白。

kotlin-android-extensions是kotlin为Android专门提供的扩展插件,在本例中用来替代findViewById功能。

要想使用此功能,需要在build.gradle文件中进行相应的配置,不过Android Studio已经为我们添加好了,当然了这是因为选择的是基于kotlin语言。
在这里插入图片描述

配置完成我们需要导入对应的布局文件,然后只需要使用控件的id就可以操作控件。

 import kotlinx.android.synthetic.main.qfirst_layout.*;

格式:kotlinx.android.synthetic.main.布局名称.*

当然了,也可以进行局部导入,需要哪个控件就导入哪一个。比如:

 import kotlinx.android.synthetic.main.qfirst_layout.firstBtn;

Java代码

public class QFirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.qfirst_layout);

        Button firstBtn = (Button) findViewById(R.id.firstBtn);

        firstBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(QFirstActivity.this, QIntentOne.class);
                startActivity(intent);
            }
        });
    }
}

希望大家可以留意两者写法差异。具体的我们就不在仔细研究了。首先我们知道能够怎么写,怎么用,其他的有兴趣可以自己仔细的查阅相关资料。

运行效果:
在这里插入图片描述

在这里插入图片描述

隐式 Intent :不指定特定的组件,但必须声明要执行的常规操作,从而允许其他应用中的组件来处理。例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。

隐式 Intent 指定能够在可以执行相应操作的设备上调用任何应用的操作。如果您的应用无法执行该操作而其他应用可以,且您希望用户选取要使用的应用,则使用隐式 Intent 非常有用。

在我们使用Intent的过程中,推荐使用resolveActivity来验证 Activity 是否会接收 Intent。如果结果为非空,则至少有一个应用能够处理该 Intent,并且可以安全调用startActivity()。如果结果为空,不要使用该 Intent。

我们想一下,QIntentOne这个Activity目前能够响应什么样的Intent呢?好像没有吧?

要想让我们的Activity响应隐式Intent,那么第一步我们需要在清单文件AndroidManifest.xml中使用 元素为每个应用组件声明一个或多个 Intent 过滤器。每个 Intent 过滤器均根据 Intent 的操作、数据和类别指定自身接受的 Intent 类型。仅当隐式 Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。

intent-filter 内部,可以使用以下三个元素中的一个或多个指定要接受的 Intent 类型:

action在 name 属性中,声明接受的 Intent 操作。该值必须是操作的文本字符串值,而不是类常量。
data使用一个或多个指定数据 URI(scheme、host、port、path)各个方面和 MIME 类型的属性,声明接受的数据类型。
category在 name 属性中,声明接受的 Intent 类别。该值必须是操作的文本字符串值,而不是类常量。要接收隐式 Intent,必须将 CATEGORY_DEFAULT 类别包括在 Intent 过滤器中,否则隐式 Intent 不会解析该 Activity.

定义自己的操作,请确保加入应用的软件包名称作为前缀,如下action内容区域所示。
在这里插入图片描述

修改QFirstActivity,以隐式Intent方式启动QIntentOne.

kotlin代码
class QFirstActivity : AppCompatActivity() {

    private val ACTION_INTENT_START = "com.qiushangge.android_kotlin.INTENT_START";

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.qfirst_layout);

        firstBtn.setOnClickListener {
            // val intent = Intent(this,QIntentOne:: class.java);
            val intent = Intent().apply {
                action = ACTION_INTENT_START;
            }
            if (intent.resolveActivity(packageManager) != null) {
                startActivity(intent);
            }
        }
    }
}

Java代码
package com.qiushangge.android_java;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class QFirstActivity extends AppCompatActivity {

    private static final String ACTION_INTENT_START = "com.qiushangge.android_kotlin.INTENT_START";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.qfirst_layout);

        Button firstBtn = (Button) findViewById(R.id.firstBtn);

        firstBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Intent intent = new Intent(QFirstActivity.this, QIntentOne.class);
                Intent intent = new Intent(ACTION_INTENT_START);
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                }
            }
        });
    }
}

注意:每个Intent只能指定一个action,但是能指定多个category

如果有多个应用响应隐式 Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。如果用户可能希望每次使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种网络浏览器),则选择默认选项的功能十分有用。

但是,如果多个应用可以响应 Intent,且用户可能希望每次使用不同的应用,则应采用显式方式显示选择器对话框。选择器对话框会要求用户选择用于操作的应用(用户无法为该操作选择默认应用)

要显示选择器,请使用 createChooser() 创建 Intent,并将其传递给 startActivity()。

kotlin代码

           val intent = Intent().apply {
                action = Intent.ACTION_SEND
                putExtra(Intent.EXTRA_TEXT, "测试数据发送")
                type = "text/plain"
            }
            val chooser: Intent = Intent.createChooser(intent, "测试");
            if (intent.resolveActivity(packageManager) != null) {
                startActivity(chooser);
            }

java代码

     			Intent intent = new Intent(Intent.ACTION_SEND);
                intent.putExtra(Intent.EXTRA_TEXT, "测试数据发送");
                intent.setType("text/plain");
                Intent chooser = Intent.createChooser(intent, "测试");
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(chooser);
                }

顺便看一个调用系统拨号界面的小例子。

kotlin代码
 firstBtn.setOnClickListener {
            val intent = Intent(Intent.ACTION_DIAL).apply {
                data = Uri.parse("tel:10000");
            }
            if (intent.resolveActivity(packageManager) != null) {
                startActivity(intent);
            }
Java代码
        firstBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_DIAL);
                intent.setData(Uri.parse("tel:10000"));
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                }
            }
        });

其中Intent.ACTION_DIAL为Android内置动作,使用setData方法设置协议和数据,更多内容请自行查阅文档。

在这里插入图片描述

Intent向下一个ACtivity数据传递

上面我们使用Intent进行Activity的启动操作,实际过程中,启动新的Activity的同时,我们在有些情况下需要将一些特定的数据传递至该Activity中。

Intent传递数据可以使用下面的方式:

Intent.putExtra(键名,键值)

假设一个场景,我们在QFirstActivity页面中得到了一个学生的年龄,姓名等信息,在QIntentOne页面中我们需要对这些信息进行编辑,那么我们就需要进行数据的传递。

修改QFirstActivity页面布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="姓名">

        </TextView>

        <EditText
            android:id="@+id/nameText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入姓名:">

        </EditText>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="年龄"
            >

        </TextView>

        <EditText
            android:id="@+id/ageText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入年龄:">

        </EditText>
    </LinearLayout>
    <Button
        android:id="@+id/firstBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="传递数据">

    </Button>
</LinearLayout>

在这里插入图片描述
QIntentOne页面布局类似,就不再贴出来。。。下面的代码就不放kotlin实现了,这里就算是一个小小的对比,如果开发android使用kotlin在搭配Anko 确实写起来方便很多。不过学习也是需要代价的。。

QFirstActivity中我们需要使用putExtra方法传递数据,实现如下:

	    String name = nameEdit.getText().toString();
        String age = ageEdit.getText().toString();
        Intent intent = new Intent(QFirstActivity.this,QIntentOne.class);
        intent.putExtra("Name",name);
        intent.putExtra("Age",age);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

然后在QIntentOne中接收数据并显示出来:

package com.qiushangge.android_java;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class QIntentOne extends AppCompatActivity {
    private EditText nameEdit;
    private EditText ageEdit;
    private Button backBtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qintent_one);

        nameEdit = findViewById(R.id.nameText);
        ageEdit = findViewById(R.id.ageText);
        backBtn = findViewById(R.id.intentOneBtn);

        Intent intent = getIntent();
        nameEdit.setText(intent.getStringExtra("Name"));
        ageEdit.setText(intent.getStringExtra("Age"));
    }
}

在这里插入图片描述

在这里插入图片描述

ok,我们已经成功的得到了数据。前面已经说过常见的有两种方式传递数据,接下来看看另外一种方式。

那么Bundle是何许人也呢?

Bundle经常使用在Activity之间或者线程间传递数据,传递的数据可以是boolean、byte、int、long、float、double、string等基本类型或它们对应的数组,也可以是对象或对象数组。当Bundle传递的是对象或对象数组时,必须实现Serializable或Parcelable接口。更多内容请参考android开发文档。

 String name = nameEdit.getText().toString();
        String age = ageEdit.getText().toString();
        Intent intent = new Intent(QFirstActivity.this, QIntentOne.class);

        /**
         * 使用Bundle进行数据传递
         */
        Bundle bundle = new Bundle();
        bundle.putString("Name", name);
        bundle.putString("Age", age);
        intent.putExtras(bundle);

        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

同理我们贴出QIntentOne接收参数的代码


        nameEdit.setText(intent.getExtras().getString("Name"));
        ageEdit.setText(intent.getExtras().getString("Age"));
返回ACtivity数据

上面介绍了如何向新调用起的Activity传递数据,但是在某些情况下我们希望得到新Activity给我们返回的一些数据。

还是上面的例子,QIntentOne页面接收数据并显示在当前页面中,同时该页面也可以修改姓名和年龄信息,我们希望在信息修改完成后,QFirstActivity能够得到修改后的信息。

这里我们需要使用startActivityForResult函数进行Activity的启动。

先来看看startActivityForResult定义

public void startActivityForResult(android.content.Intent intent,
                                   int requestCode)

intent:将要启动的intent
requestCode:请求码,如果该值大于0,那么在被调用的Acitivity销毁时会将该值返回到onActivityResult中。requestCode主要用来区分是哪一个Activity返回。
注意:requestCode必须是一个唯一值,并且requestCode <= 0xffff,一般是65535.

那么修改QFirstActivity中关于启动Activity的代码:

  if (intent.resolveActivity(getPackageManager()) != null) {
//            startActivity(intent);
            startActivityForResult(intent,1);
        }

上面提到了,接收Activity返回结果我们需要使用onActivityResult,首先我们的实现onActivityResult方法,并且根据requestCode和返回结果再做对应的处理。

函数定义:

protected void onActivityResult(int requestCode,
                                int resultCode,
                                @Nullable Intent data)

参数说明:

requestCode: 启动Activity时传入的请求码
resultCode:返回数据时传入的处理结果
data:携带返回数据的Intent

QFirstActivity中重写onActivityResult的实现:

  @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

        switch (requestCode) {
            case 1:
                if (resultCode == RESULT_OK) {
                    //接收返回数据,做相应的处理
                    nameEdit.setText(data.getExtras().getString("Name"));
                    ageEdit.setText(data.getExtras().getString("Age"));
                }
                break;
            default:
        }

一般的逻辑就是这样子的。针对我们上面的例子来说,就是获取到修改后的姓名和年龄,并且更改当前页面显示。

在需要返回数据的Activity也就是QIntentOne中,在Activity销毁的时候返回数据。

这里当返回数据按钮点击的时候,销毁Activity并返回修改后的姓名和年龄。

添加Button的点击事件:

 backBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendBackData();
            }
        });

sendBackData方法用来实现数据返回,实现如下:

private void sendBackData() {
        String name = nameEdit.getText().toString();
        String age = ageEdit.getText().toString();

        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString("Name", name);
        bundle.putString("Age", age);
        intent.putExtras(bundle);

        setResult(RESULT_OK, intent);
        finish();
    }

了解下setResult方法:

void setResult (int resultCode,
Intent data)

resultCode
int:结果返回码,通常为RESULT_CANCELED  RESULT_OK,可以自定义
data
Intent: 携带返回数据的Intent

运行效果:
在这里插入图片描述
在这里插入图片描述
修改数据并点击返回数据按钮i:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章