欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android意图-Intent详解

程序员文章站 2022-05-28 14:47:46
...

Android意图-Intent详解

Intent是Android应用里各组件之间通信的重要方式,一个Activity通过Intent来表达自己的意图—想要启动哪个组件(activity,service,broadcasts)。

一、Intent启动不同组件的方法:

1.Activity

  • startActivity()
  • startActivityForResult()

2.Service

  • startService()
  • bindService()

3.Broadcasts

  • sendBroadcast()
  • sendOrderedBroadcast()
  • sendStickyBroadcast()

二、Intent属性与过滤器

Intent属性:

  • action
  • data
  • category
  • type
  • component
  • extras

1.action

描述Intent对象要实施的动作,可以调用Intent#setAction()方法来指定action。action值为String类型。

清单文件中也有:

<action android:name="android.intent.action.MAIN" />

常见的action

  • ACTION_MAIN:值为”android.intent.action.MAIN”,表示整个程序的入口
  • ACTION_VIEW:值为”android.intent.action.VIEW”,表示用于将一些数据显示给用户
  • ACTION_EDIT:表示允许用户对一些数据进行编辑
  • ACTION_DIAL:表示打电话面板
  • ACTION_CALL:表示直接拨打电话
  • ACTION_SEND:表示发送短信
  • ACTION_SENDTO:表示选择发送短信
  • ACTION_BATTERY_LOW:表示电量低广播

2.data

Intent对象中用于进行操作的数据,可以调用Intent#setData()或Intent#setDataAndType(),data值一般为Uri类型。

3.category

描述Intent对象中的action属性属于哪个类别,也就是设置Intent对象进行某项操作时的约束,可以通过Intent#addCategory()方法设置,category值为String类型。

如清单文件中:

<category android:name="android.intent.category.LAUNCHER" />

4.type

用于描述组件能够处理的请求类型(即数据的MIME类型),可以通过Intent#setType()或Intent#setDataAndType()来进行设置。type值为String类型。

5.component

描述Intent对象中所使用的组件类名字可以通过Intent#setComponent()方法利用类名进行设定,也可以通过Intent#setClass()方法利用类型对象信息进行设置。当调用组件明确指定了component信息,组件管理服务就不再需要根据action、data、等信息去寻找满足其需求的组件,只需要按照component信息实例化对应的组件作为功能的实现者即可。一但指定了component,intent就变成了单纯的信息载体,只负责传递信息和数据。这种方式通常用于内部组件的互连互通中。component值类型为ComponentName类型。

Intent类中的相关方法:
public Intent setClass(Context packageContext, Class<?> cls) {
    mComponent = new ComponentName(packageContext, cls);
    return this;
}

public Intent setClassName(String packageName, String className) {
    mComponent = new ComponentName(packageName, className);
    return this;
}

public Intent setComponent(ComponentName component) {
    mComponent = component;
    return this;
}
ComponentName

特定应用程序组件(Android四大组件)的标识符。需要在这里封装的两条信息来标识组件:它所在的包(String)和该包中的类(String)名称。

private final String mPackage;
private final String mClass;

public ComponentName(String pkg, String cls) {
    if (pkg == null) throw new NullPointerException("package name is null");
    if (cls == null) throw new NullPointerException("class name is null");
    mPackage = pkg;
    mClass = cls;
}

public ComponentName(Context pkg, Class<?> cls) {
    mPackage = pkg.getPackageName();
    mClass = cls.getName();
}

public ComponentName(Context pkg, String cls) {
    if (cls == null) throw new NullPointerException("class name is null");
    mPackage = pkg.getPackageName();
    mClass = cls;
}

6.extras

以Bundle类的形式存储其他额外的需要的数据。可以通过Intent#putExtras()方法来设置。

public Intent putExtras(Bundle extras) {
    if (mExtras == null) {
        mExtras = new Bundle();
    }
    mExtras.putAll(extras);
    return this;
}

Intent过滤器

在清单文件中一定见过标签,这就是描述该Activity能够处理哪些Intent的过滤器。一个Activity中可以有一到多组过滤器,每组过滤器通常包括有action、category、type等属性信息。

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

三、Intent类中的构造方法与Intent使用的两种形式:

1.Intent类中的构造方法

/**
 * Create an empty intent.
 */
public Intent() {
}

/**
 * Copy constructor.
 */
public Intent(Intent o)

public Intent(String action)

public Intent(String action, Uri uri)

public Intent(Context packageContext, Class<?> cls)

public Intent(String action, Uri uri,
        Context packageContext, Class<?> cls)

2.获取Intent实例

//方式一(显式):使用上面第五种构造方法:指定当前activity和要打开的activity
Intent intent=new Intent(this,SecondActivity.class);

//方式二(显式):通过使用Intent#setClass()方法指定当前activity和要打开的activity
Intent intent=new Intent();
intent.setClass(this,SecondActivity.class);

//方式三 (隐式):通过使用Intent#setClassName()方法指定当前activity和要打开的activity
Intent intent=new Intent();
intent.setClassName(this,"包名.SecondActivity");
//或者
intent.setClassName("包名","包名.SecondActivity");

3.Intent使用的两种形式:

  • 显式
  • 隐式

显式

  • 直接使用Intent的构造方法传入类名(类名.class),构造出Intent实例。
  • 使用Intent#setClass()方法利用类型对象信息进行设置

隐式

先创建出一个Intent实例不指定其对应的Activity组件,再调用Intent#setAction()等方法设置action、category、type等属性信息。由Android系统根据属性信息选择最适合的Activity去运行这个Intent。兼具弱耦合的特性,更灵活。

  • 一个Activity如果需要隐式跳转,那么在清单文件中必须添加以下子节点

    <intent-filter >
        <action android:name="com.jeff.action"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
    
  • action节点的name是自己定义的,定义好之后,这个name的值就会成为这个activity动作,在隐式启动Activity时,意图中设置的action必须跟”com.jeff.action”是完全匹配的。
应用场景
  • 显式意图:启动同一个应用中的Activity
  • 隐式意图:启动不同应用中的Activity
  • 在启动效率上,隐式远远低于显式
  • 如果系统中有多个Activity与意图设置的Action匹配,那么在启动Activity时,会弹出一个对话框,里面包含所有匹配的Activity。
启动Service的一个异常

“java.lang.IllegalArgumentException: Service Intent must be explicit”

产生原因:API21以后启动Service的Intent必须为显式否则会抛出异常

解决方法:

Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));

/***
 * API21以后启动Service的Intent必须为显式否则会抛出异常
 * "java.lang.IllegalArgumentException: Service Intent must be explicit"
 * Android L (lollipop, API 21) introduced a new problem when trying to invoke implicit intent,
 * "java.lang.IllegalArgumentException: Service Intent must be explicit"
 *
 * 该方法可以将隐式Intent转换为显式
 * If you are using an implicit intent, and know only 1 target would answer this intent,
 * This method will help you turn the implicit intent into the explicit form.
 *
 * Inspired from SO answer: http://*.com/a/26318757/1446466
 * @param context
 * @param implicitIntent - The original implicit intent
 * @return Explicit Intent created from the implicit original intent
 */
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
    // Retrieve all services that can match the given intent
    PackageManager pm = context.getPackageManager();
    List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

    // Make sure only one match was found
    if (resolveInfo == null || resolveInfo.size() != 1) {
        return null;
    }

    // Get component info and create ComponentName
    ResolveInfo serviceInfo = resolveInfo.get(0);
    String packageName = serviceInfo.serviceInfo.packageName;
    String className = serviceInfo.serviceInfo.name;
    ComponentName component = new ComponentName(packageName, className);

    // Create a new intent. Use the old one for extras and such reuse
    Intent explicitIntent = new Intent(implicitIntent);

    // Set the component to be explicit
    explicitIntent.setComponent(component);

    return explicitIntent;
}

四、使用Intent在组件之间传递数据

1.使用Intent传递数据

//传递几种基本的数据类型:
public Intent putExtra(String name, XXX value)

//将src中的所有extras复制到此intent
public Intent putExtras(Intent src)

//传递Bundle类型的数据
public Intent putExtras(Bundle extras)

2.在intent启动的Activity获取传递的数据

//1.首先获取启动这个Activity的intent对象,在Activity类中定义了这样的方法:
/** Return the intent that started this activity. */
public Intent getIntent() {
    return mIntent;
}

2.获取到intent对象后使用Intent类中定义的下面两种方法获取传递的数据:
//获取所有类型的数据,获取后强制转换类型即可
public Object getExtra(String name) {
    return getExtra(name, null);
}

//获取XXX类型的数据
public XXX getXXXExtra(String name)

//获取Bundle类型的数据
public Bundle getExtras()