进阶
H5容器与离线包
当 H5 容器加载 URL 时,如果本地离线包中有对应的资源,就可以直接使用本地离线资源加速 H5 页面的加载。Webview 需要完成如下初始化,才会检查本地离线包中是否有对应资源。
public class X5WebClientDemo extends DefaultTMFX5WebViewClient {
private OfflineManager mOfflineManager;
public void setOfflineManager(OfflineManager manager){
this.mOfflineManager = manager;
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
android.webkit.WebResourceResponse response = null;
if(mOfflineManager != null) { // 用离线包拦截,拦截到了直接返回
response = mOfflineManager.shouldInterceptRequest(view.getContext(), url);
}
if (response != null) {
ToastUtil.showToast("拦截并使用了离线资源: " + url);
WebResourceResponse x5Response = new WebResourceResponse();
x5Response.setMimeType(response.getMimeType());
x5Response.setEncoding(response.getEncoding());
x5Response.setData(response.getData());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
x5Response.setResponseHeaders(response.getResponseHeaders());
x5Response.setStatusCodeAndReasonPhrase(response.getStatusCode(), response.getReasonPhrase());
}
return x5Response;
}
// 没有匹配的资源,走线上
return null;
}
}
H5页面打开App
H5 页面中如果要打开指定 App 或指定 App 中的页面,需要在 Webview 中拦截 URL,参考示例如下:
public class SpecialHandle {
private static final String LOGTAG = "SpecialHandle";
public static boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!TextUtils.isEmpty(url) && (url.startsWith("mailto:") || url.startsWith("tel:") || url.startsWith("smsto:"))) {
//Toast.makeText(view.getContext(), "拦截特殊intent:"+url, Toast.LENGTH_LONG).show();
return true;
}
try {
Uri uri= Uri.parse(url);
String scheme = uri.getScheme();
if ("http".equals(scheme) || "https".equals(scheme)) {
// 只放开http和https类型请求
} else {
//Log.e(LOGTAG, "donot support intent:"+url);
boolean hasApp = schemeValid(view, url);
if (hasApp) {
Intent action = new Intent(Intent.ACTION_VIEW);
action.setData(Uri.parse(url));
view.getContext().startActivity(action);
return true;
}
}
} catch (Throwable e) {
e.printStackTrace();
}
return false;
}
private static boolean schemeValid(WebView view, String url) {
PackageManager manager = view.getContext().getPackageManager();
Intent action = new Intent(Intent.ACTION_VIEW);
action.setData(Uri.parse(url));
List list = manager.queryIntentActivities(action, PackageManager.GET_RESOLVED_FILTER);
return list != null && list.size() > 0;
}
}
Webview 拦截 URL
public class X5WebClientDemo extends DefaultTMFX5WebViewClient{
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(SpecialHandle.shouldOverrideUrlLoading(view,url)){
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
内置JSAPI
JSAPI 是指 H5 层调用 Native 层代码的能力,H5 容器内置了一些 JSAPI 能力,H5 页面监听到容器初始化完成的事件后,就可以通过 TMFJSBridge.invoke 方法调用这些 JSAPI,H5 示例代码如下:
TMFJSBridge.invoke(apiName, {
param0 : param0, // any,参数 0
param1 : param1, // any,参数 1
// ...
paramN : paramN, // any,参数 n
}, function (res) {
console.log({
ret : res.ret, // integer,接入层错误码,有效值:0 表示成功,1 表示接入层失败,2 表示业务层失败,-1 表示取消(部分接口有取消操作)
errMsg : res.errMsg, // string,接入层错误详细信息
});
});
注意:这部分代码属于 H5 代码,内置 JSAPI 能力请参见JSAPI H5文档。
自定义JSAPI
H5 容器支持用户自定义 JSAPI。
- 创建自定义 JSAPI
public class JSApiCloseSysKeyboard extends JsApi {
@Override
public String method() {
return "JSApiCloseSysKeyboard";
}
@Override
public void handle(BaseTMFWeb baseTMFWeb, JsCallParam jsCallParam) {
//实现业务自己的逻辑以及是否返回数据
KeyboardUtil.closeSysKeyboard(baseTMFWeb.getContext(), baseTMFWeb.getWebViewHolder());
jsCallParam.mCallback.callback(baseTMFWeb, null);
}
}
- 自定义 JSAPI 需要继承 JSAPI。
- 实现 method,返回 JSAPI 的名字,在 H5 调用时进行使用;如果采用一下方式,需要设置自定义 JSAPI 不被混淆。
@Override
public String method() {
return JSApiCloseSysKeyboard.class.getSimpleName();
}
- 实现 handle,H5 通过 TMFJSBridge.invoke 调用时,会调用到自定义 JSAPI 的 handle 方法。
- 添加 JSAPI
mWebContainer.addJsApi(new JSApiCloseSysKeyboard());
Native调用H5
在 JS 中添加事件监听
function tmf_sample_onNavigationItemClick() {
document.addEventListener('onNavigationItemClick', function (e) {
alert(e.tmf);
}, false);
}
native 中调用触发 JS 中的 onNavigationItemClick 方法
JsonObject refreshObject = new JsonObject();
refreshObject.addProperty("index", 0);
refreshObject.addProperty("tips", "点击了关闭网页");
mWebContainer.nativeCallJs("onNavigationItemClick", refreshObject);
H5加载本地图片
Native 与 H5 页面传递大量数据时效率低下;针对于 Native 与 H5 之间图片传输的场景给出如下解决方案:
- 场景一:H5 需要通过相机或从本地图库中获取图片,然后上传到服务端。
解决方案:
- 设置本地域名
//域名不能设置为"127.0.0.1"或"localhost"
//设置的域名最后面不需要加"/"
TMFWebConfig.setLocalDomian("http://www.test.local");
- 通过自定义 JSAPI 调起相机或本地图库,用户在 Native 端拍照或选择照片后获取本地图片路径,然后将本地域名+本地图片路径转换为本地 URL,将本地 URL 回调返回至 H5 加载。
//本地url=本地域名+本地图片路径
http://www.test.local/storage/emulated/0/DCIM/Camera/IMG_20200923_155920.jpg
- 场景二:H5 调用 native,native 通过网络请求获取了图片数据, 图片数据需要传递到 H5 页面展示。
解决方案:
- native 通过网络请求获取了图片数据后,将其存储在本地,并获取本地图片路径。
- 参考场景一。
- 在适当时机,需要清除保存在本地的图片。