├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wxxiaomi │ │ └── ming │ │ └── webviewcachemodule │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── js │ │ │ └── JsBridge.js │ │ └── test1.html │ ├── java │ │ └── com │ │ │ └── wxxiaomi │ │ │ └── ming │ │ │ └── webviewcachemodule │ │ │ ├── MainActivity.java │ │ │ ├── TAG.java │ │ │ ├── common │ │ │ ├── BaseWebActivity.java │ │ │ ├── GlideImageLoader.java │ │ │ ├── PicTakeUtil.java │ │ │ └── util │ │ │ │ ├── CacheEngine.java │ │ │ │ ├── CompressUtil.java │ │ │ │ └── DiskCache.java │ │ │ ├── version_1 │ │ │ └── Version_1_Activity.java │ │ │ ├── version_2 │ │ │ └── Version_2_Activity.java │ │ │ ├── version_3 │ │ │ └── Compress_Cache_Base64_Activity.java │ │ │ └── version_4 │ │ │ └── StreamActivity.java │ └── res │ │ ├── layout │ │ ├── activity_compress__cache__base64_.xml │ │ ├── activity_main.xml │ │ ├── activity_stream.xml │ │ ├── activity_version_1_.xml │ │ └── activity_version_2_.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── wxxiaomi │ └── ming │ └── webviewcachemodule │ └── ExampleUnitTest.java ├── build.gradle ├── gallerypick ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yancy │ │ └── gallerypick │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── yancy │ │ └── gallerypick │ │ ├── activity │ │ ├── BaseActivity.java │ │ └── GalleryPickActivity.java │ │ ├── adapter │ │ ├── FolderAdapter.java │ │ ├── MiniPhotoAdapter.java │ │ └── PhotoAdapter.java │ │ ├── bean │ │ ├── FolderInfo.java │ │ └── PhotoInfo.java │ │ ├── config │ │ ├── GalleryConfig.java │ │ └── GalleryPick.java │ │ ├── inter │ │ ├── IHandlerCallBack.java │ │ └── ImageLoader.java │ │ ├── utils │ │ ├── AppUtils.java │ │ ├── FileUtils.java │ │ ├── ScreenUtils.java │ │ ├── SystemBarTintManager.java │ │ ├── UCropUtils.java │ │ └── UIUtils.java │ │ └── widget │ │ ├── FolderListPopupWindow.java │ │ ├── GalleryImageView.java │ │ └── GalleryPickView.java │ └── res │ ├── anim │ ├── in_fade.xml │ └── out_fade.xml │ ├── layout │ ├── gallery_item_camera.xml │ ├── gallery_item_folder.xml │ ├── gallery_item_photo.xml │ ├── gallery_main.xml │ ├── gallery_mini.xml │ ├── gallery_mini_item.xml │ ├── gallery_popup_folder.xml │ └── gallery_title.xml │ ├── mipmap-hdpi │ ├── gallery_pick_back_black.png │ ├── gallery_pick_back_white.png │ ├── gallery_pick_camera.png │ ├── gallery_pick_dropdown_black.png │ ├── gallery_pick_dropdown_white.png │ ├── gallery_pick_photo.png │ ├── gallery_pick_select_checked.png │ ├── gallery_pick_select_unchecked.png │ └── gallery_pick_selected.png │ ├── mipmap-mdpi │ ├── gallery_pick_back_black.png │ ├── gallery_pick_back_white.png │ ├── gallery_pick_camera.png │ ├── gallery_pick_dropdown_black.png │ ├── gallery_pick_dropdown_white.png │ ├── gallery_pick_photo.png │ ├── gallery_pick_select_checked.png │ ├── gallery_pick_select_unchecked.png │ └── gallery_pick_selected.png │ ├── mipmap-xhdpi │ ├── gallery_pick_back_black.png │ ├── gallery_pick_back_white.png │ ├── gallery_pick_camera.png │ ├── gallery_pick_dropdown_black.png │ ├── gallery_pick_dropdown_white.png │ ├── gallery_pick_photo.png │ ├── gallery_pick_select_checked.png │ ├── gallery_pick_select_unchecked.png │ └── gallery_pick_selected.png │ ├── mipmap-xxhdpi │ ├── gallery_pick_back_black.png │ ├── gallery_pick_back_white.png │ ├── gallery_pick_camera.png │ ├── gallery_pick_dropdown_black.png │ ├── gallery_pick_dropdown_white.png │ ├── gallery_pick_photo.png │ ├── gallery_pick_select_checked.png │ ├── gallery_pick_select_unchecked.png │ └── gallery_pick_selected.png │ ├── mipmap-xxxhdpi │ ├── gallery_pick_back_black.png │ ├── gallery_pick_back_white.png │ ├── gallery_pick_camera.png │ ├── gallery_pick_dropdown_black.png │ ├── gallery_pick_dropdown_white.png │ ├── gallery_pick_photo.png │ ├── gallery_pick_select_checked.png │ ├── gallery_pick_select_unchecked.png │ └── gallery_pick_selected.png │ ├── values-v19 │ └── styles.xml │ ├── values-v21 │ └── styles.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ps: 排版和图片再github显示不怎么一致,如果看得不爽请移步csdn: 2 | http://blog.csdn.net/qq122627018/article/details/53351781 3 | # 前言 4 | 先讲讲为什么会有这篇blog,话说前几天做个模块,要求是这样的: 5 | 做一个webview的页面,功能类似于微信发朋友圈一样,要求能上传本地图片到webview中进行展示,并按用户喜好添加和删除,当用户点击发布的时候,将这些图片上传至阿里云oss,收到oss响应后封装页面信息提交给服务器 6 | 7 | # 技术要点 8 | 9 | 1. webview与native的交互 10 | 2. webview显示客户端本地图片的方式 11 | 3. webview加载大量图片的问题(优化方案) 12 | 4. 自定义关于webview的缓存系统 13 | 14 | # webview与native的交互 15 | 关于交互其实网上有很多文章,在之前我也写过一篇关于webview与native交互方案的blog:[ Android混合开发的入门和方案](http://blog.csdn.net/qq122627018/article/details/52207600) 16 | 因此在这个demo中我采用的也是JsBridge的方式来让webview与native进行通信,所以主要提一下其中一些坑: 17 | 18 | 1. 使用JsBridge的方式,在子线程是无法发送消息给WebView的 19 | 2. 如果webview要加载本地文件,必须设置mWebView.getSettings().setAllowFileAccess(true); 20 | 3. 如果html文件存在于服务器中,就算你按照第二点设置了,那webview也无法读取本地文件,会报not allowed to load local resource(解决办法后面我会给出),这个坑异常深!!! 21 | 4. 使用base64编码来让webview中img便签加载编码后的图片的异常(大坑) 22 | 23 | # webview显示客户端本地图片的方式 24 | ## 直接设置 25 | 拿到本地文件的路径,然后拼装便签,设置其src属性为"file://"+路径,webview就会自动去找此地址("file://"是属性webview的一种协议,就像我们的http协议的道理) 26 | 27 | ``` 28 | //imgPaths是我们选择的那些图片的本地路径 29 | private void adapterH5Data(List imgPaths){ 30 | String result = ""; 31 | //拼装标签 32 | for(int i=0;i"; 34 | } 35 | //发送给webview容器 36 | mWebView.callHandler("adapter",result,null); 37 | } 38 | ``` 39 | 那么在我们的html文件中,有一个这样的方法: 40 | 41 | ``` 42 | function adapter(pars){ 43 | //直接把native拼装好的html添加到id为info_img的便签里面 44 | $("#info_img").prepend(pars); 45 | } 46 | ``` 47 | 48 | ## Base64编码 49 | base64编码大家应该都有接触过,还记得宝宝当年刚撸项目的时候,图片上传就是利用app把图片转化为base64,然后把这段字符串发送给服务器接收再进行解码,差点把老师气吐血。 50 | 那么这个方案,就是把目标路径的文件,通过base64编码编写成字符串,然后设置到img便签的src属性,这样img便可以显示出图片。 51 | 52 | ``` 53 | /** 54 | * 在这里必须使用多线程,因为base64对图片进行编码非常耗时, 55 | * 所以必须在子线程并且多线程处理每张图片的编码 56 | * RxJava的线程切换(这里暂时还没有使用多线程) 57 | * @param imgPaths 58 | */ 59 | private void adapterH5Data(final List imgPaths) { 60 | Observable.create(new Observable.OnSubscribe() { 61 | @Override 62 | public void call(Subscriber subscriber) { 63 | for (int i = 0; i < imgPaths.size(); i++) { 64 | String result = ""; 65 | String item = imgPaths.get(i); 66 | Log.i("wang","item:"+item); 67 | String s = Base64.encodeToString(getBytes(item), Base64.NO_WRAP); 68 | result += ""; 69 | subscriber.onNext(result); 70 | } 71 | } 72 | }) 73 | .subscribeOn(Schedulers.io()) 74 | .observeOn(AndroidSchedulers.mainThread()) 75 | .subscribe(new Action1() { 76 | @Override 77 | public void call(String result) { 78 | size++; 79 | tv.setText("编码完成:"+size+"张"); 80 | mWebView.callHandler("adapter", result, null); 81 | } 82 | }) 83 | ; 84 | } 85 | ``` 86 | 87 | ## 方案的对比 88 | 图一为第一种方案,图二为第二种方案 89 | ![这里写图片描述](http://img.blog.csdn.net/20161129002425992) ![这里写图片描述](http://img.blog.csdn.net/20161129002439915) 90 | 91 | 1. 其实还有通过shouldInterceptRequest方法来显示图片,在这里其实跟第一种方案的原理一样(放到后面讲) 92 | 2. 总体来说第一种方案的速度是比较快的,但是第一种方案有一个缺点就是如果是服务器的html的文件,是无法读取本地文件的 93 | 3. 上面写的第二种方案只是一个入门级的,对比第一种方案唯一的优点是就算是非本地的文件也可以读取,但是总体速度非常慢 94 | 95 | # 优化 96 | 很明显,上面的俩种方案都只是一个入门级的方案,根本走不上台面,缺点: 97 | 98 | - 本地图片没有经过压缩,直接放在webview中,如果图片比较多,会导致oom 99 | - 由上面的截图也可以看出从选完照片到webview显示,速度还是非常慢的,卡顿的感觉非常明显 100 | - 无法适应我们的需求,有时候html是在服务器上面的,有时候是存在本地的,无法做到统一处理 101 | 那么下面介绍我的第三种方案 102 | 103 | ## 多线程工作+压缩+缓存+base64编码 104 | ps:本来压缩想单独做个demo出来的,后面发现压缩模块如果没有缓存模块一起合作的化,每次加载图片都要进行压缩,非常恐怖哈哈! 105 | 这里讲一下主要思路: 106 | 107 | - 每当我们拿到本地图片的路径之后,把这个路径md5之后当作key,检查缓存中是否存在: 108 | - 不存在:调用压缩模块对次路径的图片进行压缩,压缩完成再存入缓存中,再返回压缩后的文件的byte数组 109 | - 存在:直接返回 110 | 111 | 在这里我的缓存系统是使用RxJAva+DiskLruCache实现的: 112 | 113 | ``` 114 | /** 115 | * 获取本地的图片 116 | * 先从硬盘获取,如果硬盘获取不到 117 | * 再拿图片进行压缩,再存到硬盘缓存 118 | * 然后返回 119 | * @param path 120 | * @return 121 | */ 122 | public Observable getLocalImg(final String path){ 123 | Observable diskCache = DiskCache.getInstance().getDiskCache(path); 124 | //存入缓存的操作,先压缩,再缓存,CompressUtil是图片压缩工具类 125 | Observable doCache = CompressUtil.compressImg(path, 100, 100) 126 | .flatMap(new Func1>() { 127 | @Override 128 | public Observable call(final byte[] bytes) { 129 | return DiskCache.getInstance().toDiskCache(path, bytes); 130 | } 131 | }); 132 | //使用concat操作符来实现先调用diskCache,再调用doCache 133 | return Observable.concat(diskCache,doCache) 134 | .first(new Func1() { 135 | @Override 136 | public Boolean call(byte[] bytes) { 137 | return bytes != null; 138 | } 139 | }); 140 | } 141 | ``` 142 | 上面是缓存系统的核心代码,避免太多代码太混乱。其中关于压缩模块的代码,DiskLruCache工具类的代码可以直接看demo。 143 | 拿到本地图片路径之后的操作: 144 | 145 | ``` 146 | private void adapter(List photoList) { 147 | CacheEngine.getInstance().getLocalImgsMany2(photoList) 148 | .flatMap((Fun1) (bytes) -> { return byte2Base64(bytes); }) 149 | .ObserverOn(AndroidSchedulers.mainThread()) 150 | .subscribe((Action1)(result) -> { sendToH5(result) }); 151 | ``` 152 | 153 | 然后就是关于多线程的实现,使用RxJava一个最大的亮点就是线程切换,在发出每个源数据的时候,只需要调用subscribeOn(Schedulers.newThread())即可,如果要看具体实现请移步demo,下面是效果: 154 | ![这里写图片描述](http://img.blog.csdn.net/20161126162145526) 155 | 这个方案大体的思想就是这样的,总体的速度提升了超级多(后面有截图对比),下面看看第四种方案 156 | 157 | ## 压缩+缓存+流 158 | 从上面第二种方案可以看出,base64编码多么消耗资源(很慢),只能说base64能绕过尽量绕过,我们都知道,webview在访问每一个连接的时候,都必须要经过shouldInterceptRequest这个方法: 159 | 160 | ``` 161 | /** 162 | * 自定义的WebViewClient 163 | */ 164 | protected class MyWebViewClient extends BridgeWebViewClient { 165 | public MyWebViewClient(BridgeWebView webView) { 166 | super(webView); 167 | } 168 | 169 | @Override 170 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) { 171 | return super.shouldInterceptRequest(view, request); 172 | } 173 | } 174 | ``` 175 | WebResourceResponse就是返回给webview的资源,然后在WebResourceResponse 有这样一个构造函数: 176 | 177 | ``` 178 | public WebResourceResponse(String mimeType, String encoding, InputStream data) { 179 | throw new RuntimeException("Stub!"); 180 | } 181 | ``` 182 | 通过传入一个流InputStream来实现发送数据流给webview,那么我现在的思路是这样的: 183 | 184 | - 取得本地图片路径之后,直接跟第一种方案一样生成img便签,例如: 185 | `` 186 | - 然后在webview加载到此标签的时候,就会被shouldInterceptRequest方法拦截 187 | - 那么此时我们要解析含有"file://"的url,解析本地路径地址 188 | - 调用我们的缓存系统,传入此path, (缓存+压缩)系统返回byte[],解析成inputStream 189 | - 构造WebResourceResponse返回 190 | 191 | 那么核心的代码就是: 192 | ``` 193 | //自定义的WebViewClient,重载shouldInterceptRequest方法 194 | class MyWebViewClient extends BridgeWebViewClient { 195 | public MyWebViewClient(BridgeWebView webView) { 196 | super(webView); 197 | } 198 | @Override 199 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) { 200 | String key = "http://localhost"; 201 | final WebResourceResponse[] response = {null}; 202 | if(url.contains(key)){ 203 | //拿到文件地址 204 | String imgPath = url.replace(key,""); 205 | CacheEngine.getInstance().getLocalImgInMain(imgPath) 206 | .subscribe(new Action1() { 207 | @Override 208 | public void call(byte[] bytes) { 209 | response[0] = new WebResourceResponse("image/png", "UTF-8", new ByteArrayInputStream(bytes)); 210 | } 211 | }); 212 | return response[0]; 213 | }else { 214 | return super.shouldInterceptRequest(view, url); 215 | } 216 | } 217 | } 218 | ``` 219 | ps:细心的朋友可能发现了的key是"http://localhost"而不是"file:///",这是因为如果是在服务器的html文件后者会被webview主动拦截了,不会经过shouldInterceptRequest方法,所以我们假装成http协议就行了(这是我暂时的解决办法,如果有更好的可以提出来哈) 220 | ## 方案的对比 221 | (图一为第三种方案。图四为第四种方案) 222 | ![这里写图片描述](http://img.blog.csdn.net/20161129085442968) ![这里写图片描述](http://img.blog.csdn.net/20161129085718781) 223 | 224 | # 总结 225 | 总体来说个人比较喜欢第四种解决方式,因为搭配缓存系统不仅可以做到本地图片的压缩+缓存,还可以做到网络图片的缓存,只需要通过前缀名来判断是哪种图片即可,而且shouldInterceptRequest方法默认是在io线程工作的,就算你的图片有多大都不会阻塞主线程。另外有一个点要说的就是,上面我的缓存系统只用到了硬盘缓存,其实还可以再加一层内存缓存,那样效果就真的完美了(不过因为这种情景下图片的再利用率不高,所以没做内存缓存,如果是网络图片的加载的话,就很有必要使用内存缓存了) 226 | 文章中的代码(start哦谢谢):[github地址](https://github.com/122627018/WebViewCacheModule) 227 | 228 | - 229 | 230 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.0" 6 | defaultConfig { 7 | applicationId "com.wxxiaomi.ming.webviewcachemodule" 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | repositories { 23 | jcenter() 24 | maven { url "https://jitpack.io" } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.android.support:appcompat-v7:25.0.0' 33 | testCompile 'junit:junit:4.12' 34 | compile 'com.github.lzyzsd:jsbridge:1.0.4' 35 | compile 'io.reactivex:rxjava:1.0.14' 36 | compile 'io.reactivex:rxandroid:1.0.1' 37 | compile 'com.github.bumptech.glide:glide:3.6.1' 38 | compile 'com.jakewharton:disklrucache:2.0.2' 39 | compile project(path: ':gallerypick') 40 | } 41 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\work\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/wxxiaomi/ming/webviewcachemodule/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wxxiaomi.ming.webviewcachemodule", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/assets/js/JsBridge.js: -------------------------------------------------------------------------------- 1 | function connectWebViewJavascriptBridge(callback) { 2 | if (window.WebViewJavascriptBridge) { 3 | callback(WebViewJavascriptBridge) 4 | } else { 5 | document.addEventListener( 6 | 'WebViewJavascriptBridgeReady' 7 | , function() { 8 | callback(WebViewJavascriptBridge) 9 | }, 10 | false 11 | ); 12 | } 13 | } 14 | 15 | function registerBridge(MyInitCallback,registerCallBack){ 16 | connectWebViewJavascriptBridge(function(bridge){ 17 | bridge.init(MyInitCallback); 18 | registerCallBack(bridge); 19 | 20 | }); 21 | } 22 | 23 | function showLog(data){ 24 | window.WebViewJavascriptBridge.callHandler( 25 | 'showLog' 26 | , data 27 | , null 28 | ); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/assets/test1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 15 | 16 | 17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 33 | 36 | 39 | 40 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | import com.wxxiaomi.ming.webviewcachemodule.version_1.Version_1_Activity; 10 | import com.wxxiaomi.ming.webviewcachemodule.version_2.Version_2_Activity; 11 | import com.wxxiaomi.ming.webviewcachemodule.version_3.Compress_Cache_Base64_Activity; 12 | import com.wxxiaomi.ming.webviewcachemodule.version_4.StreamActivity; 13 | 14 | /** 15 | * 16 | */ 17 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 18 | 19 | private Button btn_1; 20 | private Button btn_2; 21 | private Button btn_3; 22 | private Button btn_4; 23 | private Button btn_5; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | btn_1 = (Button) findViewById(R.id.btn_1); 30 | btn_1.setOnClickListener(this); 31 | btn_2 = (Button) findViewById(R.id.btn_2); 32 | btn_2.setOnClickListener(this); 33 | btn_3 = (Button) findViewById(R.id.btn_3); 34 | btn_3.setOnClickListener(this); 35 | btn_4 = (Button) findViewById(R.id.btn_4); 36 | btn_4.setOnClickListener(this); 37 | btn_5 = (Button) findViewById(R.id.btn_5); 38 | btn_5.setOnClickListener(this); 39 | } 40 | 41 | @Override 42 | public void onClick(View view) { 43 | Intent intent = null; 44 | switch (view.getId()){ 45 | 46 | case R.id.btn_1: 47 | intent = new Intent(this, Version_1_Activity.class); 48 | startActivity(intent); 49 | break; 50 | case R.id.btn_2: 51 | intent = new Intent(this, Version_2_Activity.class); 52 | startActivity(intent); 53 | break; 54 | case R.id.btn_3: 55 | intent = new Intent(this, Compress_Cache_Base64_Activity.class); 56 | startActivity(intent); 57 | break; 58 | case R.id.btn_4: 59 | intent = new Intent(this, StreamActivity.class); 60 | startActivity(intent); 61 | break; 62 | // case R.id.btn_1: 63 | // Intent intent = new Intent(this, Version_1_Activity.class); 64 | // startActivity(intent); 65 | // break; 66 | } 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/TAG.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule; 2 | 3 | /** 4 | * Created by Mr.W on 2016/11/26. 5 | * E-maiil:122627018@qq.com 6 | * github:https://github.com/122627018 7 | */ 8 | 9 | public class TAG { 10 | public final static String TAG = "SUN"; 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/common/BaseWebActivity.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.common; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.util.Log; 6 | import android.view.MenuItem; 7 | import android.webkit.WebResourceRequest; 8 | import android.webkit.WebResourceResponse; 9 | import android.webkit.WebView; 10 | 11 | import com.github.lzyzsd.jsbridge.BridgeHandler; 12 | import com.github.lzyzsd.jsbridge.BridgeWebView; 13 | import com.github.lzyzsd.jsbridge.BridgeWebViewClient; 14 | import com.github.lzyzsd.jsbridge.CallBackFunction; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * Created by 12262 on 2016/11/12. 20 | * webview的基础activity,用于webview的初始化 21 | * 不能含有toolbar,toolbar的初始化应该是在simpleActivity中 22 | */ 23 | public abstract class BaseWebActivity extends AppCompatActivity { 24 | protected BridgeWebView mWebView; 25 | 26 | 27 | @Override 28 | public void onCreate(Bundle savedInstanceState) { 29 | 30 | super.onCreate(savedInstanceState); 31 | int webViewId = initViewAndReutrnWebViewId(savedInstanceState); 32 | mWebView = (BridgeWebView) findViewById(webViewId); 33 | mWebView.setWebViewClient(new MyWebViewClient(mWebView)); 34 | 35 | initWebView(); 36 | initCommonMethod(); 37 | } 38 | 39 | protected void initCommonMethod() { 40 | mWebView.registerHandler("showLog", new BridgeHandler() { 41 | @Override 42 | public void handler(String data, CallBackFunction function) { 43 | Log.i("wang","js->showLog:"+data); 44 | } 45 | }); 46 | } 47 | 48 | /** 49 | * 自定义的WebViewClient 50 | */ 51 | protected class MyWebViewClient extends BridgeWebViewClient { 52 | public MyWebViewClient(BridgeWebView webView) { 53 | super(webView); 54 | } 55 | 56 | @Override 57 | public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { 58 | return super.shouldInterceptRequest(view, request); 59 | } 60 | } 61 | 62 | protected abstract int initViewAndReutrnWebViewId(Bundle savedInstanceState); 63 | protected abstract void initWebView(); 64 | 65 | @Override 66 | public boolean onOptionsItemSelected(MenuItem item) 67 | { 68 | // TODO Auto-generated method stub 69 | if(item.getItemId() == android.R.id.home) 70 | { 71 | finish(); 72 | return true; 73 | } 74 | return super.onOptionsItemSelected(item); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/common/GlideImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.common; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | 6 | import com.bumptech.glide.Glide; 7 | import com.wxxiaomi.ming.webviewcachemodule.R; 8 | import com.yancy.gallerypick.inter.ImageLoader; 9 | import com.yancy.gallerypick.widget.GalleryImageView; 10 | 11 | 12 | /** 13 | * Created by 12262 on 2016/11/23. 14 | * 加载图片的工具 15 | */ 16 | public class GlideImageLoader implements ImageLoader { 17 | @Override 18 | public void displayImage(Activity activity, Context context, String path, GalleryImageView galleryImageView, int width, int height) { 19 | Glide.with(context) 20 | .load(path) 21 | .placeholder(R.mipmap.gallery_pick_photo) 22 | .centerCrop() 23 | .into(galleryImageView); 24 | } 25 | 26 | @Override 27 | public void clearMemoryCache() { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/common/PicTakeUtil.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.common; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | import android.support.v4.app.ActivityCompat; 8 | import android.support.v4.content.ContextCompat; 9 | import android.widget.Toast; 10 | 11 | import com.yancy.gallerypick.config.GalleryConfig; 12 | import com.yancy.gallerypick.config.GalleryPick; 13 | import com.yancy.gallerypick.inter.IHandlerCallBack; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * Created by Mr.W on 2016/11/25. 20 | * E-maiil:122627018@qq.com 21 | * github:https://github.com/122627018 22 | * 从本地取照片的一个工具类 23 | */ 24 | public class PicTakeUtil { 25 | private Context context; 26 | private GalleryConfig galleryConfig; 27 | private List path = new ArrayList<>(); 28 | public PicTakeUtil(Context context){ 29 | this.context = context; 30 | galleryConfig = new GalleryConfig.Builder() 31 | .imageLoader(new GlideImageLoader()) // ImageLoader 加载框架(必填) 32 | .pathList(path) // 记录已选的图片 33 | .multiSelect(false) // 是否多选 默认:false 34 | .multiSelect(false, 9) // 配置是否多选的同时 配置多选数量 默认:false , 9 35 | .maxSize(9) // 配置多选时 的多选数量。 默认:9 36 | .crop(false) // 快捷开启裁剪功能,仅当单选 或直接开启相机时有效 37 | .crop(false, 1, 1, 500, 500) // 配置裁剪功能的参数, 默认裁剪比例 1:1 38 | .isShowCamera(true) // 是否现实相机按钮 默认:false 39 | .filePath("/Gallery/Pictures") // 图片存放路径 40 | .build(); 41 | galleryConfig.getBuilder().isShowCamera(true).build(); 42 | } 43 | 44 | public void takePicture(IHandlerCallBack iHandlerCallBack){ 45 | galleryConfig.getBuilder().multiSelect(true).build(); 46 | galleryConfig.getBuilder().crop(false).build(); 47 | galleryConfig.getBuilder().iHandlerCallBack(iHandlerCallBack); 48 | openTakeWindow(); 49 | } 50 | 51 | public void openTakeWindow(){ 52 | galleryConfig.getBuilder().isOpenCamera(false).build(); 53 | if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 54 | if (ActivityCompat.shouldShowRequestPermissionRationale((Activity)context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { 55 | Toast.makeText(context, "请在 设置-应用管理 中开启此应用的储存授权。", Toast.LENGTH_SHORT).show(); 56 | } else { 57 | ActivityCompat.requestPermissions((Activity)context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 8); 58 | } 59 | } else { 60 | GalleryPick.getInstance().setGalleryConfig(galleryConfig).open((Activity)context); 61 | 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/common/util/CacheEngine.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.common.util; 2 | 3 | import android.util.Log; 4 | 5 | 6 | import java.util.List; 7 | 8 | import rx.Observable; 9 | import rx.functions.Func1; 10 | 11 | /** 12 | * Created by 12262 on 2016/11/25. 13 | * 缓存的处理服务 14 | */ 15 | 16 | public class CacheEngine { 17 | private CacheEngine(){}; 18 | private static CacheEngine INSTANCE; 19 | public static CacheEngine getInstance(){ 20 | if(INSTANCE==null){ 21 | synchronized (CacheEngine.class){ 22 | INSTANCE = new CacheEngine(); 23 | } 24 | } 25 | return INSTANCE; 26 | } 27 | 28 | /** 29 | * 获取本地的图片 30 | * 先从硬盘获取,如果硬盘获取不到 31 | * 再拿图片进行压缩,再存到硬盘缓存 32 | * 然后返回 33 | * @param path 34 | * @return 35 | */ 36 | public Observable getLocalImg(final String path){ 37 | Observable diskCache = DiskCache.getInstance().getDiskCache(path); 38 | Observable doCache = CompressUtil.compressImg(path, 100, 100) 39 | .flatMap(new Func1>() { 40 | @Override 41 | public Observable call(final byte[] bytes) { 42 | return DiskCache.getInstance().toDiskCache(path, bytes); 43 | } 44 | }); 45 | return Observable.concat(diskCache,doCache) 46 | .first(new Func1() { 47 | @Override 48 | public Boolean call(byte[] bytes) { 49 | return bytes != null; 50 | } 51 | }); 52 | } 53 | 54 | /** 55 | * 获取本地的图片 56 | * 先从硬盘获取,如果硬盘获取不到 57 | * 再拿图片进行压缩,再存到硬盘缓存 58 | * 然后返回 59 | * @param path 60 | * @return 61 | */ 62 | public Observable getLocalImgInMain(final String path){ 63 | Observable diskCache = DiskCache.getInstance().getDiskCacheInMain(path); 64 | return Observable.concat(diskCache,getDoCache(path)) 65 | .first(new Func1() { 66 | @Override 67 | public Boolean call(byte[] bytes) { 68 | return bytes != null; 69 | } 70 | }); 71 | } 72 | 73 | /** 74 | * 对图片做压缩并缓存的一个操作 75 | * @param path 76 | * @return 77 | */ 78 | public Observable getDoCache(final String path){ 79 | return CompressUtil.compressImgInMain(path, 100, 100) 80 | .flatMap(new Func1>() { 81 | @Override 82 | public Observable call(final byte[] bytes) { 83 | Log.i("wang","CacheEngine-getLocalImg-doCache.currentThread:"+Thread.currentThread().getName()); 84 | return DiskCache.getInstance().toDiskCacheInMain(path, bytes); 85 | } 86 | }); 87 | } 88 | 89 | /** 90 | * 从本地数据取一些图片,分发多个源发送 91 | * @param path 92 | * @return 93 | */ 94 | public Observable getLocalImgsMany2(final List path){ 95 | return Observable.from(path) 96 | .flatMap(new Func1>() { 97 | @Override 98 | public Observable call(String s) { 99 | return getLocalImg(s); 100 | } 101 | }); 102 | } 103 | 104 | /** 105 | * 从本地数据取一些图片,合并在一个源里面一起发送 106 | * @param path 107 | * @return 108 | */ 109 | // public Observable> getLocalImgs(final List path){ 110 | // List> list = new ArrayList<>(); 111 | // final List result = new ArrayList<>(); 112 | // for(String item : path){ 113 | // Observable localImg = getLocalImg(item); 114 | // list.add(localImg); 115 | // } 116 | // return Observable.zip(list, new FuncN>() { 117 | // @Override 118 | // public List call(Object... args) { 119 | // for(Object item : args){ 120 | // result.add((byte[])item); 121 | // } 122 | // return result; 123 | // } 124 | // }); 125 | // } 126 | 127 | /** 128 | * 从本地数据取一些图片,分发多个源发送 129 | * @param path 130 | * @return 131 | */ 132 | // public Observable getLocalImgsMany(final List path){ 133 | // Log.i("wang","CacheEngine-getLocalImgsMany.currentThread:"+Thread.currentThread().getName()); 134 | // List> list = new ArrayList<>(); 135 | // for(String item : path){ 136 | // Observable localImg = getLocalImg(item); 137 | // list.add(localImg); 138 | // } 139 | // 140 | // return Observable.merge(list) 141 | // .subscribeOn(Schedulers.newThread()) 142 | // ; 143 | // 144 | // } 145 | 146 | 147 | 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/common/util/CompressUtil.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.common.util; 2 | 3 | 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Matrix; 7 | import android.media.ExifInterface; 8 | import android.util.Base64; 9 | import android.util.Log; 10 | 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | import rx.Observable; 17 | import rx.Subscriber; 18 | import rx.schedulers.Schedulers; 19 | 20 | /** 21 | * 1、避免内存过多的压缩方法: 22 | * 归根结底,图片是要显示在界面组件上的,所以还是要用到bitmap, 23 | * 从上面可得出Bitmap的在内存中的大小只和图片尺寸和色彩模式有关, 24 | * 那么要想改变Bitmap在内存中的大小,要么改变尺寸,要么改变色彩模式。 25 | * 2、避免上传浪费流量的压缩方法: 26 | * 改变图片尺寸,改变色彩模式,改变图片质量都行。正常情况下,先改变图片尺寸和色彩模式,再改变图片质量 27 | */ 28 | public class CompressUtil { 29 | 30 | 31 | /** 32 | * 传入指定的图片地址,然后进行压缩,并返回byte 33 | * 需要注意的是:最好的新的线程做压缩工作,因为这是耗时操作 34 | * 35 | * @param srcPath 36 | * @param rqsW 37 | * @param rqsH 38 | * @return 39 | */ 40 | public final static Observable compressImg(final String srcPath, final int rqsW, final int rqsH) { 41 | return 42 | Observable.create(new Observable.OnSubscribe() { 43 | @Override 44 | public void call(Subscriber subscriber) { 45 | Bitmap bitmap = compressBitmap(srcPath, rqsW, rqsH); 46 | int degree = getDegress(srcPath); 47 | try { 48 | if (degree != 0) bitmap = rotateBitmap(bitmap, degree); 49 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 50 | bitmap.compress(Bitmap.CompressFormat.PNG, 70, bos); 51 | byte[] bytes = bos.toByteArray(); 52 | subscriber.onNext(bytes); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | }) 58 | .subscribeOn(Schedulers.newThread()) 59 | ; 60 | } 61 | 62 | 63 | /** 64 | * 将img的byte数据通过base64编码转化为字符串 65 | * 66 | * @param par 67 | * @return 68 | */ 69 | public static Observable byte2Base64(final byte[] par) { 70 | return Observable.create(new Observable.OnSubscribe() { 71 | @Override 72 | public void call(Subscriber subscriber) { 73 | Log.i("cache", "处理数据的线程currentThread:" + Thread.currentThread().getName()); 74 | String temp = Base64.encodeToString(par, Base64.DEFAULT); 75 | Pattern p = Pattern.compile("\\s*|\t|\r|\n"); 76 | Matcher m = p.matcher(temp); 77 | temp = m.replaceAll(""); 78 | String img = ""; 79 | subscriber.onNext(img); 80 | subscriber.onCompleted(); 81 | } 82 | }).subscribeOn(Schedulers.newThread()); 83 | } 84 | 85 | /** 86 | * rotate the bitmap 87 | * 88 | * @param bitmap 89 | * @param degress 90 | * @return 91 | */ 92 | public static Bitmap rotateBitmap(Bitmap bitmap, int degress) { 93 | if (bitmap != null) { 94 | Matrix m = new Matrix(); 95 | m.postRotate(degress); 96 | bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); 97 | return bitmap; 98 | } 99 | return bitmap; 100 | } 101 | 102 | /** 103 | * 压缩指定路径的图片,并得到图片对象 104 | * 105 | * @param path bitmap source path 106 | * @return Bitmap {@link android.graphics.Bitmap} 107 | */ 108 | public final static Bitmap compressBitmap(String path, int rqsW, int rqsH) { 109 | final BitmapFactory.Options options = new BitmapFactory.Options(); 110 | options.inJustDecodeBounds = true; 111 | BitmapFactory.decodeFile(path, options); 112 | options.inSampleSize = caculateInSampleSize(options, rqsW, rqsH); 113 | options.inJustDecodeBounds = false; 114 | return BitmapFactory.decodeFile(path, options); 115 | } 116 | 117 | 118 | /** 119 | * caculate the bitmap sampleSize 120 | * 计算bitmap的尺寸 121 | * 122 | * @return 123 | */ 124 | public final static int caculateInSampleSize(BitmapFactory.Options options, int rqsW, int rqsH) { 125 | final int height = options.outHeight; 126 | final int width = options.outWidth; 127 | int inSampleSize = 1; 128 | if (rqsW == 0 || rqsH == 0) return 1; 129 | if (height > rqsH || width > rqsW) { 130 | final int heightRatio = Math.round((float) height / (float) rqsH); 131 | final int widthRatio = Math.round((float) width / (float) rqsW); 132 | inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 133 | } 134 | return inSampleSize; 135 | } 136 | 137 | /** 138 | * 得到指定路径图片的options 139 | * 140 | * @param srcPath 141 | * @return Options {@link android.graphics.BitmapFactory.Options} 142 | */ 143 | public final static BitmapFactory.Options getBitmapOptions(String srcPath) { 144 | BitmapFactory.Options options = new BitmapFactory.Options(); 145 | options.inJustDecodeBounds = true; 146 | BitmapFactory.decodeFile(srcPath, options); 147 | return options; 148 | } 149 | 150 | 151 | /** 152 | * get the orientation of the bitmap {@link android.media.ExifInterface} 153 | * 获取bitmap的方向信息 154 | * 155 | * @param path 156 | * @return 157 | */ 158 | public final static int getDegress(String path) { 159 | int degree = 0; 160 | try { 161 | ExifInterface exifInterface = new ExifInterface(path); 162 | int orientation = exifInterface.getAttributeInt( 163 | ExifInterface.TAG_ORIENTATION, 164 | ExifInterface.ORIENTATION_NORMAL); 165 | switch (orientation) { 166 | case ExifInterface.ORIENTATION_ROTATE_90: 167 | degree = 90; 168 | break; 169 | case ExifInterface.ORIENTATION_ROTATE_180: 170 | degree = 180; 171 | break; 172 | case ExifInterface.ORIENTATION_ROTATE_270: 173 | degree = 270; 174 | break; 175 | } 176 | } catch (IOException e) { 177 | e.printStackTrace(); 178 | } 179 | return degree; 180 | } 181 | 182 | public static Observable compressImgInMain(final String srcPath, final int rqsW, final int rqsH) { 183 | return 184 | Observable.create(new Observable.OnSubscribe() { 185 | @Override 186 | public void call(Subscriber subscriber) { 187 | Bitmap bitmap = compressBitmap(srcPath, rqsW, rqsH); 188 | int degree = getDegress(srcPath); 189 | try { 190 | if (degree != 0) bitmap = rotateBitmap(bitmap, degree); 191 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 192 | bitmap.compress(Bitmap.CompressFormat.PNG, 70, bos); 193 | byte[] bytes = bos.toByteArray(); 194 | subscriber.onNext(bytes); 195 | } catch (Exception e) { 196 | e.printStackTrace(); 197 | } 198 | } 199 | }); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/common/util/DiskCache.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.common.util; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.Environment; 7 | import android.util.Log; 8 | 9 | import com.jakewharton.disklrucache.DiskLruCache; 10 | import com.wxxiaomi.ming.webviewcachemodule.TAG; 11 | 12 | import java.io.BufferedInputStream; 13 | import java.io.BufferedOutputStream; 14 | import java.io.ByteArrayInputStream; 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.io.OutputStream; 20 | import java.security.MessageDigest; 21 | import java.security.NoSuchAlgorithmException; 22 | 23 | import rx.Observable; 24 | import rx.Subscriber; 25 | import rx.schedulers.Schedulers; 26 | 27 | /** 28 | * Created by 12262 on 2016/11/24. 29 | * DiskLruCache 30 | */ 31 | public class DiskCache { 32 | private Context context; 33 | DiskLruCache mDiskLruCache = null; 34 | public static DiskCache INSTANCE; 35 | 36 | private DiskCache() { 37 | } 38 | 39 | public static DiskCache getInstance() { 40 | if (INSTANCE == null) { 41 | synchronized (DiskCache.class) { 42 | INSTANCE = new DiskCache(); 43 | } 44 | } 45 | return INSTANCE; 46 | } 47 | 48 | 49 | public void open(Context context) { 50 | try { 51 | this.context = context; 52 | File cacheDir = getDiskCacheDir(context, "bitmap"); 53 | if (!cacheDir.exists()) { 54 | cacheDir.mkdirs(); 55 | } 56 | //第一个参数指定的是数据的缓存地址, 57 | // 第二个参数指定当前应用程序的版本号, 58 | // 第三个参数指定同一个key可以对应多少个缓存文件,基本都是传1, 59 | // 第四个参数指定最多可以缓存多少字节的数据。 60 | mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024); 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | public Observable toDiskCache(final String path, final byte[] obj) { 67 | return Observable.create(new Observable.OnSubscribe() { 68 | @Override 69 | public void call(Subscriber subscriber) { 70 | String key = hashKeyForDisk(path); 71 | 72 | BufferedInputStream in = null; 73 | BufferedOutputStream out = null; 74 | try { 75 | DiskLruCache.Snapshot snapshot1 = mDiskLruCache.get(key); 76 | if (snapshot1 == null) { 77 | Log.i(TAG.TAG, "将数据存入缓存"); 78 | DiskLruCache.Editor editor = mDiskLruCache.edit(key); 79 | 80 | in = new BufferedInputStream(new ByteArrayInputStream(obj), 8 * 1024); 81 | OutputStream outputStream = editor.newOutputStream(0); 82 | out = new BufferedOutputStream(outputStream, 8 * 1024); 83 | int b; 84 | while ((b = in.read()) != -1) { 85 | out.write(b); 86 | } 87 | editor.commit(); 88 | } else { 89 | Log.i(TAG.TAG, "数据已存在缓存中,不需要再次插入"); 90 | } 91 | subscriber.onNext(obj); 92 | subscriber.onCompleted(); 93 | 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | subscriber.onError(e); 97 | } finally { 98 | try { 99 | if (out != null) { 100 | out.close(); 101 | } 102 | if (in != null) { 103 | in.close(); 104 | } 105 | } catch (final IOException e) { 106 | e.printStackTrace(); 107 | } 108 | } 109 | } 110 | }) 111 | .subscribeOn(Schedulers.newThread()) 112 | ; 113 | } 114 | 115 | 116 | public Observable getDiskCache(final String url) { 117 | return Observable.create(new Observable.OnSubscribe() { 118 | @Override 119 | public void call(Subscriber subscriber) { 120 | try { 121 | Log.i(TAG.TAG, "从硬盘取数据的线程.currentThread:" + Thread.currentThread().getName()); 122 | String key = hashKeyForDisk(url); 123 | DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); 124 | if (snapShot != null) { 125 | Log.i(TAG.TAG, "从硬盘取到缓存"); 126 | InputStream is = snapShot.getInputStream(0); 127 | ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); 128 | byte[] buff = new byte[100]; 129 | int rc = 0; 130 | while ((rc = is.read(buff, 0, 100)) > 0) { 131 | swapStream.write(buff, 0, rc); 132 | } 133 | byte[] in2b = swapStream.toByteArray(); 134 | subscriber.onNext(in2b); 135 | } else { 136 | subscriber.onNext(null); 137 | } 138 | subscriber.onCompleted(); 139 | } catch (Exception e) { 140 | e.printStackTrace(); 141 | subscriber.onError(null); 142 | } 143 | } 144 | }) 145 | .subscribeOn(Schedulers.newThread()) 146 | ; 147 | } 148 | 149 | /** 150 | * 获取版本号 151 | * 152 | * @param context 153 | * @return 154 | */ 155 | public int getAppVersion(Context context) { 156 | try { 157 | PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 158 | return info.versionCode; 159 | } catch (PackageManager.NameNotFoundException e) { 160 | e.printStackTrace(); 161 | } 162 | return 1; 163 | } 164 | 165 | 166 | /** 167 | * 获取缓存位置 168 | * 169 | * @param context 170 | * @param uniqueName 171 | * @return 172 | */ 173 | public File getDiskCacheDir(Context context, String uniqueName) { 174 | String cachePath; 175 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 176 | || !Environment.isExternalStorageRemovable()) { 177 | cachePath = context.getExternalCacheDir().getPath(); 178 | } else { 179 | cachePath = context.getCacheDir().getPath(); 180 | } 181 | return new File(cachePath + File.separator + uniqueName); 182 | } 183 | 184 | /** 185 | * 用来将字符串进行MD5编码 186 | * 187 | * @param key 188 | * @return 189 | */ 190 | public String hashKeyForDisk(String key) { 191 | String cacheKey; 192 | try { 193 | final MessageDigest mDigest = MessageDigest.getInstance("MD5"); 194 | mDigest.update(key.getBytes()); 195 | cacheKey = bytesToHexString(mDigest.digest()); 196 | } catch (NoSuchAlgorithmException e) { 197 | cacheKey = String.valueOf(key.hashCode()); 198 | } 199 | return cacheKey; 200 | } 201 | 202 | private String bytesToHexString(byte[] bytes) { 203 | StringBuilder sb = new StringBuilder(); 204 | for (int i = 0; i < bytes.length; i++) { 205 | String hex = Integer.toHexString(0xFF & bytes[i]); 206 | if (hex.length() == 1) { 207 | sb.append('0'); 208 | } 209 | sb.append(hex); 210 | } 211 | return sb.toString(); 212 | } 213 | 214 | public Observable getDiskCacheInMain(final String path) { 215 | return Observable.create(new Observable.OnSubscribe() { 216 | @Override 217 | public void call(Subscriber subscriber) { 218 | try { 219 | Log.i("cache", "从硬盘取数据的线程.currentThread:" + Thread.currentThread().getName()); 220 | String key = hashKeyForDisk(path); 221 | DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); 222 | if (snapShot != null) { 223 | InputStream is = snapShot.getInputStream(0); 224 | ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); 225 | byte[] buff = new byte[100]; 226 | int rc = 0; 227 | while ((rc = is.read(buff, 0, 100)) > 0) { 228 | swapStream.write(buff, 0, rc); 229 | } 230 | byte[] in2b = swapStream.toByteArray(); 231 | subscriber.onNext(in2b); 232 | } else { 233 | subscriber.onNext(null); 234 | } 235 | subscriber.onCompleted(); 236 | } catch (Exception e) { 237 | e.printStackTrace(); 238 | subscriber.onError(null); 239 | } 240 | } 241 | }); 242 | } 243 | 244 | public Observable toDiskCacheInMain(final String path, final byte[] obj) { 245 | return Observable.create(new Observable.OnSubscribe() { 246 | @Override 247 | public void call(Subscriber subscriber) { 248 | String key = hashKeyForDisk(path); 249 | 250 | BufferedInputStream in = null; 251 | BufferedOutputStream out = null; 252 | try { 253 | DiskLruCache.Snapshot snapshot1 = mDiskLruCache.get(key); 254 | if (snapshot1 == null) { 255 | Log.i("wang", "存入缓存"); 256 | DiskLruCache.Editor editor = mDiskLruCache.edit(key); 257 | 258 | in = new BufferedInputStream(new ByteArrayInputStream(obj), 8 * 1024); 259 | OutputStream outputStream = editor.newOutputStream(0); 260 | out = new BufferedOutputStream(outputStream, 8 * 1024); 261 | int b; 262 | while ((b = in.read()) != -1) { 263 | out.write(b); 264 | } 265 | editor.commit(); 266 | } else { 267 | Log.i("wang", "已经存在,不用存入缓存了"); 268 | } 269 | subscriber.onNext(obj); 270 | subscriber.onCompleted(); 271 | 272 | } catch (IOException e) { 273 | e.printStackTrace(); 274 | subscriber.onError(e); 275 | } finally { 276 | try { 277 | if (out != null) { 278 | out.close(); 279 | } 280 | if (in != null) { 281 | in.close(); 282 | } 283 | } catch (final IOException e) { 284 | e.printStackTrace(); 285 | } 286 | } 287 | } 288 | }) 289 | ; 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/version_1/Version_1_Activity.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.version_1; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | import com.wxxiaomi.ming.webviewcachemodule.R; 10 | import com.wxxiaomi.ming.webviewcachemodule.common.BaseWebActivity; 11 | import com.wxxiaomi.ming.webviewcachemodule.common.PicTakeUtil; 12 | import com.yancy.gallerypick.inter.IHandlerCallBack; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * Created by Mr.W on ${DATE}. 18 | * E-maiil:122627018@qq.com 19 | * github:https://github.com/122627018 20 | * 模拟从本地取得照片地址,并在webview中加载 21 | */ 22 | public class Version_1_Activity extends BaseWebActivity { 23 | private Button btn; 24 | private PicTakeUtil util; 25 | private String path = "/storage/sdcard0/demo.jpg"; 26 | 27 | @Override 28 | protected int initViewAndReutrnWebViewId(Bundle savedInstanceState) { 29 | setContentView(R.layout.activity_version_1_); 30 | btn = (Button) findViewById(R.id.btn); 31 | btn.setOnClickListener(new View.OnClickListener() { 32 | @Override 33 | public void onClick(View view) { 34 | // adapterH5(); 35 | takePicture(); 36 | } 37 | }); 38 | return R.id.web_view; 39 | } 40 | 41 | private void takePicture() { 42 | util = new PicTakeUtil(this); 43 | util.takePicture(new IHandlerCallBack() { 44 | @Override 45 | public void onStart() { 46 | 47 | } 48 | 49 | @Override 50 | public void onSuccess(List photoList) { 51 | for(String item : photoList){ 52 | Log.i("wang","item:"+item); 53 | } 54 | adapterH5Data(photoList); 55 | 56 | } 57 | 58 | @Override 59 | public void onCancel() { 60 | 61 | } 62 | 63 | @Override 64 | public void onFinish() { 65 | 66 | } 67 | 68 | @Override 69 | public void onError() { 70 | 71 | } 72 | }); 73 | } 74 | 75 | private void adapterH5Data(List imgPaths){ 76 | String result = ""; 77 | for(int i=0;i"; 79 | } 80 | Log.i("wang","result:"+result); 81 | mWebView.callHandler("adapter",result,null); 82 | } 83 | 84 | private void adapterH5(){ 85 | String result = ""; 86 | for(int i=0;i<20;i++){ 87 | result += ""; 88 | } 89 | Log.i("wang","result:"+result); 90 | mWebView.callHandler("adapter",result,null); 91 | } 92 | 93 | @Override 94 | protected void initWebView() { 95 | mWebView.loadUrl("file:///android_asset/test1.html"); 96 | mWebView.getSettings().setAllowFileAccess(true); 97 | // util = new PicTakeUtil(this); 98 | } 99 | 100 | @Override 101 | protected void onDestroy() { 102 | super.onDestroy(); 103 | 104 | mWebView.removeAllViews(); 105 | mWebView.destroy(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/version_2/Version_2_Activity.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.version_2; 2 | 3 | import android.os.Bundle; 4 | import android.util.Base64; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.TextView; 9 | 10 | import com.wxxiaomi.ming.webviewcachemodule.R; 11 | import com.wxxiaomi.ming.webviewcachemodule.common.BaseWebActivity; 12 | import com.wxxiaomi.ming.webviewcachemodule.common.PicTakeUtil; 13 | import com.yancy.gallerypick.inter.IHandlerCallBack; 14 | 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.File; 17 | import java.io.FileInputStream; 18 | import java.io.FileNotFoundException; 19 | import java.io.IOException; 20 | import java.util.List; 21 | 22 | import rx.Observable; 23 | import rx.Subscriber; 24 | import rx.android.schedulers.AndroidSchedulers; 25 | import rx.functions.Action1; 26 | import rx.schedulers.Schedulers; 27 | 28 | /** 29 | * Created by Mr.W on ${DATE}. 30 | * E-maiil:122627018@qq.com 31 | * github:https://github.com/122627018 32 | * 模拟从本地取得照片地址,并在webview中加载 33 | */ 34 | public class Version_2_Activity extends BaseWebActivity { 35 | 36 | private Button btn; 37 | private PicTakeUtil util; 38 | private TextView tv; 39 | private int size = 0; 40 | 41 | 42 | @Override 43 | protected int initViewAndReutrnWebViewId(Bundle savedInstanceState) { 44 | setContentView(R.layout.activity_version_2_); 45 | tv = (TextView) findViewById(R.id.tv); 46 | tv.setText("建议不要选择太多相片"); 47 | btn = (Button) findViewById(R.id.btn); 48 | btn.setOnClickListener(new View.OnClickListener() { 49 | @Override 50 | public void onClick(View view) { 51 | takePicture(); 52 | } 53 | }); 54 | return R.id.web_view; 55 | } 56 | 57 | private void takePicture() { 58 | tv.setText("正在编码"); 59 | util = new PicTakeUtil(this); 60 | util.takePicture(new IHandlerCallBack() { 61 | @Override 62 | public void onStart() { 63 | } 64 | @Override 65 | public void onSuccess(List photoList) { 66 | adapterH5Data(photoList); 67 | } 68 | @Override 69 | public void onCancel() { 70 | 71 | } 72 | @Override 73 | public void onFinish() { 74 | 75 | } 76 | @Override 77 | public void onError() { 78 | 79 | } 80 | }); 81 | } 82 | 83 | /** 84 | * 在这里必须使用多线程,因为base64对图片进行编码非常耗时, 85 | * 所以必须在子线程并且多线程处理每张图片的编码 86 | * RxJava的线程切换(这里暂时还没有使用多线程) 87 | * @param imgPaths 88 | */ 89 | private void adapterH5Data(final List imgPaths) { 90 | Observable.create(new Observable.OnSubscribe() { 91 | @Override 92 | public void call(Subscriber subscriber) { 93 | for (int i = 0; i < imgPaths.size(); i++) { 94 | String result = ""; 95 | String item = imgPaths.get(i); 96 | Log.i("wang","item:"+item); 97 | String s = Base64.encodeToString(getBytes(item), Base64.NO_WRAP); 98 | result += ""; 99 | subscriber.onNext(result); 100 | } 101 | } 102 | }) 103 | .subscribeOn(Schedulers.io()) 104 | .observeOn(AndroidSchedulers.mainThread()) 105 | .subscribe(new Action1() { 106 | @Override 107 | public void call(String s) { 108 | size++; 109 | tv.setText("编码完成:"+size+"张"); 110 | mWebView.callHandler("adapter", s, null); 111 | } 112 | }) 113 | ; 114 | } 115 | 116 | 117 | @Override 118 | protected void initWebView() { 119 | mWebView.loadUrl("file:///android_asset/test1.html"); 120 | mWebView.getSettings().setAllowFileAccess(true); 121 | 122 | } 123 | 124 | /** 125 | * 获得指定文件的byte数组 126 | */ 127 | private byte[] getBytes(String filePath) { 128 | byte[] buffer = null; 129 | try { 130 | File file = new File(filePath); 131 | FileInputStream fis = new FileInputStream(file); 132 | ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); 133 | byte[] b = new byte[1000]; 134 | int n; 135 | while ((n = fis.read(b)) != -1) { 136 | bos.write(b, 0, n); 137 | } 138 | fis.close(); 139 | bos.close(); 140 | buffer = bos.toByteArray(); 141 | } catch (FileNotFoundException e) { 142 | e.printStackTrace(); 143 | } catch (IOException e) { 144 | e.printStackTrace(); 145 | } 146 | return buffer; 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/version_3/Compress_Cache_Base64_Activity.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.version_3; 2 | 3 | import android.os.Bundle; 4 | import android.util.Base64; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | import com.wxxiaomi.ming.webviewcachemodule.R; 10 | import com.wxxiaomi.ming.webviewcachemodule.TAG; 11 | import com.wxxiaomi.ming.webviewcachemodule.common.BaseWebActivity; 12 | import com.wxxiaomi.ming.webviewcachemodule.common.PicTakeUtil; 13 | import com.wxxiaomi.ming.webviewcachemodule.common.util.CacheEngine; 14 | import com.wxxiaomi.ming.webviewcachemodule.common.util.DiskCache; 15 | import com.yancy.gallerypick.inter.IHandlerCallBack; 16 | 17 | import java.util.List; 18 | 19 | import rx.Observable; 20 | import rx.Subscriber; 21 | import rx.android.schedulers.AndroidSchedulers; 22 | import rx.functions.Action1; 23 | import rx.functions.Func1; 24 | import rx.schedulers.Schedulers; 25 | 26 | /** 27 | * Created by Mr.W on ${DATE}. 28 | * E-maiil:122627018@qq.com 29 | * github:https://github.com/122627018 30 | * 使用压缩+缓存+base64编码完成(多线程工作) 31 | */ 32 | public class Compress_Cache_Base64_Activity extends BaseWebActivity { 33 | private Button btn; 34 | private PicTakeUtil util; 35 | 36 | 37 | @Override 38 | protected int initViewAndReutrnWebViewId(Bundle savedInstanceState) { 39 | setContentView(R.layout.activity_compress__cache__base64_); 40 | setContentView(R.layout.activity_version_2_); 41 | btn = (Button) findViewById(R.id.btn); 42 | btn.setOnClickListener(new View.OnClickListener() { 43 | @Override 44 | public void onClick(View view) { 45 | takePicture(); 46 | } 47 | }); 48 | DiskCache.getInstance().open(getApplicationContext()); 49 | return R.id.web_view; 50 | } 51 | 52 | @Override 53 | protected void initWebView() { 54 | mWebView.loadUrl("file:///android_asset/test1.html"); 55 | mWebView.getSettings().setAllowFileAccess(true); 56 | } 57 | 58 | private void takePicture() { 59 | util = new PicTakeUtil(this); 60 | util.takePicture(new IHandlerCallBack() { 61 | @Override 62 | public void onStart() { 63 | } 64 | 65 | @Override 66 | public void onSuccess(List photoList) { 67 | adapter(photoList); 68 | } 69 | 70 | @Override 71 | public void onCancel() { 72 | 73 | } 74 | 75 | @Override 76 | public void onFinish() { 77 | 78 | } 79 | 80 | @Override 81 | public void onError() { 82 | 83 | } 84 | }); 85 | } 86 | 87 | private void adapter(List photoList) { 88 | CacheEngine.getInstance().getLocalImgsMany2(photoList) 89 | .flatMap(new Func1>() { 90 | @Override 91 | public Observable call(byte[] bytes) { 92 | return byte2base64(bytes); 93 | } 94 | }) 95 | .observeOn(AndroidSchedulers.mainThread()) 96 | .subscribe(new Action1() { 97 | @Override 98 | public void call(String s) { 99 | sendToH5(s); 100 | } 101 | }); 102 | } 103 | 104 | private void sendToH5(String s) { 105 | String result = ""; 106 | mWebView.callHandler("adapter",result,null); 107 | } 108 | 109 | public Observable byte2base64(final byte[] bytes) { 110 | return 111 | Observable.create(new Observable.OnSubscribe() { 112 | @Override 113 | public void call(Subscriber subscriber) { 114 | Log.i(com.wxxiaomi.ming.webviewcachemodule.TAG.TAG,"我在编码,我的线程:"+Thread.currentThread().getName()); 115 | String s = Base64.encodeToString(bytes, Base64.NO_WRAP); 116 | subscriber.onNext(s); 117 | } 118 | }) 119 | .subscribeOn(Schedulers.newThread()) 120 | ; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/wxxiaomi/ming/webviewcachemodule/version_4/StreamActivity.java: -------------------------------------------------------------------------------- 1 | package com.wxxiaomi.ming.webviewcachemodule.version_4; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.webkit.WebResourceResponse; 8 | import android.webkit.WebView; 9 | import android.widget.Button; 10 | 11 | import com.github.lzyzsd.jsbridge.BridgeHandler; 12 | import com.github.lzyzsd.jsbridge.BridgeWebView; 13 | import com.github.lzyzsd.jsbridge.BridgeWebViewClient; 14 | import com.github.lzyzsd.jsbridge.CallBackFunction; 15 | import com.wxxiaomi.ming.webviewcachemodule.R; 16 | import com.wxxiaomi.ming.webviewcachemodule.common.PicTakeUtil; 17 | import com.wxxiaomi.ming.webviewcachemodule.common.util.CacheEngine; 18 | import com.wxxiaomi.ming.webviewcachemodule.common.util.DiskCache; 19 | import com.yancy.gallerypick.inter.IHandlerCallBack; 20 | 21 | import java.io.ByteArrayInputStream; 22 | import java.util.List; 23 | 24 | import rx.functions.Action1; 25 | 26 | public class StreamActivity extends AppCompatActivity { 27 | protected BridgeWebView mWebView; 28 | private Button btn; 29 | private PicTakeUtil util; 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_stream); 35 | mWebView = (BridgeWebView) findViewById(R.id.web_view); 36 | mWebView.setWebViewClient(new MyWebViewClient(mWebView)); 37 | mWebView.loadUrl("file:///android_asset/test1.html"); 38 | mWebView.getSettings().setAllowFileAccess(true); 39 | btn = (Button) findViewById(R.id.btn); 40 | btn.setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(View view) { 43 | takePicture(); 44 | } 45 | }); 46 | DiskCache.getInstance().open(getApplicationContext()); 47 | } 48 | 49 | private void takePicture() { 50 | util = new PicTakeUtil(this); 51 | util.takePicture(new IHandlerCallBack() { 52 | @Override 53 | public void onStart() { 54 | } 55 | 56 | @Override 57 | public void onSuccess(List photoList) { 58 | adapter(photoList); 59 | } 60 | 61 | @Override 62 | public void onCancel() { 63 | 64 | } 65 | 66 | @Override 67 | public void onFinish() { 68 | 69 | } 70 | 71 | @Override 72 | public void onError() { 73 | 74 | } 75 | }); 76 | } 77 | 78 | private void adapter(List photoList) { 79 | String result = ""; 80 | for(int i=0;i"; 82 | } 83 | mWebView.callHandler("adapter",result,null); 84 | } 85 | 86 | 87 | class MyWebViewClient extends BridgeWebViewClient { 88 | public MyWebViewClient(BridgeWebView webView) { 89 | super(webView); 90 | } 91 | @Override 92 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) { 93 | String key = "http://localhost"; 94 | final WebResourceResponse[] response = {null}; 95 | if(url.contains(key)){ 96 | String imgPath = url.replace(key,""); 97 | CacheEngine.getInstance().getLocalImgInMain(imgPath) 98 | .subscribe(new Action1() { 99 | @Override 100 | public void call(byte[] bytes) { 101 | response[0] = new WebResourceResponse("image/png", "UTF-8", new ByteArrayInputStream(bytes)); 102 | } 103 | }); 104 | return response[0]; 105 | }else { 106 | return super.shouldInterceptRequest(view, url); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_compress__cache__base64_.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 18 |