Cordova源码解析(4)_webview核心引擎类的封装设计

上一章在CordovaWebViewImplcreateEngine方法中根据config.xml的配置来创建不同的驱动类,默认的为SystemWebViewEngine,这里再贴一下代码:

public static CordovaWebViewEngine createEngine(Context context, CordovaPreferences preferences) {
	// 这里默认是SystemWebViewEngine类
    String className = preferences.getString("webview", SystemWebViewEngine.class.getCanonicalName());
    try {
        Class<?> webViewClass = Class.forName(className);
        Constructor<?> constructor = webViewClass.getConstructor(Context.class, CordovaPreferences.class);
        return (CordovaWebViewEngine) constructor.newInstance(context, preferences);
    } catch (Exception e) {
        throw new RuntimeException("Failed to create webview. ", e);
    }
}

所以这一篇就针对SystemWebViewEngine类的设计来做一下分析。

CordovaWebViewEngine接口

SystemWebViewEngine类实现了CordovaWebViewEngine接口,这个接口主要暴露处理webview的接口。先贴一下CordovaWebViewEngine接口类,加了中文注释,方便理解。

/**
 * Interface for all Cordova engines.
 * No methods will be added to this class (in order to be compatible with existing engines).
 * Instead, we will create a new interface: e.g. CordovaWebViewEngineV2
 *
 * 这段英文基本意思是:
 * 为了保证自定义的引擎与系统引擎一致,所以不建议修改这个接口,
 * 不过可以自己建个新的接口标准。
 *
 */
public interface CordovaWebViewEngine {
    // 初始化方法
    void init(CordovaWebView parentWebView, CordovaInterface cordova, Client client,
              CordovaResourceApi resourceApi, PluginManager pluginManager,
              NativeToJsMessageQueue nativeToJsMessageQueue);

    CordovaWebView getCordovaWebView();
    // 处理webview的cookie
    ICordovaCookieManager getCookieManager();
    View getView();
    // 加载url
    void loadUrl(String url, boolean clearNavigationStack);
    void stopLoading();
    /** Return the currently loaded URL */
    // 返回当前url
    String getUrl();
    // 清除缓存
    void clearCache();
    /** After calling clearHistory(), canGoBack() should be false. */
    // 清除访问历史
    void clearHistory();
    // 判断webview是否可以返回
    boolean canGoBack();
    /** Returns whether a navigation occurred */
    // 返回上一个url
    boolean goBack();
    /** Pauses / resumes the WebView's event loop. */
    void setPaused(boolean value);
    /** Clean up all resources associated with the WebView. */
    void destroy();
    /** Add the evaulate Javascript method **/
    // 执行js语法
    void evaluateJavascript(String js, ValueCallback<String> callback);
    /**
     * Used to retrieve the associated CordovaWebView given a View without knowing the type of Engine.
     * E.g. ((CordovaWebView.EngineView)activity.findViewById(android.R.id.webView)).getCordovaWebView();
     */
    public interface EngineView {
        CordovaWebView getCordovaWebView();
    }
    /**
     * Contains methods that an engine uses to communicate with the parent CordovaWebView.
     * Methods may be added in future cordova versions, but never removed.
     * 处理webview的基本事件方法
     */
    public interface Client {
        Boolean onDispatchKeyEvent(KeyEvent event);
        void clearLoadTimeoutTimer();
        void onPageStarted(String newUrl);// 开始加载
        void onReceivedError(int errorCode, String description, String failingUrl);
        void onPageFinishedLoading(String url);
        boolean onNavigationAttempt(String url);
    }
}

大体理解了CordovaWebViewEngine接口方法,再回过头来看SystemWebViewEngine类是如何实现的。

SystemWebViewEngine类的构造方法

构造方法没什么可说的,根据参数的不同来初始化,其中第三个构造方法初始化了SystemCookieManager类。这个类以后再做分析。

/** Used when created via reflection. */
public SystemWebViewEngine(Context context, CordovaPreferences preferences) {
    this(new SystemWebView(context), preferences);
}

public SystemWebViewEngine(SystemWebView webView) {
    this(webView, null);
}

public SystemWebViewEngine(SystemWebView webView, CordovaPreferences preferences) {
    this.preferences = preferences;
    this.webView = webView;
    cookieManager = new SystemCookieManager(webView);
}

SystemWebViewEngine类的init方法

@Override
public void init(CordovaWebView parentWebView, CordovaInterface cordova, CordovaWebViewEngine.Client client,
          CordovaResourceApi resourceApi, PluginManager pluginManager,
          NativeToJsMessageQueue nativeToJsMessageQueue) {
    if (this.cordova != null) {
        throw new IllegalStateException();
    }
    // Needed when prefs are not passed by the constructor
    if (preferences == null) {
        preferences = parentWebView.getPreferences();
    }
    // 1.传递相关参数
    this.parentWebView = parentWebView;
    this.cordova = cordova;
    this.client = client;
    this.resourceApi = resourceApi;
    this.pluginManager = pluginManager;
    this.nativeToJsMessageQueue = nativeToJsMessageQueue;
    // 2.初始化webview
    webView.init(this, cordova);
	// 3.设置webview
    initWebViewSettings();
	// 4.设置nativeToJsMessageQueue模式
    nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
        @Override
        public void setNetworkAvailable(boolean value) {
            //sometimes this can be called after calling webview.destroy() on destroy()
            //thus resulting in a NullPointerException
            if(webView!=null) {
               webView.setNetworkAvailable(value); 
            }
        }
        @Override
        public void runOnUiThread(Runnable r) {
            SystemWebViewEngine.this.cordova.getActivity().runOnUiThread(r);
        }
    }));
    nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.EvalBridgeMode(this, cordova));
    // 5.创建CordovaBridge实例
    bridge = new CordovaBridge(pluginManager, nativeToJsMessageQueue);
    // 6.调用exposeJsInterface方法
    exposeJsInterface(webView, bridge);
}

init方法中主要是做了6件事:
一、传递相关参数

if (preferences == null) {
    preferences = parentWebView.getPreferences();
}
this.parentWebView = parentWebView;
this.cordova = cordova;
this.client = client;
this.resourceApi = resourceApi;
this.pluginManager = pluginManager;
this.nativeToJsMessageQueue = nativeToJsMessageQueue;

二、初始化webview
初始化webview调用的是webView.init(this, cordova);方法,webview是SystemWebView类的实例,回头再分析此类。
三、设置webview
设置webview调用的是initWebViewSettings方法。详细api介绍可以参考:
https://blog.csdn.net/carson_ho/article/details/52693322
https://blog.csdn.net/zhanwubus/article/details/80340025
https://www.jianshu.com/p/fea5e829b30a

private void initWebViewSettings() {
     /*设置webview缩放等级,0表示默认,webview会根据html里设置的缩放来配置,
     其他数值50表示缩小50%,100表示100%不缩放,200表示200%缩大2倍。*/
     webView.setInitialScale(0);
     // 是否设置垂直滚动条
     webView.setVerticalScrollBarEnabled(false);
     // Enable JavaScript
     final WebSettings settings = webView.getSettings();
     // 是否支持js交互
     settings.setJavaScriptEnabled(true);
     // 指示JavaScript自动打开窗口。默认false。
     settings.setJavaScriptCanOpenWindowsAutomatically(true);
     // 设置基础布局算法。默认NARROW_COLUMNS。
     settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);

     String manufacturer = android.os.Build.MANUFACTURER;
     LOG.d(TAG, "CordovaWebView is running on device made by: " + manufacturer);

     //We don't save any form data in the application
     // 设置是否保留表单数据
     settings.setSaveFormData(false);
     settings.setSavePassword(false);

     // Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist
     /*
     解决跨域的问题,访问其他网站接口。
     android端可通过设置setAllowUniversalAccessFromFileURLs来解决跨域访问的目的。
     这个方法目前需要在api level 16以上使用,即对应android系统为4.1版本。也就是说4.1版本以上才可以。
     setAllowFileAccess(boolean allow)
     启用或禁用WebView中的文件访问,注意,这里仅启用或禁用文件系统访问。Assets 和 resources 文件使用file:///android_asset和file:///android_res仍是可访问的。
     setAllowFileAccessFromFileURLs主要用于设置是否允许通过file url加载的Javascript读取其他的本地文件
  setAllowUniversalAccessFromFileURLs可以设置是否允许通过file url加载的Javascript可以访问其他任何的源,也就是说,它包括其他的文件和http,https等其他的源
  如果设置了 setAllowUniversalAccessFromFileURLs为true,则setAllowFileAccessFromFileURLs就不用设置了。
     */
     settings.setAllowUniversalAccessFromFileURLs(true);
     // 设置WebView是否需要用户手势才能播放媒体。默认true。
     settings.setMediaPlaybackRequiresUserGesture(false);

     // Enable database
     // We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
     /* 设置是否支持存储和设置存储路径,设置为Context的私有模式,此模式下,只能应用本身才可以访问,
     在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND */
     /* 普及学习一下这几种模式
     Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
     Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
     MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;
     MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。*/
     String databasePath = webView.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
     settings.setDatabaseEnabled(true);
     settings.setDatabasePath(databasePath);
     //Determine whether we're in debug or release mode, and turn on Debugging!
     ApplicationInfo appInfo = webView.getContext().getApplicationContext().getApplicationInfo();
     if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
         //设置是否支持调试
         enableRemoteDebugging();
     }
     // 设置定位的数据库路径
     settings.setGeolocationDatabasePath(databasePath);

     // Enable DOM storage
     // 设置是否支持Storage存储,DOM Storage 分为 sessionStorage 和 localStorage
     settings.setDomStorageEnabled(true);

     // Enable built-in geolocation
     // 设置是否支持定位
     settings.setGeolocationEnabled(true);

     // Enable AppCache
     // Fix for CB-2282
     // 设置缓存大小5M、路径、是否支持
     settings.setAppCacheMaxSize(5 * 1048576);
     settings.setAppCachePath(databasePath);
     settings.setAppCacheEnabled(true);

     // Fix for CB-1405
     // Google issue 4641
     // 获取代理
     String defaultUserAgent = settings.getUserAgentString();

     // Fix for CB-3360
     // 设置自定义代理
     String overrideUserAgent = preferences.getString("OverrideUserAgent", null);
     if (overrideUserAgent != null) {
         settings.setUserAgentString(overrideUserAgent);
     } else {
         String appendUserAgent = preferences.getString("AppendUserAgent", null);
         if (appendUserAgent != null) {
             settings.setUserAgentString(defaultUserAgent + " " + appendUserAgent);
         }
     }
     // End CB-3360
     // todo  这个广播没看懂有什么作用
     IntentFilter intentFilter = new IntentFilter();
     intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
     if (this.receiver == null) {
         this.receiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 settings.getUserAgentString();
             }
         };
         webView.getContext().registerReceiver(this.receiver, intentFilter);
     }
     // end CB-1405
 }

四、设置nativeToJsMessageQueue模式
NativeToJsMessageQueue本地到js层消息队列,后续文章再分析。

nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
     @Override
     public void setNetworkAvailable(boolean value) {
         //sometimes this can be called after calling webview.destroy() on destroy()
         //thus resulting in a NullPointerException
         if (webView != null) {
             webView.setNetworkAvailable(value);
         }
     }

     @Override
     public void runOnUiThread(Runnable r) {
         SystemWebViewEngine.this.cordova.getActivity().runOnUiThread(r);
     }
 }));
 nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.EvalBridgeMode(this, cordova));

五、创建CordovaBridge实例
CordovaBridge通信桥,后续文章分析。

bridge = new CordovaBridge(pluginManager, nativeToJsMessageQueue);

六、调用exposeJsInterface方法
从下面代码中我们可以看出,先创建了类SystemExposedJsApi,然后调用了webview的addJavascriptInterface方法。addJavascriptInterface方法是Android API 17之前WebKit的原生API,属于WebView对象的公共方法,用于暴露一个java对象给js,使得js可以直接调用方法。因为该方法存在安全隐患问题,现在基本不会用了。详情请参考:https://blog.csdn.net/u012721519/article/details/79173377

@SuppressLint("AddJavascriptInterface")
private static void exposeJsInterface(WebView webView, CordovaBridge bridge) {
    SystemExposedJsApi exposedJsApi = new SystemExposedJsApi(bridge);
    webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
网页封装iOS苹果App源码是指将网页以WebView的形式封装成一个iOS应用程序的源码。使用该源码可以方便地将现有的网页应用快速转化为原生的iOS应用。 网页封装iOS苹果App源码的作用主要有以下几个方面: 1. 提升用户体验:通过使用原生的iOS控件和功能,可以使网页应用在iOS设备上的表现更加流畅和专业,提升用户的体验感。 2. 支持离线功能:封装后的iOS应用可以将网页内容缓存到本地,使得用户在无网络连接时仍然能够使用应用。这对于一些需要频繁访问的网页应用特别有用。 3. 提供更好的推送和通知功能:通过封装成iOS应用,可以利用iOS平台提供的推送和通知功能,使得应用能够主动向用户发送通知,提供更好的实时交互。 4. 提高应用的可发现性:将网页封装成iOS应用后,可以发布到苹果的App Store上,使得更多的用户能够方便地找到并下载使用应用。 5. 与原生功能的集成:封装后的应用可以与iOS设备的原生功能进行深度集成,比如利用摄像头、定位、传感器、联系人等,为用户提供更多的服务和功能。 封装一个网页应用为iOS苹果App源码可以通过使用一些开源的工具或框架来实现,比如Cordova(也称PhoneGap)、React Native等。这些工具/框架提供了一套API和开发工具,使得开发者能够快速地将网页应用转化为iOS应用。 总之,网页封装iOS苹果App源码是一种将现有网页应用封装成原生iOS应用的方法,可以提升用户体验、支持离线功能、添加推送和通知、提高可发现性、与原生功能集成等。通过合适的工具和技术,开发者可以快速地将网页应用封装成iOS应用,以提供给iOS用户使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ruiurrui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值