├── .gitignore ├── Image ├── 1.gif ├── 2.gif └── title.png ├── LICENSE ├── README.md ├── README_CN.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── lalamove │ │ └── huolala │ │ └── client │ │ └── offline_web │ │ ├── Constants.java │ │ ├── DemoActivity.java │ │ ├── EnhDemoActivity.java │ │ ├── FileDownLoadHelper.java │ │ ├── MainActivity.java │ │ ├── OfflineWebInitJob.java │ │ ├── request │ │ └── DefaultLocalRequest.java │ │ ├── util │ │ ├── DisplayUtils.java │ │ ├── HttpsUtils.java │ │ ├── IoThreadPool.java │ │ └── OkHttpClientManager.java │ │ └── widget │ │ ├── BottomView.java │ │ ├── GradientColor.java │ │ ├── HllDrawable.java │ │ ├── HllRoundBackground.java │ │ ├── OffWebBottomView.java │ │ └── OffWebButton.java │ └── res │ ├── anim │ ├── off_web_bottom_anim_enter.xml │ └── off_web_bottom_anim_exit.xml │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable-xxhdpi │ ├── off_web_avatar_icon.png │ ├── off_web_cash_icon.png │ ├── off_web_close_icon.webp │ ├── off_web_logo_icon.png │ ├── off_web_main_bg.png │ ├── off_web_name_logo_img.png │ ├── off_web_navigation_icon.png │ └── off_web_update_icon.png │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── off_web_activity_demo.xml │ ├── off_web_activity_main.xml │ ├── off_web_bottom_dialog_layout.xml │ └── off_web_btn_layout.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-night │ └── themes.xml │ ├── values │ ├── attrs.xml │ ├── colors.xml │ ├── strings.xml │ ├── styles.xml │ └── themes.xml │ └── xml │ └── network_security_config.xml ├── assets └── act3-2108-turntable.zip ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib_offline_webview ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── lalamove │ └── huolala │ └── offline │ └── webview │ ├── Interceptor │ ├── DefaultInterceptor.java │ └── IInterceptor.java │ ├── OfflineConfig.java │ ├── OfflinePageManager.java │ ├── OfflineParams.java │ ├── OfflineTaskManager.java │ ├── OfflineWebClient.java │ ├── OfflineWebManager.java │ ├── download │ ├── DefaultDownloader.java │ ├── FileDownLoadHelper.java │ └── IDownLoader.java │ ├── flow │ ├── FlowReportParams.java │ ├── FlowResultHandleStrategy.java │ └── IFlowResultHandleStrategy.java │ ├── info │ ├── OfflinePackageInfo.java │ ├── OfflineRuleConfig.java │ └── OfflineZipPackageConfig.java │ ├── log │ ├── Logger.java │ └── OfflineWebLog.java │ ├── matcher │ ├── BisNameMatcher.java │ └── DefaultMatcher.java │ ├── monitor │ ├── OfflineWebMonitorUtils.java │ ├── base │ │ ├── IEnhWebMonitor.java │ │ ├── IEnhWebResourceErrorAdapter.java │ │ ├── IEnhWebResourceRequestAdapter.java │ │ ├── IEnhWebResourceResponseAdapter.java │ │ └── IWebPageStatus.java │ └── impl │ │ ├── EnhWebResourceErrorAdapterImpl.java │ │ ├── EnhWebResourceRequestAdapterImpl.java │ │ ├── EnhWebResourceResponseAdapterImpl.java │ │ └── OfflineWebPageStatus.java │ ├── net │ ├── IOfflineRequest.java │ └── RequestCallback.java │ ├── proxy │ ├── EmptyOfflineWebViewProxy.java │ ├── IOfflineWebViewProxy.java │ ├── OffWebProxyFactory.java │ └── OfflineWebViewProxy.java │ ├── resource │ ├── DownloadFlow.java │ ├── FetchPackageFlow.java │ ├── ParsePackageFlow.java │ ├── ReplaceResFlow.java │ └── ResourceFlow.java │ ├── task │ ├── CheckAndUpdateTask.java │ ├── CheckVersionTask.java │ └── CleanTask.java │ ├── threadpool │ ├── IExecutorServiceProvider.java │ └── OfflineIoThreadPool.java │ ├── utils │ ├── EnhWebUriUtils.java │ ├── MemoryConstants.java │ ├── OffWebRuleUtil.java │ ├── OfflineConstant.java │ ├── OfflineFileUtils.java │ ├── OfflineGsonUtils.java │ ├── OfflineHandlerUtils.java │ ├── OfflinePackageUtil.java │ ├── OfflineStringUtils.java │ └── UrlParamsUtils.java │ └── widget │ ├── EnhOfflineWebView.java │ ├── EnhWebViewClient.java │ ├── IOfflineWebView.java │ ├── OfflineWebView.java │ └── ReloadOfflineWebView.java ├── lib_web ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── lalamove │ └── huolala │ └── client │ └── lib │ ├── DownloadHandler.java │ ├── HttpServerStarter.java │ ├── OffWebHandler.java │ ├── OfflinePackageInfo.java │ ├── ParamsUtil.java │ └── ServerConstant.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | -------------------------------------------------------------------------------- /Image/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/Image/1.gif -------------------------------------------------------------------------------- /Image/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/Image/2.gif -------------------------------------------------------------------------------- /Image/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/Image/title.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![license](https://img.shields.io/hexpm/l/plug.svg)](https://www.apache.org/licenses/LICENSE-2.0) ![release](https://img.shields.io/badge/release-v1.0.0-green.svg) ![Platform](https://img.shields.io/badge/platform-android-blue.svg) ![API](https://img.shields.io/badge/API-21-green.svg) 4 | 5 | HLLOfflineWebView is a lightweight and high-performance hybrid framework developed by HUOLALA mobile team, which is intended to improve the load speed of websites on mobile phone. It base on [WebView](https://developer.android.google.cn/guide/webapps/webview) at Android system. 6 | HLLOfflineWebView can cache html, css, js, png and other static resource on the disk. When the app load the web page, it directly load the resource from disk and reduce network request. 7 | 8 | ## Before VS After Using HLLOfflineWebView 9 | 10 | | |Before Using HLLOfflineWebVC | After Using HLLOfflineWebVC 11 | |--|:-------------------------:|:-------------------------: 12 | |Movie| | 13 | 14 | ## Features 15 | 16 | - Safe and reliable: no hook and no private API,  three degrade strategy. 17 | - Easy to maintain: three layer structure and modular design. 18 | - Fully functional: it contains H5 offline resource managing, url and offline resource mapping config, data reporting. 19 | 20 | 21 | ## How To Use 22 | If you want to bring the code into your project, you need to do the following: 23 | 24 | ### Develop A HTTP Service 25 | - HTTP Request 26 | 27 | https://www.xxx.com/queryOffline?clientType=Android&clientVer=1.0.0&offlineZipVer=1.0.0&bisName=xx 28 | 29 |  requet data parameters description: 30 | 31 | | parameter name | parameter meaning | note | 32 | |------------------|-------------------------------|-------------------------------------| 33 | | clientType | operating system type | iOS,Android | 34 | | clientVersion | app version | eg: 1.0 | 35 | | bisName | unique identifier of your offline web page | eg: act3-offline-package-test | 36 | | offlineZipVer | the local offline web file version | 0 means no offline web cache | 37 | 38 | 39 | 40 |  respond data format is a json,parameters description: 41 | 42 | | parameter name | parameter meaning | note | 43 | |--------------|-------------|--------------------------------------------------------------------------------------------| 44 | | bisName | unique identifier of your offline web page | eg: act3-offline-package-test | 45 | | result | query result | -1: disable offline web, 0: no update, 1: has new version | 46 | | url | zip file download url | if no update, the url is null | 47 | | refreshMode |notify client how to update | 0:update next(default) 1:update immediately | 48 | | version | offline web pages version | eg: 25609-j56gfa | 49 | 50 | - Cross Origin 51 | When an offline web page make a network request, the origin is null,should modify your gateway or server to support. 52 | 53 | - Set up a local service 54 | 55 | 1.Set LOCALHOST in ServerConstant to your IP 56 | 2.Run HttpServerStarter with Coverage 57 | 3.Test in the chrome 58 | http://127.0.0.1:8888/offweb?bisName=act3-2108-turntable&offlineZipVer=ab 59 | http://127.0.0.1:8888/package?bisName=act3-2108-turntable 60 | 61 | ``` 62 | requestServer(new DefaultLocalRequest()) 63 | ``` 64 | - Modify HTML And JS File 65 | use relative path, no absolute path. 66 | cookie、local storage should support the situation that host is null. 67 | add a file to describe the offline web version .the file name is ".offweb.json" and the content is: 68 | { "bisName": "xxx", "ver": "xxx" } 69 | 70 | 71 | ### Getting Started 72 | 73 | 1) Simple init HLLOfflineWebView 74 | 75 | ``` 76 | OfflineConfig offlineConfig = new OfflineConfig.Builder(true) 77 | //.addDisable("act3-2109-turntable")//disable package 78 | .addPreDownload("uappweb-offline")//PreDownload the package 79 | .build(); 80 | OfflineWebClient.init(context.getApplicationContext(), 81 | new OfflineParams() 82 | .config(offlineConfig) 83 | .requestServer(new DefaultLocalRequest()) 84 | //.requestServer(new DefaultRequest()) 85 | .isDebug(BuildConfig.DEBUG) 86 | ); 87 | ``` 88 | 89 | 2) Full init HLLOfflineWebView 90 | 91 | ``` 92 | OfflineWebClient.init(context.getApplicationContext(), 93 | new OfflineParams() 94 | //need config 95 | .config(offlineConfig) 96 | //need network request 97 | .requestServer(new DefaultLocalRequest()) 98 | //need Debug mode 99 | .isDebug(BuildConfig.DEBUG) 100 | //Optional DownLoader 101 | .downloader(new IDownLoader() {}) 102 | //Optional rule 103 | .addRule("act3-2108-turntable", host, path, fragment) 104 | //Optional Rules in Json format 105 | .setRule(Constants.RULE_CONFIG) 106 | //Optional Rules Config 107 | .setRule(offlineRuleConfig) 108 | //Optional Logger 109 | .logger(new Logger() {}) 110 | //Optional Url Matcher 111 | .matcher(new DefaultMatcher()) 112 | //Optional ExecutorService 113 | .executorServiceProvider(new IExecutorServiceProvider() {}) 114 | //Optional WebMonitor 115 | .monitor(new IEnhWebMonitor() {}) 116 | //Optional Interceptor 117 | .interceptor(new IInterceptor() {}) 118 | //Optional update callback 119 | .flowResultHandleStrategy(new IFlowResultHandleStrategy() {}) 120 | 121 | ); 122 | ``` 123 | 124 | ## Communication 125 | - If you find a bug, open an issue. 126 | - If you have a feature request, open an issue. 127 | - If you want to contribute, submit a pull request. 128 | 129 | ## Blog 130 | [货拉拉安卓端H5离线包原理与实践](https://juejin.cn/post/7119662876578545678/) 131 | 132 | ## Author 133 | [HUOLALA mobile technology team](https://juejin.cn/user/1768489241815070). 134 | ## License 135 | HLLOfflineWebView is released under the Apache 2.0 license. See [LICENSE](LICENSE) for details. -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![license](https://img.shields.io/hexpm/l/plug.svg)](https://www.apache.org/licenses/LICENSE-2.0) ![release](https://img.shields.io/badge/release-v1.0.0-green.svg) ![Platform](https://img.shields.io/badge/platform-android-blue.svg) ![API](https://img.shields.io/badge/API-21-green.svg) 4 | 5 | ## 介绍 6 | HLLOfflineWebView是货拉拉自研的轻量级高性能H5离线包sdk,可以显著的提升H5页面加载速度,Android端基于WebView实现。 7 | 主要原理为:提前缓存html、js、css、图片等资源文件到静态到本地,当H5页面请求资源时,尽量从本地获取数据,减少网络请求。 8 | 9 | ## 比较 10 | | |未使用离线包 | 使用离线包 11 | |--|:-------------------------:|:-------------------------: 12 | |视频| | 13 | 14 | 15 | - 安全可靠:无hook,无私有API,具有三重降级策略,保证可靠性 16 | - 容易维护:三层架构模式和模块化设计 17 | - 功能完备:功能可配置,数据埋点 18 | 19 | 20 | ## 项目使用 21 | 如果要在实际项目中使用,需要采取如下步骤: 22 | ### 离线包服务搭建 23 | - 实现查询接口请求格式 24 | https://www.xxx.com/queryOffline?clientType=Android&clientVer=1.0.0&offlineZipVer=1.0.0&bisName=xxx 25 | 请求参数接口参数说明: 26 | 27 | | 参数名 | 参数含义 | 备注 | 28 | |------------------|-------------------------------|-------------------------------------| 29 | | os | 终端类型 | iOS,Android | 30 | | clientVersion | 客户端版本 | | 31 | | bisName | 业务名,每个页面的离线包独立 | 自定义参数,业务名 | 32 | | offlineZipVer | 本地离线包版本 | 自定义参数,0表示本地无 | 33 | 34 | 35 | 查询结果返回结果为json,参数说明: 36 | 37 | | 参数名 | 参数含义 | 备注 | 38 | |--------------|-------------|--------------------------------------------------------------------------------------------| 39 | | bisName | 业务名 | | 40 | | result | 结果 | -1 禁用离线包 0 无更新 1 有新离线包,全量包 | 41 | | url | 离线包(zip压缩包)下载地址 | 没有时为空字符串 | 42 | | refreshMode | 刷新模式 | 0 下次刷新(默认) 1 马上强制刷新(极端情况下使用)新 | 43 | | version | 离线包版本 | 区分版本 | 44 | 45 | - 接口跨域处理 46 | H5离线包加载的路径为文件路径为,发起cgi请求时,origin为null,需要网关或者后端添加跨域支持。 47 | 48 | - 本地服务搭建 49 | 50 | 1.本地服务IP修改:ServerConstant中LOCALHOST修改为自己的ip地址(mac 获取 终端输入命令行:ifconfig | grep "inet") 51 | 2.Coverage模式运行HttpServerStarter 52 | 3.电脑浏览器测试两个接口 53 | http://127.0.0.1:8888/offweb?bisName=act3-2108-turntable&offlineZipVer=ab 54 | http://127.0.0.1:8888/package?bisName=act3-2108-turntable 55 | 56 | ``` 57 | requestServer(new DefaultLocalRequest()) 58 | ``` 59 | - H5端改造 60 | 使用相对路径。引用的本地js、css等文件路径需要改成相对路径。 61 | cookie、localstorage等存储跨域支持。 62 | 加入版本文件。离线包资源包大包时加入版本描述文件".offweb.json",格式为: 63 | { "bisName": "xxx", "ver": "xxx" } 64 | ### 客户端接入 65 | 66 | 1) 简易初始化 67 | 68 | ``` 69 | OfflineConfig offlineConfig = new OfflineConfig.Builder(true) 70 | //.addDisable("act3-2109-turntable")//禁用业务名称 71 | .addPreDownload("uappweb-offline")//预加载业务名称 72 | .build(); 73 | OfflineWebClient.init(context.getApplicationContext(), 74 | new OfflineParams() 75 | .config(offlineConfig)//必须 76 | .requestServer(new DefaultLocalRequest())//必须 77 | //.requestServer(new DefaultRequest()) 78 | .isDebug(BuildConfig.DEBUG) 79 | ); 80 | ``` 81 | 82 | 2) 完整初始化 83 | 84 | ``` 85 | OfflineWebClient.init(context.getApplicationContext(), 86 | new OfflineParams() 87 | //离线包配置必须 88 | .config(offlineConfig) 89 | //离线包更新请求必须 90 | .requestServer(new DefaultLocalRequest()) 91 | //是否为debug包 92 | .isDebug(BuildConfig.DEBUG) 93 | //可选 下载器 94 | .downloader(new IDownLoader() {}) 95 | //可选 添加单个匹配规则 96 | .addRule("act3-2108-turntable", host, path, fragment) 97 | //可选 json形式添加规则 98 | .setRule(Constants.RULE_CONFIG) 99 | //可选 实体类形式添加规则 100 | .setRule(offlineRuleConfig) 101 | //可选 日志打印 102 | .logger(new Logger() {}) 103 | //可选 匹配器 104 | .matcher(new DefaultMatcher()) 105 | //可选 线程池 106 | .executorServiceProvider(new IExecutorServiceProvider() {}) 107 | //可选 监控 108 | .monitor(new IEnhWebMonitor() {}) 109 | //可选 拦截器 110 | .interceptor(new IInterceptor() {}) 111 | //可选 获取更新包流程结束回调 实现之后 112 | .flowResultHandleStrategy(new IFlowResultHandleStrategy() {}) 113 | 114 | ); 115 | ``` 116 | 117 | ## 问题交流 118 | - 如果你发现了bug或者有其他功能诉求,欢迎提issue。 119 | - 如果想贡献代码,可以直接发起MR。 120 | 121 | ## 技术博客 122 |    [货拉拉安卓端H5离线包原理与实践](https://juejin.cn/post/7119662876578545678/) 123 | ## 作者 124 |    [货拉拉移动端技术团队](https://juejin.cn/user/1768489241815070) 125 | ## 许可证 126 |   采用Apache 2.0协议,详情参考[LICENSE](LICENSE) 127 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | id 'kotlin-android-extensions' 6 | } 7 | 8 | android { 9 | compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION) 10 | buildToolsVersion BUILDTOOLS_VERSION 11 | 12 | defaultConfig { 13 | applicationId "com.lalamove.huolala.client.offline_web" 14 | minSdkVersion Integer.parseInt(MIN_SDK_VERSION) 15 | targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION) 16 | versionCode 1 17 | versionName "1.0" 18 | manifestPlaceholders = [ 19 | AUTHORITIES: "com.lalamove.huolala.client.fileprovider", 20 | ] 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation "com.google.code.gson:gson:2.8.5" 38 | implementation "androidx.appcompat:appcompat:1.2.0" 39 | implementation "androidx.constraintlayout:constraintlayout:1.1.3" 40 | implementation "androidx.annotation:annotation:1.2.0" 41 | implementation "com.squareup.okhttp3:okhttp:3.12.10" 42 | implementation project(path: ':lib_offline_webview') 43 | implementation project(path: ':lib_web') 44 | 45 | implementation "androidx.core:core-ktx:1.6.0" 46 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 47 | implementation "com.squareup.okhttp3:okhttp:3.12.10" 48 | implementation 'androidx.cardview:cardview:1.0.0' 49 | 50 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/Constants.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web; 2 | 3 | import com.lalamove.huolala.client.lib.ServerConstant; 4 | 5 | /** 6 | * @copyright:深圳依时货拉拉科技有限公司 7 | * @fileName: Constants 8 | * @author: kelvin 9 | * @date: 3/29/22 10 | * @description: 11 | * @history: 12 | */ 13 | 14 | public interface Constants { 15 | String BASE_URL = ""; 16 | String LOCALHOST = ServerConstant.LOCALHOST; 17 | String LOCAL_BASE_URL = LOCALHOST + ServerConstant.PATH_OFF_WEB; 18 | 19 | String RULE_CONFIG = "{\"rules\":[{\"path\":[\"/uapp\"],\"offweb\":\"uappweb-offline\",\"host\":[\"www.huolala.cn\"],\"fragmentprefix\":[\"/xxxxxx\",\"/cd-index\"]},{\"path\":[\"/van/act3-turntable-x\"],\"offweb\":\"act3-2108-turntable\",\"host\":[\"www.huolala.cn\",\"www.baidu.com\"]}]}"; 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web; 2 | 3 | 4 | import android.app.Activity; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.ViewGroup; 8 | import android.webkit.WebSettings; 9 | import android.webkit.WebView; 10 | import android.webkit.WebViewClient; 11 | import android.widget.FrameLayout; 12 | 13 | import androidx.appcompat.app.AppCompatActivity; 14 | 15 | import com.lalamove.huolala.offline.webview.widget.OfflineWebView; 16 | 17 | /** 18 | * @copyright:深圳依时货拉拉科技有限公司 19 | * @fileName: DemoActivity 20 | * @author: kelvin 21 | * @date: 2022/5/13 22 | * @description: Demo页面 23 | * @history: 24 | */ 25 | public class DemoActivity extends AppCompatActivity { 26 | 27 | 28 | public static void start(Activity activity, String url) { 29 | Intent intent = new Intent(activity, DemoActivity.class); 30 | intent.putExtra("url", url); 31 | intent.putExtra("title", "demo_title"); 32 | intent.putExtra("token", ""); 33 | intent.putExtra("deviceid", "demo_id"); 34 | activity.startActivity(intent); 35 | } 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.off_web_activity_demo); 41 | 42 | FrameLayout webContainer = findViewById(R.id.web_container); 43 | OfflineWebView webView = new OfflineWebView(this); 44 | webContainer.addView(webView,new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 45 | initWebViewSettings(webView); 46 | webView.setWebViewClient(new WebViewClient(){ 47 | @Override 48 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 49 | view.loadUrl(url); 50 | return true; 51 | } 52 | 53 | }); 54 | 55 | if (getIntent() != null) { 56 | String url = getIntent().getStringExtra("url"); 57 | webView.loadUrl(url); 58 | } 59 | } 60 | public void initWebViewSettings(WebView webView) { 61 | webView.setClickable(true); 62 | webView.setOnTouchListener((v, event) -> { 63 | return false; 64 | }); 65 | WebSettings webSetting = webView.getSettings(); 66 | webSetting.setJavaScriptEnabled(true); 67 | webSetting.setBuiltInZoomControls(true); 68 | webSetting.setJavaScriptCanOpenWindowsAutomatically(true); 69 | webSetting.setDomStorageEnabled(true); 70 | webSetting.setAllowContentAccess(true); 71 | webSetting.setAllowFileAccess(true); 72 | webSetting.setSupportZoom(true); 73 | webSetting.setUseWideViewPort(true); 74 | webSetting.setSupportMultipleWindows(true); 75 | webSetting.setAppCacheEnabled(true); 76 | webSetting.setGeolocationEnabled(true); 77 | webSetting.setAppCacheMaxSize(9223372036854775807L); 78 | webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND); 79 | webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH); 80 | webSetting.setBlockNetworkImage(false); 81 | webSetting.setMixedContentMode(0); 82 | webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); 83 | webSetting.setLoadWithOverviewMode(true); 84 | webSetting.setSupportMultipleWindows(false); 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/EnhDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web; 2 | 3 | 4 | import android.app.Activity; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.ViewGroup; 8 | import android.webkit.WebSettings; 9 | import android.webkit.WebView; 10 | import android.widget.FrameLayout; 11 | 12 | import androidx.appcompat.app.AppCompatActivity; 13 | 14 | import com.lalamove.huolala.offline.webview.widget.EnhOfflineWebView; 15 | 16 | /** 17 | * @copyright:深圳依时货拉拉科技有限公司 18 | * @fileName: EnhDemoActivity 19 | * @author: kelvin 20 | * @date: 2022/5/13 21 | * @description: 带监控的Demo页面 22 | * @history: 23 | */ 24 | public class EnhDemoActivity extends AppCompatActivity { 25 | 26 | 27 | public static void start(Activity activity, String url) { 28 | Intent intent = new Intent(activity, EnhDemoActivity.class); 29 | intent.putExtra("url", url); 30 | intent.putExtra("title", "demo_title"); 31 | intent.putExtra("token", ""); 32 | intent.putExtra("deviceid", "demo_id"); 33 | activity.startActivity(intent); 34 | } 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.off_web_activity_demo); 40 | 41 | FrameLayout webContainer = findViewById(R.id.web_container); 42 | EnhOfflineWebView webView = new EnhOfflineWebView(this); 43 | webContainer.addView(webView,new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 44 | initWebViewSettings(webView); 45 | 46 | if (getIntent() != null) { 47 | String url = getIntent().getStringExtra("url"); 48 | webView.loadUrl(url); 49 | } 50 | } 51 | public void initWebViewSettings(WebView webView) { 52 | webView.setClickable(true); 53 | webView.setOnTouchListener((v, event) -> { 54 | return false; 55 | }); 56 | WebSettings webSetting = webView.getSettings(); 57 | webSetting.setJavaScriptEnabled(true); 58 | webSetting.setBuiltInZoomControls(true); 59 | webSetting.setJavaScriptCanOpenWindowsAutomatically(true); 60 | webSetting.setDomStorageEnabled(true); 61 | webSetting.setAllowContentAccess(true); 62 | webSetting.setAllowFileAccess(true); 63 | webSetting.setSupportZoom(true); 64 | webSetting.setUseWideViewPort(true); 65 | webSetting.setSupportMultipleWindows(true); 66 | webSetting.setAppCacheEnabled(true); 67 | webSetting.setGeolocationEnabled(true); 68 | webSetting.setAppCacheMaxSize(9223372036854775807L); 69 | webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND); 70 | webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH); 71 | webSetting.setBlockNetworkImage(false); 72 | webSetting.setMixedContentMode(0); 73 | webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); 74 | webSetting.setLoadWithOverviewMode(true); 75 | webSetting.setSupportMultipleWindows(false); 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | import com.lalamove.huolala.client.offline_web.widget.OffWebBottomView; 9 | import com.lalamove.huolala.offline.webview.OfflineWebClient; 10 | import com.lalamove.huolala.offline.webview.info.OfflinePackageInfo; 11 | import com.lalamove.huolala.offline.webview.resource.ResourceFlow; 12 | import com.lalamove.huolala.offline.webview.utils.OfflineHandlerUtils; 13 | 14 | /** 15 | * @copyright:深圳依时货拉拉科技有限公司 16 | * @fileName: MainActivity 17 | * @author: kelvin 18 | * @date: 2022/5/13 19 | * @description: demo首页 20 | * @history: 21 | */ 22 | public class MainActivity extends AppCompatActivity { 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.off_web_activity_main); 28 | init(); 29 | } 30 | 31 | private void init() { 32 | OfflineWebInitJob offlineWebJob = new OfflineWebInitJob(); 33 | //初始化所有自定义参数 34 | offlineWebJob.initAll(this); 35 | //只初始化必须参数 36 | offlineWebJob.init(this); 37 | initListener(); 38 | } 39 | 40 | private void initListener() { 41 | View openEhnWebView = findViewById(R.id.open_ehn_web); 42 | View updateView = findViewById(R.id.update_btn); 43 | View openNormalView = findViewById(R.id.open_normal_btn); 44 | View cleanView = findViewById(R.id.clean_btn); 45 | updateView.setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View v) { 48 | checkUpdate(); 49 | } 50 | }); 51 | openNormalView.setOnClickListener(new View.OnClickListener() { 52 | @Override 53 | public void onClick(View v) { 54 | DemoActivity.start(MainActivity.this, "https://www.baidu.com/?offweb=act3-2108-turntable"); 55 | } 56 | }); 57 | openEhnWebView.setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View v) { 60 | EnhDemoActivity.start(MainActivity.this, "https://www.baidu.com/?offweb=act3-2108-turntable"); 61 | } 62 | }); 63 | cleanView.setOnClickListener(new View.OnClickListener() { 64 | @Override 65 | public void onClick(View v) { 66 | OfflineWebClient.clean(); 67 | OffWebBottomView.show(MainActivity.this, "清理离线包", "清理成功!", null); 68 | } 69 | }); 70 | } 71 | 72 | private void checkUpdate() { 73 | //通常我们在后台静默更新,不需要回调。 74 | OfflineWebClient.checkPackage("act3-2108-turntable", new ResourceFlow.FlowListener() { 75 | @Override 76 | public void done(OfflinePackageInfo packageInfo) { 77 | OfflineHandlerUtils.runOnUiThread(new Runnable() { 78 | @Override 79 | public void run() { 80 | OffWebBottomView.show(MainActivity.this, 81 | "更新离线包", 82 | "成功:", 83 | packageInfo != null ? packageInfo.toString() : "null"); 84 | } 85 | }); 86 | } 87 | 88 | @Override 89 | public void error(OfflinePackageInfo packageInfo, Throwable e) { 90 | OfflineHandlerUtils.runOnUiThread(new Runnable() { 91 | @Override 92 | public void run() { 93 | OffWebBottomView.show(MainActivity.this, 94 | "更新离线包", 95 | "失败:", 96 | packageInfo != null ? packageInfo.toString() : "", 97 | "失败原因:", 98 | e != null ? e.toString() : "null"); 99 | } 100 | }); 101 | } 102 | }); 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/request/DefaultLocalRequest.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.request; 2 | 3 | import android.util.Log; 4 | 5 | import com.lalamove.huolala.client.offline_web.Constants; 6 | import com.lalamove.huolala.client.offline_web.util.OkHttpClientManager; 7 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 8 | import com.lalamove.huolala.offline.webview.info.OfflinePackageInfo; 9 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 10 | import com.lalamove.huolala.offline.webview.net.IOfflineRequest; 11 | import com.lalamove.huolala.offline.webview.net.RequestCallback; 12 | import com.lalamove.huolala.offline.webview.utils.OfflineConstant; 13 | import com.lalamove.huolala.offline.webview.utils.OfflineGsonUtils; 14 | 15 | import java.io.IOException; 16 | 17 | import okhttp3.Call; 18 | import okhttp3.Callback; 19 | import okhttp3.HttpUrl; 20 | import okhttp3.OkHttpClient; 21 | import okhttp3.Request; 22 | import okhttp3.Response; 23 | 24 | /** 25 | * @copyright:深圳依时货拉拉科技有限公司 26 | * @fileName: DefaultRequest 27 | * @author: kelvin 28 | * @date: 3/29/22 29 | * @description: 离线包内部网络请求 30 | * @history: 31 | */ 32 | 33 | public class DefaultLocalRequest implements IOfflineRequest { 34 | 35 | private String TAG = DefaultLocalRequest.class.getSimpleName(); 36 | 37 | /** 38 | * 拉取离线包信息 39 | * @param bizName 当前离线包业务名称 40 | * @param version 当前离线包版本 41 | * @param callback 回调 42 | */ 43 | @Override 44 | public void requestPackageInfo(String bizName, String version, RequestCallback callback) { 45 | OkHttpClient okhttpClient = OkHttpClientManager.getUnsafeOkHttpClient(); 46 | HttpUrl httpUrl = HttpUrl.parse(Constants.LOCAL_BASE_URL); 47 | if (httpUrl == null) { 48 | if (OfflineWebManager.getInstance().isDebug()) { 49 | throw new IllegalStateException("parse error url :" + Constants.LOCAL_BASE_URL); 50 | } else { 51 | OfflineWebLog.e(TAG, new IllegalStateException("parse error url :" + Constants.LOCAL_BASE_URL)); 52 | return; 53 | } 54 | } 55 | if (callback == null) { 56 | return; 57 | } 58 | Request request = getRequest(httpUrl, bizName, version); 59 | Call call = okhttpClient.newCall(request); 60 | call.enqueue(new Callback() { 61 | @Override 62 | public void onFailure(Call call, IOException e) { 63 | callback.onFail(e); 64 | Log.e(TAG, e.toString()); 65 | } 66 | 67 | @Override 68 | public void onResponse(Call call, Response response) throws IOException { 69 | String responseStr = response.body().string(); 70 | Log.e(TAG, responseStr); 71 | OfflinePackageInfo offlinePackageInfo = OfflineGsonUtils.fromJson(responseStr, OfflinePackageInfo.class); 72 | callback.onSuccess(offlinePackageInfo); 73 | } 74 | }); 75 | } 76 | 77 | private Request getRequest(HttpUrl httpUrl, String bizName, String version) { 78 | HttpUrl.Builder urlBuilder = httpUrl.newBuilder(); 79 | urlBuilder.addQueryParameter(OfflineConstant.BIS_NAME, bizName) 80 | .addQueryParameter(OfflineConstant.PARAM_OFFLINE_ZIP_VER, version); 81 | HttpUrl url = urlBuilder.build(); 82 | OfflineWebLog.i(TAG, "fetch url:" + url); 83 | return new Request.Builder() 84 | .url(url) 85 | .get() 86 | .build(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/util/DisplayUtils.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.util; 2 | 3 | import android.content.Context; 4 | import android.util.TypedValue; 5 | 6 | /** 7 | * @copyright:深圳依时货拉拉科技有限公司 8 | * @fileName: DisplayUtils 9 | * @author: huangyuchen 10 | * @date: 7/12/21 11 | * @description: 屏幕宽高,dp,sp,px转换工具类 12 | * @history: 13 | */ 14 | public class DisplayUtils { 15 | 16 | private DisplayUtils() { 17 | 18 | } 19 | 20 | /** 21 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 22 | */ 23 | public static int px2dip(Context context, float pxValue) { 24 | if (context == null) { 25 | return 0; 26 | } 27 | final float scale = context.getResources().getDisplayMetrics().density; 28 | return (int) (pxValue / scale + 0.5f); 29 | } 30 | 31 | /** 32 | * @param context 33 | * @param dpValue 34 | * @return int 35 | * @description: 单位转换:dp转px 36 | */ 37 | public static int dp2px(Context context, float dpValue) { 38 | return (int) dp2pxF(context, dpValue); 39 | } 40 | 41 | /** 42 | * @param context 43 | * @param dpValue 44 | * @return float 45 | * @description: 单位转换:dp转px 46 | */ 47 | public static float dp2pxF(Context context, float dpValue) { 48 | if (context == null) { 49 | return 0; 50 | } 51 | final float scale = context.getResources().getDisplayMetrics().density; 52 | return (dpValue * scale + 0.5f); 53 | } 54 | 55 | public static int dpTpx(Context context, float dpVal) { 56 | if (context == null) { 57 | return 0; 58 | } 59 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60 | dpVal, context.getResources().getDisplayMetrics()); 61 | } 62 | 63 | public static float sp2px(Context context, float spValue) { 64 | if (context == null) { 65 | return 0; 66 | } 67 | float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; 68 | return spValue * scaledDensity; 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/util/IoThreadPool.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.util; 2 | 3 | import android.annotation.SuppressLint; 4 | 5 | 6 | import java.util.concurrent.LinkedBlockingDeque; 7 | import java.util.concurrent.ThreadFactory; 8 | import java.util.concurrent.ThreadPoolExecutor; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | /** 13 | * @copyright:深圳依时货拉拉科技有限公司 14 | * @fileName: IoThreadPool 15 | * @author: muye 16 | * @date: 2021/7/7 17 | * @description: io型数据库,用于网络请求 18 | * @history: 19 | */ 20 | 21 | public class IoThreadPool { 22 | 23 | private final static String TAG = "IoThreadPool"; 24 | 25 | //参数初始化 26 | protected static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 27 | //线程池最大容纳线程数 28 | protected static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 29 | 30 | private static final int TIME_OUT = 60; 31 | 32 | private static final int CORE_POOL_SIZE = 5; 33 | private ThreadPoolExecutor mThreadPoolExecutor; 34 | 35 | private static IoThreadPool sCompressThreadPool; 36 | 37 | private IoThreadPool() { 38 | initThreadPool(); 39 | } 40 | 41 | public static IoThreadPool getInstance() { 42 | if (null == sCompressThreadPool) { 43 | synchronized (IoThreadPool.class) { 44 | if (null == sCompressThreadPool) { 45 | sCompressThreadPool = new IoThreadPool(); 46 | } 47 | } 48 | } 49 | return sCompressThreadPool; 50 | } 51 | 52 | public ThreadPoolExecutor getThreadPoolExecutor() { 53 | return mThreadPoolExecutor; 54 | } 55 | 56 | @SuppressLint("NewApi") 57 | private void initThreadPool() { 58 | final AtomicInteger mAtomicInteger = new AtomicInteger(1); 59 | SecurityManager var1 = System.getSecurityManager(); 60 | final ThreadGroup group = var1 != null ? var1.getThreadGroup() : Thread.currentThread().getThreadGroup(); 61 | ThreadFactory threadFactory = new ThreadFactory() { 62 | @Override 63 | public Thread newThread(Runnable runnable) { 64 | return new Thread(group, runnable, "track io-pool-thread-" + mAtomicInteger.getAndIncrement(), 0); 65 | // return new Thread(group, runnable, "track io-pool-thread-" + mAtomicInteger.getAndIncrement(), 1024 * 256); 66 | } 67 | }; 68 | mThreadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, Math.max(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE), 69 | TIME_OUT, TimeUnit.SECONDS, 70 | new LinkedBlockingDeque<>(128), 71 | threadFactory); 72 | mThreadPoolExecutor.allowCoreThreadTimeOut(true); 73 | mThreadPoolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy() { 74 | @Override 75 | public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 76 | super.rejectedExecution(r, e); 77 | 78 | } 79 | }); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/util/OkHttpClientManager.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.util; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import okhttp3.Dispatcher; 6 | import okhttp3.OkHttpClient; 7 | 8 | /** 9 | * @copyright:深圳依时货拉拉科技有限公司 10 | * @fileName: CpuThreadPool 11 | * @author: muye 12 | * @date: 2021/7/7 13 | * @description: OkHttpClient实例管理 14 | * @history: 15 | */ 16 | public class OkHttpClientManager { 17 | 18 | 19 | private OkHttpClientManager() { 20 | } 21 | 22 | /** 23 | * Glide资源下载的OkHttpClient 24 | * 25 | * @return 26 | */ 27 | public static OkHttpClient getUnsafeOkHttpClient() { 28 | try { 29 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 30 | builder.dispatcher(new Dispatcher(IoThreadPool.getInstance().getThreadPoolExecutor())); 31 | builder.sslSocketFactory(HttpsUtils.getCustomSocketFactory(), new HttpsUtils.CustomX509TrustManager()); 32 | builder.hostnameVerifier(HttpsUtils.getAndroidHostnameVerifier()); 33 | builder.connectTimeout(20, TimeUnit.SECONDS); 34 | builder.readTimeout(20, TimeUnit.SECONDS); 35 | return builder.build(); 36 | } catch (Exception e) { 37 | throw new RuntimeException(e); 38 | } 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/widget/BottomView.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.widget; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.DialogInterface; 6 | import android.content.DialogInterface.OnDismissListener; 7 | import android.view.Display; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.view.Window; 11 | import android.view.WindowManager; 12 | 13 | /** 14 | * @copyright:深圳依时货拉拉科技有限公司 15 | * @fileName: BottomView 16 | * @author: kelvin 17 | * @date: 2022/5/13 18 | * @description: 底部弹出dialog 19 | * @history: 20 | */ 21 | 22 | public class BottomView { 23 | 24 | protected Activity activity; 25 | protected int theme; 26 | protected Dialog dialog; 27 | protected int animationStyle; 28 | protected int resource; 29 | protected View convertView; 30 | private OnDismissListener mDismissListener; 31 | 32 | public BottomView(Activity activity, int theme, int resource) { 33 | this.theme = theme; 34 | this.activity = activity; 35 | this.resource = resource; 36 | 37 | } 38 | 39 | public void show(boolean canceledOnTouchOutside) { 40 | try { 41 | if (dialog == null) { 42 | if (this.theme == 0) { 43 | this.dialog = new Dialog(activity); 44 | } else { 45 | this.dialog = new Dialog(activity, this.theme); 46 | } 47 | this.dialog.setCanceledOnTouchOutside(canceledOnTouchOutside); 48 | this.dialog.setCancelable(canceledOnTouchOutside); 49 | this.dialog.getWindow().requestFeature(1); 50 | 51 | if (convertView == null) { 52 | convertView = activity.getLayoutInflater().inflate(resource, null, false); 53 | } 54 | if (convertView.getParent() != null && convertView.getParent() instanceof ViewGroup) { 55 | ((ViewGroup) convertView.getParent()).removeView(convertView); 56 | } 57 | 58 | this.dialog.setContentView(convertView); 59 | this.dialog.setOnDismissListener(new OnDismissListener() { 60 | 61 | @Override 62 | public void onDismiss(DialogInterface arg) { 63 | onDestroy(); 64 | dialog = null; 65 | } 66 | }); 67 | } 68 | Window wm = this.dialog.getWindow(); 69 | WindowManager m = wm.getWindowManager(); 70 | Display d = m.getDefaultDisplay(); 71 | WindowManager.LayoutParams p = wm.getAttributes(); 72 | p.width = d.getWidth(); 73 | p.gravity = 80; 74 | p.height = WindowManager.LayoutParams.WRAP_CONTENT; 75 | 76 | if (this.animationStyle != 0) { 77 | wm.setWindowAnimations(this.animationStyle); 78 | } 79 | wm.setAttributes(p); 80 | 81 | if (activity != null && !activity.isFinishing()) { 82 | this.dialog.show(); 83 | } 84 | } catch (Exception e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | 89 | public void setAnimation(int animationStyle) { 90 | this.animationStyle = animationStyle; 91 | } 92 | 93 | public View getView() { 94 | return this.convertView; 95 | } 96 | 97 | public boolean isShown() { 98 | return (dialog != null && dialog.isShowing()); 99 | } 100 | 101 | public void dismiss() { 102 | if (!isShown()) { 103 | return; 104 | } 105 | this.dialog.dismiss(); 106 | } 107 | 108 | public void onDestroy() { 109 | if (mDismissListener != null) { 110 | mDismissListener.onDismiss(dialog); 111 | } 112 | } 113 | 114 | public void setDismissListener(OnDismissListener mDismissListener) { 115 | this.mDismissListener = mDismissListener; 116 | } 117 | 118 | 119 | public Dialog getDialog() { 120 | return dialog; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/widget/HllDrawable.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.widget; 2 | 3 | import android.content.res.ColorStateList; 4 | import android.graphics.Rect; 5 | import android.graphics.drawable.GradientDrawable; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | /** 10 | * @copyright:深圳依时货拉拉科技有限公司 11 | * @fileName: HllDrawable 12 | * @author: kelvin 13 | * @date: 7/28/21 14 | * @description: 自定义Drawable 15 | * 主要增加 支持设置控件一半高度的圆角 16 | * @history: 17 | * 18 | */ 19 | public class HllDrawable extends GradientDrawable { 20 | 21 | /** 22 | * 圆角大小是否自适应为 View 的高度的一半 23 | */ 24 | private boolean mIsRadiusHalfHeight = true; 25 | private ColorStateList mSolidColors; 26 | private int mStrokeWidth = 0; 27 | private ColorStateList mStrokeColors; 28 | 29 | /** 30 | * 设置按钮的背景色 31 | */ 32 | public void setSolidColor(@Nullable ColorStateList colors) { 33 | mSolidColors = colors; 34 | super.setColor(colors); 35 | } 36 | 37 | /** 38 | * 设置按钮的描边粗细和颜色 39 | */ 40 | public void setStrokeData(int width, @Nullable ColorStateList colors) { 41 | mStrokeWidth = width; 42 | mStrokeColors = colors; 43 | super.setStroke(width, colors); 44 | } 45 | 46 | /** 47 | * 设置圆角大小是否自动适应为 View 的高度的一半 48 | */ 49 | public void setIsRadiusHalfHeight(boolean isRadiusHalfHeight) { 50 | mIsRadiusHalfHeight = isRadiusHalfHeight; 51 | } 52 | 53 | @Override 54 | protected boolean onStateChange(int[] stateSet) { 55 | boolean superRet = super.onStateChange(stateSet); 56 | if (mSolidColors != null) { 57 | int color = mSolidColors.getColorForState(stateSet, 0); 58 | setColor(color); 59 | superRet = true; 60 | } 61 | if (mStrokeColors != null) { 62 | int color = mStrokeColors.getColorForState(stateSet, 0); 63 | setStroke(mStrokeWidth, color); 64 | superRet = true; 65 | } 66 | return superRet; 67 | } 68 | 69 | @Override 70 | public boolean isStateful() { 71 | return (mSolidColors != null && mSolidColors.isStateful()) 72 | || (mStrokeColors != null && mStrokeColors.isStateful()) 73 | || super.isStateful(); 74 | } 75 | 76 | @Override 77 | protected void onBoundsChange(Rect r) { 78 | super.onBoundsChange(r); 79 | if (mIsRadiusHalfHeight) { 80 | // 修改圆角为短边的一半 81 | setCornerRadius(Math.min(r.width(), r.height()) / 2.0f); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/widget/OffWebBottomView.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.widget; 2 | 3 | import android.app.Activity; 4 | import android.text.TextUtils; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.lalamove.huolala.client.offline_web.R; 9 | 10 | /** 11 | * @copyright:深圳依时货拉拉科技有限公司 12 | * @fileName: OffWebBottomView 13 | * @author: kelvin 14 | * @date: 2022/5/13 15 | * @description: 底部弹出dialog 16 | * @history: 17 | */ 18 | 19 | public class OffWebBottomView extends BottomView { 20 | private String mTitle; 21 | private String mSubTitle1; 22 | private String mContent1; 23 | private String mSubTitle2; 24 | private String mContent2; 25 | 26 | public static OffWebBottomView show(Activity activity, String title, String subTitle1, String content1) { 27 | return show(activity, title, subTitle1, content1, null, null); 28 | } 29 | 30 | public static OffWebBottomView show(Activity activity, String title, String subTitle1, String content1, String subTitle2, String content2) { 31 | OffWebBottomView bottomView = new OffWebBottomView(activity, title, subTitle1, content1, subTitle2, content2); 32 | bottomView.show(true); 33 | return bottomView; 34 | } 35 | 36 | public OffWebBottomView(Activity activity, String title, String subTitle1, String content1, String subTitle2, String content2) { 37 | super(activity, R.style.BottomViewTheme_Defalut, R.layout.off_web_bottom_dialog_layout); 38 | mTitle = title; 39 | mSubTitle1 = subTitle1; 40 | mContent1 = content1; 41 | mSubTitle2 = subTitle2; 42 | mContent2 = content2; 43 | setAnimation(R.style.BottomToTopAnim); 44 | } 45 | 46 | @Override 47 | public void show(boolean canceledOnTouchOutside) { 48 | super.show(canceledOnTouchOutside); 49 | initText(); 50 | intiCloseView(); 51 | } 52 | 53 | private void initText() { 54 | TextView titleTv = convertView.findViewById(R.id.title_tv); 55 | TextView subTitle1Tv = convertView.findViewById(R.id.sub_title_one_tv); 56 | TextView content1Tv = convertView.findViewById(R.id.content_one_tv); 57 | TextView subTitle2Tv = convertView.findViewById(R.id.sub_title_two_tv); 58 | TextView content2Tv = convertView.findViewById(R.id.content_two_tv); 59 | setText(titleTv, mTitle); 60 | setText(subTitle1Tv, mSubTitle1); 61 | setText(content1Tv, mContent1); 62 | setText(subTitle2Tv, mSubTitle2); 63 | setText(content2Tv, mContent2); 64 | HllRoundBackground.with(activity) 65 | .corner(16, 16, 0, 0) 66 | .solid(R.color.white).into(convertView); 67 | } 68 | 69 | private void setText(TextView textView, String str) { 70 | if (TextUtils.isEmpty(str)) { 71 | textView.setVisibility(View.GONE); 72 | } else { 73 | textView.setVisibility(View.VISIBLE); 74 | textView.setText(str); 75 | } 76 | } 77 | 78 | private void intiCloseView() { 79 | View closeIv = convertView.findViewById(R.id.close_iv); 80 | View closeTv = convertView.findViewById(R.id.close_tv); 81 | HllRoundBackground.with(activity) 82 | .stroke(1,R.color.color_D8DEEB) 83 | .corner(8) 84 | .into(closeTv); 85 | closeIv.setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View v) { 88 | dismiss(); 89 | } 90 | }); 91 | closeTv.setOnClickListener(new View.OnClickListener() { 92 | @Override 93 | public void onClick(View v) { 94 | dismiss(); 95 | } 96 | }); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/lalamove/huolala/client/offline_web/widget/OffWebButton.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.offline_web.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.FrameLayout; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.Nullable; 14 | 15 | import com.lalamove.huolala.client.offline_web.R; 16 | 17 | /** 18 | * @copyright:深圳依时货拉拉科技有限公司 19 | * @fileName: OffwebButton 20 | * @author: kelvin 21 | * @date: 2022/5/13 22 | * @description: demo 首页按钮 23 | * @history: 24 | */ 25 | 26 | public class OffWebButton extends FrameLayout { 27 | private int mImageResource = R.drawable.off_web_logo_icon; 28 | private String mTitle = ""; 29 | 30 | public OffWebButton(@NonNull Context context) { 31 | this(context, null); 32 | } 33 | 34 | public OffWebButton(@NonNull Context context, @Nullable AttributeSet attrs) { 35 | this(context, attrs, 0); 36 | } 37 | 38 | public OffWebButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 39 | this(context, attrs, defStyleAttr, 0); 40 | } 41 | 42 | public OffWebButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 43 | super(context, attrs, defStyleAttr, defStyleRes); 44 | if (attrs != null) { 45 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.OffWebButton); 46 | mImageResource = ta.getResourceId(R.styleable.OffWebButton_image, R.drawable.off_web_logo_icon); 47 | mTitle = ta.getString(R.styleable.OffWebButton_text); 48 | ta.recycle(); 49 | } 50 | init(); 51 | } 52 | 53 | private void init() { 54 | View inflate = LayoutInflater.from(getContext()).inflate(R.layout.off_web_btn_layout, this); 55 | ImageView iconIv = inflate.findViewById(R.id.item_icon); 56 | iconIv.setImageResource(mImageResource); 57 | TextView titleTv = inflate.findViewById(R.id.title_tv); 58 | titleTv.setText(mTitle); 59 | HllRoundBackground.with(getContext()) 60 | .corner(4) 61 | .stroke(1, R.color.white) 62 | .into(inflate); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/res/anim/off_web_bottom_anim_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/anim/off_web_bottom_anim_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_avatar_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_avatar_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_cash_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_cash_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_close_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_close_icon.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_logo_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_logo_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_main_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_main_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_name_logo_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_name_logo_img.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_navigation_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_navigation_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/off_web_update_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/drawable-xxhdpi/off_web_update_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/off_web_activity_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/off_web_activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 25 | 26 | 35 | 36 | 47 | 48 | 49 | 57 | 58 | 59 | 67 | 68 | 69 | 77 | 78 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/res/layout/off_web_bottom_dialog_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 29 | 30 | 38 | 39 | 43 | 44 | 54 | 55 | 64 | 65 | 66 | 76 | 77 | 86 | 87 | 88 | 89 | 90 | 91 | 100 | 101 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /app/src/main/res/layout/off_web_btn_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 19 | 20 | 26 | 27 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | #FFFF6600 11 | #D8DEEB 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | OfflineDemo 3 | 检查更新 4 | 打开带监控的WebView 5 | 清理离线包 6 | 打开普通WebView 7 | 关闭 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 23 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/act3-2108-turntable.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/assets/act3-2108-turntable.zip -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = '1.5.31' 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath "com.android.tools.build:gradle:4.1.0" 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | maven { 19 | url 'https://maven.aliyun.com/repository/public' 20 | } 21 | 22 | google() 23 | mavenCentral() 24 | maven { url "https://jitpack.io" } 25 | } 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | MIN_SDK_VERSION=21 22 | TARGET_SDK_VERSION=31 23 | COMPILE_SDK_VERSION=29 24 | BUILDTOOLS_VERSION=30.0.3 25 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Nov 09 19:09:22 CST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /lib_offline_webview/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | build/ 10 | 11 | # Android Studio导航编辑器临时文件 12 | .navigation/ 13 | 14 | # Android Studio captures folder 15 | captures/ 16 | -------------------------------------------------------------------------------- /lib_offline_webview/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'maven' 4 | } 5 | 6 | android { 7 | compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION) 8 | buildToolsVersion BUILDTOOLS_VERSION 9 | 10 | defaultConfig { 11 | minSdkVersion Integer.parseInt(MIN_SDK_VERSION) 12 | targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION) 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | 34 | testImplementation 'junit:junit:4.12' 35 | 36 | compileOnly "com.google.code.gson:gson:2.8.5" 37 | implementation "androidx.annotation:annotation:1.2.0" 38 | } 39 | -------------------------------------------------------------------------------- /lib_offline_webview/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuolalaTech/HLLOfflineWebView-android/a2bdf987fac5e03b1544a3a5d26e9f17b121f2db/lib_offline_webview/consumer-rules.pro -------------------------------------------------------------------------------- /lib_offline_webview/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /lib_offline_webview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/Interceptor/DefaultInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.Interceptor; 2 | 3 | /** 4 | * create by zhii.yang 2021/12/6 5 | * desc : 6 | */ 7 | public class DefaultInterceptor implements IInterceptor { 8 | 9 | @Override 10 | public boolean isIntercept(String bisName) { 11 | return false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/Interceptor/IInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.Interceptor; 2 | 3 | /** 4 | * create by zhii.yang 2021/12/6 5 | * desc : 拦截器,判断当前离线包是否可用 6 | */ 7 | public interface IInterceptor { 8 | 9 | /** 10 | * @param bisName 业务名称 11 | * @return 是否拦截 12 | */ 13 | boolean isIntercept(String bisName); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/OfflineConfig.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | 8 | /** 9 | * @copyright:深圳依时货拉拉科技有限公司 10 | * @fileName: OfflineConfig 11 | * @author: kelvin 12 | * @date: 3/28/22 13 | * @description: 离线包配置参数 14 | * @history: 15 | */ 16 | 17 | public class OfflineConfig { 18 | 19 | @SerializedName("isOpen") 20 | private boolean isOpen; 21 | @SerializedName("predownloadlist") 22 | private HashSet mPreDownloadList; 23 | @SerializedName("disablelist") 24 | private HashSet disableList; 25 | 26 | public OfflineConfig(boolean isOpen, HashSet preDownloadList, HashSet disableList) { 27 | this.isOpen = isOpen; 28 | this.mPreDownloadList = preDownloadList; 29 | this.disableList = disableList; 30 | } 31 | 32 | public boolean isOpen() { 33 | return isOpen; 34 | } 35 | 36 | public HashSet getPreDownloadList() { 37 | return mPreDownloadList; 38 | } 39 | 40 | public boolean isDisable(String bisName) { 41 | if (disableList == null || disableList.size() == 0) { 42 | return false; 43 | } 44 | return disableList.contains(bisName); 45 | } 46 | 47 | public static class Builder { 48 | private boolean isOpen; 49 | private HashSet predownloadlist; 50 | private HashSet disablelist; 51 | 52 | public Builder(boolean isOpen) { 53 | this.isOpen = isOpen; 54 | predownloadlist = new HashSet<>(); 55 | disablelist = new HashSet<>(); 56 | } 57 | 58 | public Builder addPreDownload(String bizName) { 59 | predownloadlist.add(bizName); 60 | return this; 61 | } 62 | 63 | public Builder addPreDownloadList(Collection preDownloadList) { 64 | this.predownloadlist.addAll(preDownloadList); 65 | return this; 66 | } 67 | 68 | public Builder addDisable(String bizName) { 69 | disablelist.add(bizName); 70 | return this; 71 | } 72 | 73 | public Builder addDisableList(Collection disableList) { 74 | this.disablelist.addAll(disableList); 75 | return this; 76 | } 77 | 78 | public OfflineConfig build() { 79 | return new OfflineConfig(isOpen, predownloadlist, disablelist); 80 | } 81 | 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/OfflinePageManager.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview; 2 | 3 | 4 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 5 | import com.lalamove.huolala.offline.webview.proxy.OfflineWebViewProxy; 6 | 7 | import java.util.Collections; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | /** 12 | * @copyright:深圳依时货拉拉科技有限公司 13 | * @fileName: OfflinePageManager 14 | * @author: kelvin 15 | * @date: 7/19/21 16 | * @description: 页面管理器 用于reload 17 | * @history: 18 | */ 19 | 20 | public class OfflinePageManager { 21 | private static final String TAG = OfflinePageManager.class.getSimpleName(); 22 | private List mLinkedList = Collections.synchronizedList(new LinkedList<>()); 23 | 24 | public OfflinePageManager() { 25 | } 26 | 27 | public void addPage(OfflineWebViewProxy webView) { 28 | if (mLinkedList.contains(webView)) { 29 | return; 30 | } 31 | mLinkedList.add(webView); 32 | OfflineWebLog.i(TAG, "add " + mLinkedList.size()); 33 | } 34 | 35 | public void remove(OfflineWebViewProxy webView) { 36 | mLinkedList.remove(webView); 37 | OfflineWebLog.i(TAG, "remove " + mLinkedList.size()); 38 | } 39 | 40 | public void reload(String bizName) { 41 | for (OfflineWebViewProxy webViewProxy : mLinkedList) { 42 | if (webViewProxy.getBisName().equals(bizName)) { 43 | webViewProxy.reLoadUrl(); 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/OfflineParams.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.lalamove.huolala.offline.webview.Interceptor.IInterceptor; 6 | import com.lalamove.huolala.offline.webview.download.IDownLoader; 7 | import com.lalamove.huolala.offline.webview.flow.IFlowResultHandleStrategy; 8 | import com.lalamove.huolala.offline.webview.info.OfflineRuleConfig; 9 | import com.lalamove.huolala.offline.webview.log.Logger; 10 | import com.lalamove.huolala.offline.webview.matcher.BisNameMatcher; 11 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebMonitor; 12 | import com.lalamove.huolala.offline.webview.net.IOfflineRequest; 13 | import com.lalamove.huolala.offline.webview.threadpool.IExecutorServiceProvider; 14 | import com.lalamove.huolala.offline.webview.utils.OfflineGsonUtils; 15 | 16 | import java.util.List; 17 | 18 | 19 | public class OfflineParams { 20 | 21 | private Logger mLogger; 22 | 23 | private IExecutorServiceProvider mExecutorProvider; 24 | 25 | private boolean mIsDebug; 26 | 27 | private IDownLoader mDownLoader; 28 | 29 | private BisNameMatcher mMatcher; 30 | 31 | private IInterceptor mInterceptor; 32 | 33 | private IFlowResultHandleStrategy mFlowResultHandleStrategy; 34 | 35 | private OfflineConfig mOfflineConfig; 36 | 37 | private IOfflineRequest mRequest; 38 | 39 | private IEnhWebMonitor mMonitor; 40 | 41 | private OfflineRuleConfig mOfflineRuleConfig; 42 | 43 | public OfflineParams config(OfflineConfig offlineConfig) { 44 | mOfflineConfig = offlineConfig; 45 | return this; 46 | } 47 | 48 | public OfflineParams logger(Logger logger) { 49 | mLogger = logger; 50 | return this; 51 | } 52 | 53 | public OfflineParams isDebug(boolean isDebug) { 54 | mIsDebug = isDebug; 55 | return this; 56 | } 57 | 58 | public OfflineParams downloader(IDownLoader downLoader) { 59 | mDownLoader = downLoader; 60 | return this; 61 | } 62 | 63 | public OfflineParams matcher(BisNameMatcher matcher) { 64 | mMatcher = matcher; 65 | return this; 66 | } 67 | 68 | public OfflineParams executorServiceProvider(IExecutorServiceProvider provider) { 69 | mExecutorProvider = provider; 70 | return this; 71 | } 72 | 73 | public OfflineParams interceptor(IInterceptor interceptor) { 74 | mInterceptor = interceptor; 75 | return this; 76 | } 77 | 78 | public OfflineParams flowResultHandleStrategy(IFlowResultHandleStrategy flowResultHandleStrategy) { 79 | mFlowResultHandleStrategy = flowResultHandleStrategy; 80 | return this; 81 | } 82 | 83 | 84 | public OfflineParams requestServer(IOfflineRequest request) { 85 | mRequest = request; 86 | return this; 87 | } 88 | 89 | public OfflineParams monitor(IEnhWebMonitor monitor) { 90 | mMonitor = monitor; 91 | return this; 92 | } 93 | 94 | public OfflineParams setRule(OfflineRuleConfig offlineRuleConfig) { 95 | mOfflineRuleConfig = offlineRuleConfig; 96 | return this; 97 | } 98 | 99 | public OfflineParams setRule(String ruleJson) { 100 | mOfflineRuleConfig = OfflineGsonUtils.fromJson(ruleJson, OfflineRuleConfig.class); 101 | return this; 102 | } 103 | 104 | 105 | public OfflineParams addRule(String offweb, List host, List path, List fragmentprefix) { 106 | if (TextUtils.isEmpty(offweb) || 107 | host == null || host.size() == 0 || 108 | path == null || path.size() == 0) { 109 | return this; 110 | } 111 | 112 | if (mOfflineRuleConfig == null) { 113 | mOfflineRuleConfig = new OfflineRuleConfig(); 114 | } 115 | mOfflineRuleConfig.addRule(new OfflineRuleConfig.RulesInfo(offweb, host, path, fragmentprefix)); 116 | return this; 117 | } 118 | 119 | public Logger getLogger() { 120 | return mLogger; 121 | } 122 | 123 | public IExecutorServiceProvider getExecutorProvider() { 124 | return mExecutorProvider; 125 | } 126 | 127 | public boolean isDebug() { 128 | return mIsDebug; 129 | } 130 | 131 | public IDownLoader getDownLoader() { 132 | return mDownLoader; 133 | } 134 | 135 | public BisNameMatcher getMatcher() { 136 | return mMatcher; 137 | } 138 | 139 | public IInterceptor getInterceptor() { 140 | return mInterceptor; 141 | } 142 | 143 | public IFlowResultHandleStrategy getFlowResultHandleStrategy() { 144 | return mFlowResultHandleStrategy; 145 | } 146 | 147 | public OfflineConfig getOfflineConfig() { 148 | return mOfflineConfig; 149 | } 150 | 151 | public IOfflineRequest getRequest() { 152 | return mRequest; 153 | } 154 | 155 | public IEnhWebMonitor getMonitor() { 156 | return mMonitor; 157 | } 158 | 159 | public OfflineRuleConfig getOfflineRuleConfig() { 160 | return mOfflineRuleConfig; 161 | } 162 | } -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/OfflineTaskManager.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.lalamove.huolala.offline.webview.resource.ResourceFlow; 6 | import com.lalamove.huolala.offline.webview.task.CheckAndUpdateTask; 7 | import com.lalamove.huolala.offline.webview.task.CheckVersionTask; 8 | import com.lalamove.huolala.offline.webview.task.CleanTask; 9 | 10 | /** 11 | * @copyright:深圳依时货拉拉科技有限公司 12 | * @fileName: OfflineTaskManager 13 | * @author: kelvin 14 | * @date: 3/30/22 15 | * @description: 异步任务管理 16 | * @history: 17 | */ 18 | 19 | public class OfflineTaskManager { 20 | private OfflineTaskManager() { 21 | } 22 | 23 | static void startInitTask() { 24 | OfflineConfig offlineConfig = OfflineWebManager.getInstance().getOfflineConfig(); 25 | if (offlineConfig.isOpen()) { 26 | checkAllVersion(); 27 | if (offlineConfig.getPreDownloadList() != null && offlineConfig.getPreDownloadList().size() > 0) { 28 | for (String bisName : offlineConfig.getPreDownloadList()) { 29 | checkPackage(bisName, null); 30 | } 31 | } 32 | } 33 | } 34 | 35 | /** 36 | * 检查上次安装的离线包,进行改名替换删除等逻辑 37 | */ 38 | static void checkAllVersion() { 39 | OfflineWebManager.getInstance().getExecutor().execute(new CheckVersionTask()); 40 | } 41 | 42 | /** 43 | * 根据业务名称拉取离线包 44 | * 45 | * @param bisName 业务名 46 | */ 47 | static void checkPackage(final String bisName,final ResourceFlow.FlowListener listener) { 48 | if (TextUtils.isEmpty(bisName)) { 49 | if (listener!=null) { 50 | listener.error(null,new IllegalStateException("bisName == null")); 51 | } 52 | return; 53 | } 54 | OfflineWebManager.getInstance().getExecutor().execute(new CheckAndUpdateTask(bisName,listener)); 55 | } 56 | 57 | /** 58 | * 清空所有离线包 59 | */ 60 | static void clean() { 61 | OfflineWebManager.getInstance().getExecutor().execute(new CleanTask()); 62 | } 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/OfflineWebClient.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | 6 | import com.lalamove.huolala.offline.webview.resource.ResourceFlow; 7 | 8 | 9 | /** 10 | * @copyright:深圳依时货拉拉科技有限公司 11 | * @fileName: OfflineWebClient 12 | * @author: kelvin 13 | * @date: 3/30/22 14 | * @description: 对外提供 15 | * @history: 16 | */ 17 | 18 | public class OfflineWebClient { 19 | 20 | private OfflineWebClient() { 21 | } 22 | 23 | public static void init(Context context, OfflineParams offlineParams) { 24 | OfflineWebManager.getInstance().init(context, offlineParams); 25 | if (OfflineWebManager.getInstance().isInit()) { 26 | OfflineTaskManager.startInitTask(); 27 | } 28 | } 29 | 30 | public static void checkPackage(final String bisName) { 31 | checkPackage(bisName,null); 32 | } 33 | /** 34 | * 根据业务名称拉取离线包 35 | * 36 | * @param bisName 业务名 37 | */ 38 | public static void checkPackage(final String bisName, final ResourceFlow.FlowListener listener) { 39 | if (TextUtils.isEmpty(bisName)) { 40 | if (listener!=null) { 41 | listener.error(null,new IllegalStateException("bisName == null")); 42 | } 43 | return; 44 | } 45 | if (!OfflineWebManager.getInstance().isInit()) { 46 | if (listener!=null) { 47 | listener.error(null,new IllegalStateException("please init sdk first .")); 48 | } 49 | return; 50 | } 51 | OfflineTaskManager.checkPackage(bisName,listener); 52 | } 53 | 54 | public static void clean() { 55 | if (!OfflineWebManager.getInstance().isInit()) { 56 | return; 57 | } 58 | OfflineTaskManager.clean(); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/download/DefaultDownloader.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.download; 2 | 3 | 4 | import java.io.File; 5 | 6 | /** 7 | * @copyright:深圳依时货拉拉科技有限公司 8 | * @fileName: DefaultDownloader 9 | * @author: kelvin 10 | * @date: 3/29/22 11 | * @description: 默认下载器 12 | * @history: 13 | */ 14 | 15 | public class DefaultDownloader implements IDownLoader { 16 | 17 | 18 | @Override 19 | public void download(String url, String phoneDir, String fileName, DownloadCallback callback) { 20 | doDownloadByHelper(url, phoneDir, fileName, callback); 21 | } 22 | 23 | private void doDownloadByHelper(String url, String phoneDir, String fileName, IDownLoader.DownloadCallback callback) { 24 | FileDownLoadHelper fileDownLoadHelper = new FileDownLoadHelper(); 25 | fileDownLoadHelper.httpGetFile(url, phoneDir, fileName, new FileDownLoadHelper.OnHttpGetFileListener() { 26 | @Override 27 | public void onDownloaded(File file) { 28 | if (callback != null) { 29 | callback.success(file, false); 30 | } 31 | } 32 | 33 | @Override 34 | public void onDownloading(int total, int percentage) { 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/download/IDownLoader.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.download; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * @copyright:深圳依时货拉拉科技有限公司 7 | * @fileName: IDownLoader 8 | * @author: kelvin 9 | * @date: 7/27/21 10 | * @description: 下载器 11 | * @history: 12 | */ 13 | 14 | public interface IDownLoader { 15 | 16 | /** 17 | * 18 | * @param url 下载路径 19 | * @param phoneDir 本地路径 20 | * @param fileName 文件名称 21 | * @param callback 回调 22 | */ 23 | void download(String url, String phoneDir, String fileName, DownloadCallback callback); 24 | 25 | interface DownloadCallback { 26 | 27 | /** 28 | * 29 | * @param file 下载成功的文件File 30 | * @param isBrokenDown 是否断点续传 31 | */ 32 | void success(File file, boolean isBrokenDown); 33 | 34 | /** 35 | * 36 | */ 37 | void fail(Throwable throwable); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/flow/FlowReportParams.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.flow; 2 | 3 | 4 | /** 5 | * @copyright:深圳依时货拉拉科技有限公司 6 | * @fileName: FlowReportParams 7 | * @author: kelvin 8 | * @date: 8/12/21 9 | * @description: 更新包流程参数 10 | * @history: 11 | */ 12 | 13 | public class FlowReportParams { 14 | 15 | /** 16 | * 离线包业务名 17 | */ 18 | private String mBisName; 19 | /** 20 | * 查询耗时 21 | */ 22 | private long mQueryTime; 23 | /** 24 | * 查询返回结果是否正确 0 网络请求成功,-1 网络请求失败 25 | */ 26 | private int mQueryResult; 27 | /** 28 | * 查询返回信息 29 | */ 30 | private String mQueryMsg; 31 | /** 32 | * 下载耗时(没有下载过程填0) 33 | */ 34 | private long mDownloadTime; 35 | /** 36 | * -1 下载失败 0下载成功 1 无需下载 37 | */ 38 | private int mDownloadResult = 1; 39 | /** 40 | * 下载信息 41 | */ 42 | private String mDownloadMsg; 43 | /** 44 | * 解压耗时(如果没有解压过程则填0) 45 | */ 46 | private long mUnzipTime; 47 | /** 48 | * -1解压失败 0解压成功 ,1 无需解压 49 | */ 50 | private int mUnzipResult = 1; 51 | /** 52 | * 错误时填错误原因。 53 | */ 54 | private String mUnzipMsg; 55 | /** 56 | * 压缩包大小 57 | */ 58 | private long mZipSize; 59 | /** 60 | * 0 非断点续传 1 断点续传 61 | */ 62 | private boolean mIsBrokenDown; 63 | 64 | private long mQueryStartTime; 65 | private long mDownloadStartTime; 66 | private long mUnZipStartTime; 67 | 68 | public String getBisName() { 69 | return mBisName; 70 | } 71 | 72 | public long getQueryTime() { 73 | return mQueryTime; 74 | } 75 | 76 | public int getQueryResult() { 77 | return mQueryResult; 78 | } 79 | 80 | public String getQueryMsg() { 81 | return mQueryMsg; 82 | } 83 | 84 | public long getDownloadTime() { 85 | return mDownloadTime; 86 | } 87 | 88 | public int getDownloadResult() { 89 | return mDownloadResult; 90 | } 91 | 92 | public String getDownloadMsg() { 93 | return mDownloadMsg; 94 | } 95 | 96 | public long getUnzipTime() { 97 | return mUnzipTime; 98 | } 99 | 100 | public int getUnzipResult() { 101 | return mUnzipResult; 102 | } 103 | 104 | public String getUnzipMsg() { 105 | return mUnzipMsg; 106 | } 107 | 108 | public long getZipSize() { 109 | return mZipSize; 110 | } 111 | 112 | public boolean isBrokenDown() { 113 | return mIsBrokenDown; 114 | } 115 | 116 | public FlowReportParams(String bisName) { 117 | this.mBisName = bisName; 118 | } 119 | 120 | public void queryStart() { 121 | mQueryStartTime = System.currentTimeMillis(); 122 | } 123 | 124 | public void queryEnd() { 125 | mQueryTime = System.currentTimeMillis() - mQueryStartTime; 126 | } 127 | 128 | public void setQueryResult(boolean isSuccess, String queryMsg) { 129 | mQueryResult = isSuccess ? 0 : -1; 130 | mQueryMsg = queryMsg; 131 | } 132 | 133 | public void downloadStart() { 134 | mDownloadStartTime = System.currentTimeMillis(); 135 | } 136 | 137 | public void downloadResult(boolean success, String msg) { 138 | mDownloadTime = System.currentTimeMillis() - mDownloadStartTime; 139 | mDownloadResult = success ? 0 : -1; 140 | mDownloadMsg = msg; 141 | } 142 | 143 | public void unZipStart() { 144 | mUnZipStartTime = System.currentTimeMillis(); 145 | } 146 | 147 | public void unZipEnd(boolean success, String error) { 148 | mUnzipTime = System.currentTimeMillis() - mUnZipStartTime; 149 | mUnzipResult = success ? 0 : -1; 150 | mUnzipMsg = error; 151 | } 152 | 153 | public void zipSize(long size) { 154 | mZipSize = size; 155 | } 156 | 157 | public void isBrokenDown(boolean isBrokenDown) { 158 | mIsBrokenDown = isBrokenDown; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/flow/FlowResultHandleStrategy.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.flow; 2 | 3 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 4 | import com.lalamove.huolala.offline.webview.monitor.OfflineWebMonitorUtils; 5 | 6 | /** 7 | * create by zhii.yang 2021/12/12 8 | * desc : 9 | */ 10 | 11 | public class FlowResultHandleStrategy implements IFlowResultHandleStrategy { 12 | @Override 13 | public void done(FlowReportParams params) { 14 | OfflineWebLog.d(getClass().getSimpleName(), "FlowReportParams --> " + params); 15 | if (params == null) { 16 | return; 17 | } 18 | OfflineWebMonitorUtils.reportFlowParams(params); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/flow/IFlowResultHandleStrategy.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.flow; 2 | 3 | /** 4 | * create by zhii.yang 2021/12/12 5 | * desc : 6 | */ 7 | public interface IFlowResultHandleStrategy { 8 | 9 | /** 10 | * 下载更新流程执行完毕回调 11 | * @param params 更新包过程中的数据 12 | */ 13 | void done(FlowReportParams params); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/info/OfflinePackageInfo.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.info; 2 | 3 | import androidx.annotation.Keep; 4 | 5 | import java.io.Serializable; 6 | import java.util.Objects; 7 | 8 | /** 9 | * @copyright:深圳依时货拉拉科技有限公司 10 | * @fileName: OfflinePackageInfo 11 | * @author: kelvin 12 | * @date: 7/21/21 13 | * @description: 离线包接口返回数据体 14 | * @history: 15 | */ 16 | @Keep 17 | public class OfflinePackageInfo implements Serializable { 18 | 19 | 20 | /** 21 | * bisName : huoyunjie 22 | * result : 0 23 | * url : www.xxx.com 24 | * version : xxxxxx 25 | */ 26 | 27 | private String bisName; 28 | private int result; 29 | private String url; 30 | private int refreshMode; 31 | private String version; 32 | 33 | public OfflinePackageInfo(String bisName, int result, String url, int refreshMode, String offlineZipVer) { 34 | this.bisName = bisName; 35 | this.result = result; 36 | this.url = url; 37 | this.refreshMode = refreshMode; 38 | this.version = offlineZipVer; 39 | } 40 | public OfflinePackageInfo(String bisName,String offlineZipVer) { 41 | this.bisName = bisName; 42 | this.version = offlineZipVer; 43 | } 44 | 45 | public String getBisName() { 46 | return bisName; 47 | } 48 | 49 | public int getResult() { 50 | return result; 51 | } 52 | 53 | public String getUrl() { 54 | return url; 55 | } 56 | 57 | public void setBisName(String bisName) { 58 | this.bisName = bisName; 59 | } 60 | 61 | public void setResult(int result) { 62 | this.result = result; 63 | } 64 | 65 | public void setUrl(String url) { 66 | this.url = url; 67 | } 68 | 69 | public int getRefreshMode() { 70 | return refreshMode; 71 | } 72 | 73 | public boolean isForceRefresh() { 74 | return refreshMode == 1; 75 | } 76 | 77 | public void setRefreshMode(int refreshMode) { 78 | this.refreshMode = refreshMode; 79 | } 80 | 81 | public String getVersion() { 82 | return version; 83 | } 84 | 85 | public void setVersion(String version) { 86 | this.version = version; 87 | } 88 | 89 | public boolean isEnable() { 90 | return result != -1; 91 | } 92 | 93 | public boolean isSameVer() { 94 | return result == 0; 95 | } 96 | 97 | public boolean isNeedUpdate() { 98 | return result == 1; 99 | } 100 | 101 | @Override 102 | public boolean equals(Object o) { 103 | if (this == o) { 104 | return true; 105 | } 106 | if (o == null || getClass() != o.getClass()) { 107 | return false; 108 | } 109 | OfflinePackageInfo that = (OfflinePackageInfo) o; 110 | return Objects.equals(bisName, that.bisName); 111 | } 112 | 113 | @Override 114 | public int hashCode() { 115 | return Objects.hash(bisName); 116 | } 117 | 118 | @Override 119 | public String toString() { 120 | return "OfflinePackageInfo{" + 121 | "bisName='" + bisName + '\'' + 122 | ", result=" + result + 123 | ", url='" + url + '\'' + 124 | ", refreshMode=" + refreshMode + 125 | ", version='" + version + '\'' + 126 | '}'; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/info/OfflineRuleConfig.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.info; 2 | 3 | import androidx.annotation.Keep; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Objects; 10 | 11 | /** 12 | * @copyright:深圳依时货拉拉科技有限公司 13 | * @fileName: OfflineRuleConfig 14 | * @author: kelvin 15 | * @date: 1/18/22 16 | * @description: 离线包规则 17 | * @history: 18 | */ 19 | 20 | @Keep 21 | public class OfflineRuleConfig { 22 | 23 | private List rules; 24 | 25 | public OfflineRuleConfig() { 26 | rules = new ArrayList<>(); 27 | } 28 | 29 | public List getRules() { 30 | return rules; 31 | } 32 | 33 | public void addRule(RulesInfo rulesInfo) { 34 | if (rules.contains(rulesInfo)) { 35 | return; 36 | } 37 | rules.add(rulesInfo); 38 | } 39 | 40 | public void addRules(List rulesInfoList) { 41 | for (RulesInfo rulesInfo : rulesInfoList) { 42 | addRule(rulesInfo); 43 | } 44 | } 45 | 46 | @Keep 47 | public static class RulesInfo { 48 | /** 49 | * host : ["www.xxx.cn","xxxx.cn"] 50 | * path : ["/pathA"] 51 | * fragmentprefix : ["/fragmentA","/fragmentB"] 52 | * offweb : bisname 53 | */ 54 | 55 | @SerializedName("offweb") 56 | private String offWeb; 57 | private List host; 58 | private List path; 59 | @SerializedName("fragmentprefix") 60 | private List fragmentPrefix; 61 | 62 | public RulesInfo(String offweb, List host, List path, List fragmentprefix) { 63 | this.offWeb = offweb; 64 | this.host = host; 65 | this.path = path; 66 | this.fragmentPrefix = fragmentprefix; 67 | } 68 | 69 | public String getOffWeb() { 70 | return offWeb; 71 | } 72 | 73 | public List getHost() { 74 | return host; 75 | } 76 | 77 | public List getPath() { 78 | return path; 79 | } 80 | 81 | public List getFragmentPrefix() { 82 | return fragmentPrefix; 83 | } 84 | 85 | @Override 86 | public boolean equals(Object o) { 87 | if (this == o) { 88 | return true; 89 | } 90 | if (o == null || getClass() != o.getClass()) { 91 | return false; 92 | } 93 | RulesInfo rulesInfo = (RulesInfo) o; 94 | return Objects.equals(offWeb, rulesInfo.offWeb); 95 | } 96 | 97 | @Override 98 | public int hashCode() { 99 | return Objects.hash(offWeb); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/info/OfflineZipPackageConfig.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.info; 2 | 3 | import androidx.annotation.Keep; 4 | 5 | import java.io.Serializable; 6 | import java.util.Objects; 7 | 8 | /** 9 | * @copyright:深圳依时货拉拉科技有限公司 10 | * @fileName: OfflineZipPackageConfig 11 | * @author: kelvin 12 | * @date: 7/20/21 13 | * @description: 单个离线包数据 压缩包中的 offweb.json 14 | * @history: 15 | */ 16 | 17 | @Keep 18 | public class OfflineZipPackageConfig implements Serializable { 19 | 20 | 21 | /** 22 | * bisName : yanagi-test-1 23 | * ver : v1 24 | */ 25 | 26 | private String bisName; 27 | private String ver; 28 | 29 | public String getBisName() { 30 | return bisName; 31 | } 32 | 33 | public String getVer() { 34 | return ver; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return Objects.hash(bisName); 40 | } 41 | 42 | public void setBisName(String bisName) { 43 | this.bisName = bisName; 44 | } 45 | 46 | @Override 47 | public boolean equals(Object o) { 48 | if (this == o) { 49 | return true; 50 | } 51 | if (o == null || getClass() != o.getClass()) { 52 | return false; 53 | } 54 | OfflineZipPackageConfig that = (OfflineZipPackageConfig) o; 55 | return Objects.equals(bisName, that.bisName); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "OfflineZipPackageConfig{" + 61 | "bisName='" + bisName + '\'' + 62 | ", ver='" + ver + '\'' + 63 | '}'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/log/Logger.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.log; 2 | 3 | /** 4 | * @copyright:深圳依时货拉拉科技有限公司 5 | * @fileName: Logger 6 | * @author: kelvin 7 | * @date: 7/19/21 8 | * @description: 日志接口 9 | * @history: 10 | */ 11 | 12 | public interface Logger { 13 | 14 | void e(String tag, String content); 15 | 16 | void e(String tag, Throwable throwable); 17 | 18 | void d(String tag, String content); 19 | 20 | void i(String tag, String content); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/log/OfflineWebLog.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.log; 2 | 3 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 4 | 5 | /** 6 | * @copyright:深圳依时货拉拉科技有限公司 7 | * @fileName: OfflineWebLog 8 | * @author: kelvin 9 | * @date: 7/19/21 10 | * @description: 日志输出 11 | * @history: 12 | */ 13 | 14 | public class OfflineWebLog { 15 | 16 | private OfflineWebLog() { 17 | } 18 | 19 | private static final String DEFAULT_TAG = "OffLineWeb_"; 20 | 21 | public static void e(String tag, String content) { 22 | if (getLogger() == null) { 23 | if (OfflineWebManager.getInstance().isDebug()) { 24 | tag = Thread.currentThread().getName() + "-" + tag; 25 | android.util.Log.e(DEFAULT_TAG + tag, content); 26 | } 27 | } else { 28 | getLogger().e(tag, content); 29 | } 30 | } 31 | public static void e(String tag, Throwable t) { 32 | if (getLogger() == null) { 33 | if (OfflineWebManager.getInstance().isDebug()) { 34 | try { 35 | tag = Thread.currentThread().getName() + "-" + tag; 36 | android.util.Log.e(DEFAULT_TAG + tag, t.getMessage()); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } else { 42 | getLogger().e(DEFAULT_TAG + tag, t); 43 | } 44 | } 45 | 46 | private static Logger getLogger() { 47 | return OfflineWebManager.getInstance().getLogger(); 48 | } 49 | 50 | public static void d(String tag, String content) { 51 | if (getLogger() == null) { 52 | if (OfflineWebManager.getInstance().isDebug()) { 53 | tag = Thread.currentThread().getName() + "-" + tag; 54 | android.util.Log.d(DEFAULT_TAG + tag, content); 55 | } 56 | } else { 57 | getLogger().d(DEFAULT_TAG + tag, content); 58 | } 59 | } 60 | 61 | public static void i(String tag, String content) { 62 | if (getLogger() == null) { 63 | if (OfflineWebManager.getInstance().isDebug()) { 64 | tag = Thread.currentThread().getName() + "-" + tag; 65 | android.util.Log.i(DEFAULT_TAG + tag, content); 66 | } 67 | } else { 68 | getLogger().i(DEFAULT_TAG + tag, content); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/matcher/BisNameMatcher.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.matcher; 2 | 3 | /** 4 | * @copyright:深圳依时货拉拉科技有限公司 5 | * @fileName: BisNameMatcher 6 | * @author: kelvin 7 | * @date: 7/20/21 8 | * @description: 匹配器 9 | * @history: 10 | */ 11 | 12 | public interface BisNameMatcher { 13 | 14 | 15 | /** 16 | * 匹配url 17 | * @param url 源url 18 | * @return 返回匹配拼接离线包参数的链接 19 | */ 20 | String matching(String url); 21 | } 22 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/matcher/DefaultMatcher.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.matcher; 2 | 3 | import android.net.Uri; 4 | 5 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 6 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 7 | import com.lalamove.huolala.offline.webview.utils.OfflineConstant; 8 | import com.lalamove.huolala.offline.webview.utils.OfflineFileUtils; 9 | import com.lalamove.huolala.offline.webview.utils.OfflinePackageUtil; 10 | import com.lalamove.huolala.offline.webview.utils.UrlParamsUtils; 11 | 12 | import java.io.File; 13 | 14 | /** 15 | * @copyright:深圳依时货拉拉科技有限公司 16 | * @fileName: DefaultMatcher 17 | * @author: kelvin 18 | * @date: 7/20/21 19 | * @description: 业务匹配器 20 | * @history: 21 | */ 22 | 23 | public class DefaultMatcher implements BisNameMatcher { 24 | 25 | private static final String TAG = DefaultMatcher.class.getSimpleName(); 26 | 27 | public DefaultMatcher() { 28 | } 29 | 30 | @Override 31 | public String matching(String url) { 32 | String bisName = null; 33 | Uri parse = null; 34 | try { 35 | parse = Uri.parse(url); 36 | bisName = parse.getQueryParameter(OfflineConstant.OFF_WEB); 37 | } catch (Exception e) { 38 | OfflineWebLog.e(TAG, e); 39 | } 40 | 41 | if (parse == null || OfflineFileUtils.isSpaceString(bisName)) { 42 | return url; 43 | } 44 | 45 | if (!OfflineWebManager.getInstance().getSharedPreferences().getBoolean(bisName, true)) { 46 | OfflineWebLog.i(TAG, "match url :" + bisName + " is disabled by sp"); 47 | return url; 48 | } 49 | 50 | if (OfflineWebManager.getInstance().getOfflineConfig().isDisable(bisName)) { 51 | OfflineWebLog.i(TAG, "match url :" + bisName + " is disabled by user"); 52 | return url; 53 | } 54 | 55 | String bisDir = OfflinePackageUtil.getBisDir(bisName); 56 | File file = new File(bisDir + File.separator + OfflineConstant.CUR_DIR_NAME + File.separator + OfflineConstant.HTML_FILE); 57 | if (file.exists()) { 58 | OfflineWebLog.d(TAG, file.getAbsolutePath()); 59 | String params = url.substring(url.indexOf("?")); 60 | if (params.contains("#")) { 61 | String beforeParams = parse.getQuery(); 62 | String afterParams = parse.getFragment(); 63 | beforeParams = UrlParamsUtils.urlAppendParam(beforeParams, OfflineConstant.PARAM_OFFWEB_HOST, parse.getHost()); 64 | if (!"/".equals(afterParams)) { 65 | afterParams = UrlParamsUtils.urlAppendParam(afterParams, OfflineConstant.PARAM_OFFWEB_HOST, parse.getHost()); 66 | } 67 | return "file://" + file.getAbsolutePath() + "?" + beforeParams + "#" + afterParams; 68 | } else { 69 | params = UrlParamsUtils.urlAppendParam(params, OfflineConstant.PARAM_OFFWEB_HOST, parse.getHost()); 70 | return "file://" + file.getAbsolutePath() + params; 71 | } 72 | } else { 73 | OfflineWebLog.d(TAG, "file not found : " + file.getAbsolutePath()); 74 | return url; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/OfflineWebMonitorUtils.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 6 | import com.lalamove.huolala.offline.webview.flow.FlowReportParams; 7 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebMonitor; 8 | import com.lalamove.huolala.offline.webview.utils.EnhWebUriUtils; 9 | 10 | import java.util.HashMap; 11 | 12 | /** 13 | * create by zhii.yang 2021/12/6 14 | * desc : 监控降级配置 15 | */ 16 | 17 | public class OfflineWebMonitorUtils { 18 | 19 | 20 | private OfflineWebMonitorUtils() { 21 | } 22 | 23 | public static void reportFlowParams(FlowReportParams params) { 24 | HashMap hashMap = new HashMap<>(12); 25 | hashMap.put("bisName", params.getBisName()); 26 | hashMap.put("queryTime", params.getQueryTime()); 27 | hashMap.put("queryResult", params.getQueryResult()); 28 | hashMap.put("queryMsg", params.getQueryMsg()); 29 | hashMap.put("downloadTime", params.getDownloadTime()); 30 | hashMap.put("downloadResult", params.getDownloadResult()); 31 | hashMap.put("downloadMsg", params.getDownloadMsg()); 32 | hashMap.put("unzipTime", params.getUnzipTime()); 33 | hashMap.put("unzipResult", params.getUnzipResult()); 34 | hashMap.put("unzipMsg", params.getUnzipMsg()); 35 | hashMap.put("zipSize", params.getZipSize()); 36 | hashMap.put("continueDownload", params.isBrokenDown() ? 1 : 0); 37 | IEnhWebMonitor monitor = OfflineWebManager.getInstance().getMonitor(); 38 | if (monitor != null) { 39 | monitor.report("offweb_cost_time", hashMap); 40 | } 41 | } 42 | 43 | public static void reportLoadError( 44 | String simpleUrl, 45 | String originUrl, 46 | String bisName, 47 | boolean isOffline, 48 | long startTime, 49 | String error, 50 | String errorCode 51 | ) { 52 | HashMap hashMap = new HashMap<>(8); 53 | hashMap.put("simpleUrl", simpleUrl); 54 | hashMap.put("url", originUrl); 55 | hashMap.put("bisName", bisName); 56 | hashMap.put("isOffweb", isOffline ? "1" : "0"); 57 | hashMap.put("loadResult", "-1"); 58 | hashMap.put("errMsg", error); 59 | hashMap.put("errCode", errorCode); 60 | hashMap.put("loadTime", System.currentTimeMillis() - startTime); 61 | IEnhWebMonitor monitor = OfflineWebManager.getInstance().getMonitor(); 62 | if (monitor != null) { 63 | monitor.report("offweb_client_load_time", hashMap); 64 | } 65 | } 66 | 67 | public static void reportLoadFinish( 68 | String simpleUrl, 69 | String originUrl, 70 | String bisName, 71 | boolean isOffline, 72 | long startTime 73 | ) { 74 | HashMap hashMap = new HashMap<>(7); 75 | hashMap.put("simpleUrl", simpleUrl); 76 | hashMap.put("url", originUrl); 77 | hashMap.put("bisName", bisName); 78 | hashMap.put("isOffweb", isOffline ? "1" : "0"); 79 | hashMap.put("loadResult", "0"); 80 | hashMap.put("errMsg", ""); 81 | hashMap.put("loadTime", System.currentTimeMillis() - startTime); 82 | IEnhWebMonitor monitor = OfflineWebManager.getInstance().getMonitor(); 83 | if (monitor != null) { 84 | monitor.report("offweb_client_load_time", hashMap); 85 | } 86 | } 87 | 88 | /** 89 | * 请求速度(单位ms) 90 | */ 91 | public static void monitorLoadTime( 92 | String originUrl, 93 | String bisName, 94 | boolean isOffline, 95 | long startTime, 96 | String resultCode 97 | ) { 98 | HashMap hashMap = new HashMap<>(4); 99 | //(网络请求为https,走离线包为file) 100 | hashMap.put("scheme", isOffline? "file": EnhWebUriUtils.getScheme(originUrl)); 101 | hashMap.put("url", EnhWebUriUtils.getMonitorUrl(originUrl)); 102 | hashMap.put("bisName", TextUtils.isEmpty(bisName) ? "" : bisName); 103 | hashMap.put("result", TextUtils.isEmpty(resultCode) ? "-1" : resultCode); 104 | IEnhWebMonitor monitor = OfflineWebManager.getInstance().getMonitor(); 105 | if (monitor != null) { 106 | monitor.monitorSummary( 107 | "webviewLoadTime", 108 | (System.currentTimeMillis() - startTime), 109 | hashMap, 110 | "" 111 | ); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/base/IEnhWebMonitor.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.base; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * create by zhii.yang 2021/12/3 7 | * desc : 8 | */ 9 | 10 | public interface IEnhWebMonitor { 11 | 12 | /** 13 | * 埋点 14 | */ 15 | void report(String event, HashMap params); 16 | 17 | /** 18 | * 监控分析 19 | */ 20 | void monitorSummary(String name, float value, HashMap labelMap, String extra); 21 | } 22 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/base/IEnhWebResourceErrorAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.base; 2 | 3 | /** 4 | * create by zhii.yang 2021/12/27 5 | * desc : 6 | */ 7 | public abstract class IEnhWebResourceErrorAdapter { 8 | 9 | public abstract int getErrorCode(); 10 | 11 | public abstract String getDescription(); 12 | } 13 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/base/IEnhWebResourceRequestAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.base; 2 | 3 | import android.net.Uri; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * create by zhii.yang 2021/12/27 9 | * desc : 10 | */ 11 | 12 | public interface IEnhWebResourceRequestAdapter { 13 | Uri getUrl(); 14 | 15 | boolean isForMainFrame(); 16 | 17 | boolean hasGesture(); 18 | 19 | String getMethod(); 20 | 21 | Map getRequestHeaders(); 22 | } 23 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/base/IEnhWebResourceResponseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.base; 2 | 3 | /** 4 | * create by zhii.yang 2021/12/27 5 | * desc : 6 | */ 7 | public interface IEnhWebResourceResponseAdapter { 8 | int getStatusCode(); 9 | 10 | String getReasonPhrase(); 11 | } 12 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/base/IWebPageStatus.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.base; 2 | 3 | /** 4 | * @author yz 5 | * @time 2021/12/12 5:39 下午 6 | * 页面状态 7 | */ 8 | public interface IWebPageStatus { 9 | void onLoadUrl(String url); 10 | 11 | void onPageLoadFinish(String url, int progress); 12 | 13 | void onLoadError(IEnhWebResourceRequestAdapter requestAdapter, IEnhWebResourceErrorAdapter error); 14 | 15 | void onLoadError(IEnhWebResourceRequestAdapter requestAdapter, IEnhWebResourceResponseAdapter errorResponse); 16 | 17 | void onLoadError(String url, String error); 18 | } 19 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/impl/EnhWebResourceErrorAdapterImpl.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.impl; 2 | 3 | import android.os.Build; 4 | import android.webkit.WebResourceError; 5 | 6 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebResourceErrorAdapter; 7 | 8 | /** 9 | * @copyright:深圳依时货拉拉科技有限公司 10 | * @fileName: EnhWebResourceErrorAdapterImpl 11 | * @author: kelvin 12 | * @date: 3/28/22 13 | * @description: 14 | * @history: 15 | */ 16 | 17 | public class EnhWebResourceErrorAdapterImpl extends IEnhWebResourceErrorAdapter { 18 | 19 | private WebResourceError webkitError; 20 | private WebResourceError smttError; 21 | 22 | public EnhWebResourceErrorAdapterImpl(WebResourceError webkitError, WebResourceError smttError) { 23 | this.webkitError = webkitError; 24 | this.smttError = smttError; 25 | } 26 | 27 | @Override 28 | public int getErrorCode() { 29 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 30 | if (webkitError != null) { 31 | return webkitError.getErrorCode(); 32 | } 33 | if (smttError != null) { 34 | return smttError.getErrorCode(); 35 | } 36 | } 37 | return -1; 38 | } 39 | 40 | @Override 41 | public String getDescription() { 42 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 43 | if (webkitError != null) { 44 | return webkitError.getDescription().toString(); 45 | } 46 | if (smttError != null) { 47 | return smttError.getDescription().toString(); 48 | } 49 | } 50 | 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/impl/EnhWebResourceRequestAdapterImpl.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.impl; 2 | 3 | import android.net.Uri; 4 | import android.webkit.WebResourceRequest; 5 | 6 | import androidx.annotation.Nullable; 7 | 8 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebResourceRequestAdapter; 9 | 10 | import java.util.Map; 11 | 12 | /** 13 | * create by zhii.yang 2021/12/27 14 | * desc : 15 | */ 16 | public final class EnhWebResourceRequestAdapterImpl implements IEnhWebResourceRequestAdapter { 17 | @Nullable 18 | private WebResourceRequest webkitRequest; 19 | @Nullable 20 | private WebResourceRequest smttRequest; 21 | 22 | @Override 23 | @Nullable 24 | public Uri getUrl() { 25 | WebResourceRequest resourceRequest; 26 | if (this.webkitRequest != null) { 27 | resourceRequest = this.webkitRequest; 28 | return resourceRequest.getUrl(); 29 | } else if (this.smttRequest != null) { 30 | resourceRequest = this.smttRequest; 31 | return resourceRequest.getUrl(); 32 | } else { 33 | return null; 34 | } 35 | } 36 | 37 | @Override 38 | public boolean isForMainFrame() { 39 | WebResourceRequest resourceRequest; 40 | if (this.webkitRequest != null) { 41 | resourceRequest = this.webkitRequest; 42 | return resourceRequest.isForMainFrame(); 43 | } else if (this.smttRequest != null) { 44 | resourceRequest = this.smttRequest; 45 | return resourceRequest.isForMainFrame(); 46 | } else { 47 | return false; 48 | } 49 | } 50 | 51 | @Override 52 | public boolean hasGesture() { 53 | WebResourceRequest resourceRequest; 54 | if (this.webkitRequest != null) { 55 | resourceRequest = this.webkitRequest; 56 | return resourceRequest.hasGesture(); 57 | } else if (this.smttRequest != null) { 58 | resourceRequest = this.smttRequest; 59 | return resourceRequest.hasGesture(); 60 | } else { 61 | return false; 62 | } 63 | } 64 | 65 | @Override 66 | @Nullable 67 | public String getMethod() { 68 | WebResourceRequest resourceRequest; 69 | if (this.webkitRequest != null) { 70 | resourceRequest = this.webkitRequest; 71 | return resourceRequest.getMethod(); 72 | } else if (this.smttRequest != null) { 73 | resourceRequest = this.smttRequest; 74 | return resourceRequest.getMethod(); 75 | } else { 76 | return null; 77 | } 78 | } 79 | 80 | @Override 81 | @Nullable 82 | public Map getRequestHeaders() { 83 | WebResourceRequest resourceRequest; 84 | if (this.webkitRequest != null) { 85 | resourceRequest = this.webkitRequest; 86 | return resourceRequest.getRequestHeaders(); 87 | } else if (this.smttRequest != null) { 88 | resourceRequest = this.smttRequest; 89 | return resourceRequest.getRequestHeaders(); 90 | } else { 91 | return null; 92 | } 93 | } 94 | 95 | 96 | public EnhWebResourceRequestAdapterImpl(@Nullable WebResourceRequest webkitRequest, @Nullable WebResourceRequest smttRequest) { 97 | this.webkitRequest = webkitRequest; 98 | this.smttRequest = smttRequest; 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/monitor/impl/EnhWebResourceResponseAdapterImpl.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.monitor.impl; 2 | 3 | import android.webkit.WebResourceResponse; 4 | 5 | import androidx.annotation.Nullable; 6 | 7 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebResourceResponseAdapter; 8 | 9 | public final class EnhWebResourceResponseAdapterImpl implements IEnhWebResourceResponseAdapter { 10 | @Nullable 11 | private WebResourceResponse webkitResponse; 12 | @Nullable 13 | private WebResourceResponse smttResponse; 14 | 15 | @Override 16 | public int getStatusCode() { 17 | WebResourceResponse resourceResponse; 18 | if (this.webkitResponse != null) { 19 | resourceResponse = this.webkitResponse; 20 | return resourceResponse.getStatusCode(); 21 | } else if (this.smttResponse != null) { 22 | resourceResponse = this.smttResponse; 23 | return resourceResponse.getStatusCode(); 24 | } else { 25 | return -1; 26 | } 27 | } 28 | 29 | @Override 30 | @Nullable 31 | public String getReasonPhrase() { 32 | WebResourceResponse webResourceResponse; 33 | if (this.webkitResponse != null) { 34 | webResourceResponse = this.webkitResponse; 35 | return webResourceResponse.getReasonPhrase(); 36 | } else if (this.smttResponse != null) { 37 | webResourceResponse = this.smttResponse; 38 | return webResourceResponse.getReasonPhrase(); 39 | } else { 40 | return null; 41 | } 42 | } 43 | 44 | public EnhWebResourceResponseAdapterImpl(@Nullable WebResourceResponse webkitResponse, @Nullable WebResourceResponse smttResponse) { 45 | this.webkitResponse = webkitResponse; 46 | this.smttResponse = smttResponse; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/net/IOfflineRequest.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.net; 2 | 3 | import com.lalamove.huolala.offline.webview.info.OfflinePackageInfo; 4 | 5 | /** 6 | * @copyright:深圳依时货拉拉科技有限公司 7 | * @fileName: IOfflineRequest 8 | * @author: kelvin 9 | * @date: 3/29/22 10 | * @description: 网络请求 11 | * @history: 12 | */ 13 | 14 | public interface IOfflineRequest { 15 | 16 | /** 17 | * 请求离线包数据 18 | * 19 | * @param bizName 当前离线包业务名称 20 | * @param version 当前离线包版本 21 | * @param callback 回调 22 | */ 23 | void requestPackageInfo(String bizName, String version, RequestCallback callback); 24 | } 25 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/net/RequestCallback.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.net; 2 | 3 | /** 4 | * @copyright:深圳依时货拉拉科技有限公司 5 | * @fileName: RequestCallBack 6 | * @author: kelvin 7 | * @date: 3/29/22 8 | * @description: 网络请求回调 9 | * @history: 10 | */ 11 | 12 | public interface RequestCallback { 13 | 14 | /** 15 | * 请求成功 16 | * @param data 返回数据实体 17 | */ 18 | void onSuccess(T data); 19 | 20 | /** 21 | * 请求异常信息 22 | * @param throwable 异常 23 | */ 24 | void onFail(Throwable throwable); 25 | } 26 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/proxy/EmptyOfflineWebViewProxy.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.proxy; 2 | 3 | 4 | /** 5 | * @copyright:深圳依时货拉拉科技有限公司 6 | * @fileName: EmptyOfflineWebViewProxy 7 | * @author: kelvin 8 | * @date: 8/17/21 9 | * @description: 为初始化是返回空代理 10 | * @history: 11 | */ 12 | 13 | public class EmptyOfflineWebViewProxy implements IOfflineWebViewProxy{ 14 | @Override 15 | public String getBisName() { 16 | return ""; 17 | } 18 | 19 | @Override 20 | public String loadUrl(String url) { 21 | return url; 22 | } 23 | 24 | @Override 25 | public void reLoadUrl() { 26 | 27 | } 28 | 29 | @Override 30 | public void destroy() { 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/proxy/IOfflineWebViewProxy.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.proxy; 2 | 3 | /** 4 | * @copyright:深圳依时货拉拉科技有限公司 5 | * @fileName: IOfflineWebViewProxy 6 | * @author: kelvin 7 | * @date: 8/17/21 8 | * @description: 离线包功能代理 9 | * @history: 10 | */ 11 | 12 | public interface IOfflineWebViewProxy { 13 | 14 | /** 15 | * 获取 业务名称 16 | * @return 业务名称 17 | */ 18 | String getBisName(); 19 | 20 | /** 21 | * 加载url 22 | * @param url 源url 23 | * @return 拼接离线包参数的url 24 | */ 25 | String loadUrl(String url); 26 | 27 | /** 28 | * 调用重新加载页面,只有离线包返回强制更新才会调用。业务方根据需要实现,默认无操作 29 | */ 30 | void reLoadUrl(); 31 | 32 | /** 33 | * 销毁 34 | */ 35 | void destroy(); 36 | } 37 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/proxy/OffWebProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.proxy; 2 | 3 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 4 | import com.lalamove.huolala.offline.webview.widget.IOfflineWebView; 5 | 6 | /** 7 | * @copyright:深圳依时货拉拉科技有限公司 8 | * @fileName: WebProxyFactory 9 | * @author: kelvin 10 | * @date: 8/17/21 11 | * @description: 离线包功能代理工厂 12 | * @history: 13 | */ 14 | 15 | public class OffWebProxyFactory { 16 | 17 | private OffWebProxyFactory() { 18 | } 19 | 20 | public static IOfflineWebViewProxy getProxy(IOfflineWebView webView) { 21 | if (OfflineWebManager.getInstance().isInit()) { 22 | return new OfflineWebViewProxy(webView); 23 | } else { 24 | return new EmptyOfflineWebViewProxy(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/proxy/OfflineWebViewProxy.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.proxy; 2 | 3 | import android.net.Uri; 4 | import android.text.TextUtils; 5 | import android.widget.Toast; 6 | 7 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 8 | import com.lalamove.huolala.offline.webview.task.CheckAndUpdateTask; 9 | import com.lalamove.huolala.offline.webview.utils.OffWebRuleUtil; 10 | import com.lalamove.huolala.offline.webview.utils.OfflineHandlerUtils; 11 | import com.lalamove.huolala.offline.webview.utils.OfflineConstant; 12 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 13 | import com.lalamove.huolala.offline.webview.widget.IOfflineWebView; 14 | import com.lalamove.huolala.offline.webview.widget.ReloadOfflineWebView; 15 | 16 | 17 | /** 18 | * @copyright:深圳依时货拉拉科技有限公司 19 | * @fileName: OfflineWebViewProxy 20 | * @author: kelvin 21 | * @date: 7/22/21 22 | * @description: 23 | * @history: 24 | */ 25 | 26 | public class OfflineWebViewProxy implements IOfflineWebViewProxy { 27 | 28 | private static final String TAG = OfflineWebViewProxy.class.getSimpleName(); 29 | private IOfflineWebView mIOfflineWebView; 30 | private String mBisName; 31 | private boolean mIsOffline; 32 | 33 | public OfflineWebViewProxy(IOfflineWebView iOfflineWebView) { 34 | mIOfflineWebView = iOfflineWebView; 35 | } 36 | 37 | @Override 38 | public String getBisName() { 39 | return mBisName; 40 | } 41 | 42 | @Override 43 | public String loadUrl(String url) { 44 | url = OffWebRuleUtil.addOfflineParam(url); 45 | String offlineRes = url; 46 | if (isOffWebUrl(url)) { 47 | try { 48 | Uri uri = Uri.parse(url.trim()); 49 | mBisName = uri.getQueryParameter(OfflineConstant.OFF_WEB); 50 | if (!"http".equalsIgnoreCase(uri.getScheme()) && !"https".equalsIgnoreCase(uri.getScheme())) { 51 | return url; 52 | } 53 | } catch (Exception e) { 54 | OfflineWebLog.e(TAG, e); 55 | } 56 | 57 | OfflineWebLog.i(TAG, url); 58 | if (TextUtils.isEmpty(mBisName)) { 59 | return url; 60 | } 61 | if (OfflineWebManager.getInstance().getOfflineConfig().isDisable(mBisName)) { 62 | OfflineWebLog.i(TAG, "is disabled:" + mBisName); 63 | return url; 64 | } 65 | 66 | offlineRes = OfflineWebManager.getInstance().getOfflineRes(url); 67 | OfflineWebLog.i(TAG, "match url :" + offlineRes); 68 | OfflineWebManager.getInstance().getExecutor().execute(new CheckAndUpdateTask(mBisName, null)); 69 | 70 | mIsOffline = !url.equals(offlineRes); 71 | if (OfflineWebManager.getInstance().isDebug()) { 72 | Toast.makeText(OfflineWebManager.getInstance().getContext(), 73 | mIsOffline ? "离线包模式" : "在线模式", Toast.LENGTH_LONG) 74 | .show(); 75 | } 76 | } 77 | 78 | if (mIsOffline && reloadEnable()) { 79 | OfflineWebLog.i(TAG, "add reload " + mBisName); 80 | OfflineWebManager.getInstance().getPageManager().addPage(this); 81 | } 82 | 83 | return offlineRes; 84 | } 85 | 86 | private boolean isOffWebUrl(String url) { 87 | return url.contains(OfflineConstant.OFF_WEB) && (url.startsWith("http") || url.startsWith("https")); 88 | } 89 | 90 | private boolean reloadEnable() { 91 | return mIOfflineWebView instanceof ReloadOfflineWebView && OfflineWebManager.getInstance().isForceReloadEnable(); 92 | } 93 | 94 | @Override 95 | public void reLoadUrl() { 96 | OfflineHandlerUtils.post(new Runnable() { 97 | @Override 98 | public void run() { 99 | if (reloadEnable() && mIOfflineWebView != null) { 100 | OfflineWebLog.i(TAG, "reLoadUrl"); 101 | ReloadOfflineWebView reloadOfflineWebView = (ReloadOfflineWebView) mIOfflineWebView; 102 | reloadOfflineWebView.reloadOfflineWeb(); 103 | } 104 | } 105 | }); 106 | } 107 | 108 | @Override 109 | public void destroy() { 110 | if (reloadEnable()) { 111 | OfflineWebLog.i(TAG, "destroy"); 112 | OfflineWebManager.getInstance().getPageManager().remove(this); 113 | } 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/resource/DownloadFlow.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.resource; 2 | 3 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 4 | import com.lalamove.huolala.offline.webview.download.IDownLoader; 5 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 6 | import com.lalamove.huolala.offline.webview.utils.OfflineConstant; 7 | import com.lalamove.huolala.offline.webview.utils.OfflineFileUtils; 8 | import com.lalamove.huolala.offline.webview.utils.OfflineStringUtils; 9 | import com.lalamove.huolala.offline.webview.utils.OfflinePackageUtil; 10 | 11 | import java.io.File; 12 | 13 | /** 14 | * @copyright:深圳依时货拉拉科技有限公司 15 | * @fileName: DownloadFlow 16 | * @author: kelvin 17 | * @date: 7/19/21 18 | * @description: 下载包 19 | * @history: 20 | */ 21 | 22 | public class DownloadFlow implements ResourceFlow.IFlow { 23 | 24 | private static final String TAG = DownloadFlow.class.getSimpleName(); 25 | private ResourceFlow mResourceFlow; 26 | 27 | 28 | public DownloadFlow(ResourceFlow resourceFlow) { 29 | mResourceFlow = resourceFlow; 30 | } 31 | 32 | @Override 33 | public void process() { 34 | final String downloadPath = mResourceFlow.getPackageInfo().getUrl(); 35 | String destPath = OfflinePackageUtil.getBisDir(mResourceFlow.getPackageInfo().getBisName()) 36 | + File.separator + mResourceFlow.getPackageInfo().getVersion() + OfflineConstant.ZIP_SUFFIX; 37 | 38 | IDownLoader downLoader = OfflineWebManager.getInstance().getDownLoader(); 39 | File file = new File(destPath); 40 | OfflineWebLog.d(TAG, "start download ... mDestPath=" + destPath + "\n mDownloadPath=" + downloadPath); 41 | mResourceFlow.getReportParams().downloadStart(); 42 | downLoader.download(downloadPath, file.getParentFile().getAbsolutePath(), file.getName(), new IDownLoader.DownloadCallback() { 43 | @Override 44 | public void success(File file, boolean isBrokenDown) { 45 | if (file == null || !file.exists()) { 46 | mResourceFlow.getReportParams().downloadResult(false, "download error local file not found"); 47 | mResourceFlow.error(new IllegalStateException("download error local file not found")); 48 | return; 49 | } 50 | mResourceFlow.getReportParams().zipSize(OfflineFileUtils.getFileSize(file)); 51 | mResourceFlow.getReportParams().isBrokenDown(isBrokenDown); 52 | mResourceFlow.getReportParams().downloadResult(true, downloadPath); 53 | mResourceFlow.process(); 54 | } 55 | 56 | @Override 57 | public void fail(Throwable e) { 58 | mResourceFlow.getReportParams().downloadResult(false, OfflineStringUtils.getErrorString(e)); 59 | mResourceFlow.error(e); 60 | } 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/resource/FetchPackageFlow.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.resource; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 6 | import com.lalamove.huolala.offline.webview.info.OfflinePackageInfo; 7 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 8 | import com.lalamove.huolala.offline.webview.net.RequestCallback; 9 | import com.lalamove.huolala.offline.webview.utils.OfflineStringUtils; 10 | 11 | 12 | /** 13 | * @copyright:深圳依时货拉拉科技有限公司 14 | * @fileName: FetchPackageFlow 15 | * @author: kelvin 16 | * @date: 7/27/21 17 | * @description: 获取更新包 18 | * @history: 19 | */ 20 | 21 | public class FetchPackageFlow implements ResourceFlow.IFlow { 22 | 23 | private static final String TAG = FetchPackageFlow.class.getSimpleName(); 24 | private ResourceFlow mResourceFlow; 25 | 26 | public FetchPackageFlow(ResourceFlow resourceFlow) { 27 | mResourceFlow = resourceFlow; 28 | } 29 | 30 | @Override 31 | public void process() throws ResourceFlow.FlowException { 32 | if (TextUtils.isEmpty(mResourceFlow.getPackageInfo().getBisName())) { 33 | mResourceFlow.getReportParams().setQueryResult(false, "bisName == null "); 34 | mResourceFlow.error(new IllegalStateException("FetchPackageFlow bisName == null ")); 35 | return; 36 | } 37 | OfflineWebManager.getInstance().getRequest().requestPackageInfo( 38 | mResourceFlow.getPackageInfo().getBisName(), 39 | mResourceFlow.getPackageInfo().getVersion(), 40 | new RequestCallback() { 41 | @Override 42 | public void onSuccess(OfflinePackageInfo offlinePackageInfo) { 43 | mResourceFlow.getReportParams().queryEnd(); 44 | 45 | if (offlinePackageInfo == null) { 46 | //如果为空,则代表响应数据有问题 47 | mResourceFlow.getReportParams().setQueryResult(false, "onResponse:null"); 48 | mResourceFlow.setDone(); 49 | return; 50 | } 51 | OfflineWebManager.getInstance() 52 | .getSharedPreferences() 53 | .edit() 54 | .putBoolean(offlinePackageInfo.getBisName(), offlinePackageInfo.isEnable()) 55 | .apply(); 56 | mResourceFlow.getReportParams().setQueryResult(true, offlinePackageInfo.toString()); 57 | 58 | 59 | if (!mResourceFlow.getPackageInfo().getBisName().equals(offlinePackageInfo.getBisName())) { 60 | mResourceFlow.getReportParams().setQueryResult(false, "bizName error"); 61 | mResourceFlow.setDone(); 62 | return; 63 | } 64 | if (!offlinePackageInfo.isEnable()) { 65 | OfflineWebLog.i(TAG, "isEnable = false"); 66 | mResourceFlow.setDone(); 67 | return; 68 | } 69 | if (offlinePackageInfo.isSameVer()) { 70 | OfflineWebLog.i(TAG, "same version"); 71 | mResourceFlow.setDone(); 72 | return; 73 | } 74 | if (offlinePackageInfo.isNeedUpdate()) { 75 | mResourceFlow.setPackageInfo(offlinePackageInfo); 76 | mResourceFlow.process(); 77 | return; 78 | } 79 | OfflineWebLog.e(TAG, "unknown status :" + offlinePackageInfo.getResult()); 80 | } 81 | 82 | @Override 83 | public void onFail(Throwable throwable) { 84 | OfflineWebLog.e(TAG, throwable); 85 | mResourceFlow.getReportParams().queryEnd(); 86 | mResourceFlow.getReportParams().setQueryResult(false, OfflineStringUtils.getErrorString(throwable)); 87 | mResourceFlow.error(throwable); 88 | } 89 | } 90 | ); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/resource/ParsePackageFlow.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.resource; 2 | 3 | import com.lalamove.huolala.offline.webview.utils.OfflineConstant; 4 | import com.lalamove.huolala.offline.webview.utils.OfflineFileUtils; 5 | import com.lalamove.huolala.offline.webview.utils.OfflineStringUtils; 6 | import com.lalamove.huolala.offline.webview.utils.OfflinePackageUtil; 7 | 8 | import java.io.File; 9 | import java.util.List; 10 | 11 | /** 12 | * @copyright:深圳依时货拉拉科技有限公司 13 | * @fileName: ParsePackageFlow 14 | * @author: kelvin 15 | * @date: 7/19/21 16 | * @description: 解析离线包 17 | * @history: 18 | */ 19 | 20 | public class ParsePackageFlow implements ResourceFlow.IFlow { 21 | 22 | private static final String TAG = ParsePackageFlow.class.getSimpleName(); 23 | private ResourceFlow mResourceFlow; 24 | 25 | public ParsePackageFlow(ResourceFlow resourceFlow) { 26 | 27 | mResourceFlow = resourceFlow; 28 | 29 | } 30 | 31 | @Override 32 | public void process() throws ResourceFlow.FlowException { 33 | try { 34 | //data/data/pkgName/offline_web/bisname/new 35 | String unzipTempPath = OfflineStringUtils.appendUnsafeString( 36 | OfflinePackageUtil.getBisDir(mResourceFlow.getPackageInfo().getBisName()) 37 | , File.separator 38 | , OfflineConstant.TEMP_DIR_NAME); 39 | String unzipNewPath = OfflineStringUtils.appendUnsafeString( 40 | OfflinePackageUtil.getBisDir(mResourceFlow.getPackageInfo().getBisName()) 41 | , File.separator 42 | , OfflineConstant.NEW_DIR_NAME); 43 | mResourceFlow.getReportParams().unZipStart(); 44 | //删除原temp 45 | File tempDir = new File(unzipTempPath); 46 | File newDir = new File(unzipNewPath); 47 | OfflineFileUtils.deleteDir(tempDir); 48 | //data/data/pkgName/offline_web/bisname/V1.zip 49 | String zipPackagePath = OfflineStringUtils.appendUnsafeString( 50 | OfflinePackageUtil.getBisDir(mResourceFlow.getPackageInfo().getBisName()) 51 | , File.separator 52 | , mResourceFlow.getPackageInfo().getVersion() 53 | , OfflineConstant.ZIP_SUFFIX); 54 | //解压 55 | List files = OfflineFileUtils.unzipFile(zipPackagePath, unzipTempPath); 56 | //解压完毕 删除new 57 | OfflineFileUtils.deleteDir(newDir); 58 | //重命名temp为new 59 | OfflineFileUtils.rename(tempDir, OfflineConstant.NEW_DIR_NAME); 60 | mResourceFlow.getReportParams().unZipEnd(true, ""); 61 | mResourceFlow.process(); 62 | } catch (Exception e) { 63 | mResourceFlow.getReportParams().unZipEnd(false, OfflineStringUtils.getErrorString(e)); 64 | mResourceFlow.error(e); 65 | } 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/resource/ReplaceResFlow.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.resource; 2 | 3 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 4 | import com.lalamove.huolala.offline.webview.info.OfflinePackageInfo; 5 | import com.lalamove.huolala.offline.webview.utils.OfflineConstant; 6 | import com.lalamove.huolala.offline.webview.utils.OfflineFileUtils; 7 | import com.lalamove.huolala.offline.webview.utils.OfflinePackageUtil; 8 | import com.lalamove.huolala.offline.webview.utils.OfflineStringUtils; 9 | 10 | import java.io.File; 11 | 12 | /** 13 | * @copyright:深圳依时货拉拉科技有限公司 14 | * @fileName: ReplaceResFlow 15 | * @author: kelvin 16 | * @date: 7/19/21 17 | * @description: 替换离线包 18 | * @history: 19 | */ 20 | 21 | public class ReplaceResFlow implements ResourceFlow.IFlow { 22 | 23 | private ResourceFlow mFlow; 24 | 25 | public ReplaceResFlow(ResourceFlow flow) { 26 | mFlow = flow; 27 | } 28 | 29 | @Override 30 | public void process() throws ResourceFlow.FlowException { 31 | 32 | String unzipPath = OfflineStringUtils.appendUnsafeString( 33 | OfflinePackageUtil.getBisDir(mFlow.getPackageInfo().getBisName()) 34 | , File.separator 35 | , OfflineConstant.NEW_DIR_NAME); 36 | 37 | OfflinePackageInfo offlinePackageInfo = mFlow.getPackageInfo(); 38 | String destPath = OfflinePackageUtil.getBisDir(offlinePackageInfo.getBisName()); 39 | if (offlinePackageInfo.isForceRefresh()) { 40 | //强刷 41 | //原cur 重命名为old,下次启动删除 42 | File file = new File(OfflineStringUtils.appendUnsafeString(destPath, File.separator, OfflineConstant.CUR_DIR_NAME)); 43 | if (file.exists()) { 44 | OfflineFileUtils.rename(file, OfflineConstant.OLD_DIR_NAME); 45 | } 46 | OfflineFileUtils.rename(new File(unzipPath), OfflineConstant.CUR_DIR_NAME); 47 | //执行强刷 48 | OfflineWebManager.getInstance().getPageManager().reload(mFlow.getPackageInfo().getBisName()); 49 | } else { 50 | //cur 文件夹不存在则直接应用,改名为cur 51 | File file = new File(OfflineStringUtils.appendUnsafeString(destPath, File.separator, OfflineConstant.CUR_DIR_NAME)); 52 | if (!file.exists()) { 53 | OfflineFileUtils.rename(new File(unzipPath), OfflineConstant.CUR_DIR_NAME); 54 | } 55 | } 56 | mFlow.process(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/resource/ResourceFlow.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.resource; 2 | 3 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 4 | import com.lalamove.huolala.offline.webview.info.OfflinePackageInfo; 5 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 6 | import com.lalamove.huolala.offline.webview.flow.FlowReportParams; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * @copyright:深圳依时货拉拉科技有限公司 13 | * @fileName: ResourceFlow 14 | * @author: kelvin 15 | * @date: 7/19/21 16 | * @description: 获取,下载,解压,替换离线包流程管理类 17 | * @history: 18 | */ 19 | 20 | public class ResourceFlow { 21 | private static final String TAG = ResourceFlow.class.getSimpleName(); 22 | 23 | private List mFlows; 24 | 25 | private int mIndex; 26 | private OfflinePackageInfo mPackageInfo; 27 | private boolean isForceStop; 28 | private FlowListener mFlowListener; 29 | private FlowReportParams mReportParams; 30 | 31 | public ResourceFlow(OfflinePackageInfo packageInfo) { 32 | 33 | mPackageInfo = packageInfo; 34 | mReportParams = new FlowReportParams(mPackageInfo.getBisName()); 35 | } 36 | 37 | public void stop() { 38 | isForceStop = true; 39 | } 40 | 41 | public void addFlow(IFlow flow) { 42 | if (mFlows == null) { 43 | mFlows = new ArrayList<>(); 44 | } 45 | mFlows.add(flow); 46 | } 47 | 48 | public void start() { 49 | if (mFlows == null || mFlows.size() == 0) { 50 | return; 51 | } 52 | mIndex = 0; 53 | process(); 54 | } 55 | 56 | void process() { 57 | 58 | if (mIndex >= mFlows.size()) { 59 | OfflineWebLog.i(TAG, "done ... ..."); 60 | setDone(); 61 | return; 62 | } 63 | 64 | if (isForceStop) { 65 | OfflineWebLog.i(TAG, "isForceStop = " + isForceStop); 66 | return; 67 | } 68 | 69 | final IFlow iFlow = mFlows.get(mIndex++); 70 | OfflineWebManager.getInstance().getExecutor().execute(new Runnable() { 71 | @Override 72 | public void run() { 73 | try { 74 | OfflineWebLog.i(TAG, "Flow start process :" + iFlow.getClass().getSimpleName()); 75 | iFlow.process(); 76 | } catch (FlowException e) { 77 | e.printStackTrace(); 78 | OfflineWebLog.e(TAG, e); 79 | error(e); 80 | } 81 | } 82 | }); 83 | } 84 | 85 | void error(Throwable throwable) { 86 | OfflineWebLog.e(TAG, throwable); 87 | if (mFlowListener != null) { 88 | mFlowListener.error(mPackageInfo, throwable); 89 | } 90 | OfflineWebManager.getInstance().getFlowResultHandleStrategy().done(mReportParams); 91 | } 92 | 93 | public FlowReportParams getReportParams() { 94 | return this.mReportParams; 95 | } 96 | 97 | public OfflinePackageInfo getPackageInfo() { 98 | return mPackageInfo; 99 | } 100 | 101 | public void setPackageInfo(OfflinePackageInfo packageInfo) { 102 | mPackageInfo = packageInfo; 103 | } 104 | 105 | public void setFlowListener(FlowListener flowListener) { 106 | 107 | mFlowListener = flowListener; 108 | } 109 | 110 | public void setDone() { 111 | if (mFlowListener != null) { 112 | mFlowListener.done(mPackageInfo); 113 | } 114 | OfflineWebManager.getInstance().getFlowResultHandleStrategy().done(mReportParams); 115 | } 116 | 117 | public interface FlowListener { 118 | void done(OfflinePackageInfo packageInfo); 119 | 120 | void error(OfflinePackageInfo packageInfo, Throwable e); 121 | } 122 | 123 | public interface IFlow { 124 | void process() throws FlowException; 125 | } 126 | 127 | public static class FlowException extends Exception { 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/task/CheckAndUpdateTask.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.task; 2 | 3 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 4 | import com.lalamove.huolala.offline.webview.info.OfflinePackageInfo; 5 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 6 | import com.lalamove.huolala.offline.webview.resource.DownloadFlow; 7 | import com.lalamove.huolala.offline.webview.resource.FetchPackageFlow; 8 | import com.lalamove.huolala.offline.webview.resource.ParsePackageFlow; 9 | import com.lalamove.huolala.offline.webview.resource.ReplaceResFlow; 10 | import com.lalamove.huolala.offline.webview.resource.ResourceFlow; 11 | import com.lalamove.huolala.offline.webview.utils.OfflinePackageUtil; 12 | 13 | /** 14 | * @copyright:深圳依时货拉拉科技有限公司 15 | * @fileName: CheckAndUpdateTask 16 | * @author: kelvin 17 | * @date: 8/4/21 18 | * @description: 根据业务名称检查更新 离线包 19 | * @history: 20 | */ 21 | 22 | public class CheckAndUpdateTask implements Runnable { 23 | private static final String TAG = CheckAndUpdateTask.class.getSimpleName(); 24 | private String mBisName; 25 | private ResourceFlow.FlowListener mListener; 26 | 27 | public CheckAndUpdateTask(String bisName,final ResourceFlow.FlowListener listener) { 28 | mBisName = bisName; 29 | mListener = listener; 30 | } 31 | 32 | /** 33 | * 单线程 34 | */ 35 | @Override 36 | public void run() { 37 | synchronized (CheckAndUpdateTask.class) { 38 | //当前bisName 是否在禁用列表 39 | if (OfflineWebManager.getInstance().getInterceptor().isIntercept(mBisName)) { 40 | OfflineWebLog.i(TAG, "interceptor bisName :" + mBisName); 41 | return; 42 | } 43 | //判断是否已经在更新 44 | for (ResourceFlow resourceFlow : OfflineWebManager.getInstance().getResourceFlows()) { 45 | if (mBisName.equals(resourceFlow.getPackageInfo().getBisName())) { 46 | OfflineWebLog.i(TAG, "same flow running :" + mBisName); 47 | return; 48 | } 49 | } 50 | //新建更新任务 51 | String packageVersion = OfflinePackageUtil.getPackageVersion(mBisName); 52 | OfflineWebLog.i(TAG, "checkPackage:" + mBisName + ",packageVersion:" + packageVersion); 53 | ResourceFlow resourceFlow = new ResourceFlow(new OfflinePackageInfo(mBisName, packageVersion)); 54 | resourceFlow.addFlow(new FetchPackageFlow(resourceFlow)); 55 | resourceFlow.addFlow(new DownloadFlow(resourceFlow)); 56 | resourceFlow.addFlow(new ParsePackageFlow(resourceFlow)); 57 | resourceFlow.addFlow(new ReplaceResFlow(resourceFlow)); 58 | resourceFlow.setFlowListener(new ResourceFlow.FlowListener() { 59 | @Override 60 | public void done(OfflinePackageInfo packageInfo) { 61 | OfflineWebLog.i(TAG, "done :"); 62 | if (mListener != null) { 63 | mListener.done(packageInfo); 64 | } 65 | doneFlow(packageInfo); 66 | } 67 | 68 | @Override 69 | public void error(OfflinePackageInfo packageInfo, Throwable throwable) { 70 | if (mListener != null) { 71 | mListener.error(packageInfo, throwable); 72 | } 73 | doneFlow(packageInfo); 74 | OfflineWebLog.i(TAG, "error"); 75 | 76 | } 77 | 78 | private void doneFlow(OfflinePackageInfo packageInfo) { 79 | //移除任务 80 | for (ResourceFlow flow : OfflineWebManager.getInstance().getResourceFlows()) { 81 | if (packageInfo.equals(flow.getPackageInfo())) { 82 | OfflineWebManager.getInstance().getResourceFlows().remove(flow); 83 | break; 84 | } 85 | } 86 | } 87 | }); 88 | resourceFlow.start(); 89 | OfflineWebManager.getInstance().getResourceFlows().add(resourceFlow); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/task/CheckVersionTask.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.task; 2 | 3 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 4 | import com.lalamove.huolala.offline.webview.utils.OfflineConstant; 5 | import com.lalamove.huolala.offline.webview.utils.OfflineFileUtils; 6 | import com.lalamove.huolala.offline.webview.utils.OfflinePackageUtil; 7 | 8 | import java.io.File; 9 | import java.io.FileFilter; 10 | 11 | /** 12 | * @copyright:深圳依时货拉拉科技有限公司 13 | * @fileName: CheckVersionTask 14 | * @author: kelvin 15 | * @date: 8/6/21 16 | * @description: 初始化之后检测新旧离线包版本 进行删除替换 17 | * @history: 18 | */ 19 | 20 | public class CheckVersionTask implements Runnable { 21 | 22 | private static final String TAG = CheckVersionTask.class.getSimpleName(); 23 | 24 | @Override 25 | public void run() { 26 | OfflineWebLog.i(TAG, "checkVersions"); 27 | File file = new File(OfflinePackageUtil.getResDir()); 28 | if (file.exists()) { 29 | File[] packageDirs = file.listFiles(new FileFilter() { 30 | @Override 31 | public boolean accept(File pathname) { 32 | return pathname.isDirectory(); 33 | } 34 | }); 35 | if (packageDirs != null && packageDirs.length != 0) { 36 | for (File packageDir : packageDirs) { 37 | OfflineWebLog.i(TAG, "check :" + packageDir.getName()); 38 | File nowFile = new File(packageDir, OfflineConstant.NEW_DIR_NAME); 39 | File curFile = new File(packageDir, OfflineConstant.CUR_DIR_NAME); 40 | if (nowFile.exists() && curFile.exists()) { 41 | //存在 cur 和new 42 | //cur->old 43 | OfflineFileUtils.rename(curFile, OfflineConstant.OLD_DIR_NAME); 44 | //new->cur 45 | OfflineFileUtils.rename(nowFile, OfflineConstant.CUR_DIR_NAME); 46 | OfflineWebLog.i(TAG, "update replace"); 47 | } else if (nowFile.exists()) { 48 | OfflineWebLog.i(TAG, "update"); 49 | //new->cur 50 | OfflineFileUtils.rename(nowFile, OfflineConstant.CUR_DIR_NAME); 51 | } 52 | 53 | File oldFile = new File(packageDir, OfflineConstant.OLD_DIR_NAME); 54 | 55 | //删除old 56 | if (oldFile.exists()) { 57 | OfflineWebLog.i(TAG, "del start"); 58 | OfflineFileUtils.deleteDir(oldFile); 59 | OfflineWebLog.i(TAG, "del done"); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/task/CleanTask.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.task; 2 | 3 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 4 | import com.lalamove.huolala.offline.webview.utils.OfflineFileUtils; 5 | import com.lalamove.huolala.offline.webview.utils.OfflinePackageUtil; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * @copyright:深圳依时货拉拉科技有限公司 11 | * @fileName: CleanTask 12 | * @author: kelvin 13 | * @date: 3/30/22 14 | * @description: 清理所有离线包 15 | * @history: 16 | */ 17 | 18 | public class CleanTask implements Runnable { 19 | private static final String TAG = CleanTask.class.getSimpleName(); 20 | 21 | @Override 22 | public void run() { 23 | try { 24 | OfflineFileUtils.deleteDir(new File(OfflinePackageUtil.getResDir())); 25 | } catch (Exception e) { 26 | OfflineWebLog.e(TAG, e); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/threadpool/IExecutorServiceProvider.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.threadpool; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | 5 | /** 6 | * create by zhii.yang 2021/12/16 7 | * desc : 8 | */ 9 | public interface IExecutorServiceProvider { 10 | 11 | /** 12 | * 获取线程池 13 | * @return 线程池 14 | */ 15 | ExecutorService get(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/threadpool/OfflineIoThreadPool.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.threadpool; 2 | 3 | import android.annotation.SuppressLint; 4 | 5 | import java.util.concurrent.LinkedBlockingDeque; 6 | import java.util.concurrent.ThreadFactory; 7 | import java.util.concurrent.ThreadPoolExecutor; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | /** 12 | * @copyright:深圳依时货拉拉科技有限公司 13 | * @fileName: OfflineIoThreadPool 14 | * @author: muye 15 | * @date: 2021/7/7 16 | * @description: io型数据库,用于网络请求 17 | * @history: 18 | */ 19 | 20 | public class OfflineIoThreadPool { 21 | 22 | private final static String TAG = "OfflineIoThreadPool"; 23 | 24 | //参数初始化 25 | protected static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 26 | //线程池最大容纳线程数 27 | protected static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 28 | 29 | private static final int TIME_OUT = 60; 30 | 31 | private static final int CORE_POOL_SIZE = 5; 32 | private ThreadPoolExecutor mThreadPoolExecutor; 33 | 34 | private static OfflineIoThreadPool sCompressThreadPool; 35 | 36 | private OfflineIoThreadPool() { 37 | initThreadPool(); 38 | } 39 | 40 | public static OfflineIoThreadPool getInstance() { 41 | if (null == sCompressThreadPool) { 42 | synchronized (OfflineIoThreadPool.class) { 43 | if (null == sCompressThreadPool) { 44 | sCompressThreadPool = new OfflineIoThreadPool(); 45 | } 46 | } 47 | } 48 | return sCompressThreadPool; 49 | } 50 | 51 | public ThreadPoolExecutor getThreadPoolExecutor() { 52 | return mThreadPoolExecutor; 53 | } 54 | 55 | @SuppressLint("NewApi") 56 | private void initThreadPool() { 57 | final AtomicInteger mAtomicInteger = new AtomicInteger(1); 58 | SecurityManager var1 = System.getSecurityManager(); 59 | final ThreadGroup group = var1 != null ? var1.getThreadGroup() : Thread.currentThread().getThreadGroup(); 60 | ThreadFactory threadFactory = new ThreadFactory() { 61 | @Override 62 | public Thread newThread(Runnable runnable) { 63 | return new Thread(group, runnable, "track io-pool-thread-" + mAtomicInteger.getAndIncrement(), 0); 64 | // return new Thread(group, runnable, "track io-pool-thread-" + mAtomicInteger.getAndIncrement(), 1024 * 256); 65 | } 66 | }; 67 | mThreadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, Math.max(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE), 68 | TIME_OUT, TimeUnit.SECONDS, 69 | new LinkedBlockingDeque<>(128), 70 | threadFactory); 71 | mThreadPoolExecutor.allowCoreThreadTimeOut(true); 72 | mThreadPoolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy() { 73 | @Override 74 | public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 75 | super.rejectedExecution(r, e); 76 | 77 | } 78 | }); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/EnhWebUriUtils.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | import android.net.Uri; 4 | 5 | import com.lalamove.huolala.offline.webview.log.OfflineWebLog; 6 | 7 | 8 | /** 9 | * @author yz 10 | * @time 2021/12/22 3:38 下午 11 | */ 12 | public class EnhWebUriUtils { 13 | 14 | private static final String TAG = "EnhWebUriUtils"; 15 | 16 | public static String getScheme(String urlPath) { 17 | try { 18 | return Uri.parse(urlPath).getScheme(); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | } 22 | return ""; 23 | } 24 | 25 | /** 26 | * 获取监控需要的url 27 | * https://aaaa.cn/path?offweb=bisName#/fragment?paramA=1¶msB=2 28 | * -> https://aaaa.cn/path#/fragment 29 | * 30 | * @param urlPath 31 | * @return 32 | */ 33 | public static String getMonitorUrl(String urlPath) { 34 | try { 35 | int pathIndex = urlPath.indexOf("?"); 36 | if (pathIndex != -1) { 37 | StringBuilder urlBuild = new StringBuilder(); 38 | urlBuild.append(urlPath.substring(0, pathIndex)); 39 | 40 | int jinIndex = urlPath.indexOf("#"); 41 | if (jinIndex != -1) { 42 | //截取后边的字符串 43 | urlPath = urlPath.substring(jinIndex); 44 | if (urlPath.contains("?")) { 45 | //截取#跟? 之间的字符串 46 | String jinAndWen = urlPath.substring(0, urlPath.indexOf("?")); 47 | //过滤当前只有一个#号字符串 48 | if (!"#".equals(jinAndWen.trim())) { 49 | urlBuild.append(jinAndWen); 50 | } 51 | 52 | OfflineWebLog.d(TAG, "monitorUrl -> " + urlBuild); 53 | } 54 | } 55 | return urlBuild.toString(); 56 | } 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | return urlPath == null ? "" : urlPath; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/MemoryConstants.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | import androidx.annotation.IntDef; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * @copyright:深圳依时货拉拉科技有限公司 10 | * @fileName: MemoryConstants 11 | * @author: kelvin 12 | * @date: 7/19/21 13 | * @description: 内存大小 常量类 14 | * @history: 15 | */ 16 | public final class MemoryConstants { 17 | 18 | public static final int BYTE = 1; 19 | public static final int KB = 1024; 20 | public static final int MB = 1048576; 21 | public static final int GB = 1073741824; 22 | 23 | @IntDef({BYTE, KB, MB, GB}) 24 | @Retention(RetentionPolicy.SOURCE) 25 | public @interface Unit { 26 | } 27 | } -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/OfflineConstant.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | /** 4 | * @copyright:深圳依时货拉拉科技有限公司 5 | * @fileName: OfflineConstant 6 | * @author: kelvin 7 | * @date: 7/19/21 8 | * @description: 9 | * @history: 10 | */ 11 | 12 | public interface OfflineConstant { 13 | 14 | String ZIP_SUFFIX = ".zip"; 15 | 16 | String ROOT_DIR_NAME = "offline_web"; 17 | 18 | String CONFIG_FILE_NAME = ".offweb.json"; 19 | 20 | String OFFLINE_PACKAGE_INFO = "offline_package_info"; 21 | 22 | String BIS_NAME = "bisName"; 23 | 24 | String OFF_WEB = "offweb"; 25 | 26 | String MODULE_SP_NAME = "off_web"; 27 | 28 | String HTML_FILE = "index.html"; 29 | 30 | String CUR_DIR_NAME = "cur"; 31 | 32 | String OLD_DIR_NAME = "old"; 33 | 34 | String NEW_DIR_NAME = "new"; 35 | 36 | String TEMP_DIR_NAME = "temp"; 37 | 38 | String PARAM_OFFWEB_HOST = "offweb_host"; 39 | 40 | String PARAM_OFFLINE_ZIP_VER = "offlineZipVer"; 41 | 42 | String PARAM_ENV = "env"; 43 | 44 | String PARAM_CLIENT_VERSION = "clientVersion"; 45 | 46 | String PARAM_OS= "os"; 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/OfflineGsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | import android.text.TextUtils; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import com.google.gson.reflect.TypeToken; 7 | 8 | import java.lang.reflect.ParameterizedType; 9 | import java.lang.reflect.Type; 10 | import java.util.List; 11 | 12 | /** 13 | * @author joseph.gao 14 | *

15 | * gson工具类 16 | *

17 | * toJson 18 | * fromJson 19 | */ 20 | public class OfflineGsonUtils { 21 | 22 | private static final Gson GSON; 23 | 24 | private OfflineGsonUtils() { 25 | } 26 | 27 | static { 28 | GSON = new GsonBuilder() 29 | .serializeNulls() //支持序列化null的参数 30 | .enableComplexMapKeySerialization()//支持将序列化key为object的map,默认只能序列化key为string的map 31 | .create(); 32 | } 33 | 34 | public static String toJson(Object obj) { 35 | if (null == obj) { 36 | return ""; 37 | } 38 | try { 39 | return GSON.toJson(obj); 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | return ""; 44 | } 45 | 46 | public static Object fromJson(String json, Type classType) { 47 | if (null == json) { 48 | return null; 49 | } 50 | try { 51 | return GSON.fromJson(json, classType); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | return null; 56 | } 57 | 58 | public static T fromJson(String jsonStr, Class clazz) { 59 | T jsonObj = null; 60 | if (!TextUtils.isEmpty(jsonStr)) { 61 | try { 62 | jsonObj = GSON.fromJson(jsonStr, clazz); 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | return jsonObj; 68 | } 69 | 70 | public static T fromJson(String jsonStr) { 71 | T jsonObj = null; 72 | if (!TextUtils.isEmpty(jsonStr)) { 73 | try { 74 | jsonObj = GSON.fromJson(jsonStr, new TypeToken() { 75 | }.getType()); 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | return jsonObj; 81 | } 82 | 83 | public static List fromJsonToList(String jsonStr, Class clazz) { 84 | List jsonObj = null; 85 | 86 | if (!TextUtils.isEmpty(jsonStr)) { 87 | try { 88 | Type type = new ParameterizedTypeImpl(clazz); 89 | jsonObj = GSON.fromJson(jsonStr, type); 90 | } catch (Exception e) { 91 | e.printStackTrace(); 92 | } 93 | } 94 | return jsonObj; 95 | } 96 | 97 | 98 | private static class ParameterizedTypeImpl implements ParameterizedType { 99 | Class clazz; 100 | 101 | public ParameterizedTypeImpl(Class clz) { 102 | clazz = clz; 103 | } 104 | 105 | @Override 106 | public Type[] getActualTypeArguments() { 107 | return new Type[]{clazz}; 108 | } 109 | 110 | @Override 111 | public Type getRawType() { 112 | return List.class; 113 | } 114 | 115 | @Override 116 | public Type getOwnerType() { 117 | return null; 118 | } 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/OfflineHandlerUtils.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import com.lalamove.huolala.offline.webview.OfflineWebManager; 7 | 8 | /** 9 | * @copyright:深圳依时货拉拉科技有限公司 10 | * @fileName: OfflineHandlerUtils 11 | * @author: kelvin 12 | * @date: 7/19/21 13 | * @description: 离线包 handler 14 | * @history: 15 | */ 16 | public class OfflineHandlerUtils { 17 | private static final String TAG = OfflineHandlerUtils.class.getSimpleName(); 18 | 19 | private static Handler sHandler; 20 | 21 | private OfflineHandlerUtils() { 22 | } 23 | 24 | public static void runOnUiThread(Runnable action) { 25 | if (Looper.myLooper() != Looper.getMainLooper()) { 26 | if (sHandler == null) { 27 | sHandler = new Handler(Looper.getMainLooper()); 28 | } 29 | sHandler.post(action); 30 | } else { 31 | try { 32 | action.run(); 33 | } catch (Exception e) { 34 | OfflineWebManager.getInstance().getLogger().e(TAG, e); 35 | } 36 | } 37 | } 38 | 39 | public static void post(Runnable action) { 40 | if (sHandler == null) { 41 | sHandler = new Handler(Looper.getMainLooper()); 42 | } 43 | sHandler.post(action); 44 | } 45 | 46 | public static void postDelayed(Runnable action, long delayMillis) { 47 | if (sHandler == null) { 48 | sHandler = new Handler(Looper.getMainLooper()); 49 | } 50 | sHandler.postDelayed(action, delayMillis); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/OfflinePackageUtil.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.lalamove.huolala.offline.webview.info.OfflineZipPackageConfig; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * @copyright:深圳依时货拉拉科技有限公司 11 | * @fileName: OfflinePackageUtil 12 | * @author: kelvin 13 | * @date: 7/21/21 14 | * @description: 更新包文件 相关方法 15 | * @history: 16 | */ 17 | 18 | public class OfflinePackageUtil { 19 | 20 | private OfflinePackageUtil() { 21 | } 22 | 23 | public static OfflineZipPackageConfig getOfflineConfig(String bisName) { 24 | String configJsonStr = OfflineFileUtils.readFile2String(getBisDir(bisName)+File.separator+OfflineConstant.CUR_DIR_NAME 25 | + File.separator + OfflineConstant.CONFIG_FILE_NAME); 26 | if (TextUtils.isEmpty(configJsonStr)) { 27 | return null; 28 | } 29 | return OfflineGsonUtils.fromJson(configJsonStr, OfflineZipPackageConfig.class); 30 | } 31 | 32 | public static String getResDir() { 33 | return OfflineFileUtils.getInternalAppDataPath() + File.separator + OfflineConstant.ROOT_DIR_NAME; 34 | } 35 | 36 | public static String getBisDir(String bisName) { 37 | return getResDir() + File.separator + bisName; 38 | } 39 | 40 | 41 | public static String getPackageVersion(String bisName) { 42 | OfflineZipPackageConfig offlineConfig = OfflinePackageUtil.getOfflineConfig(bisName); 43 | if (offlineConfig == null) { 44 | return "0"; 45 | } 46 | return offlineConfig.getVer(); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/OfflineStringUtils.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | /** 4 | * @copyright:深圳依时货拉拉科技有限公司 5 | * @fileName: OfflineStringUtils 6 | * @author: kelvin 7 | * @date: 8/12/21 8 | * @description: 字符串工具 9 | * @history: 10 | */ 11 | 12 | public class OfflineStringUtils { 13 | 14 | private OfflineStringUtils() { 15 | } 16 | 17 | public static String getErrorString(Throwable t) { 18 | if (t == null) { 19 | return "UnknownError"; 20 | } 21 | try { 22 | return t.getMessage(); 23 | } catch (Exception e) { 24 | return "UnknownError"; 25 | } 26 | } 27 | 28 | public static String appendUnsafeString(String... args) { 29 | StringBuilder stringBuilder = new StringBuilder(); 30 | for (String arg : args) { 31 | stringBuilder.append(arg); 32 | } 33 | return stringBuilder.toString(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/utils/UrlParamsUtils.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | /** 6 | * @copyright:深圳依时货拉拉科技有限公司 7 | * @fileName: UrlParamsUtils 8 | * @author: kelvin 9 | * @date: 7/23/21 10 | * @description: url参数工具类 11 | * @history: 12 | */ 13 | 14 | public class UrlParamsUtils { 15 | 16 | private UrlParamsUtils() { 17 | } 18 | 19 | /** 20 | * url后追加参数 21 | */ 22 | public static String urlAppendParam(String url, String key, String value) { 23 | if (TextUtils.isEmpty(url)||url.contains(key)) { 24 | return url; 25 | } 26 | StringBuilder stringBuilder = new StringBuilder(url); 27 | if (url.contains("=")) { 28 | return stringBuilder.append("&").append(key).append("=").append(value).toString(); 29 | } else { 30 | return stringBuilder.append("?").append(key).append("=").append(value).toString(); 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/widget/EnhOfflineWebView.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.webkit.WebView; 6 | import android.webkit.WebViewClient; 7 | 8 | import androidx.annotation.Nullable; 9 | 10 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebResourceErrorAdapter; 11 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebResourceRequestAdapter; 12 | import com.lalamove.huolala.offline.webview.monitor.base.IEnhWebResourceResponseAdapter; 13 | import com.lalamove.huolala.offline.webview.monitor.base.IWebPageStatus; 14 | import com.lalamove.huolala.offline.webview.monitor.impl.OfflineWebPageStatus; 15 | import com.lalamove.huolala.offline.webview.proxy.IOfflineWebViewProxy; 16 | import com.lalamove.huolala.offline.webview.proxy.OffWebProxyFactory; 17 | 18 | import java.util.Map; 19 | 20 | /** 21 | * @copyright:深圳依时货拉拉科技有限公司 22 | * @fileName: EnhOfflineWebView 23 | * @author: kelvin 24 | * @date: 7/22/21 25 | * @description: 增强webview 26 | * @history: 27 | */ 28 | public class EnhOfflineWebView extends WebView implements ReloadOfflineWebView, IWebPageStatus { 29 | 30 | private IOfflineWebViewProxy mOfflineWebViewProxy; 31 | private IWebPageStatus mWebPageStatus; 32 | private EnhWebViewClient mEnhWebViewClient; 33 | 34 | public EnhOfflineWebView(Context context) { 35 | super(context); 36 | initProxy(); 37 | } 38 | 39 | public EnhOfflineWebView(Context context, AttributeSet attrs) { 40 | super(context, attrs); 41 | initProxy(); 42 | } 43 | 44 | public EnhOfflineWebView(Context context, AttributeSet attrs, int defStyleAttr) { 45 | super(context, attrs, defStyleAttr); 46 | initProxy(); 47 | } 48 | 49 | public EnhOfflineWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 50 | super(context, attrs, defStyleAttr, defStyleRes); 51 | initProxy(); 52 | } 53 | 54 | private void initProxy() { 55 | mOfflineWebViewProxy = OffWebProxyFactory.getProxy(this); 56 | mWebPageStatus = new OfflineWebPageStatus(); 57 | 58 | mEnhWebViewClient = new EnhWebViewClient(this); 59 | super.setWebViewClient(mEnhWebViewClient); 60 | } 61 | 62 | @Override 63 | public void setWebViewClient(WebViewClient webViewClient){ 64 | mEnhWebViewClient.setDelegate(webViewClient); 65 | } 66 | 67 | @Override 68 | public void loadUrl(String url) { 69 | mWebPageStatus.onLoadUrl(url); 70 | super.loadUrl(mOfflineWebViewProxy.loadUrl(url)); 71 | } 72 | 73 | @Override 74 | public void loadUrl(String url, Map additionalHttpHeaders) { 75 | mWebPageStatus.onLoadUrl(url); 76 | super.loadUrl(mOfflineWebViewProxy.loadUrl(url), additionalHttpHeaders); 77 | } 78 | 79 | @Override 80 | public void reloadOfflineWeb() { 81 | // refresh this web view 82 | } 83 | 84 | @Override 85 | public void destroy() { 86 | super.destroy(); 87 | mOfflineWebViewProxy.destroy(); 88 | } 89 | 90 | @Override 91 | protected void onDetachedFromWindow() { 92 | super.onDetachedFromWindow(); 93 | mOfflineWebViewProxy.destroy(); 94 | } 95 | 96 | @Override 97 | public void onLoadError(IEnhWebResourceRequestAdapter request, IEnhWebResourceErrorAdapter error) { 98 | mWebPageStatus.onLoadError(request, error); 99 | } 100 | 101 | @Override 102 | public void onLoadError(IEnhWebResourceRequestAdapter request, IEnhWebResourceResponseAdapter errorResponse) { 103 | mWebPageStatus.onLoadError(request, errorResponse); 104 | } 105 | 106 | @Override 107 | public void onLoadError(String url, String error) { 108 | mWebPageStatus.onLoadError(url, error); 109 | } 110 | 111 | @Override 112 | public void onLoadUrl(@Nullable String url) { 113 | mWebPageStatus.onLoadUrl(url); 114 | } 115 | 116 | @Override 117 | public void onPageLoadFinish(@Nullable String url, int progress) { 118 | mWebPageStatus.onPageLoadFinish(url, progress); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/widget/IOfflineWebView.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.widget; 2 | 3 | 4 | /** 5 | * @copyright:深圳依时货拉拉科技有限公司 6 | * @fileName: IOfflineWebView 7 | * @author: kelvin 8 | * @date: 7/19/21 9 | * @description: webview接口 10 | * @history: 11 | */ 12 | 13 | public interface IOfflineWebView { 14 | /** 15 | * 销毁 16 | */ 17 | void destroy(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/widget/OfflineWebView.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.webkit.WebView; 6 | 7 | import com.lalamove.huolala.offline.webview.proxy.IOfflineWebViewProxy; 8 | import com.lalamove.huolala.offline.webview.proxy.OffWebProxyFactory; 9 | 10 | import java.util.Map; 11 | 12 | /** 13 | * @copyright:深圳依时货拉拉科技有限公司 14 | * @fileName: OfflineWebView 15 | * @author: kelvin 16 | * @date: 7/22/21 17 | * @description: 基础webview 18 | * @history: 19 | */ 20 | public class OfflineWebView extends WebView implements ReloadOfflineWebView { 21 | 22 | private IOfflineWebViewProxy mOfflineWebViewProxy; 23 | 24 | public OfflineWebView(Context context) { 25 | super(context); 26 | initProxy(); 27 | } 28 | 29 | public OfflineWebView(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | initProxy(); 32 | } 33 | 34 | public OfflineWebView(Context context, AttributeSet attrs, int defStyleAttr) { 35 | super(context, attrs, defStyleAttr); 36 | initProxy(); 37 | } 38 | 39 | public OfflineWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 40 | super(context, attrs, defStyleAttr, defStyleRes); 41 | initProxy(); 42 | } 43 | 44 | private void initProxy() { 45 | mOfflineWebViewProxy = OffWebProxyFactory.getProxy(this); 46 | } 47 | 48 | @Override 49 | public void loadUrl(String url) { 50 | super.loadUrl(mOfflineWebViewProxy.loadUrl(url)); 51 | } 52 | 53 | @Override 54 | public void loadUrl(String url, Map additionalHttpHeaders) { 55 | super.loadUrl(mOfflineWebViewProxy.loadUrl(url), additionalHttpHeaders); 56 | } 57 | 58 | @Override 59 | public void reloadOfflineWeb() { 60 | // refresh this web view 61 | } 62 | 63 | @Override 64 | public void destroy() { 65 | super.destroy(); 66 | mOfflineWebViewProxy.destroy(); 67 | } 68 | 69 | @Override 70 | protected void onDetachedFromWindow() { 71 | super.onDetachedFromWindow(); 72 | mOfflineWebViewProxy.destroy(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lib_offline_webview/src/main/java/com/lalamove/huolala/offline/webview/widget/ReloadOfflineWebView.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.offline.webview.widget; 2 | 3 | /** 4 | * @copyright:深圳依时货拉拉科技有限公司 5 | * @fileName: ReloadOfflineWebView 6 | * @author: kelvin 7 | * @date: 7/27/21 8 | * @description: 使用端 实现接口 支持reload 9 | * @history: 10 | */ 11 | 12 | public interface ReloadOfflineWebView extends IOfflineWebView { 13 | /** 14 | * 重载webview ,只有下发强制重载才会执行,默认无操作,业务方自己实现 15 | */ 16 | void reloadOfflineWeb(); 17 | } 18 | -------------------------------------------------------------------------------- /lib_web/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib_web/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | java { 6 | sourceCompatibility = JavaVersion.VERSION_1_8 7 | targetCompatibility = JavaVersion.VERSION_1_8 8 | } 9 | tasks.withType(JavaCompile) { 10 | options.encoding = "UTF-8" 11 | } 12 | 13 | jar { 14 | manifest { 15 | attributes 'Main-Class': 'com.lalamove.huolala.client.lib.HttpServerStarter' 16 | } 17 | } 18 | 19 | dependencies { 20 | implementation "com.google.guava:guava:28.1-android" 21 | implementation "com.google.code.gson:gson:2.8.5" 22 | } -------------------------------------------------------------------------------- /lib_web/src/main/java/com/lalamove/huolala/client/lib/DownloadHandler.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.lib; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import com.sun.net.httpserver.HttpHandler; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.io.OutputStream; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.Map; 15 | 16 | /** 17 | * 下载处理器类 18 | */ 19 | public class DownloadHandler implements HttpHandler { 20 | 21 | @Override 22 | public void handle(HttpExchange httpExchange) { 23 | try { 24 | System.out.println("DownloadHandler handle start"); 25 | String requestParam = getRequestParam(httpExchange); 26 | Map paramMap = ParamsUtil.getParamMap(requestParam); 27 | String bisName = paramMap.get(ServerConstant.BIS_NAME); 28 | String zipVersion = paramMap.get(ServerConstant.PARAM_OFFLINE_ZIP_VER); 29 | File zipFile = null; 30 | System.out.println("download bisName = " + bisName); 31 | if (ServerConstant.NAME_ACT3.equals(bisName)) { 32 | zipFile = new File(ServerConstant.ACT3_PATH); 33 | } 34 | if (zipFile != null) { 35 | System.out.println("download path = " + zipFile.getAbsolutePath()); 36 | } 37 | handleResponse(httpExchange, zipFile, bisName); 38 | } catch (Exception ex) { 39 | ex.printStackTrace(); 40 | } 41 | } 42 | 43 | /** 44 | * 获取请求参数 45 | * 46 | * @param httpExchange 47 | * @return 48 | * @throws Exception 49 | */ 50 | private String getRequestParam(HttpExchange httpExchange) throws Exception { 51 | String paramStr = ""; 52 | 53 | if ("GET".equals(httpExchange.getRequestMethod())) { 54 | //GET请求读queryString 55 | paramStr = httpExchange.getRequestURI().getQuery(); 56 | } else { 57 | //非GET请求读请求体 58 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpExchange.getRequestBody(), StandardCharsets.UTF_8)); 59 | StringBuilder requestBodyContent = new StringBuilder(); 60 | String line = ""; 61 | while ((line = bufferedReader.readLine()) != null) { 62 | requestBodyContent.append(line); 63 | } 64 | paramStr = requestBodyContent.toString(); 65 | } 66 | 67 | return paramStr; 68 | } 69 | 70 | /** 71 | * 处理响应 72 | * 73 | * @param httpExchange 74 | * @param file 75 | * @throws Exception 76 | */ 77 | private void handleResponse(HttpExchange httpExchange, File file, String fileName) throws Exception { 78 | //设置响应头,必须在sendResponseHeaders方法之前设置! 79 | httpExchange.getResponseHeaders().add("Content-Type:", "application/vnd.ms-excel;charset=utf-8"); 80 | httpExchange.getResponseHeaders().add("Content-Disposition", "attachment;filename=" + fileName + ".zip"); 81 | // 设置响应码和响应体长度,必须在getResponseBody方法之前调用! 82 | if (file == null || !file.exists()) { 83 | httpExchange.sendResponseHeaders(200, 0); 84 | System.out.println("下载失败 文件无法找到"); 85 | OutputStream out = httpExchange.getResponseBody(); 86 | out.flush(); 87 | out.close(); 88 | return; 89 | } 90 | httpExchange.sendResponseHeaders(200, file.length()); 91 | InputStream in = null; 92 | OutputStream out = null; 93 | try { 94 | //获取要下载的文件输入流 95 | in = new FileInputStream(file); 96 | int len = 0; 97 | //创建数据缓冲区 98 | byte[] buffer = new byte[1024]; 99 | //通过response对象获取outputStream流 100 | out = httpExchange.getResponseBody(); 101 | //将FileInputStream流写入到buffer缓冲区 102 | while ((len = in.read(buffer)) > 0) { 103 | //使用OutputStream将缓冲区的数据输出到浏览器 104 | out.write(buffer, 0, len); 105 | } 106 | out.flush(); 107 | in.close(); 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } finally { 111 | if (out != null) { 112 | try { 113 | out.close(); 114 | } catch (IOException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib_web/src/main/java/com/lalamove/huolala/client/lib/HttpServerStarter.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.lib; 2 | 3 | import com.sun.net.httpserver.HttpServer; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.net.InetSocketAddress; 8 | import java.util.concurrent.Executors; 9 | 10 | public class HttpServerStarter { 11 | 12 | public static void main(String[] args) throws IOException { 13 | //创建一个HttpServer实例,并绑定到指定的IP地址和端口号 14 | File assetFile = new File(ServerConstant.ASSETS_PATH); 15 | if (!assetFile.exists()) { 16 | System.out.println("请配置asset目录 或者 设置asset路径"); 17 | } 18 | System.out.println("ASSETS_PATH = " + ServerConstant.ASSETS_PATH); 19 | HttpServer httpServer = HttpServer.create(new InetSocketAddress(8888), 0); 20 | //创建一个HttpContext,将路径为/myserver请求映射到MyHttpHandler处理器 21 | httpServer.createContext("/"+ServerConstant.PATH_OFF_WEB, new OffWebHandler()); 22 | httpServer.createContext("/"+ServerConstant.PAHT_PACKAGE, new DownloadHandler()); 23 | 24 | //设置服务器的线程池对象 25 | httpServer.setExecutor(Executors.newFixedThreadPool(10)); 26 | 27 | //启动服务器 28 | httpServer.start(); 29 | } 30 | } -------------------------------------------------------------------------------- /lib_web/src/main/java/com/lalamove/huolala/client/lib/OffWebHandler.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.lib; 2 | 3 | import com.google.gson.Gson; 4 | import com.sun.net.httpserver.HttpExchange; 5 | import com.sun.net.httpserver.HttpHandler; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.InputStreamReader; 9 | import java.io.OutputStream; 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.Map; 12 | 13 | /** 14 | * 拉取离线包信息处理器类 15 | */ 16 | public class OffWebHandler implements HttpHandler { 17 | 18 | private Gson mGson = new Gson(); 19 | 20 | @Override 21 | public void handle(HttpExchange httpExchange) { 22 | try { 23 | String requestParam = getRequestParam(httpExchange); 24 | String bisName =""; 25 | String zipVersion =""; 26 | Map paramMap = ParamsUtil.getParamMap(requestParam); 27 | if (paramMap != null) { 28 | bisName = paramMap.get(ServerConstant.BIS_NAME); 29 | zipVersion = paramMap.get(ServerConstant.PARAM_OFFLINE_ZIP_VER); 30 | } 31 | String downloadUrl = null; 32 | if (ServerConstant.NAME_ACT3.equals(bisName)) { 33 | StringBuilder downloadSb = new StringBuilder(); 34 | downloadSb.append(ServerConstant.LOCALHOST) 35 | .append(ServerConstant.PAHT_PACKAGE) 36 | .append("?") 37 | .append(ServerConstant.BIS_NAME) 38 | .append("=") 39 | .append(ServerConstant.NAME_ACT3); 40 | downloadUrl = downloadSb.toString(); 41 | } 42 | //result==1 43 | OfflinePackageInfo offlinePackageInfo = new OfflinePackageInfo(bisName, 1, downloadUrl, 0, zipVersion + "_1"); 44 | 45 | //result == 0 无需下载 46 | // OfflinePackageInfo offlinePackageInfo = new OfflinePackageInfo(bisName, 0, downloadUrl, 0, zipVersion + "_1"); 47 | String s = mGson.toJson(offlinePackageInfo); 48 | handleResponse(httpExchange, s); 49 | } catch (Exception ex) { 50 | ex.printStackTrace(); 51 | } 52 | } 53 | 54 | /** 55 | * 获取请求参数 56 | * 57 | * @param httpExchange 58 | * @return 59 | * @throws Exception 60 | */ 61 | private String getRequestParam(HttpExchange httpExchange) throws Exception { 62 | String paramStr; 63 | 64 | if ("GET".equals(httpExchange.getRequestMethod())) { 65 | //GET请求读queryString 66 | paramStr = httpExchange.getRequestURI().getQuery(); 67 | } else { 68 | //非GET请求读请求体 69 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpExchange.getRequestBody(), StandardCharsets.UTF_8)); 70 | StringBuilder requestBodyContent = new StringBuilder(); 71 | String line; 72 | while ((line = bufferedReader.readLine()) != null) { 73 | requestBodyContent.append(line); 74 | } 75 | paramStr = requestBodyContent.toString(); 76 | } 77 | 78 | return paramStr; 79 | } 80 | 81 | /** 82 | * 处理响应 83 | * 84 | * @param httpExchange 85 | * @param responsetext 86 | * @throws Exception 87 | */ 88 | private void handleResponse(HttpExchange httpExchange, String responsetext) throws Exception { 89 | System.out.println("resp:"+responsetext); 90 | byte[] responseContentByte = responsetext.getBytes(StandardCharsets.UTF_8); 91 | //设置响应头,必须在sendResponseHeaders方法之前设置! 92 | httpExchange.getResponseHeaders().add("Content-Type:", "application/json"); 93 | //设置响应码和响应体长度,必须在getResponseBody方法之前调用! 94 | httpExchange.sendResponseHeaders(200, responseContentByte.length); 95 | OutputStream out = httpExchange.getResponseBody(); 96 | out.write(responseContentByte); 97 | out.flush(); 98 | out.close(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib_web/src/main/java/com/lalamove/huolala/client/lib/OfflinePackageInfo.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.lib; 2 | 3 | import java.io.Serializable; 4 | import java.util.Objects; 5 | 6 | /** 7 | * @copyright:深圳依时货拉拉科技有限公司 8 | * @fileName: OfflinePackageInfo 9 | * @author: kelvin 10 | * @date: 7/21/21 11 | * @description: 离线包接口返回数据体 12 | * @history: 13 | */ 14 | 15 | public class OfflinePackageInfo implements Serializable { 16 | 17 | 18 | /** 19 | * bisName : huoyunjie 20 | * result : 0 21 | * url : www.xxx.com 22 | * version : xxxxxx 23 | */ 24 | 25 | private String bisName; 26 | private int result; 27 | private String url; 28 | private int refreshMode; 29 | private String version; 30 | 31 | public OfflinePackageInfo(String bisName, int result, String url, int refreshMode, String offlineZipVer) { 32 | this.bisName = bisName; 33 | this.result = result; 34 | this.url = url; 35 | this.refreshMode = refreshMode; 36 | this.version = offlineZipVer; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) { 42 | return true; 43 | } 44 | if (o == null || getClass() != o.getClass()) { 45 | return false; 46 | } 47 | OfflinePackageInfo that = (OfflinePackageInfo) o; 48 | return Objects.equals(bisName, that.bisName); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return Objects.hash(bisName); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "OfflinePackageInfo{" + 59 | "bisName='" + bisName + '\'' + 60 | ", result=" + result + 61 | ", url='" + url + '\'' + 62 | ", refreshMode=" + refreshMode + 63 | ", version='" + version + '\'' + 64 | '}'; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib_web/src/main/java/com/lalamove/huolala/client/lib/ParamsUtil.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.lib; 2 | 3 | import com.google.common.base.Splitter; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * @copyright:深圳依时货拉拉科技有限公司 9 | * @fileName: ParamsUtil 10 | * @author: kelvin 11 | * @date: 3/30/22 12 | * @description: 参数拼接工具类 13 | * @history: 14 | */ 15 | 16 | public class ParamsUtil { 17 | 18 | 19 | public static Map getParamMap(String url) { 20 | if (isEmpty(url)) { 21 | return null; 22 | } 23 | try { 24 | String params = url.substring(url.indexOf("?") + 1); 25 | return Splitter.on("&") 26 | .withKeyValueSeparator("=") 27 | .split(params); 28 | } catch (Exception e) { 29 | return null; 30 | } 31 | 32 | } 33 | 34 | private static boolean isEmpty(String str) { 35 | return str == null || str.length() == 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib_web/src/main/java/com/lalamove/huolala/client/lib/ServerConstant.java: -------------------------------------------------------------------------------- 1 | package com.lalamove.huolala.client.lib; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * @copyright:深圳依时货拉拉科技有限公司 7 | * @fileName: ServerConstant 8 | * @author: kelvin 9 | * @date: 3/30/22 10 | * @description: 服务端配置 11 | * @history: 12 | */ 13 | 14 | public interface ServerConstant { 15 | 16 | 17 | String LOCALHOST = "http://xxx.xxx.xxx.xxx:8888/"; 18 | 19 | String BIS_NAME = "bisName"; 20 | 21 | String PATH_OFF_WEB = "queryOffline"; 22 | 23 | String PAHT_PACKAGE = "package"; 24 | 25 | String PARAM_OFFLINE_ZIP_VER = "offlineZipVer"; 26 | 27 | String ASSETS_PATH = new File("").getAbsolutePath()+File.separator+"assets"; 28 | 29 | String NAME_ACT3 = "act3-2108-turntable"; 30 | 31 | String PKG_ACT3 = "act3-2108-turntable.zip"; 32 | 33 | String ACT3_PATH = ASSETS_PATH + File.separator + PKG_ACT3; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':lib_web' 3 | include ':lib_offline_webview' 4 | rootProject.name = "OfflineDemo" --------------------------------------------------------------------------------