背景

大家如果经历过Hybrid项目的开发,即项目中涉及到H5与Native之间的交互,那么很有可能会遇到各种各样的H5容器。为什么会有那么多各种各样的容器呢...这也是轮子多的通病了,轮子多到业务方不知道选哪个。当然,也有可能大家压根就不会使用到H5容器,直接用系统WebView就完事儿了,比如我的前东家就是这样做的。那这篇文章的主题就是与前者相关的:当项目中拥有很多个H5容器时,怎样封装才能让业务侧用得爽呢? 

下面按惯例,抛出这篇文章要解决的三个问题:

  • 如何优雅地提供接口调用?
  • 怎样封装多个不同类型的H5容器?
  • 这样的架构能带来什么样的好处?

术语对齐

术语 描述
H5框架容器 特指目前项目中正在使用的二方/三方H5容器。
H5通用容器 特指将项目中所有的框架容器抽象到一个容器中,让业务侧不需要感知到具体的框架细节。
A页面 url query参数中带a=true的页面,项目中用A容器承载。
B页面 url query参数中带b=true的页面,项目中用B容器承载。

探索

如何优雅地提供接口调用?

业务侧调用的接口其实很有限,可以说90%的业务都只是打开一个网页而已。

我们根据业务需要新建一个接口类:

/**
 * WebPage服务接口类
 */
public interface IWebPageService {
    /**
     * 打开url
     * @param url
     */
    void openUrl(String url);

    /**
     * 创建Fragment内置到某页面中使用
     * @return
     */
    Fragment buildFragment(String url, Context context);

    /**
     * 获取当前Url
     * @return
     */
    String getCurrentUrl();

    /**
     * 给h5发通知
     * @param eventData
     * @param eventName
     */
    void postNotificationToJS(String eventName, String eventData);
}

以上列举了4个非常常见的接口。如果有需要,还可以扩展其它的,比如设置容器生命周期相关的监听、设置H5发通知来时的监听等等。拿openUrl的实现举例,实现类通过url中的参数判断需要打开A容器还是B容器

@Override
public void openUrl(String url) {
     // 若url中含有A参数,则用A容器打开
     // if (urlParamHasA) {
        // startAContainer();
     // } else {
        // 否则用B容器打开
        // startBContainer();
     //}
}

这就从接口层这一层面屏蔽了内部框架容器。其它接口也是类似,原则就是不让业务侧感知到具体的实现细节,而不是跳个页面还需要知道这个url是要用A容器打开还是B容器打开。在应用启动时,可以将实现类注入到公共依赖中去,也可以通过ARouter等框架实现依赖注入,方便业务侧的调用。

怎样封装多个不同类型的H5容器容器?

整体架构

先上一张图:

接口层上面已经讲过了,这里我们直接看容器层。

通用容器

第一层就是我们抽出来的通用容器,WebPageActivity与WebPageFragement。项目中,不管是哪个H5框架容器,势必都是用Activity或者Fragment来承载的,所以我们相应的也需要有这两者作为父类去进行封装。而通用容器中,最重要的职责就是去执行通用逻辑,即每个框架容器都需要执行的逻辑。

通用职责可以有以下几点:

  • 设置页面属性,包括window flag、activity的主题等。
  • 解析url,将url中的关键参数记录下来,从而决定以哪种策略渲染UI。比如可以通过disableNav参数决定是否显示导航栏,通过statusBarStyle参数决定状态栏的风格等。
  • 控制通用UI的渲染,包括何时改变TitleBar、何时加载/隐藏Loading,何时渲染异常态UI等。
  • 决定框架容器加载时的逻辑顺序,如setContentView()->loadUrl()。
  • 接收生命周期事件与系统回调事件,并且分发回调。生命周期就不用说了,系统回调这里特指onActivityResultonRequestPermissionResult这类事件,然后将结果分发给各listener。

WebPageActivity#onCreate举例:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // setWindowFlags(); 设置window属性
    // setTheme(); 设置主题
    // setTransition(); 设置转场动画
    
    super.onCreate(savedInstanceState);

    // parseUrl(); 解析url
    // initCommonUI(); 渲染通用UI 比如TitleBar、底部导航栏等
    
    setContentView();
    loadUrl();
}

protected abstract void setContentView();

protected abstract void loadUrl();

我们可以将设置window属性、设置主题这些通用行为放在通用容器中执行,而setContentViewloadUrl交由框架容器去执行。setContentView是因为不同的容器,layout xml可能是不一样的。loadUrl是因为每个容器用WebView去加载url的方式也可能不同,无法统一。类似的行为,若执行时机可统一,则由通用容器统一执行时机,比如setContentViewloadUrl。若时机也没法确定,甚至逻辑只会存在于某个框架容器中,那么就要写到子类中去了。

框架容器

图中第二层的容器A、容器B、容器C指的就是框架容器。有了通用容器承载共同的逻辑后,框架容器需要做的事情就比较少了。只需要加载各自的布局,初始化WebView,然后将url加载到WebView中就可以了(除非有某个框架容器需要特殊适配,那就得写些额外的代码)。具体的实现需要根据项目中框架容器的使用方式来确定。

基础组件

第三层表示基础组件。这里只列一些常见的基础组件。

  • Components:各类UI组件,比如TitleBar与异常态控件。
  • JS Bridge:JS Bridge可以单独放到一个类中做收口,然后分发给各个listener。listenr通过接口层进行注入。
  • WebViewClient:WebViewClient在日常开发中算是维护的非常频繁的一个类,职责主要是在shouldOverrideUrlLoading重定向时对url进行拦截,以及根据相应的生命周期回调(onPageStarted、onPageFinished、onReceviedError等)进行loading或异常态UI的渲染时。若每个框架容器可以设置同一个WebViewClient,那就非常方便了,只要改一处地方,所有容器就都生效了。
  • Interceptors:即用于WebView重定向时的拦截器。像我的项目中会有很多拦截相关的逻辑,比如将A页面拦截跳转到原生页面,就需要在重定向时进行拦截。但逻辑堆叠太多会提高维护的成本,因此建议用责任链模式(没用过的同学可以百度一下,很简单滴~)去处理拦截的这块逻辑。
  • Managers、Listeners:容器中总是需要很多辅助类去来帮助我们去管理一些东西,比如WebView的管理,url的管理。这里就不延伸了,根据实际需要去加。Listener也一样,关于容器生命周期的Listener,与JS Bridge交互相关的Listener,都可以通过接口层去实现注入与销毁。

当然,除此之外,根据每个项目的完整度与负责度,还会衍生出很多基础组件。比如H5的监控体系,H5的预加载体系,以及WebView预创建相关的缓存体系等。他们作为框架容器共同的底座承担相应的职责。

这样的架构能带来什么样的好处?

  • 对业务侧而言,接口统一收口到IWebPageService中去,调用方式更加简单明了,不需要再关注容器的具体类型。
  • 对容器侧而言,代码逻辑性清晰的同时,有效降低了日后的维护成本与开发成本。Loading、异常态不需要再在各类容器中重复写N遍,各种url params都统一收口到通用容器中去,重定向拦截逻辑也不再需要重复写N遍...总而言之,通用逻辑统一给通用容器与基础组件去承担,框架容器的代码,都是各框架容器所“独有”的。

到此这篇关于Android H5通用容器架构设计详解的文章就介绍到这了,更多相关Android H5容器架构内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

​​​​​​​Android H5通用容器架构设计详解的更多相关文章

  1. HTML5页面无缝闪开的问题及解决方案

    这篇文章主要介绍了HTML5页面无缝闪开方案,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. ios – Xcode 9架构x86_64的未定义符号

    先谢谢你的帮助.解决方法陷入类似的问题.只需在此处发布,以防它可以帮助任何人.在我们的应用程序的Objective-c类B中使用来自框架的类AbcService.从Xcode8/Swift3升级到Xcode9/Swift4后,由于未定义的符号,它无法为发布配置构建存档链接期间此类的错误:奇怪的是,它可以存档调试配置,并在模拟器中运行.花了差不多1天才找出根本原因:框架中的Swift类AbcService缺少一个公共修饰符.

  3. ios – 将容器带到视图前方

    我怎样才能解决这个问题?

  4. ios – 如何使用XCode 6.4下载和替换AppGroup容器

    我知道如何使用XCode6的Devices窗口下载和替换特定iOS应用程序的文件系统容器.但是对于我正在开发的应用程序,我需要能够下载和替换共享的AppGroup容器以进行调试.这将使我能够模拟AppGroup文件夹内容中的情况以进行测试.任何人都可以告诉我如何做到这一点?

  5. ios – Xcode 7.1 PrototypeTools链接器错误(仅限模拟器)

    我正在尝试使用Xcode7.1在iOS模拟器中运行我的应用程序,但我收到链接器错误.这是错误:clang:错误:链接器命令失败,退出代码为1这似乎是一个新问题,因为我在升级到新的Xcode之前没有它.我在Google上搜索过,但是这个问题几乎没有相关主题.有趣的是,该应用程序在设备上运行良好.我已经尝试重置模拟器并再次清洁/建造,但两者都没有奏效.有没有其他人遇到过这个问题,你能提供什么建议吗?

  6. ios – 在UITableView上移动UIView – 触摸顶部UIView仍然选择表行

    =======用一些代码编辑:这是我在容器B中所做的代码.这是B帧的一个非常直接的动画.self.view是ContainerB的UIView.所有视图都在屏幕上通过故事板.其他容器是B的子视图.请让我知道你想看到的其他代码.解决方法嗯……不确定这是否适用于您的情况,但尝试在容纳所有其他容器的更大容器中管理您的动画.我的意思是,创建一个包含A,B,O及其子视图的ContainerZ,并尝试从Z中设置B的位置动画,检查B是否在A前面.

  7. ios – 与容器视图通信的最佳实践是什么?

    我最近经常使用容器VC,我一直想知道主Vc和容器VC之间的最佳通信方式是什么.现在我正在使用通知,但我宁愿使用更好的东西.如何获取指向容器VC的指针,以便至少可以使用委托?

  8. xcode – 这是什么意思:架构的未定义符号x86_64“_main”

    在Xcode上构建我的应用程序时,我不断收到错误:我的代码没有错误,错误是奇怪的错误,我做的任何事情都无法解决.我不认为该代码与问题相关,因为它在错误中未被引用.这是错误的屏幕截图.解决方法链接器无法在主swift入口点的链接过程中找到任何文件.这可能是由于文件在项目中根本不存在,或者它存在但尚未包含在编译或链接构建阶段.在Swift中,主要的快速入口点在主要的SWF模块中定义,或者在AppDel

  9. ios – 当架构设置为64位时,图像不显示

    任何线索?

  10. ios – 编程嵌入UIViewController?

    我有一个带有一个容器视图的UIViewController的Storyboard设置,以便我可以在其中嵌入另一个UIViewController.在某种情况下,我需要更改嵌入式视图控制器.在我的故事板中,我的容器视图不能有两个区段.这导致我以编程方式进行.我的容器视图在我的故事板,没有连接的嵌入.现在从这一点开始,我如何以编程方式嵌入我所选择的UIViewController对象?

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部