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. Qt栅格布局、ScrollArea和用户选择界面

    用户选择界面 就我们在实际开发的时候可能需要面对这样一个界面 做个demo试试看 其实我们可以分解一下这个界面 就是除了控制相关的内容,最主要的就是这个界面之上,有一个个动态的输入的控件,上面都是学生 ...

  2. Mattermost 笔记

    目录 部署 配置 客户端 桌面程序 Android 使用 扩展 Jenkins Hubot 机器人 Jira GitHub Mattermost 是一个开源.可私有化部署的在线通讯平台,可以和Gith ...

  3. 笑死,面试官又问我SpringBoot自动配置原理

    面试官:好久没见,甚是想念.今天来聊聊SpringBoot的自动配置吧? 候选者:嗯,SpringBoot的自动配置我觉得是SpringBoot很重要的"特性"了.众所周知,Spr ...

  4. 基于U-Net网络的图像分割的MindStudio实践

    摘要:本实践是基于Windows版MindStudio 5.0.RC3,远程连接ECS服务器使用,ECS是基于官方分享的CANN6.0.RC1_MindX_Vision3.0.RC3镜像创建的. 本文 ...

  5. 【转载】VFP编写DLL,并调用

    1. 编制DLL文件 ,保存为Temp.prg Define Class vfptools As Session OlePublic    Procedure Add As Integer    Lp ...

  6. [编程基础] Python lambda函数总结

    Python lambda函数教程展示了如何在Python中创建匿名函数.Python中的匿名函数是使用lambda关键字创建的. 文章目录 1 介绍 1.1 简单使用 1.2 Python lamb ...

  7. Flutter异常监控 - 叁 | 从bugsnag源码学习如何追溯异常产生路径

    如果觉得文章对你有帮助,点赞.收藏.关注.评论,一键四连支持,你的支持就是我创作最大的动力. ️ 本文原创听蝉 公众号:码里特别有禅 欢迎关注原创技术文章第一时间推送  ️ 前言 没错,继Flutte ...

  8. flutter Error:Cannot run with sound null safety, because the following dependencies don't support

    学习flutter新版本的路上,真的是一天一个新惊喜啊 今天遇到的坑是 Flutter 升级高版本后,运行和build 报错 Error: Cannot run with sound null saf ...

  9. 痞子衡单片机排行榜(2022Q4)

    痞子衡单片机排行榜(2022Q4) 继2020年开办的<痞子衡嵌入式半月刊>之后,从2023年1月份开始痞子衡将为大家带来新项目<痞子衡单片机排行榜>(一年分四季,每个季度发布 ...

  10. 《深入理解Java虚拟机》第三章读书笔记(一)——垃圾回收算法

    参考书籍<深入理解java虚拟机>周志明著 系列文章目录和关于我 本文主要介绍垃圾回收理论知识 1.jvm哪些区域需要进行垃圾回收 虚拟机栈,本地方法栈,程序计数器都是线程私有的,随线程而 ...