Android java 设计封装增强型WebView组件

发布于:2025-03-22 ⋅ 阅读:(16) ⋅ 点赞:(0)

Android java 设计封装增强型WebView组件(兼容Android 4.4+)
* 特性:
* 1. 全生命周期管理
* 2. 智能硬件加速
* 3. 链式配置API
* 4. 安全下载管理
* 5. 全屏视频支持

public class EnhancedWebView extends WebView {
    private CustomWebChromeClient mChromeClient;
    private DownloadManager mDownloadManager;

    // 链式配置构建器
    public static class Builder {
        private final Context mContext;
        private boolean mJavaScriptEnabled = true;
        private int mCacheMode = WebSettings.LOAD_DEFAULT;

        public Builder(Context context) {
            mContext = context;
        }

        public Builder setJavaScriptEnabled(boolean enabled) {
            mJavaScriptEnabled = enabled;
            return this;
        }

        public Builder setCacheMode(@NonNull int cacheMode) {
            mCacheMode = cacheMode;
            return this;
        }

        public EnhancedWebView build() {
            EnhancedWebView webView = new EnhancedWebView(mContext);
            WebSettings settings = webView.getSettings();
            settings.setJavaScriptEnabled(mJavaScriptEnabled);
            settings.setCacheMode(mCacheMode);
            // ... 其他配置项
            return webView;
        }
    }

    public EnhancedWebView(Context context) {
        super(context);
        initWebView(context);
    }

    private void initWebView(Context context) {
        // 智能硬件加速(API 14+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setLayerType(View.LAYER_TYPE_HARDWARE, null);
        }
        
        // 安全下载管理
        mDownloadManager = new DownloadManager(context);
        setDownloadListener(this::handleDownload);
    }

    // 全生命周期管理
    public void onResume() {
        resumeTimers();
        if (mChromeClient != null) mChromeClient.onResume();
    }

    public void onPause() {
        pauseTimers();
        if (mChromeClient != null) mChromeClient.onPause();
    }

    public void onDestroy() {
        loadUrl("about:blank");
        stopLoading();
        setWebChromeClient(null);
        setWebViewClient(null);
        destroy();
    }

    // 全屏视频支持
    @Override
    public void setWebChromeClient(WebChromeClient client) {
        mChromeClient = new CustomWebChromeClient(getContext());
        super.setWebChromeClient(mChromeClient);
    }

    // 安全下载处理
    private void handleDownload(String url, String userAgent, 
                              String contentDisposition, 
                              String mimeType, long contentLength) {
        if (isUnsafeFileType(mimeType)) {
            showSecurityWarning();
            return;
        }
        mDownloadManager.startDownload(url, mimeType);
    }

    // 自定义WebChromeClient(处理全屏视频)
    private class CustomWebChromeClient extends WebChromeClient {
        private View mCustomView;
        private CustomViewCallback mCustomViewCallback;

        public CustomWebChromeClient(Context context) {
            // 初始化全屏处理相关参数
        }

        @Override
        public void onShowCustomView(View view, CustomViewCallback callback) {
            // 实现全屏视频逻辑
            if (mCustomView != null) {
                onHideCustomView();
                return;
            }
            mCustomView = view;
            mCustomViewCallback = callback;
            // 将自定义视图添加到DecorView
        }

        @Override
        public void onHideCustomView() {
            // 退出全屏处理
            if (mCustomView == null) return;
            // 移除自定义视图
            mCustomViewCallback.onCustomViewHidden();
            mCustomView = null;
        }
    }
}

关键实现说明:

  • 生命周期管理
  • 提供onResume()/onPause()/onDestroy()方法与Activity/Fragment生命周期绑定
  • 在销毁时主动清理资源,防止内存泄漏
  • 智能硬件加速
  • 根据API版本自动启用硬件加速层(LAYER_TYPE_HARDWARE)
  • 兼容Android 4.0+设备
  • 链式配置API
  • 采用Builder模式实现流畅配置接口
  • 支持常见配置项:缓存策略、JavaScript开关等

4. 安全下载管理

  • 内置文件类型安全检查
  • 集成DownloadManager实现安全下载
  • 支持自定义安全策略扩展
  • 全屏视频支持
  • 通过自定义WebChromeClient处理全屏回调
  • 维护全屏视图状态机
  • 兼容系统全屏回调接口

使用示例:

// 链式配置
EnhancedWebView webView = new EnhancedWebView.Builder(context)
    .setJavaScriptEnabled(true)
    .setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK)
    .build();

// 生命周期绑定
@Override
protected void onResume() {
    super.onResume();
    webView.onResume();
}

@Override
protected void onDestroy() {
    webView.onDestroy();
    super.onDestroy();
}

重新整理兼容低版本

/**
 * EnhancedWebView.java
 *
 * 增强型 WebView 组件(兼容 Android 4.4+ 与低版本 Java)
 *
 * 主要特性:
 * 1. 多进程数据隔离(API 17+)
 * 2. 硬件加速与网络优化设置
 * 3. 安全策略:URL 白名单和禁止协议验证、SSL 错误处理
 * 4. 全屏视频支持(通过自定义 WebChromeClient 与 FullScreenHandler)
 * 5. 内存优化:分步释放资源、反射销毁处理低版本兼容
 * 6. Builder 链式配置 API
 * 7. 提供 JavaScript 桥接接口(注意安全性)
 *
 * 注意:
 * (1)所有 WebView 操作务必在主线程中执行
 * (2)请在 Activity 的生命周期 onResume/onPause/onDestroy 中调用相应的 onResumeWebView()/onPauseWebView()/destroy() 方法
 * (3)多进程模式需要在 AndroidManifest.xml 中做相应配置,例如:
 *
 * <activity 
 *   android:name=".WebActivity"
 *   android:process=":webview" />
 */
 
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.DownloadListener;
import android.webkit.SslErrorHandler;
import android.webkit.SslError;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class EnhancedWebView extends WebView {
    private static final String PROCESS_SUFFIX = ":webview";
    private FullScreenHandler mFullScreenHandler;
    private SecurityConfig mSecurityConfig = new SecurityConfig();
    private boolean mIsMultiProcess = false;

    // ========== 构造函数 ==========
    public EnhancedWebView(Context context) {
        super(context);
        initWebView(context);
    }

    public EnhancedWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initWebView(context);
    }

    public EnhancedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initWebView(context);
    }

    // ========== 初始化各模块 ==========
    /**
     * 初始化 WebView 参数、功能配置、下载监听等
     */
    private void initWebView(Context context) {
        initMultiProcess(context);
        initHardwareAcceleration();
        initNetworkSettings();
        initSecuritySettings();
        initFullScreenSupport(context);
        
        // 设置下载监听器(低版本 Java,不使用 lambda)
        setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadStart(String url, String userAgent,
                                        String contentDisposition,
                                        String mimeType, long contentLength) {
                handleDownload(url, userAgent, contentDisposition, mimeType, contentLength);
            }
        });
    }

    /**
     * 初始化多进程支持(仅 API 17 及以上有效)
     */
    private void initMultiProcess(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            String processName = getProcessName(context);
            if (!processName.endsWith(PROCESS_SUFFIX)) {
                mIsMultiProcess = true;
                WebView.setDataDirectorySuffix(processName); // 设置独立数据目录
            }
        }
    }

    /**
     * 根据系统版本设置硬件或软件加速
     */
    private void initHardwareAcceleration() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setLayerType(View.LAYER_TYPE_HARDWARE, null);
        } else {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
    }

    /**
     * 初始化 WebSettings 相关配置,包括 JS、DOM 存储、混合内容模式等。
     */
    private void initNetworkSettings() {
        WebSettings settings = getSettings();
        // 启用 JavaScript、DOM 及数据库支持
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setDatabaseEnabled(true);

        // 针对 Lollipop 及以上版本,允许混合内容(兼容 HTTP 与 HTTPS 混合)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
        }
        
        // 视图和布局优化
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
        } else {
            settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
        }

        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
    }

    /**
     * 初始化安全设置,可根据需要扩展
     */
    private void initSecuritySettings() {
        // 例如:禁用文件访问,防止本地文件注入风险
        getSettings().setAllowFileAccess(false);
    }

    /**
     * 初始化全屏视频支持,该功能需要传入 Activity 对象
     */
    private void initFullScreenSupport(Context context) {
        if (context instanceof Activity) {
            mFullScreenHandler = new FullScreenHandler((Activity) context);
        }
    }

    // ========== 安全策略处理 ==========
    /**
     * 对 URL 进行安全验证
     * 检查是否采用禁止的协议,或是否符合配置的白名单规则
     * @param url 待验证的 URL
     * @return 如果 URL 符合安全策略则返回 true,否则返回 false
     */
    private boolean validateUrlSecurity(String url) {
        try {
            Uri uri = Uri.parse(url);
            // 检查协议是否被禁止
            String scheme = uri.getScheme();
            if (scheme != null) {
                for (int i = 0; i < mSecurityConfig.mForbiddenSchemes.size(); i++) {
                    if (scheme.equals(mSecurityConfig.mForbiddenSchemes.get(i))) {
                        return false;
                    }
                }
            }
            // 如果设置了白名单,URL 必须匹配其中至少一个规则
            if (!mSecurityConfig.mUrlWhitelist.isEmpty()) {
                for (int i = 0; i < mSecurityConfig.mUrlWhitelist.size(); i++) {
                    Pattern pattern = mSecurityConfig.mUrlWhitelist.get(i);
                    if (pattern.matcher(url).matches()) {
                        return true;
                    }
                }
                return false; // 未匹配白名单
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 重载 loadUrl 方法,加载之前进行安全策略检测
     */
    @Override
    public void loadUrl(String url) {
        if (validateUrlSecurity(url)) {
            super.loadUrl(url);
        } else {
            handleSecurityViolation(url);
        }
    }
    
    @Override
    public void loadUrl(String url, java.util.Map<String, String> additionalHttpHeaders) {
        if (validateUrlSecurity(url)) {
            super.loadUrl(url, additionalHttpHeaders);
        } else {
            handleSecurityViolation(url);
        }
    }

    /**
     * 当检测到不安全的 URL 请求时,展示安全提示对话框
     */
    private void handleSecurityViolation(String url) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("安全警告")
               .setMessage("尝试访问不安全资源:" + url)
               .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int which) {
                       dialog.dismiss();
                   }
               })
               .create().show();
    }

    // ========== 下载管理 ==========
    /**
     * 下载文件时的处理逻辑。实际项目中建议接入系统 DownloadManager,注意运行时权限。
     */
    private void handleDownload(String url, String userAgent, String contentDisposition,
                                String mimeType, long contentLength) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("下载提示")
               .setMessage("是否下载文件?\n" + url)
               .setNegativeButton("取消", new DialogInterface.OnClickListener(){
                   @Override
                   public void onClick(DialogInterface dialog, int which) {
                       dialog.dismiss();
                   }
               })
               .setPositiveButton("下载", new DialogInterface.OnClickListener(){
                   @Override
                   public void onClick(DialogInterface dialog, int which) {
                       // 此处实现下载逻辑,例如调用系统 DownloadManager
                       dialog.dismiss();
                   }
               })
               .create().show();
    }

    // ========== 全屏视频支持 ==========
    /**
     * 重写 setWebChromeClient 方法,注入自定义的 WebChromeClient
     * 用于处理全屏视频的进入与退出
     */
    @Override
    public void setWebChromeClient(WebChromeClient client) {
        // 无论外部如何设置,我们强制使用内部 CustomWebChromeClient
        super.setWebChromeClient(new CustomWebChromeClient());
    }

    /**
     * 自定义 WebChromeClient,用于全屏视频、SSL 错误处理等
     */
    private class CustomWebChromeClient extends WebChromeClient {
        @Override
        public void onShowCustomView(View view, CustomViewCallback callback) {
            if (mFullScreenHandler != null) {
                mFullScreenHandler.enterFullScreen(EnhancedWebView.this, view, callback);
            }
        }

        @Override
        public void onHideCustomView() {
            if (mFullScreenHandler != null) {
                mFullScreenHandler.exitFullScreen();
            }
        }

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            // 如果允许自签名证书,则放行;否则取消请求
            if (!mSecurityConfig.mAllowSelfSignedCerts) {
                handler.cancel();
            } else {
                // 实际开发中建议显示提示对话框,让用户决定
                handler.proceed();
            }
        }
    }

    // ========== 生命周期处理 ==========
    /**
     * 请在 Activity 的 onResume 中调用此方法,以恢复 WebView 内部定时器等
     */
    public void onResumeWebView() {
        resumeTimers();
        if (mFullScreenHandler != null) {
            mFullScreenHandler.onResume();
        }
    }

    /**
     * 请在 Activity 的 onPause 中调用此方法,暂停 WebView 的状态
     */
    public void onPauseWebView() {
        pauseTimers();
        if (mFullScreenHandler != null) {
            mFullScreenHandler.onPause();
        }
    }

    /**
     * 释放 WebView 的资源,防止内存泄漏
     */
    @Override
    public void destroy() {
        removeAllViews();
        clearHistory();
        clearCache(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            super.destroy();
        } else {
            try {
                Method method = WebView.class.getDeclaredMethod("destroy");
                method.setAccessible(true);
                method.invoke(this, (Object[]) null);
            } catch (Exception e) {
                super.destroy();
            }
        }
    }

    // ========== 工具方法 ==========
    /**
     * 获取当前进程名称
     * @param context 上下文
     * @return 当前进程名称,若获取失败返回空字符串
     */
    private static String getProcessName(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
        if (processes != null) {
            int pid = android.os.Process.myPid();
            for (int i = 0; i < processes.size(); i++) {
                ActivityManager.RunningAppProcessInfo info = processes.get(i);
                if (info.pid == pid) {
                    return info.processName;
                }
            }
        }
        return "";
    }

    // ========== Builder 链式配置 ==========
    public static class Builder {
        private Context mContext;
        private SecurityConfig mSecurityConfig = new SecurityConfig();

        public Builder(Context context) {
            mContext = context.getApplicationContext();
        }

        /**
         * 添加 URL 白名单规则(正则表达式)
         */
        public Builder addUrlWhitelist(String regex) {
            mSecurityConfig.addUrlWhitelist(regex);
            return this;
        }

        /**
         * 设置是否允许自签名证书(仅建议开发阶段测试)
         */
        public Builder setAllowSelfSignedCerts(boolean allow) {
            mSecurityConfig.mAllowSelfSignedCerts = allow;
            return this;
        }

        /**
         * 构建 EnhancedWebView 实例
         */
        public EnhancedWebView build() {
            EnhancedWebView webView = new EnhancedWebView(mContext);
            webView.mSecurityConfig = this.mSecurityConfig;
            return webView;
        }
    }

    // ========== 安全配置内部类 ==========
    /**
     * SecurityConfig 用于配置 URL 白名单、禁止协议、以及是否允许自签名证书
     */
    public static class SecurityConfig {
        private List<Pattern> mUrlWhitelist;
        private List<String> mForbiddenSchemes;
        private boolean mAllowSelfSignedCerts;

        public SecurityConfig() {
            mUrlWhitelist = new ArrayList<Pattern>();
            mForbiddenSchemes = new ArrayList<String>();
            // 默认禁止 file 与 tel 协议
            mForbiddenSchemes.add("file");
            mForbiddenSchemes.add("tel");
            mAllowSelfSignedCerts = false;
        }

        /**
         * 添加白名单规则(正则表达式字符串)
         */
        public SecurityConfig addUrlWhitelist(String regex) {
            try {
                Pattern pattern = Pattern.compile(regex);
                mUrlWhitelist.add(pattern);
            } catch (Exception e) {
                // 如果正则表达式错误,则忽略该规则
            }
            return this;
        }

        /**
         * 设置禁止协议列表
         */
        public SecurityConfig setForbiddenSchemes(List<String> schemes) {
            if (schemes != null) {
                mForbiddenSchemes = schemes;
            }
            return this;
        }
    }

    // ========== JavaScript 桥接 ==========
    /**
     * 添加 JavaScript 接口,以便页面调用
     * 注意:请确保接口安全,避免暴露敏感接口!
     * @param bridge 接口对象,需在方法上添加 @JavascriptInterface 注解
     * @param name 在 JavaScript 中使用的名称
     */
    public void addJavascriptBridge(Object bridge, String name) {
        super.addJavascriptInterface(bridge, name);
    }
}

FullScreenHandler.java

/**
 * FullScreenHandler.java
 *
 * 该类用于处理 WebView 全屏视频播放模式,
 * 通过接管 Activity 的 DecorView 添加/移除全屏自定义视图。
 *
 * 使用说明:
 * 1. 由 EnhancedWebView 在初始化时传入 Activity 对象。
 * 2. 在 WebChromeClient 的 onShowCustomView() 和 onHideCustomView() 回调中调用本类的方法。
 * 3. 确保 Activity 的屏幕方向及系统 UI 状态在全屏与正常模式间切换。
 */
 
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;

public class FullScreenHandler {
    private Activity mActivity;
    private View mCustomView;
    private WebChromeClient.CustomViewCallback mCustomViewCallback;
    private int mOriginalSystemUiVisibility;
    private int mOriginalOrientation;

    /**
     * 构造函数,传入当前 Activity 对象
     * @param activity 当前 Activity
     */
    public FullScreenHandler(Activity activity) {
        mActivity = activity;
    }

    /**
     * 进入全屏模式
     * @param webView 当前使用的 WebView 对象(用于退出时恢复显示)
     * @param customView 自定义的全屏视图(例如视频播放界面)
     * @param callback 全屏视图回调接口,当退出全屏时调用
     */
    public void enterFullScreen(View webView, View customView, WebChromeClient.CustomViewCallback callback) {
        // 保存当前系统 UI 状态与屏幕方向
        mOriginalSystemUiVisibility = mActivity.getWindow().getDecorView().getSystemUiVisibility();
        mOriginalOrientation = mActivity.getRequestedOrientation();

        // 隐藏 WebView
        webView.setVisibility(View.GONE);

        // 添加全屏视图到 Activity 的 DecorView
        ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
        decorView.addView(customView, new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

        mCustomView = customView;
        mCustomViewCallback = callback;

        // 设置全屏标志(隐藏状态栏、导航栏)
        mActivity.getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_FULLSCREEN | 
                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | 
                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    }

    /**
     * 退出全屏模式,移除全屏视图并恢复之前的 UI 状态
     */
    public void exitFullScreen() {
        if (mCustomView == null) {
            return;
        }
        // 从 DecorView 中移除全屏视图
        ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
        decorView.removeView(mCustomView);
        mCustomView = null;

        // 恢复之前的系统 UI 状态与屏幕方向
        mActivity.getWindow().getDecorView().setSystemUiVisibility(mOriginalSystemUiVisibility);
        mActivity.setRequestedOrientation(mOriginalOrientation);

        // 通知 WebView 全屏视图已被隐藏
        if (mCustomViewCallback != null) {
            mCustomViewCallback.onCustomViewHidden();
        }
    }

    /**
     * Activity onResume 时调用(扩展接口,如需恢复状态可添加代码)
     */
    public void onResume() {
        // 可扩展代码,如重置全屏 UI 状态
    }

    /**
     * Activity onPause 时调用(扩展接口,如需暂停视频播放可添加代码)
     */
    public void onPause() {
        // 可扩展代码
    }
}

使用示例

在 Activity 中使用上述组件示例代码如下:

// 在 Activity 的 onCreate 中构建 EnhancedWebView 实例
EnhancedWebView webView = new EnhancedWebView.Builder(this)
        .addUrlWhitelist("^https://(.*\\.)?example\\.com/")
        .setAllowSelfSignedCerts(false) // 生产环境建议设为 false
        .build();
        
// 将 webView 添加到布局中,例如:
setContentView(webView);
webView.loadUrl("https://www.example.com");

// 在 Activity onResume 中调用
@Override
protected void onResume() {
    super.onResume();
    webView.onResumeWebView();
}

// 在 Activity onPause 中调用
@Override
protected void onPause() {
    webView.onPauseWebView();
    super.onPause();
}

// 在 Activity onDestroy 中调用
@Override
protected void onDestroy() {
    webView.destroy();
    super.onDestroy();
}


网站公告

今日签到

点亮在社区的每一天
去签到