android browser支持支持Intent Scheme URL语法的可以在wrap页面加载或点击时,通过特定的intent uri链接可以打开对应app页面,例如

<a href="intent://whatsapp/#Intent;scheme=myapp;package=com.xiaoyu.myapp;S.help_url=http://Fzxing.org;end">what can I do</a>

通过android系统解析查找符合规则的Activity后启动对应activity,如果未找到匹配Activity则browser进行处理。

配置

 <a href="intent://whatsapp/#Intent;scheme=myapp;package=com.xiaoyu.myapp;S.help_url=http://Fzxing.org;end">what can I do</a>
//这样如果没有对应应用,该链接就会跳转到S.help_url指定的url上
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /><!-- 定义浏览器类型,有URL需要处理时会过滤 -->
<data android:scheme="myapp" android:host="whatsapp" android:path="/" /><!-- 打开以whatsapp协议的URL,这个自己定义 -->
</intent-filter>

  

URI的解析与生成:

在Intent 类内部有个parseUri(String uri, int flags)方法用来解析Uri生成Intent返回,下面主要分析该方法:

在分析该解析方法时候,首先我们需要理解intent uri规则,在intent类里规则串如下,

android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;<br />i.some_int=100;S.some_str=hello;end

根据此规则,看出主要分为三个部分:

Header: android-app://android app为判断是否是android 应用

Action: com.example.MY_ACTION //组件的action内容

Package: com.example.app //包名启动组件使用

Extra: some_int=(int)100,some_str=(String)hello //传递的数据,i代表int,S代表String ,c代表char等基本类型

除了这几部分内容也包含intent内的其它内容,如categry,type,component,selector等

在简单分析理解Uri规则串后,再来分析parseUri就很好理解了,下面看一下主要代码

 public static Intent parseUri(String uri, int flags) throws URISyntaxException {
int i = 0;
try {
final boolean androidApp = uri.startsWith("android-app:");//判断开头是否为android, // Validate intent scheme if requested.
if ((flags&(URI_INTENT_SCHEME|URI_ANDROID_APP_SCHEME)) != 0) {
if (!uri.startsWith("intent:") && !androidApp) {//是否为intent Uri
Intent intent = new Intent(ACTION_VIEW);
try {
intent.setData(Uri.parse(uri));
} catch (IllegalArgumentException e) {
throw new URISyntaxException(uri, e.getMessage());
}
return intent;
}
} i = uri.lastIndexOf("#");//查找标记位,开始解析
// simple case
if (i == -1) {
if (!androidApp) {
return new Intent(ACTION_VIEW, Uri.parse(uri));
} // old format Intent URI
} else if (!uri.startsWith("#Intent;", i)) {
if (!androidApp) {
return getIntentOld(uri, flags);
} else {
i = -1;
}
} // new format
Intent intent = new Intent(ACTION_VIEW);
Intent baseIntent = intent;
boolean explicitAction = false;
boolean inSelector = false; // fetch data part, if present
String scheme = null;
String data;
if (i >= 0) {
data = uri.substring(0, i);
i += 8; // length of "#Intent;"
} else {
data = uri;
} // loop over contents of Intent, all name=value;
while (i >= 0 && !uri.startsWith("end", i)) {//按类别分离循环处理(解析action,categry,type,extra data)
int eq = uri.indexOf('=', i);
if (eq < 0) eq = i-1;
int semi = uri.indexOf(';', i);
String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : ""; // action
if (uri.startsWith("action=", i)) {
intent.setAction(value);
if (!inSelector) {
explicitAction = true;
}
} // categories
else if (uri.startsWith("category=", i)) {
intent.addCategory(value);
} // type
else if (uri.startsWith("type=", i)) {
intent.mType = value;
} // launch flags
else if (uri.startsWith("launchFlags=", i)) {
intent.mFlags = Integer.decode(value).intValue();
if ((flags& URI_ALLOW_UNSAFE) == 0) {
intent.mFlags &= ~IMMUTABLE_FLAGS;
}
} // package
else if (uri.startsWith("package=", i)) {
intent.mPackage = value;
} // component
else if (uri.startsWith("component=", i)) {
intent.mComponent = ComponentName.unflattenFromString(value);
} // scheme
else if (uri.startsWith("scheme=", i)) {
if (inSelector) {
intent.mData = Uri.parse(value + ":");
} else {
scheme = value;
}
} // source bounds
else if (uri.startsWith("sourceBounds=", i)) {
intent.mSourceBounds = Rect.unflattenFromString(value);
} // selector
else if (semi == (i+3) && uri.startsWith("SEL", i)) {
intent = new Intent();
inSelector = true;
} // extra data parse
else {
String key = Uri.decode(uri.substring(i + 2, eq));
// create Bundle if it doesn't already exist
if (intent.mExtras == null) intent.mExtras = new Bundle();
Bundle b = intent.mExtras;
// add EXTRA,这里巧妙的对基本数据类型进行处理,(字母.var)值得学习借鉴
if (uri.startsWith("S.", i)) b.putString(key, value);
else if (uri.startsWith("B.", i)) b.putBoolean(key, Boolean.parseBoolean(value));
else if (uri.startsWith("b.", i)) b.putByte(key, Byte.parseByte(value));
else if (uri.startsWith("c.", i)) b.putChar(key, value.charAt(0));
else if (uri.startsWith("d.", i)) b.putDouble(key, Double.parseDouble(value));
else if (uri.startsWith("f.", i)) b.putFloat(key, Float.parseFloat(value));
else if (uri.startsWith("i.", i)) b.putInt(key, Integer.parseInt(value));
else if (uri.startsWith("l.", i)) b.putLong(key, Long.parseLong(value));
else if (uri.startsWith("s.", i)) b.putShort(key, Short.parseShort(value));
else throw new URISyntaxException(uri, "unknown EXTRA type", i);
} // move to the next item
i = semi + 1;
} if (inSelector) {
// The Intent had a selector; fix it up.
if (baseIntent.mPackage == null) {
baseIntent.setSelector(intent);
}
intent = baseIntent;
} ...... return intent;//解析生产内容,对应到生产的intent,返回处理 } catch (IndexOutOfBoundsException e) {
throw new URISyntaxException(uri, "illegal Intent URI format", i);
}
}

经过上面简要分析,我们了解Uri intent生成过程,这种设计非常巧妙可以达到很好的解耦处理,相比于显示与隐士启动方式,这种方式更加灵活。

例如,我们可以由Server端动态下发,本地通过解析后在进行跳转页面,即可达到动态控制页面功能,其实不仅可以应用到页面跳转中,如果进一步扩展,可以应用到更多功能中,

比如我们可以扩展自定义规则,如header改为:http,https,代表页面跳转到webActivity,header改为:tipe时为toast提示,改为dialog时为弹框显示等。

我们还可以更为细致的控制,如可加入版本号指定对应的版本的app执行这规则,其余版本默认行为,适用于修复部分bug。由此可见我们可以通过修改parseUri方法即可以扩展到更多功能中,下面看一下我的修改,

static final String TAG="PageJump";

    static final String SCHEME_INTENT = "page";
static final String SCHEME_ANDROIDAPP = "android-app:";
static final String SCHEME_HTTP = "http";
static final String SCHEME_HTTPS = "https";
static final String SCHEME_TIPS_DIALOG = "tips_dialog";
static final String SCHEME_TIPS_TOAST = "tips_toast"; //动态解析实现对页面行为控制
public static void jumpPageUri(Context context, String strUri) throws URISyntaxException{
//{(2,5][8,12)}android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;
if (TextUtils.isEmpty(strUri)) {
throw new NullPointerException("parser uri content is empty");
} String data = strUri.trim();
//uri是否在版本内
final int curVer = Utils.getVerCode(context);
if(isInRangeVersion(data,curVer)){
return;
}
//remove command version part
if(data.startsWith("{")) {
int verEnd = data.indexOf('}', 1);
data = data.substring(verEnd+1);
}
String uriData = data; if(uriData.startsWith(SCHEME_ANDROIDAPP)){
Intent intent = Intent.parseUri(uriData, Intent.URI_INTENT_SCHEME);
String appPackage = context.getPackageName();
ComponentName componentName = intent.getComponent(); //out intent
if (componentName == null || !appPackage.contains(componentName.getPackageName())){
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
else if (uriData.startsWith(SCHEME_INTENT)) {
Intent sendIntent = UriProcessor.parseIntentUri(data, Intent.URI_INTENT_SCHEME); // Verify that the intent will resolve to an activity
// if (null == sendIntent.resolveActivity(context.getPackageManager())) {
// throw new URISyntaxException(data, "not found match page");
// }
sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(sendIntent);
}
else if (uriData.startsWith(SCHEME_HTTP) || uriData.startsWith(SCHEME_HTTPS)) {
// WebViewActivity.launch(context, uri);
}
else if(uriData.startsWith(SCHEME_TIPS_DIALOG)){
// DialogUtil.showNormal("test");
}
else if(uriData.startsWith(SCHEME_TIPS_TOAST)){
// ToastUtils.showShortMessage("");
} }

规则串前面增加了应用版本范围,{(2,5][8,12)},这里我使用开闭区间的方式来指定及范围,这种方式更精简使用,版本解析处理

 /**
* current command version whether contain current app version
* @param data
* @param curVer
* @return
*/
public static boolean isInRangeVersion(String data,final int curVer){
if(data.startsWith("{")){
int verEnd = data.indexOf('}', 1); if(verEnd>0) {
String verStr = data.substring(0, verEnd+1); boolean in_range=true;
int pos=1;
try {
while (pos >= 0 && !verStr.startsWith("}")) {
in_range=true;
char ch = verStr.charAt(pos);
if (ch == '[' || ch == '(') {
boolean[] border=new boolean[2];
int semi = verStr.indexOf(',', pos);
int startVer = Integer.valueOf(verStr.substring(pos + 1, semi));
border[0]= (ch=='[');
int toVer = 0, flagVer = 0;
if ((flagVer = verStr.indexOf(']', semi)) >= 0 || (flagVer = verStr.indexOf(')', semi)) >= 0) {
toVer = Integer.valueOf(verStr.substring(semi + 1, flagVer));
border[1]= (verStr.charAt(flagVer)==']');
}
// judge current version code not inside range
// jude min version code < <=
if((border[0] && curVer<startVer) ||(!border[0] && curVer<=startVer)){
in_range=false;
}
// judge max version code > >=
if((border[1] && curVer>toVer) ||(!border[1] && curVer>=toVer)){
in_range=false;
}
pos = flagVer + 1;
if (pos + 1 >= verStr.length())
break;
}
}
return in_range;
}catch (NumberFormatException ex){
Log.e(TAG,"parse regular expression version error!");
} }
return true;
}
return true;
}

测试使用:

//    String jumpUri1="{(2,5][8,12)}android-app://com.example.app/#Intent;action=com.example.MY_ACTION;i.some_int=100;S.some_str=hello;end";
String jumpUri2="{(0,3][6,12)}android-app://com.example.app/#Intent;action=com.example.MY_ACTION;i.some_int=100;end";
String jumpUri3="{(0,6]}android-app://com.example.app/#Intent;action=com.example.MY_ACTION;i.some_int=100;end";
String jumpUriPage="{(2,6]}android-app://com.example.myapp/#Intent;action=com.example.myapp.SecondActivity;package=com.example.myapp;category=android.intent.category.DEFAULT;S.some=systemFrom;end";
String jumpUriPage2="{[1,8]}page#Intent;action=com.example.myaction;package=com.example.myapp;category=android.intent.category.DEFAULT;S.some=innerFrom;end"; try {
// PageJump.jumpPageUri(getApplicationContext(),jumpUri1);
PageJump.jumpPageUri(getApplicationContext(),jumpUri2);
PageJump.jumpPageUri(getApplicationContext(),jumpUri3);
} catch (URISyntaxException e) {
e.printStackTrace();
}

分析intent的代码设计后,真是觉得源码设计的十分巧妙,值得仔细认真琢磨。

关于Intent Uri页面跳转的更多相关文章

  1. Intent实现页面跳转

    Intent实现页面跳转: 1. startActivity(intent) 2. startActivityForResult(intent,requestCode); onActivityResu ...

  2. Android成长日记-使用Intent实现页面跳转

    Intent:可以理解为信使(意图),由Intent来协助完成Android各个组件之间的通讯 Intent实现页面之间的跳转 1->startActivity(intent) 2->st ...

  3. Intent实现页面跳转和传值

    *Intent称为意图,是Android各大组件连接的桥梁 1.Activity页面跳转 同一个包内 Intent intent = new Intent(); intent.setClass(Mai ...

  4. Android Intent实现页面跳转

      Intent可以来协助完成Android各个组件之间的通信   1:startActivity(intent);     //直接启动                /*              ...

  5. Activity中使用Intent实现页面跳转与参数的传递(转)

    新建一个FirstAvtivity.java package com.zhuguangwei; import android.app.Activity; import android.content. ...

  6. [Android应用开发] 04.页面跳转和数据传输

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  7. .Net程序猿玩转Android开发---(11)页面跳转

    在不论什么程序开发中,都会遇到页面之间跳转的情况,Android开发也不例外.这一节,我们来认识下Android项目中如何进行页面跳转.页面跳转分为有參数和无參数页面跳转,已经接受还有一个页面的返回值 ...

  8. Android Studio 使用Intent实现页面的跳转(带参数)

    不管是在APP,还是在网站中,页面之间的跳转都是很常见的,本文主要讲一下在APP中,如何通过Intent实现页面的跳转. 不带参数: 写在MainActivity页面的代码: Intent inten ...

  9. Android Intent实现页面之间跳转

    什么是IntentIntent可以理解为信使(意图)由Intent来协助完成Android各个组件之间的通讯Intent实现页面逐渐的跳转1.startActivity(inetnt)2.startA ...

  10. Android中实现activity的页面跳转并传值

    一个Android应用程序很少会只有一个Activity对象,如何在多个Activity之间进行跳转,而且能够互相传值是一个很基本的要求. 本次我们就讲一下,Android中页面跳转以及传值的几种方式 ...

随机推荐

  1. python注释、变量、数据类型详细

    目录 1.python注释 2.PEP8规范 3.变量与常量 1.python中的变量 2.变量名的命名规范 3.常量的基本使用 1.python注释 什么是注释? 注释是对代码的解释说明,写注释是为 ...

  2. java线程基础知识整理

    目录 线程基本概念 1.java实现线程 2.线程的生命周期 3.线程常用的方法 3.1.sleep() 3.2.interrupt方法 3.3.stop方法 4.线程调度 4.1.常见的线程调度模型 ...

  3. 基于jQuery的三种AJAX请求

    基于jQuery的三种AJAX请求 1. 介绍 get请求 通常用于 获取服务端资源(向服务器要资源) ​ 例如:根据URL地址,从服务器获取HTML文件.CSS文件.JS文件.图片文件.数据资源等. ...

  4. python进阶之路21 正则应用 第三方模块之requests模块 openpyxl模块 简易爬虫(pandas)

    作业讲解 """ 网络爬虫没有我们现在接触的那么简单 有时候页面数据无法直接拷贝获取 有时候页面还存在防爬机制 弄不好ip会被短暂拉黑 """ ...

  5. MySQL union 和 order by 同时使用

    目录 一.出现错误的情况 二.解决上述问题的两种方法 三.案例分析: 求解: 常见的错误解法 (1)使用 union 和 多个order by 不加括号 [报错] (2)order by 在 unio ...

  6. cordova完整版本创建、修改自定义插件重新调试步骤带截图

    创建第三方插件 npx plugman create --name myplugin --plugin_id xiao.jin.plugin --plugin_version 1.0.0 添加平台支持 ...

  7. 【学习笔记】QT从入门到实战完整版(菜单栏、工具栏、浮动窗口、状态栏、中心部件)(3)

    QMainWindow QMainWindow 是一个为用户提供主窗口程序的类,包含以下几种类型部件,是许多应用程序的基础. 示例代码 void MainWindow::test() { // --- ...

  8. Windows性能监控工具Perfmon的使用、性能指标分析

    Fighting_001 关注  0.1 2018.08.25 22:18* 字数 1488 阅读 7604评论 0喜欢 4 目录结构 一.Perfmon简介.性能监控指标.性能对象指标 1.常用的性 ...

  9. Pytest插件pytest-order指定用例顺序

    Pytest插件pytest-order指定用例顺序 安装  pip install pytest-order 注意不是pytest-ordering 说起来这里有个故事 关于pytest-order ...

  10. Nginx04 反向代理和负载均衡

    1 反向代理介绍 https://www.cnblogs.com/jthr/p/16827214.html 2 负载均衡介绍 https://www.cnblogs.com/jthr/p/168273 ...