在Android中使用WindVane所遇到的坑

最近做了一个需求,需要在Android中显示H5页面,且H5与Native之间有很多的交互,所以采用了WindVane

1.如何使用WindVane JSBridge

简介:

WindVane JSBridge 提供了 JS 与 Native 之间的通信链路,基于这个通信基础,Native 可以暴露出一些服务 API 提供给 JS 调用,并通过方法返回值或事件将数据返回给 JS。

JS 端会使用 ClassName.HandlerName 来调用 JSBridge,WindVane 会根据 ClassName 与 HandlerName,来找到对应的 Native 方法。

注册JSBridge:

JSBridge 需要注册给 WindVane,才能够让 JS 正确的调用到对应的 Native 方法

Android只提供了静态注册方法:

public class MyJSBridge extends WVApiPlugin {


    @Override
    public boolean execute(String action, String params, WVCallBackContext wvCallBackContext) {
        if ("showToast".equals(action)) {
            showToast(params, wvCallBackContext);
            return true;
        }
        return false;
    }

    private void showToast(String params, WVCallBackContext wvCallBackContext) {
        if (params != null && params.length() > 0) {
            try {
                JSONObject jsonObject = new JSONObject(params);
                String content = jsonObject.optString("content");
                Toast.makeText(mContext, content, Toast.LENGTH_SHORT).show();
                wvCallBackContext.success();
            } catch (JSONException e) {
                e.printStackTrace();
                wvCallBackContext.error();
            }
        } else {
            wvCallBackContext.error();
        }
    }
}

注册具体的类:

WVPluginManager.registerPlugin("MyJSBridge", MyJSBridge.class);

注意:第一参数为类名,必须是”MyJSBridge”具体字符串的类名,不能是像”MyJSBridge.class.getSimpleName()”这样动态获取的类名,因为在Release包中,类会进行混淆,获取到的类名就不是”MyJSBridge”了。这样容易在Debug包中调试正确(因为没有进行类混淆),但在Release就发生错误。

在js代码中调用:

var params = {
    'content': 'Something to Native',
};
window.WindVane.call('MyJSBridge', 'showToast', params, function(e) {
    alert('success' + JSON.stringify(e));
}, function(e) {
    alert('failure' + JSON.stringify(e));
});

另外,除了上面H5通过JSBridge调用Native的方法外,WindVane还提供了标准的事件机制

其中H5 向 Native 发送事件:

var params = {
    event: 'eventName',
    param: {
        // 事件要传递的数据。
    }
};
window.WindVane.call('WVStandardEventCenter','postNotificationToNative', params, function(e) {
    alert('success');
}, function(e) {
    alert('failure: ' + JSON.stringify(e));
});

在Android中需要实现WVEventListener接口:

@import android.taobao.windvane.service;
@import android.taobao.windvane.standardmodal
// 首先,需要自己实现监听用的 Listener。
public class JsEventListener implements WVEventListener {
    @Override
    public WVEventResult onEvent(int id, WVEventContext ctx, Object... obj) {
        // 所有事件均派发到这里,WVEventId.H5TONATIVE_EVENT 表示 H5 发送过来的事件。
        if (id == WVEventId.H5TONATIVE_EVENT) {
            if (obj[0] instanceof String) {
                String params = (String)obj[0];
                // 这里的 params 是包含 event 和 param 的 JSON String,需要自行反序列化。
            }
        }
        return null;
    }
}
// 然后,注册监听器。
WVEventService.getInstance().addEventListener(new JsEventListener());

这上面的两种方法,H5都可以和Native进行交互。第一种方法,主要用于H5获取一些Native的一些信息,比如设备信息,用户信息等。第二种方法,主要H5传递一些数据通知Native进行UI改变。

2.关于访问Https的链接

当访问Https链接的时候,由于证书验证问题,通常会发生SSL验证错误,导致页面加载失败。在WebViewClient类中有一个方法:

    /**
     * Notify the host application that an SSL error occurred while loading a
     * resource. The host application must call either handler.cancel() or
     * handler.proceed(). Note that the decision may be retained for use in
     * response to future SSL errors. The default behavior is to cancel the
     * load.
     *
     * @param view The WebView that is initiating the callback.
     * @param handler An SslErrorHandler object that will handle the user's
     *            response.
     * @param error The SSL error object.
     */
    public void onReceivedSslError(WebView view, SslErrorHandler handler,
            SslError error) {
        handler.cancel();
    }

上面说,当发生SSL验证错误的时候,默认会取消掉请求。这就导致WebView加载了H5页面但是H5无法显示。所以,如果想不管SSL错误,让WebView继续加载H5页面,则需要覆盖WebViewClient类中的onReceivedSslError方法:

   @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                // 不要使用super,否则有些手机访问不了,因为包含了一条 handler.cancel()
                // 接受所有网站的证书,忽略SSL错误,执行访问网页
                handler.proceed();
            }

3.关于混淆导致找不到类和类方法

在发布Release包的时候,都会进行类和类方法的混淆,那么使用第一种静态注册方法时,H5通知Native就会发生错误。有一种简单的方式就是实现Serializable接口,反序列化。

public class MyJSBridge extends WVApiPlugin implements Serializable

但是这种方法的前提是在proguard-rules.pro文件中配制了实现Serializable接口的类和类的方法不被混淆。配置的规则如下:

-keep class * implements java.io.Serializable

-keepclassmembers class * implements java.io.Serializable {
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    !private <fields>;
    !private <methods>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

除了这种方法外,同理,还可以在项目的proguard-rules.pro文件中配置不混淆的类和类方法。关于配置的一些规则和例子,请查看ProGuard manual

但这样不混淆之后,容易被反编译查看到源代码,所以编写代码时尽量注意代码的安全性。

4.WebView内存泄漏

在使用WindVane中的WVWebView时,文档中有一句提示:

你也可以直接使用WVWebView,不过,直接使用WVWebView的时候需要注意webview在对应生命周期中得处理,特别是销毁的时候。一定要先将该webview从夫view中移除,然后再调用的webview的destroy操作。

其实,不管是使用WVWebView还是单独的使用WebView都要注意内存泄漏,因此在Activity或者Fragment中一定要销毁WebView。

   @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWebView != null) {
            // 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再
            // destory()
            ViewParent parent = mWebView.getParent();
            if (parent != null) {
                ((ViewGroup) parent).removeView(mWebView);
            }
            // 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
            mWebView.getSettings().setJavaScriptEnabled(false);
            mWebView.clearHistory();
            mWebView.clearCache(true);
            mWebView.onPause();
            mWebView.removeAllViews();
            mWebView.destroyDrawingCache();
            mWebView.destroy();
        }
    }

5.使用小技巧

隐藏滚动条:

 webView.setVerticalScrollBarEnabled(false);
 webView.setHorizontalScrollBarEnabled(false);

禁止滚动:

webView.setFocusable(false);
webView.setFocusableInTouchMode(false);

加速渲染:

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        webView.setWebViewClient(new WebViewClient() {

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                // 不要使用super,否则有些手机访问不了,因为包含了一条 handler.cancel()
                // super.onReceivedSslError(view, handler, error);
                // 接受所有网站的证书,忽略SSL错误,执行访问网页
                handler.proceed();
            }
        });

访问JavaScript接口:

webView.getSettings().setJavaScriptEnabled(true);

白屏,需支持DOM storage:

webView.getSettings().setDomStorageEnabled(true);

参考:
1. Android 5.1 WebView内存泄漏分析
2. Disable scrolling in webview?

猜你喜欢

转载自blog.csdn.net/wangjiang_qianmo/article/details/72846597