• Develop系列-API Guides-应用组件-Intents and Intent Filters


    Intents 和 Intent Filters

    (Intent译为意图,让人比较费解,实际上解释为“消息”更加合理,干脆就不翻译了)

    Intent是能在app组件间传递的消息体,基本使用方式有如下三种:

    • 启动activity
      • startActivity:intent描述需要启动的activity和必须的数据
      • startActivityForResult:intent启动的activity结束后,会触发onActivityResult回调
    • 启动服务
      • startService:intent描述需要启动的service和必须的数据
      • bindService
    • 发送广播

    Intent类型

    • 显式intents:通过指定包名+类名来明确需要启动的组件,一般用在app内部使用。
    • 隐式intents:不指定具体的组件,通过定义一些动作或者条件,由系统来匹配能够执行动作或者满足条件的组件。

    1. A创建intent,作为入参startActivity
    2. Android系统搜索所有app的intent filter用于适配A发出的intent。(如果有多个匹配上,会弹框给用户选择)
    3. Android系统通过onCreate启动匹配上的B,并把A的intent当做入参

    Caution: 为了确保你的app是安全的,通常用显式intent的方式来启动service,因为用隐式intent启动服务是有安全风险的,隐式intent无法预知启动的service就是你想要的那个。

    创建一个Intent

    Intent主要包含如下属性:

    组件名

    需要启动的组件名,一般指包名+类名:com.example.ExampleActivity

    对于显式intent是必选的,对于隐式intent,不能指定。

    image

    Action(动作?)

    指定执行一般动作的字符串,比如:

        /**
         * Activity Action: 给用户显示数据。比如:在联系人中显示具体
         * 联系人的信息;在邮件发件人中,显示可用的发件人列表。
         * <p>Input: {@link #getData} is URI from which to retrieve data.
         * <p>Output: nothing.
         */
        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
        public static final String ACTION_VIEW = "android.intent.action.VIEW";

    你可以自定义Action,一般优先使用Intent类中默认支持的Action。

    在Intent构造函数或者通过setAction指定:

        public Intent setAction(String action) {
            mAction = action != null ? action.intern() : null;
            return this;
        }
        public Intent(String action) {
            setAction(action);
        }

    Data(数据)

    URI一般是具体MIME类型的数据,由action来执行。比如:action是ACTION_EDIT,那么数据一般是可编辑文档的URI。

    为了让系统能够更精确地适配到你发出来的Intent,在指定Data的同时最好把Type也指定。当你的数据以content:开头时,系统可以判断出具体类型。

        public Intent setType(String type) {
            mData = null;
            mType = type;
            return this;
        }
        public Intent setData(Uri data) {
            mData = data;
            mType = null;
            return this;
        }
        public Intent setDataAndType(Uri data, String type) {
            mData = data;
            mType = type;
            return this;
        }

    上述代码说明type和data如果需要同时指定,请使用setDataAndType,否则不论是setType还是setData都会将另一个设置为null。

    Category(类别)

    任意数量的category可以放在一个intent中,但是大多数intents不需要category。

    常用的如下:

    • CATEGORY_BROWSABLE:activity可以通用web浏览器来显示链接索引,比如图片或者emai信息。
    • CATEGORY_LAUNCHER:activity是此应用初始启动的activity,并且会在launcher中显示。
        public Intent addCategory(String category) {
            if (mCategories == null) {
                mCategories = new ArraySet<String>();
            }
            mCategories.add(category.intern());
            return this;
        }

    系统可以通过componet name、action、data、category这四个属性来匹配具体启动的组件,然而intent还可以设置更多不影响组件匹配的属性,如下介绍的Extras和Flags:

    Extras(额外信息)

        public Intent putExtra(String name, 类型 value) {
            if (mExtras == null) {
                mExtras = new Bundle();
            }
            mExtras.put类型(name, value);
            return this;
        }

    其中类型可以是:boolean、byte、char,short、int、long、float、double、String、CharSequence、Parcelable +上述类型的数组(类型[ ]) + Serializable、Bundle、IBinder

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

    比如,当创建发送email的intent,可以用EXTRA_EMAIL 来指定收件人,用EXTRA_SUBJECT 来指定邮件主题。

    可以自定义Extra_*的常量。

    Flags(标记)

    flags可以通知Android系统怎么启动一个activity(比如,activity的所属task)和如何对待启动后的activity(比如,activity是否属于最近activities启动列表)

        public Intent setFlags(int flags) {
            mFlags = flags;
            return this;
        }

    显式intent例子

    // 在Activity中执行,this表示当前Context
    // fileUrl是个URL字串,比如"http://www.example.com/image.png"
    Intent downloadIntent = new Intent(this, DownloadService.class);
    downloadIntent.setData(Uri.parse(fileUrl));
    startService(downloadIntent);

    隐式intent例子

    Caution: 隐式intent通过startActivity时,有可能系统没有任何apps可以处理你发出的intent,这会导致你的app crash,为了确保系统中至少有一个app能够处理你发出的intent,请发送intent之前先用resolveActivity进行判断

    // 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
    
    // Verify that the intent will resolve to an activity
    if (sendIntent.resolveActivity(getPackageManager()) != null) {
        startActivity(sendIntent);
    }

    应用程序选择器

    当有多个app可以处理你发出的隐式intent,系统会弹框给用户选择,用户可以选择一个并且将其设为默认项。

    然后如果多个app可以处理你发出的隐式intent,并且用户想每次都选择不同的app来处理,有就必须强制显示选择框给用户选择。这种选择框每次都会询问用户使用哪个app,并且没法将其中一个设为默认。比较常见的例子:通过ACTION_SEND来分享数据,用户通常根据使用场景选择不同的app进行分享。

    Intent intent = new Intent(Intent.ACTION_SEND);
    ...
    
    // Always use string resources for UI text.
    // This says something like "Share this photo with"
    String title = getResources().getString(R.string.chooser_title);
    // Create intent to show chooser
    Intent chooser = Intent.createChooser(intent, title);
    
    // Verify the intent will resolve to at least one activity
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(chooser);
    }

    接收隐式intent

    在manifest文件中定义<intent-filter>可以指定app能够处理的隐式intent,当然显式intent的话只匹配组件名,其他的都直接忽略。

    一组filter定义一个app能够支撑的能力

    • <action>
    • <data>
    • <category>:对于activity,filter必须包含CATEGORY_DEFAULT,系统在startActivity和startActivityForResult时只会处理带CATEGORY_DEFAULT类型的activities。
    <activity android:name="ShareActivity">
        <intent-filter>
            <action android:name="android.intent.action.SEND"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="text/plain"/>
        </intent-filter>
    </activity>

    系统对<action>, <data>, <category>三个属性都会进行匹配,缺一不可。

    用filter并不是一种安全的方式来保护你的组件不被其他apps启动,何况开发者可用通过包名+类名来直接显式intent启动你的组件,如果你的组件只能由同一个app里面的其他组件来启动,那么请将exported属性设置为false

    Caution:还是那句话,服务请显式启动。

    Note:对于广播来说,可以在代码中动态注册和去注册,运行时指定时间内有效的广播可以用此方法。

    过滤器例子

    <activity android:name="MainActivity">
        <!-- This activity is the main entry, should appear in app launcher -->
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    
    <activity android:name="ShareActivity">
        <!-- This activity handles "SEND" actions with text data -->
        <intent-filter>
            <action android:name="android.intent.action.SEND"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="text/plain"/>
        </intent-filter>
        <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
        <intent-filter>
            <action android:name="android.intent.action.SEND"/>
            <action android:name="android.intent.action.SEND_MULTIPLE"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="application/vnd.google.panorama360+jpg"/>
            <data android:mimeType="image/*"/>
            <data android:mimeType="video/*"/>
        </intent-filter>
    </activity>

    使用Pending Intent

    需要预定好,但是有触发条件之后才能执行的intent,常用场景:

    • Notification:Android中点击每个下拉栏的通知,可以跳转到其他界面,这个通知中包含的用户点击后才执行的动作就是pendingintent,在NotificationManager中执行这个intent
    • App Widget:用于预先定义widget上具体需要执行的动作,比如按Music Widget上的播放按钮,来播放音乐同时刷新widget视图,滚动显示歌词,这个响应按键动作的intent就是pendingintent
    • AlarmManager:指定时间之后执行的intent

    创建方式:

    Intent匹配测试

    Action测试

    可匹配场景:

    1. intent包含的action在filter定义的action列表中
    2. intent如果没有action,filter包含至少一个action

    filter没有定义action,必定匹配失败

    Category测试

    可匹配场景:

    1. filter中的category至少包含所有intent中的category
    2. intent如果没有category

    Data测试

    每个<data>可以指定一个URI结构:<scheme>://<host>:<port>/<path>

    结构中每个属性都是可选项,但是有具体关系:

    • scheme不匹配,host直接忽略
    • host不匹配,port直接忽略
    • scheme和host都不匹配,path直接忽略

    intent中URI和filter中URI比较时,只匹配filter中的URI

    • filter只定义scheme,所有scheme的URIs都匹配
    • filter定义scheme和authority,但是没有path,那么同样scheme和authority能匹配上,而不管path
    • filter定义scheme、authority、path,全匹配

    Note:path可用通配符*来适配


    intent中URI和MIME type,与filter中URI和MIME type匹配规则:

    1. intent定义URI或者MIME,而filter都不定义,匹配上。
    2. intent定义URI,但没有定义MIME(没有定义,也不能从URI中识别出MIME),filter定义能匹配上的URI并且没有定义MIME,匹配上
    3. intent定义MIME,但没有定义URI,filer定义相同的MIME,并且没有指定URI
    4. intent定义MIME(可从URI中识别出MIME也算)和URI,filter定义的MIME必须包含intent定义的MIME,同时URI必须匹配上,有一种情况例外:intent定义的URI是以content:和file:开头的,那么filter只需要能匹配MIME,就算匹配上。

    Intent匹配

    intent匹配不仅仅用于启动组件,还可以有其他用途,比如Launcher通过匹配规格来查找需要在桌面显示的所有图标(activities必须包含ACTION_MAINCATEGORY_LAUNCHER category)

    packageManager有一系列query…()和resolve…()方法来列出可适配对应intent的组件集合。

  • 相关阅读:
    【原】Spark on YARN
    【原】Spark Standalone模式
    【原】日志处理-Spark
    怎样从官网下载Spring的jar包
    成功安装mysql(mysql-5.5.32-winx64.msi)后,为何服务管理器里找不到MYSQL服务名?
    SQL如何取得一个面的中心点
    Kindle2018 一周使用报告
    迅雷导致文件损坏
    苹果中国官网全面更新,官翻产品不容错过!
    如何选择windows 10 系统中默认打开程序
  • 原文地址:https://www.cnblogs.com/konger/p/3900393.html
Copyright © 2020-2023  润新知