Weex 开发:页面跳转以及 Android 端多应用选择窗口的处理

用户头像
brave heart
关注
发布于: 2020 年 05 月 29 日
Weex开发:页面跳转以及Android端多应用选择窗口的处理

前言

在使用Weex开发项目的过程中,官网上面实现页面跳转的方式在iOS端是没问题的,但是在WebAndroid两端上面就会有问题,本文主要是将探索的过程记录一下。

页面跳转

Weex的页面跳转,主要是通过使用 navigator 来实现,但是三端使用的过程中,因为url的规则其实并不统一,所以需要根据不同的平台,做不同的跳转处理。在浏览器里,我们可以通过前进或者回退按钮来切换页面,Android/iOS的 navigator 模块就是用来实现类似的效果,除了前进、回退功能,该模块还允许我们指定在切换页面的时候是否应用动画效果,使用方式如下代码所示:

<script>
var navigator = weex.requireModule('navigator')
var modal = weex.requireModule('modal')
export default {
methods: {
//跳转,进入
jump (event) {
console.log('will jump')
navigator.push({
url: 'http://dotwe.org/raw/dist/519962541fcf6acd911986357ad9c2ed.js',
animated: "true"
}, event => {
modal.toast({ message: '页面跳转callback: ' + event })
})
},
// 返回,退出
back(event) {
navigator.pop({ animated: 'true' }, event => {
modal.toast({ message: '页面返回 callback: ' + event })
})
}
}
};
</script>

Web端

Web端其实最好处理,只需要将路径设置为目标vue文件的文件名,再加上.html即可,比如从登录页面Login.vue跳转到注册页面Register.vue,代码可以这样写,如下所示:

register(event) {
navigator.push(
{
url: 'Register.html',
animated: 'true'
},
event => {
modal.toast({
message: 'callback:' + event
})
}
)
}

Android端

Android端的跳转方案,官方文档并没有详细说明,对于新人来说,根本就无从下手。首先,我们查看Android端的源码,可以发现,SplashActivity是用于实现闪屏页的,在AndroidManifest.xml文件中,有如下设置:

<activity
android:name="com.weex.app.SplashActivity"
...
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
...
</activity>

在闪屏页旋转动画结束后,跳转到了WXPageActivity,代码如下所示:

Intent intent = new Intent(SplashActivity.this, WXPageActivity.class);
Uri data = getIntent().getData();
if (data != null) {
intent.setData(data);
}
intent.putExtra("from", "splash");
startActivity(intent);
finish();

很明显,WXPageActivity就是代理所有Weex页面的渲染的Activity。 

WXPageActivity中,我们运行Weex的官方Demo,可以发现Weex项目在闪屏页结束后,跳转到的页面,也就是如下图所示:

 

这个页面的url其实就是file://assets/index.js,源码如下所示:

if (mUri == null) {
mUri = Uri.parse(AppConfig.getLaunchUrl());
}

由此可以得出,Android的跳转uri是以file开头的路径,而且打包的js文件是存放在Android端代码的assets文件夹下。 

这个时候,我们知道了Android端的跳转uri制定规则,是不是就可以解决并完成Android端的页面跳转功能了?其实不是。 

Weex上使用 navigator 时,Android端需要做的事情是注册IntentFilter,具体情况可以查看sdk源码:com.taobao.weex.appfram.navigator.WXNavigatorModule类。

private final static String WEEX = "com.taobao.android.intent.category.WEEX";
@JSMethod(uiThread = true)
public void push(String param, JSCallback callback) {
...
Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
intent.addCategory(WEEX);
intent.putExtra(INSTANCE_ID, mWXSDKInstance.getInstanceId());
mWXSDKInstance.getContext().startActivity(intent);
...
}

因此,我们对能够加载Weex-js页面的Activity需要在AndroidManifest.xml中进行清单文件的注册,使之能够响应隐式意图,所以我们需要在WXPageActivity中加入如下代码:

<activity
android:name="com.weex.app.WXPageActivity"
...
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="com.taobao.android.intent.action.WEEX" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.moon.android.intent.category.WEEX" />
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="file" />
<data android:scheme="wxpage" />
</intent-filter>
...
</activity>

接着,我们需要在WXPageActivity中看到如下代码:

Intent intent = getIntent();
Uri uri = intent.getData();
String from = intent.getStringExtra("from");
mFromSplash = "splash".equals(from);

可知,uri就是所传入的js文件路径地址,也就是说,我们要新增判断,只要将mUri=uri即可实现这个页面在Activity里的渲染,为了识别传入的js文件路径地址,并且能够处理启动页index.vue的逻辑,将WXPageActivity中的代码:

if (mUri == null) {
mUri = Uri.parse(AppConfig.getLaunchUrl());
}

更改为:

if (mUri == null) {
if (uri.toString().startsWith("file:")) {
mUri = uri;
} else {
mUri = Uri.parse(AppConfig.getLaunchUrl());
}
}

至此,使用Weex时实现了Android端页面跳转的功能,比如从登录页面Login.vue跳转到注册页面Register.vue,代码可以这样写,如下所示:

register(event) {
navigator.push(
{
url: 'file://assets/Register.js',
animated: 'true'
},
event => {
modal.toast({
message: 'callback:' + event
})
}
)
}

Android 7.0 以上,使用以上方案Weex页面无法成功跳转的问题

因为从Android 7.0开始,一个应用提供自身文件给其他应用使用时,如果给出一个file://格式的URI的话,应用会抛出FileUriExposedException异常,这是由于Google认为目标App可能不具有文件权限,会造成潜在的安全问题,所以让这一行为快速失败。解决方案是,只需在Android端代码WXApplication文件的onCreate()方法中加上如下代码即可:

//Android 5.0 6.0不会出现问题,但是
//Android 7.0以上,使用file:///会失败,通过这个判断去掉限制。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
}

Android端多应用选择窗口的处理

  1. 我们知道,navigator 的跳转是一个AndroidstartActivity的过程,所打开的Activity就是写有拦截器的Activity

  2. 所谓的拦截器,其实就是将一段标签代码<intent-filter><intent-filter/>放到AndroidManifest.xml 的 <activity> 标签下。而拦截器是跨应用的,当我们手机中安装了多个利用Weex开发的手机应用的时候,总是会尴尬的弹出一个应用选择框,这是因为当一个手机中有多个带有相同拦截器的Activity的时候,Android系统就会让我们选择进入到哪个Activity中。一旦用户不小心点击到别人的应用甚至设置了默认的时候,就会错乱了数据配置。 

  3. 如果Intent中的存在category,那么这些category都必须和Activity过滤规则中的category相同,才能和这个Activity匹配。Intent中的category数量可能少于Activity中配置的category数量,但是Intent中的这category必须和Activity中配置的category相同才能匹配。通俗的讲就是,比如Intent中有3个categoryactivity的过滤规则中有5个category,那intent中的3个category需要是activity的过滤规则中有5个category中的3个,若有任意一个未出现在这5个里面,匹配就会失败。 

  4. 我们在nativie触发了jsClick事件之后,由js通过主动WXModuleAndroid通信,传递相应的信息如链接地址、参数信息等等,Android收到这些信息后通过Activity跳转加载对应的地址,也就是说由js进行的跳转工作全权委托给Native进行处理,可以通过隐式意图、显示意图跳转皆可。 

  5. 由上可知,Weex官方sdk里默认的Intent设置如下所示:

private final static String WEEX = "com.taobao.android.intent.category.WEEX";
@JSMethod(uiThread = true)
public void push(String param, JSCallback callback) {
...
Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
intent.addCategory(WEEX);
intent.putExtra(INSTANCE_ID, mWXSDKInstance.getInstanceId());
mWXSDKInstance.getContext().startActivity(intent);
...
}

只是设置了Category为:com.taobao.android.intent.category.WEEX,这样,当我们在AndroidManifest.xml文件中的WXPageActivity添加:

<category android:name="com.taobao.android.intent.category.WEEX" />

时,是可以完成页面跳转功能的,只是这是官方默认的过滤规则,也就是意味着,如果手机安装多个Weex开发的应用程序,在页面跳转的时候,就会出现多个Weex应用响应隐式意图,然后Android系统就会出现多应用选择窗口。所以这里,将Category更改为:com.moon.android.intent.category.WEEX,使之唯一,也就是,只能响应本应用程序中的WXPageActivity

  1. 解决Android端多应用选择窗口的问题,步骤如下所示:

1.新建TestModule类,代码如下所示:

public class TestModule extends WXModule {
@JSMethod(uiThread = false)
public void push(String param) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory("com.moon.android.intent.category.WEEX");
Activity activity = (Activity) mWXSDKInstance.getContext();
intent.setData(Uri.parse(param));
activity.startActivity(intent);
}
}

2.在WXApplication中注册TestModule,代码如下所示:

WXSDKEngine.registerModule("TestModule", TestModule.class);

3.在vue文件中使用,代码如下所示:

<script>
var navigatorTest = weex.requireModule('TestModule')
export default {
methods: {
register(event) {
navigatorTest.push('file://assets/Register.js')
}
}
};
</script>

写在最后

接触Weex快两年了,参与开发了公司里的两个项目,总的来说,跨平台相比原生开发,在效率方面提升确实很明显,很多样式一两行代码就可以了,但如果是使用Android原生来开发,光写XML都很费时间了,当然Weex也有不足之处,就是在与各端交互的过程中,需要兼容,所以很容易出现各种问题,比如本文提到的页面跳转,就是一个很棘手的问题,至少对我来说是如此,因此,也是想要将一些心得记录下来,也算是接触Weex以来,给自己留一点念想,后续不打算投入精力到Weex方面,而是Flutter,从18年知道Flutter,就很看好,现在发展迅速,越发壮大了。



发布于: 2020 年 05 月 29 日 阅读数: 655
用户头像

brave heart

关注

唯一不变的是变化本身。 2018.04.17 加入

🗡 她只唱只想这首止战之殇。

评论 (4 条评论)

发布
用户头像
这是另外一个解决方案,更兼容一些,https://juejin.im/post/6859608835267493902/ ,供参考
2020 年 08 月 11 日 14:28
回复
哈哈哈,好久没捣鼓这个了,有时间研究下。
2020 年 10 月 26 日 19:15
回复
用户头像
感谢分享,InfoQ首页推荐。
2020 年 05 月 30 日 09:41
回复
谢谢鼓励。
2020 年 05 月 30 日 09:59
回复
没有更多了
Weex开发:页面跳转以及Android端多应用选择窗口的处理