专栏首页终身开发者Android使用WebView开发常见的坑

Android使用WebView开发常见的坑

Android WebView开发常见的坑

现在的App基本上都会使用Native+H5的方式来开发的,例如网易新闻详情页面,微信公号详情页面都会使用WebView开发。这样可以很容易实现图文排版的需求,而且混合开发的好处也是显而易见的。

AC在开发项目的时候也经常使用WebView这个控件,这个控件使用很方便,但却也有诸多问题。以下是AC在开发过程中踩过的坑,希望对使用这个控件的小伙伴们有用。

1、WebView无法显示html中的alert和confirm对话框

WebView要显示html中的alert和confirm对话框,需要实现WebViewChromClient接口。WebView只是一个承载体,各种内容的渲染需要使用WebviewChromClient去实现,所以set一个默认的基类WebChromeClient就行

mWebView.setWebChromeClient(new WebChromeClient());

用于弹起alert等,如果要定制alert,confirm对话框就必需重写onAlert和onConfirm方法

2、WebView中实现的JS方法无法调用

在实现WebView与JS交互的过程中,如果遇到点击后JS方法无响应,应该注意以下问题:

(1)WebView.addJavascriptInterface(new AndroidClick(), "app");这个方法的别名android是否与JS中的对象名称一致如

<div style="text-align:center;"><a onclick="window.app.onclick('www.115.com')" >内容已自动优化阅读,点击查看原文</a></div>

(2)如果是H5通过alert方法来提示对话框的信息的时候,WebView需要实现注册这个回调函数

mWebView.setWebChromeClient(new CustomWebChromeClient());

并实现以下alert回调方法,并可以实现自定义的对话框样式。

@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
    //return super.onJsAlert(view, url, message, result);
    showConfirmDialog(view.getContext(), message, result, false);
    return true;
}

类似的还有onJsConfirm方法等。

(3)如果发布的APP有进行混淆,那么AndroidClick这个JS 与 JAVA交互的类需要proguard.cfg文件忽略这个类的混淆,否则混淆后JS将执行不了。

-keepclassmembers class net.angrycode.js2java.AndroidClick{ *; }

(4)JS调用Native方法时,如果前端执行一些比较耗时的操作,前端代码就有可能会跑在线程里,这时候如果JS方法调用Native方法做一些逻辑操作,调用就会有问题,虽然不会Crash但是会报错。参考解决方法是(mJSInterface是通过addJavaInterface注入到WebView中的对象)

/**
 * 提交创建申请单数据
 */
mJSInterface.setOnPutApplyListener(json -> {
    mWebView.post(() -> {
        //确保是在主线程中访问Native相关控件
    });
});

3、快速打开和关闭WebView页面发生了控件空指针异常问题

这个问题可能有很多原因,但WebView加载过程中如果关闭了页面控件被回收而加载线程还在继续跑,那么数据返回时页面就有可能发生空指针异常。这个时候可以在WebViewClient以及WebViewChrome接口中的onPageStart以及onPageFinish,onProgressChange这几个回调方法中判断当前页面是否存在,若不存在则直接返回。

@Override
public void onPageFinished(WebView view, String url) {
    if (getActivity() == null || getActivity().isFinishing()) {
        return;
    }
    super.onPageFinished(view, url);
    //...
}

4、WebView与JS交互引起的安全问题

4.2以下系统这里推荐一个开源项目https://github.com/pedant/safe-java-js-webview-bridge

当然WebView的安全不止这个,可以自行百度或者Google脑部相关姿势。AC也会在之后的文章中专门整理这个问题。

同时此问题在官方4.2(API Level 17)以上手机已经得到修复,使用@JavascriptInterface 注解声明addJavascriptInterface注入的方法。即只有使用@JavascriptInterface的方法才会被注入到WebView中。

5、WebView长按弹出ActionMode菜单样式问题

三星手机WebView弹出的菜单样式有可能会出现此问题,解决方法可以继承WebView重写startActionMode()方法,然后修改menu的菜单样式。

@Override
public ActionMode startActionMode(ActionMode.Callback callback) {
    return super.startActionMode(new CustomCallback(getContext(), callback));
}

public static class CustomCallback implements ActionMode.Callback {
    private ActionMode.Callback callback;
    private Context context;

    public CustomCallback(Context context, ActionMode.Callback callback) {
        this.callback = callback;
        this.context = context;
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        return callback.onCreateActionMode(mode, menu);
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        int size = menu.size();
        for (int i = 0; i < size; i++) {
            MenuItem menuItem = menu.getItem(i);
            final Drawable moreMenuDrawable = menuItem.getIcon();

            if (moreMenuDrawable != null) {

                menuItem.setIcon(DrawableUtil.getThemeDrawable(context, moreMenuDrawable));

            }
        }
        return true;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        return callback.onActionItemClicked(mode, item);
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        callback.onDestroyActionMode(mode);
        context = null;
    }
}

当然如果不需要长按事件,可以注册WebView的长按事件

mWebView.setOnLongClickListener(v -> {
    return true;
});

6、硬件加速问题

一般情况下,使用WebView开发都会使用硬件加速来提高WebView的渲染速度。可以在AndroidManifest.xml文件中设置

android:hardwareAccelerated="true"

也可以在页面中使用

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);

但是简单的使用以上两种方法,开启硬件加速以及不开启硬件加速在一些手机上都会出现这样或者那样的问题,例如,如果一直开启了硬件加速,某些手机有可能会出现屏幕花屏的问题;还有WebView在不同厂商的手机中依然可能会出现Crash问题。而Crash的问题一般是报了WebView底层的错误。可以参考以下处理方式:

在onPageStart中开启硬件加速,在onPageFinish中关闭硬件加速。

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            if (getActivity() == null || getActivity().isFinishing()) {
                return;
            }
            view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
            super.onPageStarted(view, url, favicon);
        }
        @Override
        public void onPageFinished(WebView view, String url) {
            if (getActivity() == null || getActivity().isFinishing()) {
                return;
            }
            view.setLayerType(View.LAYER_TYPE_NONE, null);
            super.onPageFinished(view, url);
        }

7、使用独立进程跑WebView

有一定使用WebView经验的老司机可能都把项目中的WebView模块抽取出来,并跑在独立的进程中去。例如在manifest文件中使用属性process指定独立的进程。

<!-- Web 页面 -->
<activity
    android:name=".UI.CommonUI.Activity.WebBrowserActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:hardwareAccelerated="true"
    android:label=""
    android:process=":web"
    android:screenOrientation="portrait" />

这样做的是因为WebView在以前的版本的底层实现中会发生内存泄漏,导致页面关闭但是依然没有释放内存,而在独立进程中的WebView模块就可以很好解决此问题,在关闭WebView的时候就关闭进程,这样就可以释放相关的内存了。

但是使用多进程架构,进程间数据共享就是一个问题了。例如进程A设置了cookie,同样我也要在进程B共享这个cookie。目前AC认为可行的解决方案是使用ContentProvider来共享数据。此问题AC没有写相应的Demo,希望有老司机可以带路。

8、WebView生命周期回调

WebView也有生命周期回调方法,这些方法需要在Activity或Fragment相应的生命方法中回调。主要是onResume(),onPasuse()和onDestory()(或者onDestoryView())这几个方法的回调实现。

@Override
public void onResume() {
    if (mWebView != null) {
        mWebView.resumeTimers();
        mWebView.onResume();
    }
    super.onResume();
}

@Override
public void onPause() {
    if (mWebView != null) {
        mWebView.pauseTimers();
        mWebView.onPause();
    }
    super.onPause();
}
@Override
public void onDestroyView() {
    if (mWebView != null) {
        mWebView.stopLoading();
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);
        mWebView.removeAllViews();
        mWebView.setWebChromeClient(null);
        mWebView.setWebViewClient(null);
        unregisterForContextMenu(mWebView);
        mWebView.destroy();
    }
    super.onDestroyView();
}

这几个方法的回调实现有利无弊,可以很好地避免翻车。例如WebView中播放声音在页面关闭之后还声音的问题,WebView页面跳转其他页面后返回显示空白不刷新的问题等等。

以上便是AngryCode在使用WebView开发过程中踩过的坑,相应解决方案纯粹是经验参考,因为使用环境以及能力的局限,如果文章出现错误,欢迎老司机留言指出。

本文分享自微信公众号 - 终身开发者(AngryCode),作者:AngryCode

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-09-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 老司机带路:15个Android撸代码常见的坑

    例如有一个 Activity 页面跳转中,经常会使用 Intent 传递数据,但是稍不留神就会把一个很大的对象(列表, bitmap 等)传递过去,这时候就会出...

    阳仔
  • 在Android中使用枚举注解而不是枚举

    很多开发规范都是不建议在Android中使用枚举的,在Android系统中使用枚举的开销是使用常量的2倍。一般地,在一个文件中定义常量

    阳仔
  • Android 组件化/模块化之路——在展示层搭建MVP结构

    Model–View–Presenter (MVP) 源于 Model–View–Controller (MVC) 的结构设计模式,它是用于展示层(Presen...

    阳仔
  • 快速梳理23种常用的设计模式(上篇)

    本文旨在快速梳理常用的设计模式,了解每个模式主要针对的是哪些情况以及其基础特征,每个模式前都有列举出一个或多个可以深入阅读的参考网页,以供读者详细了解其实现。

    Rude3Knife的公众号
  • 批处理作业调度-回溯法

    问题描述:   给定n个作业,集合J=(J1,J2,J3)。每一个作业Ji都有两项任务分别在2台机器上完成。每个作业必须先有机器1处理,然后再由机器2处理。作业...

    用户1154259
  • C#图片处理示例(裁剪,缩放,清晰度,水印)

    原创文章,转载必需注明出处:http://www.cnblogs.com/wu-jian/

    跟着阿笨一起玩NET
  • ImageHelper.cs-支持高清缩略图

    ken.io
  • 全站缓存时代

    原则:动静分离,分级缓存,主动失效。 Web 开发中,接口会被分为以下几类: 纯静态页面。打死我都不会修改的页面。很长一段时间内,基本上不会修改。比如:关于我们...

    逸鹏
  • 腾讯云年中大促,低至三折优惠

    神无月
  • 腾讯云年中大促,低至三折优惠

    站长朋友们注意啦,最近腾讯云活动不断,新出活动腾讯云年中大促,部分热销商品限时5折,更有年付三折优惠,现在购买服务器再合适不过了。已有腾讯云服务器的站长朋友也不...

    神无月

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动