美文网首页
从封装Fcwebview Sdk看第三方库的集成

从封装Fcwebview Sdk看第三方库的集成

作者: 浩林Leon | 来源:发表于2019-06-10 21:34 被阅读0次

    1.引子

    因为系统原生的webview存在不同手机性能多差异化,android版本不同webview运行H5也有体验也存在差别。经过对比分析,目前tbs x5 内核 和corsswalk对webview的性能有优化。最终选择x5 内核进行替换系统webview,从而封装成自有webview库Fcwebview。

    2.需要达到的目标

    • 封装合理
    • 简易使用
    • 易于扩展
    • 方便切换

    3.目标拆解和解决思路

    3.1 怎么算封装合理?

    符合自己的需求才是合理的。

    1. 迪米特法则(高内聚,低耦合)
    2. 单一职责原则(Fcwebview与具体的x5内核模块隔离)
    3. 把常用webview操作的方法提取,并且封装
    4. 对app而言只需要操作Fcwebview,不直接操作x5内核相关的具体方法
    3.2 怎么算简易使用?
    1. 使用Fcwebview 和使用正常的标准webview 的方法,以及属性保持一致,不增加额外的学习成本
    2. 在常用的webSetting中通常需要进行一系列的设置操作,结合我们自身的app,基本上设置保持一致。可以简化操作,甚至集成到Fcwebview中
    3. webview js的操作进行简化
    4. CookieManager 同步操作进行简化
    3.3 怎么算易于扩展?
    1. 依赖倒转原则(Fcwebview的方法都面向接口,而不是面向具体x5内核的实现。)
    2. 采用适配器模式对于标准webview的接口可以有多套实现方式,而不能固定某种具体方案。
    3.4 怎么算方便切换?
    1. 采用代理模式可以切换不同webview内核去具体实现。
    2. 切换内核的过程让用户之前调用Fcwebview 的方法或者引入的包尽可能少的改动。

    4. FcWebview的架构图

    由上面的思路,先进行FcWebview的整体模块划分。

    4.1 划分成3个模块:
    image.png
    1. 抽象一个webview 相关的通用接口层,也是webviewFramework模块。包括webview,webSetting,CookieManager,Webviewchrome,webviewClient 等的公开接口。保证所有的内核都能够实现,对于具体应用层都是相同的调用。

    2. x5webviewcore模块。实现了抽象webviewFramework接口,并且适配成x5内核的具体实现。

    3. Fcwebview模块。对外暴露的通用自定义webview,内部默认设置x5webviewcore进行代理,可以动态支持替换其他的webview内核。Fcwebview进行对一些常用的方法进行进一步封装,对外提供一个简易的webview调用。对app而言只需要和Fcwebview进行通信,包括布局文件,WebSetting等等。

    4. 白色区域表示后续可以进行扩展其他webview 内核。

    4.2 架构UML图
    image.png

    5.各个模块的代码分析

    5.1 webviewFramework
    image.png

    这个模块包括了webview的通用接口,最主要的目的就是抽象出一套具有自己特色的webview的接口/方法,使得这些与具体的实现类分离.

      1. IWebview 这是核心接口.进行对webview的裁剪,把相对常用的方法提取出来了.
    package com.fcbox.anglib.fcwebview.base;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.view.View;
    
    import java.util.Map;
    
    /**
     * @author: 朱丽君
     * @create: 19-5-22 上午11:54
     * @description:webview的通用接口
     */
    public interface IWebview {
    
        public View getWebview();
    
        public View getView();
    
        public void loadUrl(String url, Map<String, String> additionalHttpHeaders);
    
        public void loadUrl(String url);
    
        public void postUrl(String url, byte[] postData);
    
        public void loadData(String data, String mimeType, String encoding);
    
        public void loadDataWithBaseURL(String baseUrl, String data,
                                        String mimeType, String encoding, String historyUrl);
    
        public void stopLoading();
    
        public void reload();
    
        public boolean canGoBack();
    
        public void goBack();
    
        public boolean canGoForward();
    
        public void goForward();
    
        public boolean canGoBackOrForward(int steps);
    
        public void goBackOrForward(int steps);
    
        public String getUrl();
    
        public String getOriginalUrl();
    
        public String getTitle();
    
        public Bitmap getFavicon();
    
        public int getProgress();
    
        public int getContentHeight();
    
        public int getContentWidth();
    
        public void onPause();
    
        public void onResume();
    
        public void freeMemory();
    
        public void clearCache(boolean includeDiskFiles);
    
        public void clearFormData();
    
        public void clearHistory();
    
        public void clearSslPreferences();
    
        public void setWebViewClient(WebViewClient client);
    
    
        public void setDownloadListener(DownloadListener listener);
    
        public void setWebChromeClient(WebChromeClient client);
    
    
        public void addJavascriptInterface(Object obj, String interfaceName);
    
        public void removeJavascriptInterface(String interfaceName);
    
        public IWebSettings getFcWebviewSettings();
    
        public boolean canZoomIn();
    
        public boolean canZoomOut();
    
        public boolean zoomIn();
    
        public boolean zoomOut();
    
        public void destory();
    }
    
    

    其中在webview中包含 WebSetting,DownloadListener,WebViewClient,WebChromeClient 等系统的类,为了隔离性更强,把他们都与系统api分离,单独做成我们基础库中.

    1. 所以把WebSetting 也进行接口化,IWebSettings
    public interface IWebSettings {
    
    
        /**
         * Used with {@link #setMixedContentMode}
         * <p>
         * In this mode, the WebView will allow a secure origin to load content from any other origin,
         * even if that origin is insecure. This is the least secure mode of operation for the WebView,
         * and where possible apps should not set this mode.
         */
        public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0;
    
        /**
         * Used with {@link #setMixedContentMode}
         * <p>
         * In this mode, the WebView will not allow a secure origin to load content from an insecure
         * origin. This is the preferred and most secure mode of operation for the WebView and apps are
         * strongly advised to use this mode.
         */
        public static final int MIXED_CONTENT_NEVER_ALLOW = 1;
    
        /**
         * Used with {@link #setMixedContentMode}
         * <p>
         * In this mode, the WebView will attempt to be compatible with the approach of a modern web
         * browser with regard to mixed content. Some insecure content may be allowed to be loaded by
         * a secure origin and other types of content will be blocked. The types of content are allowed
         * or blocked may change release to release and are not explicitly defined.
         * <p>
         * This mode is intended to be used by apps that are not in control of the content that they
         * render but desire to operate in a reasonably secure environment. For highest security, apps
         * are recommended to use {@link #MIXED_CONTENT_NEVER_ALLOW}.
         */
        public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2;
    
        @TargetApi(5)
        public void setDatabaseEnabled(boolean var1);
    
        @TargetApi(5)
        public void setGeolocationEnabled(boolean var1);
    
        @TargetApi(5)
        public void setGeolocationDatabasePath(String var1);
    
        public void setJavaScriptEnabled(boolean var1);
    
        public void setBlockNetworkImage(boolean var1);
    
        @TargetApi(7)
        public void setDomStorageEnabled(boolean var1);
    
        public void setSupportZoom(boolean var1);
    
        @TargetApi(3)
        public void setBuiltInZoomControls(boolean var1);
    
        public void setUseWideViewPort(boolean var1);
    
        @TargetApi(7)
        public void setAppCacheEnabled(boolean var1);
    
        @TargetApi(7)
        public void setLoadWithOverviewMode(boolean var1);
    
        public void setJavaScriptCanOpenWindowsAutomatically(boolean var1);
    
        @TargetApi(3)
        public void setAllowFileAccess(boolean var1);
    
        @TargetApi(21)
        public void setMixedContentMode(int mode);
    }
    

    3.对于WebviewClient ,进行了结合我们自己的app的一些常用设置进行裁剪,把一些平时用的少的方法舍弃了.至保留了两个用的多重要的方法.

    package com.fcbox.anglib.fcwebview.base;
    
    import com.fcbox.anglib.fcwebview.base.IWebview;
    
    /**
     * @author: 朱丽君
     * @create: 19-5-22 上午11:57
     * @description:这里的WebviewClient进行了相应的裁剪.保留了2个重要的方法
     */
    public class WebViewClient {
        public boolean shouldOverrideUrlLoading(IWebview var1, String var2) {
            return false;
        }
    
        public void onReceivedError(IWebview view, int errorCode, String description, String failingUrl) {
        }
    
    }
    
    
    1. 对于WebChromeClient 类也进行了裁剪.保留了以下的方法
    package com.fcbox.anglib.fcwebview.base;
    
    import android.graphics.Bitmap;
    import android.net.Uri;
    
    import com.fcbox.anglib.fcwebview.base.GeolocationPermissionsCallback;
    
    /**
     * @author: 朱丽君
     * @create: 19-5-22 下午3:23
     * @description:
     */
    public class WebChromeClient {
    
        public void onGeolocationPermissionsShowPrompt(String var1, GeolocationPermissionsCallback callback) {
        }
    
        public void onProgressChanged(IWebview webview, int var2) {
        }
    
        public void onReceivedIcon(IWebview webview, Bitmap var2) {
        }
    
        public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
        }
    
        public boolean onShowFileChooser(IWebview webView, ValueCallback<Uri[]> valueCallback) {
            return false;
        }
    
    }
    
    1. webview中的CookieManager ,对于cookie的同步通常使用个CookieSyncManager 的sync方法.为了方便管理,我把这两个类合并到一起. 即ICookieManager
    package com.fcbox.anglib.fcwebview.base;
    
    /**
     * @author: 朱丽君
     * @create: 19-5-23 下午6:10
     * @description:
     */
    public interface ICookieManager {
    
        /**
         * Sets whether the application's {@link IWebview} instances should send and
         * accept cookies.
         * By default this is set to true and the WebView accepts cookies.
         * <p>
         * When this is true
         * {@link CookieManager#setAcceptThirdPartyCookies setAcceptThirdPartyCookies} and
         * {@link CookieManager#setAcceptFileSchemeCookies setAcceptFileSchemeCookies}
         * can be used to control the policy for those specific types of cookie.
         *
         * @param accept whether {@link WebView} instances should send and accept
         *               cookies
         */
        public abstract void setAcceptCookie(boolean accept);
    
        /**
         * Gets whether the application's {@link WebView} instances send and accept
         * cookies.
         *
         * @return true if {@link WebView} instances send and accept cookies
         */
        public abstract boolean acceptCookie();
    
        /**
         * Sets whether the {@link WebView} should allow third party cookies to be set.
         * Allowing third party cookies is a per WebView policy and can be set
         * differently on different WebView instances.
         * <p>
         * Apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or below
         * default to allowing third party cookies. Apps targeting
         * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later default to disallowing
         * third party cookies.
         *
         * @param webview the {@link WebView} instance to set the cookie policy on
         * @param accept whether the {@link WebView} instance should accept
         *               third party cookies
         */
        public abstract void setAcceptThirdPartyCookies(IWebview webview, boolean accept);
    
        /**
         * Gets whether the {@link WebView} should allow third party cookies to be set.
         *
         * @param webview the {@link WebView} instance to get the cookie policy for
         * @return true if the {@link WebView} accepts third party cookies
         */
        public abstract boolean acceptThirdPartyCookies(IWebview webview);
    
        /**
         * Sets a cookie for the given URL. Any existing cookie with the same host,
         * path and name will be replaced with the new cookie. The cookie being set
         * will be ignored if it is expired.
         *
         * @param url the URL for which the cookie is to be set
         * @param value the cookie as a string, using the format of the 'Set-Cookie'
         *              HTTP response header
         */
        public abstract void setCookie(String url, String value);
    
        /**
         * Sets a cookie for the given URL. Any existing cookie with the same host,
         * path and name will be replaced with the new cookie. The cookie being set
         * will be ignored if it is expired.
         * <p>
         * This method is asynchronous.
         * If a {@link ValueCallback} is provided,
         * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
         * thread's {@link android.os.Looper} once the operation is complete.
         * The value provided to the callback indicates whether the cookie was set successfully.
         * You can pass {@code null} as the callback if you don't need to know when the operation
         * completes or whether it succeeded, and in this case it is safe to call the method from a
         * thread without a Looper.
         *
         * @param url the URL for which the cookie is to be set
         * @param value the cookie as a string, using the format of the 'Set-Cookie'
         *              HTTP response header
         * @param callback a callback to be executed when the cookie has been set
         */
        public abstract void setCookie(String url, String value, ValueCallback<Boolean> callback);
    
        /**
         * Gets the cookies for the given URL.
         *
         * @param url the URL for which the cookies are requested
         * @return value the cookies as a string, using the format of the 'Cookie'
         *               HTTP request header
         */
        public abstract String getCookie(String url);
    
        /**
         * Removes all session cookies, which are cookies without an expiration
         * date.
         * @deprecated use {@link #removeSessionCookies(ValueCallback)} instead.
         */
        @Deprecated
        public abstract void removeSessionCookie();
    
        /**
         * Removes all session cookies, which are cookies without an expiration
         * date.
         * <p>
         * This method is asynchronous.
         * If a {@link ValueCallback} is provided,
         * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
         * thread's {@link android.os.Looper} once the operation is complete.
         * The value provided to the callback indicates whether any cookies were removed.
         * You can pass {@code null} as the callback if you don't need to know when the operation
         * completes or whether any cookie were removed, and in this case it is safe to call the
         * method from a thread without a Looper.
         * @param callback a callback which is executed when the session cookies have been removed
         */
        public abstract void removeSessionCookies(ValueCallback<Boolean> callback);
    
        /**
         * Removes all cookies.
         * @deprecated Use {@link #removeAllCookies(ValueCallback)} instead.
         */
        @Deprecated
        public abstract void removeAllCookie();
    
        /**
         * Removes all cookies.
         * <p>
         * This method is asynchronous.
         * If a {@link ValueCallback} is provided,
         * {@link ValueCallback#onReceiveValue(Object)}  onReceiveValue()} will be called on the current
         * thread's {@link android.os.Looper} once the operation is complete.
         * The value provided to the callback indicates whether any cookies were removed.
         * You can pass {@code null} as the callback if you don't need to know when the operation
         * completes or whether any cookies were removed, and in this case it is safe to call the
         * method from a thread without a Looper.
         * @param callback a callback which is executed when the cookies have been removed
         */
        public abstract void removeAllCookies(ValueCallback<Boolean> callback);
    
        /**
         * Gets whether there are stored cookies.
         *
         * @return true if there are stored cookies
         */
        public abstract boolean hasCookies();
    
    
        /**
         * Removes all expired cookies.
         * @deprecated The WebView handles removing expired cookies automatically.
         */
        @Deprecated
        public abstract void removeExpiredCookie();
    
        /**
         * Ensures all cookies currently accessible through the getCookie API are
         * written to persistent storage.
         * This call will block the caller until it is done and may perform I/O.
         */
        public abstract void flush();
    
    
        /**
         * 增加一个同步的方法
         */
       public void sync();
    }
    
    

    以上就是webviewFramework的核心内容.

    5.2 x5core 模块
    image.png

    这个模块是具体的第三方sdk适配层.从这个模块可以衍生出其他的模块,例如增加crosswalk模块,以及未来其他的webview内核模块.这些模块的作用是 用自己的特性来适配成我们自己约定的webview的接口(即webviewFramework模块).里面采用的当然是适配器模式.

    5.1 简单介绍下适配器模式:
    • 1.类适配器模式


      image.png
    • 2.对象适配器模式
    image.png

    两者的区别是,类适配器模式是继承,而对象适配器是引用对象.考虑到我们这边的x5内核提供了一个webview,包括视图,刚好我们也需要,所以选择类适配器是比较合适的.
    3. X5WebviewAdapter

    image.png
    package com.fcbox.anglib.x5core.x5Adapter;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.net.Uri;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.view.View;
    
    import com.fcbox.anglib.fcwebview.base.DownloadListener;
    import com.fcbox.anglib.fcwebview.base.IWebSettings;
    import com.fcbox.anglib.fcwebview.base.IWebview;
    import com.fcbox.anglib.fcwebview.base.WebChromeClient;
    import com.fcbox.anglib.fcwebview.base.WebViewClient;
    import com.tencent.smtt.export.external.interfaces.GeolocationPermissionsCallback;
    import com.tencent.smtt.export.external.interfaces.SslError;
    import com.tencent.smtt.export.external.interfaces.SslErrorHandler;
    import com.tencent.smtt.sdk.ValueCallback;
    import com.tencent.smtt.sdk.WebView;
    
    import java.util.Map;
    
    /**
     * @author: 朱丽君
     * @create: 19-5-22 上午11:49
     * @description: X5webview 作为 Iwebview的一个Adapter
     */
    public class X5WebviewAdapter extends WebView implements IWebview {
        private IWebSettings fcWebSetting;
    
    
        public X5WebviewAdapter(@NonNull Context context) {
            this(context, null);
        }
    
        public X5WebviewAdapter(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public X5WebviewAdapter(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public X5WebviewAdapter(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
            super(context, attrs, defStyleAttr, javaScriptInterfaces, privateBrowsing);
        }
    
        @Override
        public View getView() {
            return super.getView();
        }
    
    
        @Override
        public View getWebview() {
            return this;
        }
    
        @Override
        public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
            super.loadUrl(url, additionalHttpHeaders);
        }
    
        @Override
        public void loadUrl(String url) {
            super.loadUrl(url);
    
        }
    
        @Override
        public void postUrl(String url, byte[] postData) {
            super.postUrl(url, postData);
        }
    
        @Override
        public void loadData(String data, String mimeType, String encoding) {
            super.loadData(data, mimeType, encoding);
        }
    
        @Override
        public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) {
            super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
        }
    
        @Override
        public void stopLoading() {
            super.stopLoading();
        }
    
        @Override
        public void reload() {
            super.reload();
        }
    
        @Override
        public boolean canGoBack() {
            return super.canGoBack();
        }
    
        @Override
        public void goBack() {
            super.goBack();
        }
    
        @Override
        public boolean canGoForward() {
            return super.canGoForward();
        }
    
        @Override
        public void goForward() {
            super.goForward();
        }
    
        @Override
        public boolean canGoBackOrForward(int steps) {
            return super.canGoBackOrForward(steps);
        }
    
        @Override
        public void goBackOrForward(int steps) {
            super.goBackOrForward(steps);
        }
    
        @Override
        public String getUrl() {
            return super.getUrl();
        }
    
        @Override
        public String getOriginalUrl() {
            return super.getOriginalUrl();
        }
    
        @Override
        public String getTitle() {
            return super.getTitle();
        }
    
        @Override
        public Bitmap getFavicon() {
            return super.getFavicon();
        }
    
    
        @Override
        public int getProgress() {
            return super.getProgress();
        }
    
        @Override
        public int getContentHeight() {
            return super.getContentHeight();
        }
    
        @Override
        public int getContentWidth() {
            return super.getContentWidth();
        }
    
        @Override
        public void onPause() {
            super.onPause();
        }
    
        @Override
        public void onResume() {
            super.onResume();
        }
    
    
        @Override
        public void freeMemory() {
            super.freeMemory();
        }
    
        @Override
        public void clearCache(boolean includeDiskFiles) {
            super.clearCache(includeDiskFiles);
        }
    
        @Override
        public void clearFormData() {
            super.clearFormData();
        }
    
        @Override
        public void clearHistory() {
            super.clearHistory();
        }
    
        @Override
        public void clearSslPreferences() {
            super.clearSslPreferences();
        }
    
        @Override
        public void setWebViewClient(final WebViewClient client) {
            super.setWebViewClient(new com.tencent.smtt.sdk.WebViewClient() {
                @Override
                public boolean shouldOverrideUrlLoading(WebView webView, String s) {
                    return client.shouldOverrideUrlLoading(X5WebviewAdapter.this, s);
    
                }
    
                @Override
                public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {
                    sslErrorHandler.proceed();
                }
    
            });
        }
    
    
        @Override
        public void setDownloadListener(final DownloadListener listener) {
            super.setDownloadListener(new com.tencent.smtt.sdk.DownloadListener() {
                @Override
                public void onDownloadStart(String url, String userAgent,
                                            String contentDisposition, String mimetype, long contentLength) {
                    listener.onDownloadStart(url, userAgent, contentDisposition, mimetype, contentLength);
                }
            });
        }
    
        @Override
        public void setWebChromeClient(final WebChromeClient client) {
    
            super.setWebChromeClient(new com.tencent.smtt.sdk.WebChromeClient() {
    
                @Override
                public void onGeolocationPermissionsShowPrompt(String s, final GeolocationPermissionsCallback geolocationPermissionsCallback) {
                    client.onGeolocationPermissionsShowPrompt(s, new com.fcbox.anglib.fcwebview.base.GeolocationPermissionsCallback() {
                        @Override
                        public void invoke(String origin, boolean allow, boolean retain) {
                            geolocationPermissionsCallback.invoke(origin, allow, retain);
                        }
                    });
                }
    
                @Override
                public void onProgressChanged(WebView webView, int i) {
                    client.onProgressChanged(X5WebviewAdapter.this, i);
                }
    
                @Override
                public void onReceivedIcon(WebView webView, Bitmap bitmap) {
                    client.onReceivedIcon(X5WebviewAdapter.this, bitmap);
                }
    
                @Override
                public void openFileChooser(final ValueCallback<Uri> uploadFile, String acceptType, String capture) {
                    client.openFileChooser(new com.fcbox.anglib.fcwebview.base.ValueCallback<Uri>() {
                        @Override
                        public void onReceiveValue(Uri var1) {
                            uploadFile.onReceiveValue(var1);
                        }
                    }, acceptType, capture);
                }
    
                @Override
                public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
    
                    return client.onShowFileChooser(X5WebviewAdapter.this, new com.fcbox.anglib.fcwebview.base.ValueCallback<Uri[]>() {
                        @Override
                        public void onReceiveValue(Uri[] var1) {
                            valueCallback.onReceiveValue(var1);
                        }
                    });
                }
            });
        }
    
    
        @Override
        public void addJavascriptInterface(Object obj, String interfaceName) {
            super.addJavascriptInterface(obj, interfaceName);
        }
    
        @Override
        public void removeJavascriptInterface(String interfaceName) {
            super.removeJavascriptInterface(interfaceName);
        }
    
    
        @Override
        public IWebSettings getFcWebviewSettings() {
            if (fcWebSetting != null) {
                return fcWebSetting;
            } else {
                fcWebSetting = new X5WebSettingsAdapter(super.getSettings());
            }
            return fcWebSetting;
        }
    
        @Override
        public boolean canZoomIn() {
            return super.canZoomIn();
        }
    
        @Override
        public boolean canZoomOut() {
            return super.canZoomOut();
        }
    
    
        @Override
        public boolean zoomIn() {
            return super.zoomIn();
        }
    
        @Override
        public boolean zoomOut() {
            return super.zoomOut();
        }
    
    
        @Override
        public void destory() {
            super.destroy();
        }
    }
    
    

    5.对于X5WebSettingsAdapter 采用了对象适配器模式

    public class X5WebSettingsAdapter implements IWebSettings {
    
        private WebSettings x5WebSetting;
    
        public X5WebSettingsAdapter(WebSettings x5WebSetting) {
            this.x5WebSetting = x5WebSetting;
        }
    
        @Override
        public void setDatabaseEnabled(boolean flag) {
            x5WebSetting.setDatabaseEnabled(flag);
        }
    
        @Override
        public void setGeolocationEnabled(boolean flag) {
            x5WebSetting.setGeolocationEnabled(flag);
        }
    
        @Override
        public void setGeolocationDatabasePath(String databasePath) {
            x5WebSetting.setGeolocationDatabasePath(databasePath);
        }
    
        @Override
        public void setJavaScriptEnabled(boolean flag) {
            x5WebSetting.setJavaScriptEnabled(flag);
        }
    
        @Override
        public void setBlockNetworkImage(boolean flag) {
            x5WebSetting.setBlockNetworkImage(flag);
        }
    
        @Override
        public void setDomStorageEnabled(boolean flag) {
            x5WebSetting.setDomStorageEnabled(flag);
        }
    
        @Override
        public void setSupportZoom(boolean flag) {
            x5WebSetting.setSupportZoom(flag);
        }
    
        @Override
        public void setBuiltInZoomControls(boolean flag) {
            x5WebSetting.setBuiltInZoomControls(flag);
        }
    
        @Override
        public void setUseWideViewPort(boolean flag) {
            x5WebSetting.setUseWideViewPort(flag);
        }
    
        @Override
        public void setAppCacheEnabled(boolean flag) {
            x5WebSetting.setAppCacheEnabled(flag);
        }
    
        @Override
        public void setLoadWithOverviewMode(boolean flag) {
            x5WebSetting.setLoadWithOverviewMode(flag);
        }
    
        @Override
        public void setJavaScriptCanOpenWindowsAutomatically(boolean flag) {
            x5WebSetting.setJavaScriptCanOpenWindowsAutomatically(flag);
        }
    
        @Override
        public void setAllowFileAccess(boolean flag) {
            x5WebSetting.setAllowFileAccess(flag);
        }
    
        @Override
        public void setMixedContentMode(int mode) {
            x5WebSetting.setMixedContentMode(mode);
        }
    }
    
    
    5.3 fcwebview模块
    image.png

    这个模块是一个与具体用户打交道的模块,而且用户也只需要和这个模块进行交互.并且在未来的不管接入其他的浏览器内核.用户调用这个模块的代码都不需要改动.这就是最终我们需要Fcwebview达到的效果.实际上要达到这种效果,只需要提供给用户一个动态设置不同内核的权利即可.
    从而我们很容易想到两种设计模式:
    1.策略模式


    image.png 2.代理模式 683744-20160929112107375-1656629935.png

    两种模式是有区别的:
    策略模式实际上是对一系列的算法的具体实现进行封装,在不同的场景中使用不同的算法.强调的是一个动作,或者行为.
    而代理模式,则强调的是一个对象.这里面可以有多个代理对象,代理类与被代理类实现的是同一个接口,因此代理类与被代理类的结构是相同的.

    显然在我们这里用代理模式会比较合理,我可以设置不同的浏览器内核作为代理,从而实现Fcwebview的接口.

    package com.fcbox.anglib.fcwebview;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.os.Build;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    import android.widget.FrameLayout;
    
    
    import com.fcbox.anglib.fcwebview.base.DownloadListener;
    import com.fcbox.anglib.fcwebview.base.IWebSettings;
    import com.fcbox.anglib.fcwebview.base.IWebview;
    import com.fcbox.anglib.fcwebview.base.WebChromeClient;
    import com.fcbox.anglib.fcwebview.base.WebViewClient;
    
    import java.util.Map;
    
    /**
     * @author: 朱丽君
     * @create: 19-5-29 上午10:42
     * @description: 丰巢webview ,这个类是一个代理,可以设置其他类型的webview作为实际类
     */
    public class FcWebview extends FrameLayout implements IWebview {
    
    
        private IWebview webviewProxy;
    
        public FcWebview(@NonNull Context context) {
            this(context, null);
        }
    
        public FcWebview(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public FcWebview(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(FcWebviewInit.webCoreType);
        }
    
        public void setWebviewCore(IWebview webview) {
            webviewProxy = webview;
            init();
        }
    
        private void init(int type) {
            webviewProxy = FcWebviewFactory.getWebview(getContext(), type);
            init();
        }
    
        private void init() {
            if (webviewProxy == null) {
                return;
            }
            removeAllViews();
            addView(webviewProxy.getWebview());
        }
    
        @Override
        public View getWebview() {
            if (checkNull()) {
                return null;
            }
            return webviewProxy.getWebview();
        }
    
        @Override
        public View getView() {
            return this;
        }
    
        @Override
        public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
            if (checkNull()) {
                return;
            }
            webviewProxy.loadUrl(url, additionalHttpHeaders);
        }
    
        @Override
        public void loadUrl(String url) {
            if (checkNull()) {
                return;
            }
            webviewProxy.loadUrl(url);
        }
    
        @Override
        public void postUrl(String url, byte[] postData) {
            if (checkNull()) {
                return;
            }
            webviewProxy.postUrl(url, postData);
        }
    
        @Override
        public void loadData(String data, String mimeType, String encoding) {
            if (checkNull()) {
                return;
            }
            webviewProxy.loadData(data, mimeType, encoding);
        }
    
        @Override
        public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) {
            if (checkNull()) {
                return;
            }
            webviewProxy.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
        }
    
        @Override
        public void stopLoading() {
            if (checkNull()) {
                return;
            }
            webviewProxy.stopLoading();
        }
    
        @Override
        public void reload() {
            if (checkNull()) {
                return;
            }
            webviewProxy.reload();
        }
    
        @Override
        public boolean canGoBack() {
            if (checkNull()) {
                return false;
            }
            return webviewProxy.canGoBack();
        }
    
        @Override
        public void goBack() {
            if (checkNull()) {
                return;
            }
            webviewProxy.goBack();
        }
    
        @Override
        public boolean canGoForward() {
            if (checkNull()) {
                return false;
            }
            return webviewProxy.canGoForward();
        }
    
        @Override
        public void goForward() {
            if (checkNull()) {
                return;
            }
            webviewProxy.goForward();
        }
    
        @Override
        public boolean canGoBackOrForward(int steps) {
            if (checkNull()) {
                return false;
            }
            return webviewProxy.canGoBackOrForward(steps);
    
        }
    
        @Override
        public void goBackOrForward(int steps) {
            if (checkNull()) {
                return;
            }
            webviewProxy.goBackOrForward(steps);
        }
    
        @Override
        public String getUrl() {
            if (checkNull()) {
                return "";
            }
            return webviewProxy.getUrl();
        }
    
        @Override
        public String getOriginalUrl() {
            if (checkNull()) {
                return "";
            }
            return webviewProxy.getOriginalUrl();
        }
    
        @Override
        public String getTitle() {
            if (checkNull()) {
                return "";
            }
            return webviewProxy.getTitle();
        }
    
        @Override
        public Bitmap getFavicon() {
            if (checkNull()) {
                return null;
            }
            return webviewProxy.getFavicon();
        }
    
        @Override
        public int getProgress() {
            if (checkNull()) {
                return 0;
            }
            return webviewProxy.getProgress();
        }
    
        @Override
        public int getContentHeight() {
            if (checkNull()) {
                return 0;
            }
            return webviewProxy.getContentHeight();
        }
    
        @Override
        public int getContentWidth() {
            if (checkNull()) {
                return 0;
            }
            return webviewProxy.getContentWidth();
        }
    
        @Override
        public void onPause() {
            if (checkNull()) {
                return;
            }
            webviewProxy.onPause();
        }
    
        @Override
        public void onResume() {
            if (checkNull()) {
                return;
            }
            webviewProxy.onResume();
        }
    
        @Override
        public void freeMemory() {
            if (checkNull()) {
                return;
            }
            webviewProxy.freeMemory();
        }
    
        @Override
        public void clearCache(boolean includeDiskFiles) {
            if (checkNull()) {
                return;
            }
            webviewProxy.clearCache(includeDiskFiles);
        }
    
        @Override
        public void clearFormData() {
            if (checkNull()) {
                return;
            }
            webviewProxy.clearFormData();
        }
    
        @Override
        public void clearHistory() {
            if (checkNull()) {
                return;
            }
            webviewProxy.clearHistory();
        }
    
        @Override
        public void clearSslPreferences() {
            if (checkNull()) {
                return;
            }
            webviewProxy.clearSslPreferences();
        }
    
        @Override
        public void setWebViewClient(WebViewClient client) {
            if (checkNull()) {
                return;
            }
            webviewProxy.setWebViewClient(client);
        }
    
        @Override
        public void setDownloadListener(DownloadListener listener) {
            if (checkNull()) {
                return;
            }
            webviewProxy.setDownloadListener(listener);
        }
    
        @Override
        public void setWebChromeClient(WebChromeClient client) {
            if (checkNull()) {
                return;
            }
            webviewProxy.setWebChromeClient(client);
        }
    
        @Override
        public void addJavascriptInterface(Object obj, String interfaceName) {
            if (checkNull()) {
                return;
            }
            webviewProxy.addJavascriptInterface(obj, interfaceName);
        }
    
        @Override
        public void removeJavascriptInterface(String interfaceName) {
            if (checkNull()) {
                return;
            }
            webviewProxy.removeJavascriptInterface(interfaceName);
        }
    
        @Override
        public IWebSettings getFcWebviewSettings() {
            if (checkNull()) {
                return null;
            }
            return webviewProxy.getFcWebviewSettings();
        }
    
        @Override
        public boolean canZoomIn() {
            if (checkNull()) {
                return false;
            }
            return webviewProxy.canZoomIn();
        }
    
        @Override
        public boolean canZoomOut() {
            if (checkNull()) {
                return false;
            }
            return webviewProxy.canZoomOut();
        }
    
        @Override
        public boolean zoomIn() {
            if (checkNull()) {
                return false;
            }
            return webviewProxy.zoomIn();
        }
    
        @Override
        public boolean zoomOut() {
            if (checkNull()) {
                return false;
            }
            return webviewProxy.zoomOut();
        }
    
        @Override
        public void destory() {
            if (checkNull()) {
                return;
            }
            webviewProxy.destory();
        }
    
        private boolean checkNull() {
            return (webviewProxy == null);
        }
    
    
        public void setDefaultSetting() {
            IWebSettings ws = getFcWebviewSettings();
            //启用数据库
            ws.setDatabaseEnabled(true);
            String dir = getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
            //启用地理定位
            ws.setGeolocationEnabled(true);
            //设置定位的数据库路径
            ws.setGeolocationDatabasePath(dir);
    
            // 设置javascript是可以执行的
            ws.setJavaScriptEnabled(true);
    
            ws.setBlockNetworkImage(false);//解决图片不显示
            // 开启DOM缓存。
            ws.setDomStorageEnabled(true);
            ws.setSupportZoom(false);
            ws.setBuiltInZoomControls(false);
            ws.setUseWideViewPort(false);
            ws.setAppCacheEnabled(false);
            ws.setLoadWithOverviewMode(true);
            ws.setUseWideViewPort(true);
            ws.setGeolocationEnabled(true);
            ws.setJavaScriptCanOpenWindowsAutomatically(true);
            ws.setAllowFileAccess(true);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                ws.setMixedContentMode(IWebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
            }
        }
    
    
        /**
         * 执行js方法,可以传多个参数
         *
         * @param funcName
         * @param params    1个或者多个参数
         */
        public void callJsFunc(String funcName, String... params) {
            if (TextUtils.isEmpty(funcName)) {
                return;
            }
            if (params == null || params.length == 0) {
                return;
            }
            StringBuffer param = new StringBuffer();
            for (String eachParam : params) {
                param.append(eachParam);
                param.append(",");
            }
    
            //去掉最后一个逗号
            param.deleteCharAt(param.length() - 1);
    
            String callBackUrl = "javascript:" + funcName + "(" +"\'"+ param.toString() +"\'"+ ")";
            loadUrl(callBackUrl);
            if (FcWebviewInit.isDebug) {
                Log.d("Fcwebview", "callJavascript:" + callBackUrl);
            }
        }
    
        /**
         * 设置fc默认js回调,这里进行了通用数据格式的解析
         *
         * @param callBack
         */
        public void setFcDefaultJavascript(FcJsActionBridge.OnDataCallBack callBack) {
            addJavascriptInterface(new FcJsActionBridge(this, callBack), "npc4fc");
        }
    
    
    }
    

    FcWebviewFactory
    一个简单工厂类,返回不同的浏览器内核.

    package com.fcbox.anglib.fcwebview;
    
    import android.content.Context;
    
    import com.fcbox.anglib.fcwebview.base.IWebview;
    import com.fcbox.anglib.x5core.x5Adapter.X5WebviewAdapter;
    
    /**
     * @author: 朱丽君
     * @create: 19-6-4 下午4:22
     * @description:简单工厂,选择具体的webview适配器
     */
    public class FcWebviewFactory {
    
        public static IWebview getWebview(Context context, int type) {
    
            if (type == FcWebviewInit.X5) {
                return new X5WebviewAdapter(context);
            }
            return null;
        }
    
    }
    
    

    6. 测试

    布局文件activity_fc_webview_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.fcbox.anglib.fcwebview.FcWebview
            android:id="@+id/fcwebview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></com.fcbox.anglib.fcwebview.FcWebview>
    
    </android.support.constraint.ConstraintLayout>
    

    测试代码java
    application初始化

    public class MyApp extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            initX5();
    
        }
    
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            MultiDex.install(this);
        }
    
        /**
         * 初始化X5
         */
        private void initX5() {
            FcWebviewInit.setIsDebug(true);
            FcWebviewInit.init(this, new FcWebviewInit.Result() {
                @Override
                public void initResult(boolean isSucc) {
    
                }
            });
        }
    }
    

    TestFcwebview.java

    import android.app.Activity;
    import android.content.ActivityNotFoundException;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.util.Log;
    import android.webkit.JavascriptInterface;
    
    import com.fcbox.anglib.fcwebview.FcCookieManager;
    import com.fcbox.anglib.fcwebview.FcJsActionBridge;
    import com.fcbox.anglib.fcwebview.FcWebview;
    import com.fcbox.anglib.fcwebview.base.IWebview;
    import com.fcbox.anglib.fcwebview.base.ValueCallback;
    import com.fcbox.anglib.fcwebview.base.WebChromeClient;
    import com.fcbox.anglib.fcwebview.base.WebViewClient;
    import org.json.JSONObject;
    import top.wintp.mytbsdemo.R;
    
    /**
     * @author: 朱丽君
     * @create: 19-5-22 下午3:05
     * @description:Fcwebview的 测试类
     */
    public class TestFcwebview extends Activity {
        private FcWebview mWebView;
        private ValueCallback<Uri> valueCallback;
        private ValueCallback<Uri[]> valueCallBackAboveL;
        private static String TAG = TestFcwebview.class.getSimpleName();
    
       private String url = "https://mall.fcbox.com";
    //    private String url = "file:///android_asset/testH5.html";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_fc_webview_main);
            mWebView = findViewById(R.id.fcwebview);
            //设置浏览器内核
            mWebView.setDefaultSetting();
            setCookie(url);
            mWebView.loadUrl(url);
            mWebView.setWebViewClient(new WebViewClient() {
    
    
            });
    
    
            mWebView.setWebChromeClient(new WebChromeClient() {
                //For Android  >= 4.1
                @Override
                public void openFileChooser(ValueCallback<Uri> fileCallback, String acceptType,
                                            String capture) {
                    valueCallback = fileCallback;
                    album(TestFcwebview.this, 2001);
                }
    
                // For Android >= 5.0
                @Override
                public boolean onShowFileChooser(IWebview webView, ValueCallback<Uri[]> filePathCallback) {
                    valueCallBackAboveL = filePathCallback;
                    album(TestFcwebview.this, 2001);
                    return true;
                }
            });
    
            mWebView.addJavascriptInterface(new Object() {
                @JavascriptInterface
                public void callAction(String jsonStr) {
                    testJs(jsonStr);
                }
            }, "npc4fc");
    
    
            //设置fc默认的js
    //        mWebView.setFcDefaultJavascript(new FcJsActionBridge.OnDataCallBack() {
    //            @Override
    //            public void onCallBack(int actionType, JSONObject actionParam, String functionName) {
    //
    //            }
    //        });
    
        }
    
        private void testJs(String jsonStr) {
            Log.d(TAG, "JsCall" + jsonStr);
            if (jsonStr.equals("callBack")) {
                TestFcwebview.this.runOnUiThread(() -> {
                    mWebView.callJsFunc("fccallback", "test");
                });
            }
        }
    
    
        private void setCookie(String url) {
            FcCookieManager fcCookieManager = FcCookieManager.getInstance(getApplicationContext());
            FcCookieManager.Cookie cookie = new FcCookieManager.Cookie(url)
                    .addCookieMap("key", "test")
                    .addCookieMap("key2", "----2");
            fcCookieManager.addCookie(cookie);
            fcCookieManager.setCookieToUrl(mWebView, url);
            Log.d(TAG, "cookie" + fcCookieManager.getCookie(url));
        }
    
        public static void album(Activity context, int requestCode) {
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("image/*");
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//如果大于等于7.0使用FileProvider
                    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    context.startActivityForResult(intent, requestCode);
                } else {
                    context.startActivityForResult(intent, requestCode);
                }
            } catch (ActivityNotFoundException e) {
                Log.d(TAG, "not found");
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mWebView.destory();
        }
    }
    
    

    6. 总结:

    说一千,道一万.软件的设计以及第三方库的集成.最终还是要符合面向对象的六大原则:

    • 单一职责原则
    • 开闭原则
    • 里氏替换原则
    • 依赖倒置原则
    • 接口隔离原则
    • 迪米特原则(最少知识原则)

    相关文章

      网友评论

          本文标题:从封装Fcwebview Sdk看第三方库的集成

          本文链接:https://www.haomeiwen.com/subject/vjmmxctx.html