隐式Intent解析

回顾Android中的隐式Intent的相关知识点。

      • 概述
      • 使用
        • action标签
        • category标签
        • data标签
          • mimeType
          • scheme
          • host
          • port
          • path
          • pathPrefix
          • pathPattern
        • data总结
      • 从网页启动app

概述

隐式启动主要解决了界面间的跳转解耦,主要涉及intent-filter中的三个标签:

  • category
  • action
  • data

    它们构成了隐式启动的匹配项,通过不同的配置完成不同的路由跳转。

使用

列一个例子,新建一个APP有两个界面,一为MainActivity,一个为ActionActivityMainActivity中有一个按钮,通过按钮启动隐式Intent来启动ActionActivity
布局和代码非常简单:
MainActivity的布局和代码:

//布局中只有一个按钮
xml version="1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    

ActionActivity的布局和代码:

//布局中就是一行文字说明
xml version="1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ActionActivity">
"我是跳转的页面"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

//代码
public class ActionActivity extends AppCompatActivity {

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

action标签

MainActiity的按钮添加代码,如果找不到的话会抛出异常,因此捕获并打印启动失败的信息:

btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
          try{
                //Intent action1 = new Intent("action1");//可以使用构造传入action参数
                Intent action1 = new Intent();
                action1.setAction("action1");
                startActivity(action1);
            }catch (Exception e){
                    Toast.makeText(MainActivity.this,"启动失败",Toast.LENGTH_LONG).show();
                    e.printStackTrace();
                }
        });

清单文件中的ActionActivity的配置,可以看到只使用了action标签:

        <activity android:name=".ActionActivity">
            <intent-filter>
                <action android:name="action1"/>
            intent-filter>
        activity>

启动MainActivity,点击按钮,结果抛出异常:
隐式Intent解析_第1张图片
异常结果为:

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=action1 }

所以实际上ActionActivity只配置action标签是不够的。

category标签

增加默认的android.intent.category.DEFAULT

<activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>
                <action android:name="action1"/>
            intent-filter>
        activity>

intent的代码不做变动,这时点击按钮可以正常跳转:
隐式Intent解析_第2张图片
说明这里category 是必须要配置的,Intent虽然没有设置category,但是默认就是这个android.intent.category.DEFAULT,所以能够匹配到。
再增加一个action试试:

        <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>
                <action android:name="action1"/>
                <action android:name="action2"/>
            intent-filter>
        activity>

intent的代码不做变动,点击按钮依旧可以正常跳转,效果不再贴出。
如果给Intent增加一个intent-filter标签中没有的action试试:

 Intent action1 = new Intent();
                action1.setAction("action1");
                action1.setAction("action3");
                startActivity(action1);

点击抛出ActivityNotFoundException异常,,效果不再贴出。
通过上面的测试说明intent设置的action必须为目标页面intent-filteraction的子集才能正常跳转,并且是区分大小写的。

测试完了action,更换下category为自定义的category1试试:

<activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="category1"/>
                <action android:name="action1"/>
            intent-filter>
        activity>

代码中:

                Intent action1 = new Intent();
                action1.setAction("action1");
                action1.addCategory("category1");
                startActivity(action1);

运行点击抛出ActivityNotFoundException异常,效果不再贴出。
然后增加android.intent.category.DEFAULT这个默认的action在测试:

         <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="category1"/>
                <action android:name="action1"/>
            intent-filter>
        activity>

运行点击按钮可以正常跳转,效果不再贴出。
说明android.intent.category.DEFAULT这个默认的action是必须要在intent-filter标签中声明的。
如果存在多个action匹配的页面,那么启动后会弹出选择对话框,比如编写一个新的界面为Action2Activity,它也接受action1的动作:
布局:


<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Action2Activity">
    <TextView
        android:text="我也是跳转的页面!"
        android:textSize="32dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
LinearLayout>

清单文件:

 <activity android:name=".Action2Activity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="action1" />
            intent-filter>
        activity>

代码只设置action1

                    Intent action1 = new Intent();
                    action1.setAction("action1");
                    startActivity(action1);

运行效果:
隐式Intent解析_第3张图片
如果再增加一个自定义的category2试试:

 <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="category1"/>
                <category android:name="category2"/>
                <action android:name="action1" />
            intent-filter>
        activity>

代码不做变动,点击按钮可以正常跳转。
如果在代码中增加一个category3试试:

 Intent action1 = new Intent();
                action1.addCategory("category1");
                action1.addCategory("category3");
                action1.setAction("action1");
                startActivity(action1);

运行点击抛出ActivityNotFoundException异常,效果不再贴出。
同样说明intent中的category,必须为目标页面intent-filtercategory的子集才能正常跳转,和action的匹配规则是一样的。
另外说明下,只使用category而不添加任何action也是无法匹配的,说明intent中的action是必须要设置的。而且如果配置了多个intent-filter标签也是按照一组一组进行匹配的,直到与其中一组匹配成功。

data标签

data标签自然不是必须的,主要为URL的表现形式,它的格式如下:

                <data android:mimeType=""/>//媒体类型 例如 "image/*"就是匹配图片类型
                <data android:scheme=""/>//例如: http,https
                <data android:host=""/>//例如:www.csdn.net
                <data android:port=""/>//例如:8080
                <data android:path=""/> // 路径:如 /nav/blockchain
                <data android:pathPrefix=""/>
                <data android:pathPattern=""/>// 正则表达式标识的路径
                <data android:ssp=""/>
                <data android:sspPrefix=""/>
                <data android:sspPattern=""/>

data的属性比较多:

  • mimeType 媒体类型 例如 image/*就是匹配图片类型,mimeType类型比较多 ,这里不再列出;
  • scheme类似如 http,https
  • host类似www.csnd.com
  • port类似 8080
  • path 路径:/nav/blockchain
  • pathPrefix 路径前缀:/nav
  • pathPattern 路径的正则表达式方式
  • ssp //可以匹配系统特定intent 相关文章通过android:ssp高效过滤Android Intents
  • sspPrefix//可以匹配系统特定intent
  • sspPattern//可以匹配系统特定intent

    类似这样://:[||]
    例如action://action1:44/abc/xyz

mimeType

改动下上面的例子:

        <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT"/>
                <action android:name="action1"/>
                <data android:mimeType="image/*"/>//指定媒体类型为图片的类型
            intent-filter>
        activity>

代码中不设置action,只设置mimeType

                    Intent action1 = new Intent();
                    action1.setType("image/png");//指定了媒体类型为png的图片类型
                    startActivity(action1);

运行效果:
隐式Intent解析_第4张图片
这里可以看到只要有任何能够匹配的mimeTypeimage/png的应用都会在底部的列表弹出,但是如果Intent不设置mimeType而只设置action,那么是无法匹配的,说明mimeType的需要优先匹配的。
如果actionmimeType完全匹配,就可以更为精准的匹配。
需要注意的是清单文件中设置了mimeType而不设置scheme的话,intent中的默认的schemecontent或者file才能进行匹配(在API 24以后只能对应contentscheme了),意味着mimeType其实对应了默认的scheme
看下代码实现:

                    Intent action1 = new Intent();
                    action1.setDataAndType(Uri.parse("content://action1"),"image/png");
                    action1.setAction("action1");
                    startActivity(action1);

这里setDataAndType方法才能同时设置datamimeType,单独使用setData或者setType两者都会将对方清空。
同样如果清单文件中指定了多个mimeType那么,只要Intent匹配到一个就可以。

scheme

在上面的例子中scheme就是content://action1中的content了,更改个自定义的scheme测试:

 <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="action1" />
                <data android:mimeType="image/*"/>
                <data android:scheme="action"/>
            intent-filter>
        activity>
 Intent action1 = new Intent();
  action1.setDataAndType(Uri.parse("action://action1"),"image/png");
  action1.setAction("action1");
  startActivity(action1);

运行可以正常启动的,这里不再帖效果。当然清单文件不设置mimeType的话,Intent就可以使用setData方法来设置URL的。

host

上面例子的action://action1action1就是host了,需要注意的是如果清单文件中没有指定host的话, portpathpathPrefixpathPattern都不会生效。

port

port需要和host放在同一个data标签内才能生效:

 <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="action1" />
                <data android:scheme="action"/>
                <data android:host="action1" android:port="44"/>
                <data />
            intent-filter>
        activity>

代码:

 Intent action1 = new Intent();
 action1.setData(Uri.parse("action://action1:44"));
 action1.setAction("action1");
 startActivity(action1);

可以正常跳转。

path

代码增加/abc/xyz

 Intent action1 = new Intent();
 action1.setData(Uri.parse("action://action1:44/abc/xyz"));
 action1.setAction("action1");
 startActivity(action1);

清单文件:

 <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="action1" />
                <data android:scheme="action"/>
                <data android:host="action1" android:port="44"/>
                <data android:path="/abc/xyz"/>//必须以/开头
                <data />
            intent-filter>
        activity>

这样完全匹配。

pathPrefix

匹配path的前缀,上面例子中就是/abc

 <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="action1" />
                <data android:scheme="action"/>
                <data android:host="action1" android:port="44"/>
                <data android:pathPrefix="/abc"/>
                <data />
            intent-filter>
        activity>
pathPattern

利用正则表达式/.*匹配/abc:

 <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="action1" />
                <data android:scheme="action"/>
                <data android:host="action1" android:port="44"/>
                <data android:pathPattern="/.*/xyz"/>
            intent-filter>
        activity>

.*就是表示匹配任意字符,这样也能够灵活匹配,需要注意正则表达式中的符号如果当做字符处理需要进行转义。

data总结

比如要匹配action://action1:44/abc/xyz这样的URL的话,清单文件中至少需要写到scheme:

   <data android:scheme="action"/>

其实也就是说Intent中的data应该为清单文件中data的子集。

从网页启动app

隐式Intent也可以从网页中启动Activity,比如有一个远程网页内容如下:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<a href="action://action1:44/abc/xyz?m=1&n=2">启动页面a>
body>
html>

a标签中的href为自定义链接,对应的在清单文件中做出改动:

        <activity android:name=".ActionActivity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <action android:name="android.intent.action.VIEW" />
                <data
                    android:scheme="action"
                    android:host="action1"
                    android:port="44"
                    android:path="/abc/xyz"
                   />
            intent-filter>
        activity>

ActionActivity中可以获取各种数据:

public class ActionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_action);
        Uri data = getIntent().getData();
        if (data != null){
            Log.d("url",data.toString());
            String scheme = data.getScheme();
            Log.e("scheme", scheme);
            String host = data.getHost();
            Log.e("host", host);
            int port = data.getPort();
            Log.e("port", port+"");
            String path = data.getPath();
            Log.e("path", path);
            List<String> pathSegments = data.getPathSegments();//路径分割
            String params = data.getQuery();
            Log.e("params", params);
            String value = data.getQueryParameter("m");
            Log.e("m值", value);
        }
    }
}

如果从浏览器中打开页面,点击a标签链接,可以成功跳转并看到打印结果:
隐式Intent解析_第5张图片

08-20 11:33:01.181 4491-4491/cn.franky.test E/scheme: action
08-20 11:33:01.182 4491-4491/cn.franky.test E/host: action1
08-20 11:33:01.182 4491-4491/cn.franky.test E/port: 44
08-20 11:33:01.182 4491-4491/cn.franky.test E/path: /abc/xyz
08-20 11:33:01.182 4491-4491/cn.franky.test E/params: m=1&n=2
08-20 11:33:01.182 4491-4491/cn.franky.test E/m值: 1

隐式Intent总结到此。

你可能感兴趣的:(Android)