进阶

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);
    }
}
  1. 自定义 JSAPI 需要继承 JSAPI。
  2. 实现 method,返回 JSAPI 的名字,在 H5 调用时进行使用;如果采用一下方式,需要设置自定义 JSAPI 不被混淆。
@Override
public String method() {
    return JSApiCloseSysKeyboard.class.getSimpleName();
}
  1. 实现 handle,H5 通过 TMFJSBridge.invoke 调用时,会调用到自定义 JSAPI 的 handle 方法。

注意: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 需要通过相机或从本地图库中获取图片,然后上传到服务端。

解决方案

  1. 设置本地域名
//域名不能设置为"127.0.0.1"或"localhost"
//设置的域名最后面不需要加"/"
TMFWebConfig.setLocalDomian("http://www.test.local");
  1. 通过自定义 JSAPI 调起相机或本地图库,用户在 Native 端拍照或选择照片后获取本地图片路径,然后将本地域名+本地图片路径转换为本地 URL,将本地 URL 回调返回至 H5 加载。
//本地url=本地域名+本地图片路径
http://www.test.local/storage/emulated/0/DCIM/Camera/IMG_20200923_155920.jpg
  • 场景二:H5 调用 native,native 通过网络请求获取了图片数据, 图片数据需要传递到 H5 页面展示。

解决方案

  1. native 通过网络请求获取了图片数据后,将其存储在本地,并获取本地图片路径。
  2. 参考场景一。
  3. 在适当时机,需要清除保存在本地的图片。
Copyright © 2013-2023 Tencent Cloud. all right reserved,powered by GitbookUpdate Time 2023-08-31 14:46:07

results matching ""

    No results matching ""