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. Golang Gorm 封装 分页查询 Where Order 查询

    说说为什么写Gorm,因为公司新项目需要,研究了下Go下的gorm.对于一个项目首先考虑的问题,就是封装一些常用的工具方法,例如多参数查询 where or Like 还有order by Limit ...

  2. 5、枚举Enum

    枚举类会隐式的继承Enum类,无法再继承其它类(单继承机制) 一.无实参枚举类型: 1.定义: /** * 1.无实参枚举类型 */ public enum NoParamTypeEnums { SP ...

  3. Vue中实现自定义excel下载

    目录 第一种:后端生成excel 第二种:前端合成excel 总结 参考资料 最近在工作中遇到一个需求,就是需要在前端实现一个错误模板Excel的下载功能. 实现下载有两种方式,一种是后端生成一个ex ...

  4. [OpenCV实战]14 使用OpenCV实现单目标跟踪

    目录 1 背景 1.1 什么是目标跟踪 1.2 跟踪与检测 2 OpenCV的目标跟踪函数 2.1 函数调用 2.2 函数详解 2.3 综合评价 3 参考 在本教程中,我们将了解OpenCV 3中引入 ...

  5. xpath解析数据的方法

    1 功能描述 2 1.实例化一个etree对象,且需要将被解析的页面源码数据加载到该对象中 3 2.调用etree对象中的XPath表达式实现标签的定位和内容捕获 4 3.环境安装 pip insta ...

  6. 《Effective C++》定制new和delete

    Item49:了解new_handler的行为 当operator new抛出异常以反映出一个未获得满足的内存需求之前,它会先调用一个用户制定的错误处理函数,一个所谓的new-handler,为了制定 ...

  7. FFmpeg 解码内存泄漏汇总,sws_getContext函数无法释放问题

    使用FFmpeg库做的项目,调试项目的时候发现,连续解视频进行播放,会有明显内存增加问题.连续工作10个小时后就会 被linux 内核kill掉. 通过逐步注掉代码和网上查阅资料.最后发现内存泄漏有一 ...

  8. angular引入http服务创建服务注入

  9. Echarts自适应屏幕,无需刷新网页,可根据屏幕大小完美展现,内有详细代码注释,我可真是个小机灵~~O(∩_∩)O哈哈~

    Echarts自适应屏幕,无需刷新网页,可根据屏幕大小完美展现 效果如图 随意拖拉,无惧检验 ~ ~ ~ ~ 下面上代码 里边有详细解释 <template> <div class= ...

  10. STM32F4寄存器串口DMA汇总

    1.初始化 //RCC RCC->APB1ENR|=1<<20; //使能串口5时钟 RCC->APB1ENR|=1<<19; //使能串口4时钟 RCC-> ...