Android Intent 使用及其详解

Intent这东西,在Android中的地位至关重要,甚至成为"Android第五大组件"。

在我们的常规业务开发中,各个页面的跳转,service的使用,打开相机,app内分享,广播的使用等等都必须用到它。在我们的framework层里,它的身影也随处可见。

一、什么是Intent?

Intent是Android系统用来抽象描述要执行的一个操作,也可以在不同组件之间进行沟通和消息传递。

Intent意图可以是明确的指定组件的名称,这样你可以精确的启动某个系统组件,比如启动一个Activity。它也可以是模糊的,没有指定组件名称,只要是能够匹配到这个Intent的应用都可以接收到,比如发送一个拍照Intent,所有的拍照应用都会响应。

二、Intent的分类

intent分为显式Intent和隐式Intent:

显式的Intent就是你已经知道要启动的组件名称,比如某个Activity的包名和类名,在Intent中明确的指定了这个组件(Activity),一般来说这种Intent经常用在一个应用中,因为你已经明确的知道要启动的组件名称。

隐式的Intent就是你不知道要启动的组件名称,只知道一个Intent动作要执行,比如:拍照,分享,打开相册,查看地图。一般来说这种Intent用在不同的应用之间传递信息。

当你创建一个显式Intent来启动一个Activity或者Service时,系统会立刻启动那个组件通过你的Intent对象。

当你创建一个隐式Intent,系统会根据manifest中的intent filter寻找匹配的组件,如果你发送的Intent匹配到一个intent filter,系统会把你的Intent传递给该filter对应的组件(Activity、Service等),并且启动它。如果找到多个匹配的intent filter对应的应用程序,则会弹出一个对话框让你选择哪个应用程序接受你的Intent。

三、intent的简单使用

1.跳转指定页面(显示)

 mBinding.acLockSettingIcon.setOnClickListener {
            startActivity(Intent(this, SettingActivity::class.java)
            )
        }

2.打开网页(隐式)

 public void invokeWebBrowser(View view) {  
    Intent intent = new Intent(Intent.ACTION_VIEW);  
    intent.setData(Uri.parse("http://www.google.com"));  
    startActivity(intent);  
}

3.打电话(隐式)

public void call(View view) {  
    Intent intent = new Intent(Intent.ACTION_CALL);  
    intent.setData(Uri.parse("tel:110"));  
    startActivity(intent);  
}

4.发送短信(隐式)

// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType(HTTP.PLAIN_TEXT_TYPE); // "text/plain" MIME type

5.分享图片

private void share(String content, Uri uri){
 Intent shareIntent = new Intent(Intent.ACTION_SEND);
 if(uri!=null){
 shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
 shareIntent.setType("image/*");
 //当用户选择短信时使用sms_body取得文字
 shareIntent.putExtra("sms_body", content);
 }else{
 shareIntent.setType("text/plain");
 }
 shareIntent.putExtra(Intent.EXTRA_TEXT, content);
 //自定义选择框的标题
 startActivity(Intent.createChooser(shareIntent, "邀请好友"));
 //系统默认标题
       
}

6.打开指定应用

String package_name="xx.xx.xx";
PackageManager packageManager = context.getPackageManager();
Intent it = packageManager.getLaunchIntentForPackage(package_name);
startActivity(it);

四、Intent的属性 

在Intent里,可以配置以下属性

1.setAction:意图,一个字符串变量,用来指定Intent要执行的动作类别

2.setData:一个Uri对象,对应着一个数据,这个数据可能是MIME类型的。当创建一个intent时,除了要指定数据的URI之外,指定数据的类型(MIME type)也很重要,它能够帮助系统找到最合适的那个系统组件来处理你的intent请求。然而,MIME type有时能够通过URI来推测出来,特别是当data是content:的URI,这样的data表明在设备中由ContentProvider提供。

只设置数据的URI可以调用setData()方法,只设置MIME类型可以调用setType()方法,如果要同时设置这两个可以调用setDataAndType()。

3.Category:一个包含Intent额外信息的字符串,表示哪种类型的组件来处理这个Intent.

例如:

        <receiver
            android:name=".receiver.BootBroadcastReceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </receiver>
  • CATEGORY_LAUNCHER:表示这个Activity是Activity栈最初的Activity,应用程序的主Activity,可以在桌面应用程序列表找到并启动它。
  • 这个receiver监听 Android手机开机后,会发送android.intent.action.BOOT_COMPLETED广播,监听这个广播就能监听开机。
  • 当然,我在动态注册广播的时候,也可以通过
    private void registerListener() {
    	        if (mContext != null) {
    	            IntentFilter filter = new IntentFilter();
    	            filter.addAction(Intent.ACTION_SCREEN_ON);
    	            filter.addAction(Intent.ACTION_SCREEN_OFF);
    	            filter.addAction(Intent.ACTION_USER_PRESENT);
    	            filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);//home键
    	            mContext.registerReceiver(mScreenReceiver, filter);
    	        }

    这样去监听锁屏/熄屏/home键等操作

以上Intent的属性(component name、action、data and category)都是Intent的特征属性,通过这些属性Android系统可以找到哪个应用组件将会被启动。

还有以下属性:

1.Extras:Intent可以携带的额外key-value数据,你可以通过调用putExtra()方法设置数据,每一个key对应一个value数据。你也可以通过创建Bundle对象来存储所有数据,然后通过调用putExtras()方法来设置数据。对于数据key的名字要尽量用包名做前缀,然后再加上其他,这样来保证key的唯一性。

2.Flags:用来指示系统如何启动一个Activity(比如:这个Activity属于哪个Activity栈)和Activity启动后如何处理它(比如:是否把这个Activity归为最近的活动列表中)。例如我在点击通知栏的im信息后,我把flag设置为FLAG_ACTIVITY_CLEAR_TOP,那么在该activity之上的activity都会关闭,并且intent会传递给老的activity(现在在栈顶),设置为FLAG.ACTIVITY_NEW_TASK 标志主要用于确保当从一个非 Activity 类型的 Context(例如,一个 Service 或 Application)启动 Activity 时,该 Activity 会被放置在一个新的任务(task)栈中。

上面就是Intent的一些基本使用和介绍

五、Intent传递数据

在常规的开发中,我们通常会使用Intent传递一些简单的数据。如果数据过大,就会爆TransactionTooLargeException异常。

简单来说,Intent 传输数据的机制中,用到了 Binder。Intent 中的数据,会作为 Parcel 被存储在 Binder 的事务缓冲区(Binder transaction buffer)中的对象进行传输。而这个 Binder 事务缓冲区具有一个有限的固定大小,当前为 1MB。你可别以为传递 1MB 以下的数据就安全了,这里的 1MB 空间并不是当前操作独享的,而是由当前进程所共享。也就是说 Intent 在 Activity 间传输数据,本身也不适合传递太大的数据。

在Intent的数据传输中:使用 Bundle 存储数据,是用的值传递(深拷贝),Intent 传输的数据,都存放在一个 Bundle 类型的对象 mExtras 中,Bundle 要求所有存储的数据,都是可被序列化的。在 Android 中,序列化数据需要实现 Serializable 或者 Parcelable。对于基础数据类型的包装类,本身就是实现了 Serializable,而我们自定义的对象,按需实现这两个序列化接口的其中一个即可。

为什么用的深拷贝?因为在Activity 之间传递数据,首先要考虑跨进程的问题,而 Android 中又是通过 Binder 机制来解决跨进程通信的问题。所以说这个传递数据的大小和binder传输数据相关。

所以:

  • Intent 无法传递大数据是因为其内部使用了 Binder 通信机制,Binder 事务缓冲区限制了传递数据的大小。
  • Binder 事务缓冲区的大小限定在 1MB,但是这个尺寸是共享的,也就是并不是传递 1MB 以下的数据就绝对安全,要视当前的环境而定。
  • 不要挑战 Intent 传递数据大小的极限,对于大数据,例如长字符串、Bitmap 等,不要考虑 Intent 传递数据的方案。

其他的tips:

Intent传递数据和Bundle传递数据是一回事,Intent传递时内部还是调用了Bundle。

Bundle只是一个信息的载体,内部其实就是维护了一个Map<String,Object>。Intent负责Activity之间的交互,内部是持有一个Bundle的。但是我们不能直接传递Map对象哦。

六、PendingIntent

  • 从字面意思上理解,PendingIntent 是一种延迟的 Intent,表示一种延迟执行的意图操作。

    PendingIntent 的应用场景关键在于间接的 Intent 跳转需求, 即先通过一级 Intent 跳转到某个组件,在该组件完成任务后再间接地跳转到二级的 Intent。PendingIntent 中的单词 “pending” 指延迟或挂起,就是指它是延迟的或挂起的。例如,你在以下场景中就可以使用 PendingIntent:

    • 场景 1 - 系统通知消息的点击操作(例如im消息跳转)
    • 场景 2 - 桌面微件的点击操作
    • 场景 3 - 系统闹钟操作
    • 场景 4 - 第三方应用回调操作

在这些场景中,我们真正感兴趣的操作是挂起的,并且该操作并不是由当前应用执行,而是由某个外部应用来 “间接” 执行的。例如,我们在发送系统通知消息时,会通过 PendingIntent 构造一个系统通知 Notification ,并调用 NotificationManagerCompat.notify(…) 发送通知,此时并不会直接执行 PendingIntent。而是当系统显示通知,并且用户点击通知时,才会由系统通知这个系统应用间接执行 PendingIntent#send() ,而不是通过当前应用执行。

1. PendingIntent 和 Intent 的区别

1、执行进程不同 —— PendingIntent 在其他进程执行: Intent 通常会在创建进程中执行,而 PendingIntent 通常不会在创建进程中执行;

2、执行时间不同 —— PendingIntent 会延迟执行: Intent 通常会立即执行,而 PendingIntent 通常会延迟执行,延迟到其他进程完成任务后再执行,甚至延迟到创建进程消亡后。例如,在 场景 1 - 系统通知消息的点击操作 中,即使发送系统通知消息的进程已经消亡了,依然不妨碍二级 Intent 的跳转;

3、执行身份不同 —— PendingIntent 支持授权: PendingIntent 内部持有授权信息,支持其他应用以当前应用的身份执行,这有利于避免嵌套 Intent 存在的安全隐患。而直接使用 Intent 的话,一般只能以当前应用的身份执行(为什么说一般?因为有 Activity#startActivityAsUser() 这个 API,但一般你拿不到所需的参数)。

2.PendingIntent 的使用方法

// 启动 Activity
PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags)
// 启动 Service
PendingIntent.Service(Context context, int requestCode, Intent intent, int flags)
// 启动 BroadcastReceiver(发送广播)
PendingIntent.getBroadcast(Context context, int requestCode, Intent intent, int flags)

PendingIntent 的 4 个参数:

1、context: 当前应用的上下文,PendingIntent 将从中抽取授权信息;

2、requestCode: PendingIntent 的请求码,与 Intent 的请求码类似;

3、intent: 最终的意图操作;

4、flag: 控制标记位,通过标记位 FLAG_MUTABLE 和 FLAG_IMMUTABLE 控制 PendingIntent 可变或不可变。可变性意味着在消费 PendingIntent 时,可以针对其中包装的 Intent 进行修改,即使用 PendingIntent#send(Context, int, Intent) 进行修改。需要注意的是,这里的 Intent 参数并不会完全替换 PendingIntent 中包装的 Intent,而是将修改的信息填充到原有的 Intent 上。FLAG_UPDATE_CURRENT: 更新标记位 1,如果系统中已经存在相同的 PendingIntent,那么将保留原有 PendingIntent 对象,而更新其中的 Intent。即使不可变 PendingIntent,依然可以在当前应用更新;FLAG_CANCEL_CURRENT: 更新标记位 2,如果系统中已经存在相同的 PendingIntent,那么将先取消原有的 PendingIntent,并重新创建新的 PendingIntent。: 更新标记位 3,如果系统中已经存在相同的 PendingIntent,那么不会重新创建,而是直接返回 null;

FLAG_ONE_SHOT: 一次有效标记位,PendingIntent 被消费后不支持重复消费,即只能使用一次。

  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&岁月不待人&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值