关于H5框架的一些知识

关于H5框架的一些知识

入职新公司之后,从事了部分H5框架的工做,包括性能优化,新功能开发等。在这以前从没有接触过H5,因此接触之后,就想弄明白底层的一些原理。javascript

  • 1 native层和H5层怎么交互?
  • 2 Android H5底层的实现是怎么样的?
  • 3 chrome底层源码是什么样的,结构复杂吗?有哪些原理?
  • 4 目前大厂中,在用的H5框架是什么样的?

1 native层和H5层怎么交互?

在Android的SDK中,有一个WebView的类,这个类是一个网页界面的控件类。它有个方法是html

/** * @param object the Java object to inject into this WebView's JavaScript context. {@code null} values are ignored. * @param name the name used to expose the object in JavaScript */
public void addJavascriptInterface(Object object, String name) {
    checkThread();
    mProvider.addJavascriptInterface(object, name);
}
复制代码

addJavascriptInterface字面意思在java中添加js接口,也就是说,能够从js中调用这个的java层的接口。具体接口实现是什么样子呢?前端

testWeb.addJavascriptInterface(this, "android");

@JavascriptInterface
public String back() {
  Log.i("jin", "我是JAVA中的back方法,你们好");
  return "123";
}

复制代码

对应的从JS代码中怎么调用呢?注意addJavascriptInterface这个方法中的两个参数,第一个就是注入到JS上下文的对象,第二个参数就是在JS中可使用的对象的名字。因此我看看JS代码中的使用是什么样子的呢?java

<script type="text/javascript">

  function sum(a,b){
    console.log("sum sum sum");
	  return a+b;
  }
  
  function alertMessage(message){
	  alert(message);
  }
  
  function show(){
	 document.getElementById("p").innerHTML="hello,damo";
  }
  
  function s(){
    alert("123");
    console.log("test");
    var result = window.android.back();
    document.getElementById("p").innerHTML=result;
  }
</script>
复制代码

注意是直接调用的window.android.back(),这样JS层就能够调用java层的方法,那么java层如何调用native层呢?只需一个方法便可:android

testWeb.evaluateJavascript("sum(5,20)", new 
    ValueCallback<String>() {
  @Override
  public void onReceiveValue(String value) {   
    Toast.makeText(MainActivity.this, "JS返回告终果, :" + value, Toast.LENGTH_SHORT).
      show();
    }
});
复制代码

2 Android H5底层的实现是怎么样的?

上面简单介绍了一下H5中JS和java层的一个交互,api接口实际上是比较简单的,可是Android系统底层的实现是什么样子的呢?你们必定用过chrome浏览器吧?chrome浏览器是基于google的chromium开源项目 www.chromium.org/ 项目,Android webview底层其实也是基于chromium来实现的。ios

webview的初始化

大体流程,在MainActivity setContentView时候,会去加载页面,最后调用WebView的初始化,WebView初始化流程比较重,有个重要方法是 c++

private void ensureProviderCreated() {
  checkThread();
  if (mProvider == null) {
  // As this can get called during the base class constructor chain, pass the minimum
   // number of dependencies here; the rest are deferred to init().
    mProvider = getFactory().createWebView(this, new PrivateAccess());
  }
}
复制代码

会去先getFactory(),再去执行createWebView,getFactory是经过WebViewFactory.getProvider建立web

static WebViewFactoryProvider getProvider() {
    synchronized (sProviderLock) {
      // For now the main purpose of this function (and the factory abstraction) is to keep
      // us honest and minimize usage of WebView internals when binding the proxy.
      if (sProviderInstance != null) {
        return sProviderInstance;
      }

      final int uid = android.os.Process.myUid();
      if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
          || uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
          || uid == android.os.Process.BLUETOOTH_UID) {
        throw new UnsupportedOperationException(
            "For security reasons, WebView is not allowed in privileged processes");
      }

      if (!isWebViewSupported()) {
        // Device doesn't support WebView; don't try to load it, just throw.
        throw new UnsupportedOperationException();
      }

      if (sWebViewDisabled) {
        throw new IllegalStateException(
            "WebView.disableWebView() was called: WebView is disabled");
      }

      Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
      try {
        Class<WebViewFactoryProvider> providerClass = getProviderClass();
        Method staticFactory = null;
        try {
          staticFactory = providerClass.getMethod(
              CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
        } catch (Exception e) {
          if (DEBUG) {
            Log.w(LOGTAG, "error instantiating provider with static factory method", e);
          }
        }

        Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation");
        try {
          sProviderInstance = (WebViewFactoryProvider)
              staticFactory.invoke(null, new WebViewDelegate());
          if (DEBUG) {
            Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
          }
          return sProviderInstance;
        } catch (Exception e) {
          Log.e(LOGTAG, "error instantiating provider", e);
          throw new AndroidRuntimeException(e);
        } finally {
          Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
        }
      } finally {
        Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
      }
    }
  }
复制代码

里面有个关键方法是getProviderClass,这个方法是这样:chrome

private static Class<WebViewFactoryProvider> getProviderClass() {
    Context webViewContext = null;
    Application initialApplication = AppGlobals.getInitialApplication();
 
    try {
        Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
                "WebViewFactory.getWebViewContextAndSetProvider()");
        try {
            webViewContext = getWebViewContextAndSetProvider();
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
        }
        Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
                sPackageInfo.versionName + " (code " + sPackageInfo.getLongVersionCode() + ")");
 
        Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
        try {
            initialApplication.getAssets().addAssetPathAsSharedLibrary(
                    webViewContext.getApplicationInfo().sourceDir);
            ClassLoader clazzLoader = webViewContext.getClassLoader();
 
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
            WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
                    getWebViewLibrary(sPackageInfo.applicationInfo));
            Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
 
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
            try {
                return getWebViewProviderClass(clazzLoader);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
        } catch (ClassNotFoundException e) {
            Log.e(LOGTAG, "error loading provider", e);
            throw new AndroidRuntimeException(e);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
        }
    } catch (MissingWebViewPackageException e) {
        Log.e(LOGTAG, "Chromium WebView package does not exist", e);
        throw new AndroidRuntimeException(e);
    }
}
复制代码

无非就是经过addAssetPathAsSharedLibrary去加载webview的资源,而后去loadNativeLibrary,回到上面经过反射构造好WebViewFactoryProvider,这个WebViewFactoryProvider就是WebViewChromiumFactoryProvider 这个代码在Android系统库里没有,是Chromium的源码,在Android里是以apk形式存在了,也就是刚才addAssetPathAsSharedLibrary去加载webview的资源, 在个人小米手机Android10.0 上加载的APK名字是 WebViewGoogle.apk就是webview实现的apk,下边那个是啥?暂时无论他 ApkAssets{path=/system/product/app/WebViewGoogle/WebViewGoogle.apk} ApkAssets{path=/system/product/app/TrichromeLibrary/TrichromeLibrary.apk}小程序

回到WebView中 ,mProvider = getFactory().createWebView(this, new PrivateAccess()); 这个mProvider就是 WebViewChromiumFactoryProvider,而后执行createWebView,最后获得的就是WebViewChromium类

public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
    return new WebViewChromium(this, webView, privateAccess, mShouldDisableThreadChecking);
}
复制代码

3 chrome底层源码是什么样的,结构复杂吗?有哪些原理?

参考网上的chromium源码下载,编译,顺利完成了chrome的编译,可是在个人mac上会crash掉,不过暂时不影响。看看chromium的源码目录,操做系统级别的目录吧,有点相似Android源码的感受(记得刚毕业接触Android源码,强哥曾经给我发过一个文档记录每一个Android目录的做用哈),

android_webview,这个前面说过,就是Android系统WebView的底层实现 chrome,chrome的源码 chromeos,chromeos操做系统的源码 fuchsia,fuchsi操做系统的源码 gpu,gpu相关的一些代码 ipc,进程间通讯,chrome是一个多进程的架构 v8,著名的JS的v8引擎 pdf,chrome中pdf插件。 cronet网络库,目前不少大厂,双端网络库底层都是cronet实现的。 等等等等。 总体代码结构,代码量都太庞大,有时间慢慢看吧,chrome的入口在chrome_main.cc中

#define DLLEXPORT __declspec(dllexport)

// We use extern C for the prototype DLLEXPORT to avoid C++ name mangling.
extern "C" {
DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance, sandbox::SandboxInterfaceInfo* sandbox_info, int64_t exe_entry_point_ticks);
}
#elif defined(OS_POSIX)
extern "C" {
__attribute__((visibility("default")))
int ChromeMain(int argc, const char** argv);
}
#endif

#if defined(OS_WIN)
DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance, sandbox::SandboxInterfaceInfo* sandbox_info, int64_t exe_entry_point_ticks) {
#elif defined(OS_POSIX)
int ChromeMain(int argc, const char** argv) {
  int64_t exe_entry_point_ticks = 0;
#endif

#if defined(OS_WIN)
  install_static::InitializeFromPrimaryModule();
#endif

  ChromeMainDelegate chrome_main_delegate( base::TimeTicks::FromInternalValue(exe_entry_point_ticks));
  content::ContentMainParams params(&chrome_main_delegate);

#if defined(OS_WIN)
  // The process should crash when going through abnormal termination, but we
  // must be sure to reset this setting when ChromeMain returns normally.
  auto crash_on_detach_resetter = base::ScopedClosureRunner(
      base::BindOnce(&base::win::SetShouldCrashOnProcessDetach,
                     base::win::ShouldCrashOnProcessDetach()));
  base::win::SetShouldCrashOnProcessDetach(true);
  base::win::SetAbortBehaviorForCrashReporting();
  params.instance = instance;
  params.sandbox_info = sandbox_info;

  // Pass chrome_elf's copy of DumpProcessWithoutCrash resolved via load-time
  // dynamic linking.
  base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash);

  // Verify that chrome_elf and this module (chrome.dll and chrome_child.dll)
  // have the same version.
  if (install_static::InstallDetails::Get().VersionMismatch())
    base::debug::DumpWithoutCrashing();
#else
  params.argc = argc;
  params.argv = argv;
  base::CommandLine::Init(params.argc, params.argv);
#endif // defined(OS_WIN)
  base::CommandLine::Init(0, nullptr);
  const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess());
  ALLOW_UNUSED_LOCAL(command_line);

#if defined(OS_MACOSX)
  SetUpBundleOverrides();
#endif

  // Start the sampling profiler as early as possible - namely, once the command
  // line data is available. Allocated as an object on the stack to ensure that
  // the destructor runs on shutdown, which is important to avoid the profiler
  // thread's destruction racing with main thread destruction.
  MainThreadStackSamplingProfiler scoped_sampling_profiler;

  // Chrome-specific process modes.
#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
  if (command_line->HasSwitch(switches::kHeadless)) {
    return headless::HeadlessShellMain(params);
  }
#endif // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)

  int rv = content::ContentMain(params);

  return rv;
}
复制代码

具体学习教程,参考老罗的Android之旅吧,从中仍是能学习到很多东西的blog.csdn.net/Luoshengyan… 这里想讲下微信小程序中的WebView同层渲染的原理在Android中的实现。所谓的同层渲染,是指java的view和webview在一层渲染,具体优势我不细说了。微信是修改的chrome的内核,扩充了chrome的plugin机制,好比在chrome中支持的pdf的plugin,embed标签,id是plugin。

<embed id="plugin" type="application/x-google-chrome-pdf" src="https://arxiv.org/pdf/1406.2661.pdf"... 复制代码

4 目前大厂中,在用的H5框架是什么样的?

为何要用H5框架呢?所谓的框架无非就是通用功能的一个组合。大厂的App中,有几百个H5界面,若是每一个H5界面和java代码的交互方式都不同,再加上Android和ios两端,估计开发人员脑壳会爆炸,因此在此情境下,前端开发人员须要和客户端人员肯定好一套协议,基于这套协议,咱们就能够开发出一套H5的框架。同时H5框架须要具有加载离线包,api预加载包,WebView池,cookie注入检查,UI操做,同层渲染等等太多的功能。显然每一个H5框架不会简单。

相关文章
相关标签/搜索