├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── org │ │ └── zzy │ │ └── imagemonitor │ │ ├── MainActivity.java │ │ └── MyApplication.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable-xxhdpi │ ├── ic_test_four.jpg │ ├── ic_test_one.jpg │ ├── ic_test_three.jpg │ └── ic_test_two.jpg │ ├── drawable │ ├── ic_launcher_background.xml │ └── ic_launcher_foreground.xml │ ├── layout │ └── activity_main.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 │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── network_security_config.xml ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── largeimage-plugin ├── build.gradle ├── src │ └── main │ │ ├── groovy │ │ └── org │ │ │ └── zzy │ │ │ └── largeimage │ │ │ ├── Config.java │ │ │ ├── LargeImageExtension.java │ │ │ ├── LargeImageMonitorPlugin.java │ │ │ ├── adapter │ │ │ ├── LargeImageClassAdapter.java │ │ │ ├── OkHttpClassAdapter.java │ │ │ └── UrlConnectionClassAdapter.java │ │ │ ├── method │ │ │ ├── FrescoMethodAdapter.java │ │ │ ├── GlideMethodAdapter.java │ │ │ ├── ImageLoaderMethodAdapter.java │ │ │ ├── OkHttpMethodAdapter.java │ │ │ ├── PicassoMethodAdapter.java │ │ │ └── UrlConnectionMethodAdapter.java │ │ │ ├── transform │ │ │ ├── LargeImageTransform.java │ │ │ ├── OkHttpTransform.java │ │ │ └── UrlConnectionTransform.java │ │ │ └── weaver │ │ │ ├── LargeImageWeaver.java │ │ │ ├── OkHttpWeaver.java │ │ │ └── UrlConnectionWeaver.java │ │ └── resources │ │ └── META-INF │ │ └── gradle-plugins │ │ └── largeimage-monitor.properties └── upload.gradle ├── largeimage ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── ic_launcher-playstore.png │ │ ├── java │ │ └── org │ │ │ └── zzy │ │ │ └── lib │ │ │ └── largeimage │ │ │ ├── LargeImage.java │ │ │ ├── LargeImageInfo.java │ │ │ ├── LargeImageManager.java │ │ │ ├── Test.java │ │ │ ├── activity │ │ │ ├── LargeImageListActivity.java │ │ │ └── SettingActivity.java │ │ │ ├── adapter │ │ │ └── LargeImageListAdapter.java │ │ │ ├── aop │ │ │ ├── fresco │ │ │ │ ├── FrescoHook.java │ │ │ │ └── FrescoLargeImageProcessor.java │ │ │ ├── glide │ │ │ │ ├── GlideHook.java │ │ │ │ └── GlideLargeImageListener.java │ │ │ ├── imageloader │ │ │ │ ├── ImageLoaderHook.java │ │ │ │ └── ImageLoaderLargeImageListener.java │ │ │ ├── okhttp │ │ │ │ └── LargeImageInterceptor.java │ │ │ ├── picasso │ │ │ │ ├── PicassoHook.java │ │ │ │ └── PicassoLargeImageTransformation.java │ │ │ └── urlconnection │ │ │ │ ├── ObsoleteUrlFactory.java │ │ │ │ └── UrlConnectionHook.java │ │ │ └── util │ │ │ ├── ConvertUtils.java │ │ │ ├── ImageUtil.java │ │ │ ├── Reflector.java │ │ │ └── ResHelper.java │ │ └── res │ │ ├── drawable-xxhdpi │ │ ├── checkbox_checked.png │ │ ├── checkbox_unchecked.png │ │ ├── ic_default.jpg │ │ └── ic_failed.jpg │ │ ├── drawable │ │ ├── ic_alarm.xml │ │ ├── ic_arrow_back_black_24dp.xml │ │ ├── ic_background.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_logo.xml │ │ ├── ic_more_vert_black_24dp.xml │ │ ├── ic_satellite.xml │ │ └── switch_background.xml │ │ ├── layout │ │ ├── activity_largeimage_list.xml │ │ ├── activity_setting.xml │ │ └── dialog_custom.xml │ │ ├── menu │ │ └── toolbar.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 │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml └── upload.gradle ├── settings.gradle └── wiki ├── LargeImageTheory.md ├── alipay.jpg ├── example1.jpg ├── example2.jpg ├── example3.jpg ├── example4.jpg ├── logo.png └── wxpay.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LargeImageMonitor 2 | 3 |

4 | logo 5 |

6 | 7 | [![Download](https://api.bintray.com/packages/zzy/imageMonitor/large-image-monitor/images/download.svg?version=1.0.0)](https://bintray.com/zzy/imageMonitor/large-image-monitor/1.0.0/link) 8 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/121880399/LargeImageMonitor?color=red) 9 | ![](https://img.shields.io/badge/language-java-orange.svg) 10 | ![GitHub](https://img.shields.io/github/license/121880399/LargeImageMonitor) 11 | 12 | ### 背景 13 | 在平时开发中,我们经常会出现OOM和APK体积过大的情况,其中有很大的原因在于图片过大。当图片占用内存过大,内存不足时就会导致OOM。而图片的文件太大,会导致在打包成APK后,APK的体积增加。对于本地加载的图片,UI给我们图时,我们或许会下意识看一眼文件大小,如果文件太大我们可能会跟UI小姐姐进行沟通。但是对于从网络加载图片,很多时候客户端拿到的只是一个URL,我们并不知道要加载的图片大小,这时候就有可能造成图片过大的情况。 14 | 15 | ### 介绍 16 | LargeImageMonitor是一个使用ASM进行字节码插桩的大图监控框架,可以对我们要加载的图片进行监控,如果出现图片超过阈值的情况会进行报警。 17 | 18 | ### 功能与特点 19 |     1.支持Glide,Fresco,Picasso,Universal Image Loader。 20 | 21 |     2.可以对图片的文件大小设置阈值。 22 | 23 |     3.可以对图片所占用的内存大小设置阈值。 24 | 25 |     4.可以通过弹窗或者列表的方式查看超过阈值的图片。 26 | 27 |     5.可以为Okhttp添加全局的应用监听器和网络监听器(包括第三方使用到的okhttp也会添加上)。 28 | 29 |     6.可以为Okhttp添加全局的自定义DNS,方便实现HTTPDNS(包括第三方使用到的okhttp也会添加上)。 30 | 31 |     6.支持Androidx。 32 | 33 | ### 效果 34 |

35 | 36 | 37 |

38 |

39 | 40 | 41 |

42 | 43 | ### 快速接入 44 | 1.在根目录的build.gralde中添加如下代码: 45 | ```groovy 46 | buildscript { 47 | //添加jcenter 48 | repositories { 49 | jcenter() 50 | } 51 | //添加插件classpath 52 | dependencies { 53 | classpath 'org.zzy.largeimage:large-image-monitor-plugin:1.0.0' 54 | } 55 | } 56 | ``` 57 | 2.在要使用的模块的build.gradle中添加如下代码: 58 | ```groovy 59 | //添加使用插件 60 | apply plugin: 'largeimage-monitor' 61 | //添加依赖库 62 | implementation 'org.zzy.lib.largeimage:large-image-monitor:1.0.0' 63 | ``` 64 | 3.在Application类中初始化框架: 65 | ```java 66 | //初始化大图监控 67 | LargeImage.getInstance() 68 | .install(this)//一定要调用该方法进行初始化 69 | .setFileSizeThreshold(400.0)//设置文件大小阈值单位为KB (可选) 70 | .setMemorySizeThreshold(100)//设置内存占用大小阈值单位为KB (可选) 71 | .setLargeImageOpen(true)//是否开启大图监控,默认为开启,如果false,则不会在大图列表和弹窗显示超标图片 (可选) 72 | .addOkHttpInterceptor(new CustomGlobalInterceptor())//添加OKhttp自定义全局应用监听器 (可选) 73 | .addOkHttpNetworkInterceptor(new CustomGlobalNetworkInterceptor())//添加Okhttp值得你故意全局网络监听器 (可选) 74 | .setDns(new CustomHttpDns);//设置自定义的全局DNS,可以自己实现HttpDns (可选) 75 | ``` 76 | 77 | 4.记得打开浮窗权限 78 | 79 | ### 支持版本 80 | - Glide:(v4.10.0 - v4.11.0) 81 | - Fresco:(v0.1.0 - v2.2.0) 82 | - Picasso:(v2.4.0 - v2.71828) 83 | - ImageLoader:(v1.9.1 - v1.9.5) 84 | - Okhttp:(v3.0.0 - v3.14.7) 85 | 86 | ### TodoList 87 | - [ ] 对各大框架进行版本兼容。 88 | - [ ] 得到View的尺寸。 89 | 90 | ### Change Log 91 | v1.0.0 (2020/4/15) 92 | - 项目初始化实现基本功能 93 | 94 | ### 原理分析 95 | - [通过ASM实现大图监控](https://github.com/121880399/LargeImageMonitor/blob/master/wiki/LargeImageTheory.md) 96 | 97 | ### 鸣谢 98 | - [DoraemonKit](https://github.com/didi/DoraemonKit) 99 | - [Hunter](https://github.com/Leaking/Hunter) 100 | - [bintray-release](https://github.com/novoda/bintray-release) 101 | 102 | ## License 103 | Apache 2.0 104 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'largeimage-monitor' 3 | android { 4 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 5 | 6 | defaultConfig { 7 | applicationId rootProject.ext.android["applicationId"] 8 | minSdkVersion rootProject.ext.android["minSdkVersion"] 9 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 10 | versionCode rootProject.ext.android["versionCode"] 11 | versionName rootProject.ext.android["versionName"] 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | //largeImageMonitor{ 22 | // //开关测试成功 23 | // largeImagePluginSwitch = true 24 | //} 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | implementation rootProject.ext.dependencies["androidx"] 29 | implementation rootProject.ext.dependencies["glide"] 30 | implementation rootProject.ext.dependencies["picasso"] 31 | implementation rootProject.ext.dependencies["fresco"] 32 | implementation rootProject.ext.dependencies["image-loader"] 33 | implementation rootProject.ext.dependencies["image-monitor"] 34 | } 35 | -------------------------------------------------------------------------------- /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 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/org/zzy/imagemonitor/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.zzy.imagemonitor; 2 | 3 | import android.net.Uri; 4 | import android.os.Bundle; 5 | import android.widget.ImageView; 6 | 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | import com.bumptech.glide.Glide; 11 | import com.facebook.drawee.view.SimpleDraweeView; 12 | import com.nostra13.universalimageloader.core.ImageLoader; 13 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; 14 | import com.squareup.picasso.Picasso; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | private ImageView mIVImageLoader; 19 | private ImageView mIVGlide; 20 | private SimpleDraweeView mIVFresco; 21 | private ImageView mIVPicasso; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | initView(); 28 | initData(); 29 | } 30 | 31 | private void initView(){ 32 | mIVImageLoader = findViewById(R.id.iv_imageloader); 33 | mIVGlide = findViewById(R.id.iv_glide); 34 | mIVFresco = (SimpleDraweeView)findViewById(R.id.iv_fresco); 35 | mIVPicasso = findViewById(R.id.iv_picasso); 36 | } 37 | 38 | 39 | private void initData(){ 40 | setImageLoader(); 41 | setGlide(); 42 | setFresco(); 43 | setPicasso(); 44 | } 45 | 46 | private void setImageLoader(){ 47 | ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(this)); 48 | //1.测试文件和内存大小未超过的图片,预期结果:不弹出提示 测试结果:通过 49 | // ImageLoader.getInstance().displayImage("https://c-ssl.duitang.com/uploads/item/202004/03/20200403204640_mNNyA.thumb.300_300_c.jpeg",mIVImageLoader); 50 | //2.测试内存大小超过的图片,测试文件大小超过的图片 预期结果:弹出提示(测试第一次加载和从缓存加载的一致性) 测试结果:通过 51 | // ImageLoader.getInstance().displayImage("http://desk.fd.zol-img.com.cn/t_s1680x1050/g5/M00/09/03/ChMkJ13I0eyIdyApAAMWAKdhvL4AAvKLwFDZMcAAxYY629.jpg",mIVImageLoader); 52 | //3.测试本地图片 53 | ImageLoader.getInstance().displayImage("drawable://"+R.drawable.ic_test_one,mIVImageLoader); 54 | } 55 | 56 | private void setGlide(){ 57 | //1.测试文件和内存大小未超过的图片,预期结果:不弹出提示 测试结果:通过 58 | // Glide.with(this).load("https://c-ssl.duitang.com/uploads/item/202004/03/20200403204618_d2Ucc.thumb.300_300_c.jpeg").into(mIVGlide); 59 | //2.测试内存大小超过的图片,测试文件大小超过的图片 预期结果:弹出提示(测试第一次加载和从缓存加载的一致性) 测试结果:通过 60 | // Glide.with(this).load("http://desk.fd.zol-img.com.cn/t_s1680x1050/g5/M00/09/03/ChMkJ13I0O-IUxnqAA6O1fF1dWwAAvKJgAYVvEADo7t268.jpg").into(mIVGlide); 61 | //3.测试本地图片 62 | Glide.with(this).load(R.drawable.ic_test_two).into(mIVGlide); 63 | // Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_test_three); 64 | // Glide.with(this).load(bitmap).into(mIVGlide); 65 | 66 | } 67 | 68 | private void setFresco(){ 69 | //1.测试文件和内存大小未超过的图片,预期结果:不弹出提示 测试结果:通过 70 | // Uri uri = Uri.parse("https://c-ssl.duitang.com/uploads/item/202004/03/20200403204602_5zHGC.thumb.300_300_c.jpeg"); 71 | //2.测试内存大小超过的图片,测试文件大小超过的图片 预期结果:弹出提示(测试第一次加载和从缓存加载的一致性) 测试结果:通过 72 | // Uri uri = Uri.parse("https://desk-fd.zol-img.com.cn/t_s960x600c5/g5/M00/09/03/ChMkJl3I0dmIAiyXABDF46MVj1IAAvKLgFJsZUAEMX7384.jpg"); 73 | //3.测试本地图片 74 | Uri uri = Uri.parse("res://"+"drawable/"+R.drawable.ic_test_three); 75 | mIVFresco.setImageURI(uri); 76 | } 77 | 78 | private void setPicasso(){ 79 | //1.测试文件和内存大小未超过的图片,预期结果:不弹出提示 测试结果:通过 80 | // Picasso.get().load("https://c-ssl.duitang.com/uploads/item/202004/03/20200403204520_H2cti.thumb.300_300_c.jpeg").into(mIVPicasso); 81 | //2.测试内存大小超过的图片,测试文件大小超过的图片 预期结果:弹出提示(测试第一次加载和从缓存加载的一致性) 测试结果:通过 82 | // Picasso.get().load("https://desk-fd.zol-img.com.cn/t_s960x600c5/g5/M00/0B/0D/ChMkJ1vfv8CIbx2-AAYDrq1u-J8AAs9xAHJAuMABgPG789.jpg").into(mIVPicasso); 83 | //3.测试本地图片 84 | Picasso.get().load(R.drawable.ic_test_four).into(mIVPicasso); 85 | 86 | 87 | } 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/org/zzy/imagemonitor/MyApplication.java: -------------------------------------------------------------------------------- 1 | package org.zzy.imagemonitor; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.drawee.backends.pipeline.Fresco; 6 | import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; 7 | import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator; 8 | import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; 9 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 10 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; 11 | import com.nostra13.universalimageloader.core.assist.QueueProcessingType; 12 | import com.nostra13.universalimageloader.core.decode.BaseImageDecoder; 13 | import com.nostra13.universalimageloader.core.download.BaseImageDownloader; 14 | import com.nostra13.universalimageloader.utils.StorageUtils; 15 | 16 | import org.zzy.lib.largeimage.LargeImage; 17 | 18 | import java.io.File; 19 | 20 | /** 21 | * ================================================ 22 | * 作 者:ZhouZhengyi 23 | * 创建日期:2020/4/3 11:16 24 | * 描 述: 25 | * 修订历史: 26 | * ================================================ 27 | */ 28 | public class MyApplication extends Application { 29 | 30 | @Override 31 | public void onCreate() { 32 | super.onCreate(); 33 | //初始化大图监控 34 | LargeImage.getInstance().install(this).setFileSizeThreshold(400.0).setMemorySizeThreshold(100); 35 | Fresco.initialize(this); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /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/ic_test_four.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/drawable-xxhdpi/ic_test_four.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_test_one.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/drawable-xxhdpi/ic_test_one.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_test_three.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/drawable-xxhdpi/ic_test_three.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_test_two.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/drawable-xxhdpi/ic_test_two.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 27 | 28 | 35 | 43 | 44 | 53 | 61 | 62 | 69 | 77 | 78 | -------------------------------------------------------------------------------- /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/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ImageMonitor 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: "config.gradle" 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } 8 | maven { url "http://maven.aliyun.com/nexus/content/repositories/jcente" } 9 | maven { url 'https://jitpack.io' } 10 | 11 | //https://jcenter.bintray.com/org/zzy/largeimage/large-image-monitor-plugin/1.0.0/ 12 | // maven { 13 | // credentials { 14 | // username "${mavenUser}" 15 | // password "${mavenPassword}" 16 | // } 17 | // url 'http://127.0.0.1:8081/repository/maven-snapshots/'} 18 | } 19 | dependencies { 20 | classpath 'com.android.tools.build:gradle:3.6.1' 21 | // classpath 'org.zzy.lib:largeimage:1.0.0-SNAPSHOT' 22 | classpath 'com.novoda:bintray-release:0.9.2' 23 | classpath 'org.zzy.largeimage:large-image-monitor-plugin:1.0.0' 24 | // NOTE: Do not place your application dependencies here; they belong 25 | // in the individual module build.gradle files 26 | } 27 | } 28 | 29 | allprojects { 30 | tasks.withType(Javadoc).all { enabled = false } 31 | repositories { 32 | jcenter() 33 | google() 34 | maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } 35 | maven { url "http://maven.aliyun.com/nexus/content/repositories/jcente" } 36 | maven { url 'https://jitpack.io' } 37 | 38 | 39 | 40 | // maven { 41 | // credentials { 42 | // username "${mavenUser}" 43 | // password "${mavenPassword}" 44 | // } 45 | // url 'http://127.0.0.1:8081/repository/maven-snapshots/'} 46 | } 47 | } 48 | 49 | task clean(type: Delete) { 50 | delete rootProject.buildDir 51 | } 52 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | android = [ 3 | applicationId : "org.zzy.imagemonitor", 4 | compileSdkVersion : 29, 5 | minSdkVersion : 19, 6 | targetSdkVersion : 29, 7 | versionCode : 1, 8 | versionName : "1.0.0", 9 | //插件版本号 10 | pluginVersionName : "1.0.0", 11 | //jcenter 12 | jcenterArchivesVersionName: "1.0.0", 13 | ] 14 | dependencies = [ 15 | "okhttp" : "com.squareup.okhttp3:okhttp:3.12.1", 16 | //将urlconnection代理到okhttp 17 | //"okhttp_urlconnection" : "com.squareup.okhttp3:okhttp-urlconnection:3.12.1", 18 | //"okio" : "com.squareup.okio:okio:1.15.0", 19 | "glide" : 'com.github.bumptech.glide:glide:4.11.0', 20 | "picasso" : 'com.squareup.picasso:picasso:2.71828', 21 | "fresco" : 'com.facebook.fresco:fresco:2.1.0', 22 | "image-loader" : 'com.nostra13.universalimageloader:universal-image-loader:1.9.5', 23 | "MMKV" : 'com.tencent:mmkv-static:1.1.0', 24 | "FloatWindow" : 'com.github.yhaolpz:FloatWindow:1.0.9', 25 | "androidx" : 'androidx.appcompat:appcompat:1.1.0', 26 | "recylcerview" : 'androidx.recyclerview:recyclerview:1.0.0', 27 | "image-monitor" : 'org.zzy.lib.largeimage:large-image-monitor:1.0.0', 28 | ] 29 | } 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=-Xmx1536m 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 | 22 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Mar 30 18:53:03 CST 2020 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-5.6.4-all.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 | -------------------------------------------------------------------------------- /largeimage-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | //apply plugin: 'maven' 3 | apply from: 'upload.gradle' 4 | 5 | //上传到本地maven库 6 | //uploadArchives { 7 | // repositories { 8 | // mavenDeployer { 9 | // pom.groupId = "org.zzy.lib" 10 | // pom.artifactId = "largeimage" 11 | // pom.version = "1.0.0-SNAPSHOT" 12 | // //要上传的maven仓库地址 13 | // repository(url:"http://127.0.0.1:8081/repository/maven-snapshots/") { 14 | // authentication(userName: "admin", password: "admin123") 15 | // } 16 | // } 17 | // } 18 | //} 19 | dependencies { 20 | implementation gradleApi() 21 | implementation localGroovy() 22 | implementation 'com.android.tools.build:gradle:3.0.1'//版本不能太高,防止跟项目的起冲突 23 | implementation 'com.android.tools.build:gradle-api:3.0.0' 24 | implementation('com.quinn.hunter:hunter-transform:0.9.2') { //使用hunter框架 25 | exclude group: 'com.android.tools.build' //排除hunter带来的gradle,使用自定义gradle版本 26 | } 27 | //这里不引入asm的相关包是因为hunter中已经引入,如果想使用新版本则可以引入,但是要排除掉hunter使用的版本 28 | } 29 | 30 | repositories { 31 | google() 32 | jcenter() 33 | mavenCentral() 34 | } 35 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/Config.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage; 2 | 3 | /** 4 | * ================================================ 5 | * 作 者:ZhouZhengyi 6 | * 创建日期:2020/4/5 21:50 7 | * 描 述: 8 | * 修订历史: 9 | * ================================================ 10 | */ 11 | public class Config { 12 | public static String TAG = "LargeImageMonitor"; 13 | /** 14 | * 大图检测插件的开关 15 | */ 16 | private boolean largeImagePluginSwitch = true; 17 | 18 | private Config(){} 19 | 20 | private static class Holder{ 21 | private static Config INSTANCE = new Config(); 22 | } 23 | 24 | public static Config getInstance(){ 25 | return Holder.INSTANCE; 26 | } 27 | 28 | public boolean largeImagePluginSwitch() { 29 | return largeImagePluginSwitch; 30 | } 31 | 32 | public void init(LargeImageExtension extension){ 33 | if(null != extension){ 34 | this.largeImagePluginSwitch = extension.largeImagePluginSwitch; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/LargeImageExtension.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage; 2 | 3 | /** 4 | * ================================================ 5 | * 作 者:ZhouZhengyi 6 | * 创建日期:2020/4/5 21:24 7 | * 描 述:JAVABean类,对应gradle中配置的参数 8 | * 属性必须为public类型,否则无法赋值 9 | * 修订历史: 10 | * ================================================ 11 | */ 12 | public class LargeImageExtension { 13 | /** 14 | * 大图检测插件的开关 15 | */ 16 | public boolean largeImagePluginSwitch = true; 17 | } 18 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/LargeImageMonitorPlugin.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage; 2 | 3 | import com.android.build.gradle.AppExtension; 4 | 5 | import org.gradle.api.Action; 6 | import org.gradle.api.Plugin; 7 | import org.gradle.api.Project; 8 | import org.zzy.largeimage.transform.LargeImageTransform; 9 | import org.zzy.largeimage.transform.OkHttpTransform; 10 | import org.zzy.largeimage.transform.UrlConnectionTransform; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | /** 16 | * ================================================ 17 | * 作 者:ZhouZhengyi 18 | * 创建日期:2020/3/31 20:49 19 | * 描 述:调试前执行gradle assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true命令,然后按debug按钮,接着执行task debug便会生效 20 | * 修订历史:2020/4/13 v1.0.0 21 | * github:https://github.com/121880399/LargeImageMonitor 22 | * ================================================ 23 | */ 24 | public class LargeImageMonitorPlugin implements Plugin { 25 | 26 | @Override 27 | public void apply(Project project) { 28 | List taskNames = project.getGradle().getStartParameter().getTaskNames(); 29 | //如果是Release版本,则不进行字节码替换 30 | for(String taskName : taskNames){ 31 | if(taskName.contains("Release")){ 32 | return; 33 | } 34 | } 35 | 36 | AppExtension appExtension = (AppExtension)project.getProperties().get("android"); 37 | //创建自定义扩展 38 | project.getExtensions().create("largeImageMonitor",LargeImageExtension.class); 39 | project.afterEvaluate(new Action() { 40 | @Override 41 | public void execute(Project project) { 42 | LargeImageExtension extension = project.getExtensions().getByType(LargeImageExtension.class); 43 | Config.getInstance().init(extension); 44 | } 45 | }); 46 | //将自定义Transform添加到编译流程中 47 | appExtension.registerTransform(new LargeImageTransform(project), Collections.EMPTY_LIST); 48 | //添加OkHttp 49 | appExtension.registerTransform(new OkHttpTransform(project),Collections.EMPTY_LIST); 50 | //添加UrlConnection 51 | appExtension.registerTransform(new UrlConnectionTransform(project),Collections.EMPTY_LIST); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/adapter/LargeImageClassAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.adapter; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.zzy.largeimage.Config; 8 | import org.zzy.largeimage.method.FrescoMethodAdapter; 9 | import org.zzy.largeimage.method.GlideMethodAdapter; 10 | import org.zzy.largeimage.method.ImageLoaderMethodAdapter; 11 | import org.zzy.largeimage.method.PicassoMethodAdapter; 12 | 13 | 14 | /** 15 | * ================================================ 16 | * 作 者:ZhouZhengyi 17 | * 创建日期:2020/3/31 21:27 18 | * 描 述:对Glide,Picasso,Fresco,ImageLoader框架中 19 | * 的方法进行字节码替换 20 | * 修订历史: 21 | * ================================================ 22 | */ 23 | public class LargeImageClassAdapter extends ClassVisitor { 24 | private static final String IMAGELOADER_METHOD_NAME_DESC = "(Ljava/lang/String;Lcom/nostra13/universalimageloader/core/imageaware/ImageAware;Lcom/nostra13/universalimageloader/core/DisplayImageOptions;Lcom/nostra13/universalimageloader/core/assist/ImageSize;Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;Lcom/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener;)V"; 25 | /** 26 | * 当前类名 27 | */ 28 | private String className; 29 | 30 | public LargeImageClassAdapter(ClassVisitor classWriter) { 31 | super(Opcodes.ASM5, classWriter); 32 | } 33 | 34 | @Override 35 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 36 | super.visit(version, access, name, signature, superName, interfaces); 37 | this.className = name; 38 | } 39 | 40 | @Override 41 | public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) { 42 | MethodVisitor mv = cv.visitMethod(access, methodName, desc, signature, exceptions); 43 | //如果插件开关关闭,则不插入字节码 44 | if(!Config.getInstance().largeImagePluginSwitch()) { 45 | return mv; 46 | } 47 | 48 | // TODO: 2020/4/2 这里考虑做版本兼容 49 | //对Glide4.11版本的SingleRequest类的构造方法进行字节码修改 50 | if(className.equals("com/bumptech/glide/request/SingleRequest") && methodName.equals("") && desc!=null){ 51 | return mv == null ? null : new GlideMethodAdapter(mv,access,methodName,desc); 52 | } 53 | 54 | //对picasso的Request类的构造方法进行字节码修改 55 | if(className.equals("com/squareup/picasso/Request") && methodName.equals("") && desc!=null){ 56 | return mv == null ? null : new PicassoMethodAdapter(mv,access,methodName,desc); 57 | } 58 | 59 | //对Fresco的ImageRequest类的构造方法进行字节码修改 60 | if(className.equals("com/facebook/imagepipeline/request/ImageRequest") && methodName.equals("") && desc!=null){ 61 | return mv == null ? null : new FrescoMethodAdapter(mv,access,methodName,desc); 62 | } 63 | 64 | //对ImageLoader的ImageLoader类的displayImage方法进行字节码修改 65 | if(className.equals("com/nostra13/universalimageloader/core/ImageLoader") && methodName.equals("displayImage") && desc.equals(IMAGELOADER_METHOD_NAME_DESC)){ 66 | log(className,access,methodName,desc,signature); 67 | return mv == null ? null : new ImageLoaderMethodAdapter(mv,access,methodName,desc); 68 | } 69 | return mv; 70 | } 71 | 72 | private void log(String className, int access, String name, String desc, String signature) { 73 | System.out.println("LargeImageClassAdapter===matched====>" + " className===" + className + " access===" + access + " methodName===" + name + " desc===" + desc + " signature===" + signature); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/adapter/OkHttpClassAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.adapter; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.zzy.largeimage.Config; 7 | import org.zzy.largeimage.method.OkHttpMethodAdapter; 8 | 9 | /** 10 | * ================================================ 11 | * 作 者:ZhouZhengyi 12 | * 创建日期:2020/4/5 7:31 13 | * 描 述: 14 | * 修订历史: 15 | * ================================================ 16 | */ 17 | public class OkHttpClassAdapter extends ClassVisitor { 18 | 19 | private String className; 20 | 21 | public OkHttpClassAdapter(ClassVisitor cv) { 22 | super(Opcodes.ASM5, cv); 23 | } 24 | 25 | @Override 26 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 27 | super.visit(version, access, name, signature, superName, interfaces); 28 | this.className = name; 29 | } 30 | 31 | @Override 32 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 33 | MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions); 34 | //如果插件开关关闭,则不插入字节码 35 | if(!Config.getInstance().largeImagePluginSwitch()) { 36 | return methodVisitor; 37 | } 38 | if(className.equals("okhttp3/OkHttpClient$Builder") && name.equals("") && desc.equals("()V")){ 39 | return methodVisitor == null ? null : new OkHttpMethodAdapter(methodVisitor,access,name,desc); 40 | } 41 | return methodVisitor; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/adapter/UrlConnectionClassAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.adapter; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.zzy.largeimage.Config; 7 | import org.zzy.largeimage.method.UrlConnectionMethodAdapter; 8 | 9 | /** 10 | * ================================================ 11 | * 作 者:ZhouZhengyi 12 | * 创建日期:2020/4/5 16:20 13 | * 描 述:Hook UrlConnection的目的是为了将 14 | * UrlConnection代理到OKhttp上面,这样通过网络下载图片就可以 15 | * 通过okhttp统一的处理。 16 | * 修订历史: 17 | * ================================================ 18 | */ 19 | public class UrlConnectionClassAdapter extends ClassVisitor { 20 | 21 | /** 22 | * 类名 23 | */ 24 | private String className; 25 | 26 | public UrlConnectionClassAdapter(ClassVisitor cv) { 27 | super(Opcodes.ASM5, cv); 28 | } 29 | 30 | @Override 31 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 32 | super.visit(version, access, name, signature, superName, interfaces); 33 | this.className = name; 34 | } 35 | 36 | /** 37 | * 这个方法跟其他几个methodAdapter不一样 38 | * 其他的methodAdapter是根据类名和方法名来进行hook 39 | * 也就是说当访问到某个类的某个方法时进行 40 | * 而这个方法是,所有的类和方法都有可能存在hook, 41 | * 所以这里不做类和方法的判断 42 | * 作者: ZhouZhengyi 43 | * 创建时间: 2020/4/5 17:25 44 | */ 45 | @Override 46 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 47 | MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions); 48 | //如果插件开关关闭,则不插入字节码 49 | if (!Config.getInstance().largeImagePluginSwitch()) { 50 | return methodVisitor; 51 | } 52 | return methodVisitor == null ? null : new UrlConnectionMethodAdapter(className, methodVisitor, access, name, desc); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/method/FrescoMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.method; 2 | 3 | import org.objectweb.asm.MethodVisitor; 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.commons.AdviceAdapter; 6 | 7 | /** 8 | * ================================================ 9 | * 作 者:ZhouZhengyi 10 | * 创建日期:2020/4/2 22:00 11 | * 描 述:对ImageRequest类的构造方法进行字节码修改 12 | * ImageRequest构造方法中会对mPostprocessor字段赋值, 13 | * 改字段是一个后处理器,我们通过hook改字段,可以得到 14 | * bitmap对象。 15 | * 修订历史: 16 | * ================================================ 17 | */ 18 | public class FrescoMethodAdapter extends AdviceAdapter { 19 | 20 | /** 21 | * Creates a new {@link AdviceAdapter}. 22 | * 23 | * 24 | * @param mv the method visitor to which this adapter delegates calls. 25 | * @param access the method's access flags 26 | * @param name the method's name. 27 | * @param desc the method's descriptor 28 | */ 29 | public FrescoMethodAdapter( MethodVisitor mv, int access, String name, String desc) { 30 | super(Opcodes.ASM5, mv, access, name, desc); 31 | } 32 | 33 | /** 34 | * 方法进入时 35 | * 1.调用ImageRequestBuilder的getSourceUri() 36 | * 2.调用getPostprocessor() 37 | * 3.设置进FrescoHook的process方法 38 | * 4.将返回的Postprocessor再设置进ImageRequestBuilder 39 | * builder.setPostprocessor(FrescoHook.process(builder.getSourceUri(),builder.getPostprocessor(),getResizeOptions())); 40 | * 作者: ZhouZhengyi 41 | * 创建时间: 2020/4/2 23:53 42 | */ 43 | @Override 44 | protected void onMethodEnter() { 45 | super.onMethodEnter(); 46 | mv.visitVarInsn(ALOAD, 1); 47 | mv.visitVarInsn(ALOAD, 1); 48 | mv.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "getSourceUri", "()Landroid/net/Uri;", false); 49 | mv.visitVarInsn(ALOAD, 1); 50 | mv.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "getPostprocessor", "()Lcom/facebook/imagepipeline/request/Postprocessor;", false); 51 | mv.visitVarInsn(ALOAD, 1); 52 | mv.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "getResizeOptions", "()Lcom/facebook/imagepipeline/common/ResizeOptions;", false); 53 | mv.visitMethodInsn(INVOKESTATIC, "org/zzy/lib/largeimage/aop/fresco/FrescoHook", "process", "(Landroid/net/Uri;Lcom/facebook/imagepipeline/request/Postprocessor;Lcom/facebook/imagepipeline/common/ResizeOptions;)Lcom/facebook/imagepipeline/request/Postprocessor;", false); 54 | mv.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/imagepipeline/request/ImageRequestBuilder", "setPostprocessor", "(Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/ImageRequestBuilder;", false); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/method/GlideMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.method; 2 | 3 | import org.objectweb.asm.MethodVisitor; 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.commons.AdviceAdapter; 6 | 7 | /** 8 | * ================================================ 9 | * 作 者:ZhouZhengyi 10 | * 创建日期:2020/4/1 15:43 11 | * 描 述:对SingleRequest类中的构造方法进行字节码修改 12 | * 构造方法会对SingleRequest类进行初始化,其中有一个 13 | * requestListeners参数,它是一个list类型, 14 | * 在图片准备完毕时会对该list进行遍历回调,我们只需要在 15 | * 该list中添加我们自定义的listener,遍历时就会回调我们 16 | * 的方法,从而拿到图片数据。 17 | * 修订历史: 18 | * ================================================ 19 | */ 20 | public class GlideMethodAdapter extends AdviceAdapter { 21 | 22 | /** 23 | * Creates a new {@link AdviceAdapter}. 24 | * 25 | * @param mv the method visitor to which this adapter delegates calls. 26 | * @param access the method's access flags (see {@link Opcodes}). 27 | * @param name the method's name. 28 | * @param desc the method's descriptor ). 29 | */ 30 | public GlideMethodAdapter( MethodVisitor mv, int access, String name, String desc) { 31 | super(Opcodes.ASM5, mv, access, name, desc); 32 | } 33 | 34 | /** 35 | * 方法退出时 36 | * 1.先拿到requestListeners 37 | * 2.然后对其进行修改 38 | * 3.将修改后的requestListeners设置回去 39 | * requestListeners=GlideHook.process(requestListeners); 40 | * 作者: ZhouZhengyi 41 | * 创建时间: 2020/4/1 15:51 42 | */ 43 | @Override 44 | protected void onMethodExit(int opcode) { 45 | super.onMethodExit(opcode); 46 | mv.visitVarInsn(ALOAD, 0); 47 | mv.visitFieldInsn(GETFIELD, "com/bumptech/glide/request/SingleRequest", "requestListeners", "Ljava/util/List;"); 48 | mv.visitMethodInsn(INVOKESTATIC, "org/zzy/lib/largeimage/aop/glide/GlideHook", "process", "(Ljava/util/List;)Ljava/util/List;", false); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/method/ImageLoaderMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.method; 2 | 3 | import org.objectweb.asm.MethodVisitor; 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.commons.AdviceAdapter; 6 | 7 | /** 8 | * ================================================ 9 | * 作 者:ZhouZhengyi 10 | * 创建日期:2020/4/3 11:49 11 | * 描 述: 12 | * 修订历史: 13 | * ================================================ 14 | */ 15 | public class ImageLoaderMethodAdapter extends AdviceAdapter { 16 | 17 | /** 18 | * Creates a new {@link AdviceAdapter}. 19 | * @param mv the method visitor to which this adapter delegates calls. 20 | * @param access the method's access flags 21 | * @param name the method's name. 22 | * @param desc the method's descriptor 23 | */ 24 | public ImageLoaderMethodAdapter(MethodVisitor mv, int access, String name, String desc) { 25 | super(Opcodes.ASM5, mv, access, name, desc); 26 | } 27 | 28 | @Override 29 | protected void onMethodEnter() { 30 | super.onMethodEnter(); 31 | mv.visitVarInsn(ALOAD, 5); 32 | mv.visitMethodInsn(INVOKESTATIC, "org/zzy/lib/largeimage/aop/imageloader/ImageLoaderHook", "process", "(Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;)Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;", false); 33 | mv.visitVarInsn(ASTORE, 5); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/method/OkHttpMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.method; 2 | 3 | import com.android.ddmlib.Log; 4 | 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.commons.AdviceAdapter; 8 | import org.zzy.largeimage.Config; 9 | 10 | /** 11 | * ================================================ 12 | * 作 者:ZhouZhengyi 13 | * 创建日期:2020/4/5 7:47 14 | * 描 述: 15 | * 修订历史: 16 | * ================================================ 17 | */ 18 | public class OkHttpMethodAdapter extends AdviceAdapter { 19 | 20 | /** 21 | * Creates a new {@link AdviceAdapter}. 22 | * 23 | * @param mv the method visitor to which this adapter delegates calls. 24 | * @param access the method's access flags (see {@link Opcodes}). 25 | * @param name the method's name. 26 | * @param desc the method's descriptor 27 | */ 28 | public OkHttpMethodAdapter(MethodVisitor mv, int access, String name, String desc) { 29 | super(Opcodes.ASM5, mv, access, name, desc); 30 | } 31 | 32 | 33 | /** 34 | * 方法退出时插入 35 | * 这里不知道为什么在onMethodEnter方法插入会报空指针 36 | * interceptors.addAll(LargeImage.getInstance().getOkHttpInterceptors()); 37 | * networkInterceptors.addAll(LargeImage.getInstance().getOkHttpNetworkInterceptors()); 38 | * 作者: ZhouZhengyi 39 | * 创建时间: 2020/4/5 9:39 40 | */ 41 | @Override 42 | protected void onMethodExit(int opcode) { 43 | super.onMethodExit(opcode); 44 | //添加应用拦截器 45 | mv.visitVarInsn(ALOAD, 0); 46 | mv.visitFieldInsn(GETFIELD, "okhttp3/OkHttpClient$Builder", "interceptors", "Ljava/util/List;"); 47 | mv.visitMethodInsn(INVOKESTATIC, "org/zzy/lib/largeimage/LargeImage", "getInstance", "()Lorg/zzy/lib/largeimage/LargeImage;", false); 48 | mv.visitMethodInsn(INVOKEVIRTUAL, "org/zzy/lib/largeimage/LargeImage", "getOkHttpInterceptors", "()Ljava/util/List;", false); 49 | mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "addAll", "(Ljava/util/Collection;)Z", true); 50 | mv.visitInsn(POP); 51 | //添加网络拦截器 52 | mv.visitVarInsn(ALOAD, 0); 53 | mv.visitFieldInsn(GETFIELD, "okhttp3/OkHttpClient$Builder", "networkInterceptors", "Ljava/util/List;"); 54 | mv.visitMethodInsn(INVOKESTATIC, "org/zzy/lib/largeimage/LargeImage", "getInstance", "()Lorg/zzy/lib/largeimage/LargeImage;", false); 55 | mv.visitMethodInsn(INVOKEVIRTUAL, "org/zzy/lib/largeimage/LargeImage", "getOkHttpNetworkInterceptors", "()Ljava/util/List;", false); 56 | mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "addAll", "(Ljava/util/Collection;)Z", true); 57 | mv.visitInsn(POP); 58 | //添加DNS 59 | mv.visitVarInsn(ALOAD, 0); 60 | mv.visitMethodInsn(INVOKESTATIC, "org/zzy/lib/largeimage/LargeImage", "getInstance", "()Lorg/zzy/lib/largeimage/LargeImage;", false); 61 | mv.visitMethodInsn(INVOKEVIRTUAL, "org/zzy/lib/largeimage/LargeImage", "getDns", "()Lokhttp3/Dns;", false); 62 | mv.visitFieldInsn(PUTFIELD, "okhttp3/OkHttpClient$Builder", "dns", "Lokhttp3/Dns;"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/method/PicassoMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.method; 2 | 3 | import org.objectweb.asm.MethodVisitor; 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.commons.AdviceAdapter; 6 | 7 | /** 8 | * ================================================ 9 | * 作 者:ZhouZhengyi 10 | * 创建日期:2020/4/2 10:38 11 | * 描 述:对Request类的构造方法字节码进行修改 12 | * Request类构造方法中有一个List transformations 13 | * 参数,该参数会在piaccso得到bitmap以后进行遍历,对bitmap进行 14 | * 一些变换操作,我们可以插入自己的Transformation然后在里面 15 | * 得到我们想要的数据。 16 | * 修订历史: 17 | * ================================================ 18 | */ 19 | public class PicassoMethodAdapter extends AdviceAdapter { 20 | 21 | /** 22 | * Creates a new {@link AdviceAdapter}. 23 | * 24 | * @param mv the method visitor to which this adapter delegates calls. 25 | * @param access the method's access flags . 26 | * @param name the method's name. 27 | * @param desc the method's descriptor. 28 | */ 29 | public PicassoMethodAdapter(MethodVisitor mv, int access, String name, String desc) { 30 | super(Opcodes.ASM5, mv, access, name, desc); 31 | } 32 | 33 | /** 34 | * 方法进入时 35 | * 1.拿到方法第一个参数Uri 36 | * 2.拿到方法第四个参数 List transformations 37 | * 3.把它们传入hook方法 38 | * 4.在方法中加入我们自己的Transformation 39 | * 5.将设置好以后的 List transformations返回给第四个参数 40 | * transformations = PicassoHook.process(uri,transformations,resourceId,targetWidth,targetHeight); 41 | * 作者: ZhouZhengyi 42 | * 创建时间: 2020/4/2 11:26 43 | */ 44 | @Override 45 | protected void onMethodEnter() { 46 | super.onMethodEnter(); 47 | mv.visitVarInsn(ALOAD, 1); 48 | mv.visitVarInsn(ALOAD, 4); 49 | mv.visitVarInsn(ILOAD, 2); 50 | mv.visitVarInsn(ILOAD, 5); 51 | mv.visitVarInsn(ILOAD, 6); 52 | mv.visitMethodInsn(INVOKESTATIC, "org/zzy/lib/largeimage/aop/picasso/PicassoHook", "process", "(Landroid/net/Uri;Ljava/util/List;III)Ljava/util/List;", false); 53 | mv.visitVarInsn(ASTORE, 4); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/method/UrlConnectionMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.method; 2 | 3 | import org.objectweb.asm.MethodVisitor; 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.commons.AdviceAdapter; 6 | 7 | /** 8 | * ================================================ 9 | * 作 者:ZhouZhengyi 10 | * 创建日期:2020/4/5 16:50 11 | * 描 述: 12 | * 修订历史: 13 | * ================================================ 14 | */ 15 | public class UrlConnectionMethodAdapter extends AdviceAdapter { 16 | 17 | private String className; 18 | 19 | /** 20 | * Creates a new {@link AdviceAdapter}. 21 | * 22 | * @param mv the method visitor to which this adapter delegates calls. 23 | * @param access the method's access flags). 24 | * @param name the method's name. 25 | * @param desc the method's descriptor 26 | */ 27 | public UrlConnectionMethodAdapter(String className, MethodVisitor mv, int access, String name, String desc) { 28 | super(Opcodes.ASM5, mv, access, name, desc); 29 | this.className = className; 30 | } 31 | 32 | /** 33 | * 这里复写的方法与其他的methodAdapter也不同 34 | * 其他的methodAdapter是在方法进入或者退出时操作 35 | * 而这个methodAdapter是根据指令比较的 36 | * 这个方法的意思是当方法被访问时调用 37 | * @param opcode 指令 38 | * @param owner 操作的类 39 | * @param name 方法名称 40 | * @param desc 方法描述 (参数)返回值类型 41 | * 作者: ZhouZhengyi 42 | * 创建时间: 2020/4/5 17:29 43 | */ 44 | @Override 45 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 46 | //所有的类和方法,只要存在调用openConnection方法的指令,就进行hook 47 | if(opcode == Opcodes.INVOKEVIRTUAL && owner.equals("java/net/URL") 48 | && name.equals("openConnection")&& desc.equals("()Ljava/net/URLConnection;")){ 49 | mv.visitMethodInsn(INVOKEVIRTUAL,"java/net/URL", "openConnection", "()Ljava/net/URLConnection;", false); 50 | super.visitMethodInsn(INVOKESTATIC,"org/zzy/lib/largeimage/aop/urlconnection/UrlConnectionHook","process","(Ljava/net/URLConnection;)Ljava/net/URLConnection;",false); 51 | }else if(opcode == Opcodes.INVOKEVIRTUAL && owner.equals("java/net/URL") 52 | && name.equals("openConnection")&& desc.equals("(Ljava/net/Proxy;)Ljava/net/URLConnection;")){ 53 | //public URLConnection openConnection(Proxy proxy) 54 | mv.visitMethodInsn(INVOKEVIRTUAL,"java/net/URL", "openConnection", "(Ljava/net/Proxy;)Ljava/net/URLConnection;", false); 55 | super.visitMethodInsn(INVOKESTATIC,"org/zzy/lib/largeimage/aop/urlconnection/UrlConnectionHook","process","(Ljava/net/URLConnection;)Ljava/net/URLConnection;",false); 56 | }else{ 57 | super.visitMethodInsn(opcode, owner, name, desc, itf); 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/transform/LargeImageTransform.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.transform; 2 | 3 | import com.android.build.api.transform.Context; 4 | import com.android.build.api.transform.TransformException; 5 | import com.android.build.api.transform.TransformInput; 6 | import com.android.build.api.transform.TransformInvocation; 7 | import com.android.build.api.transform.TransformOutputProvider; 8 | import com.quinn.hunter.transform.HunterTransform; 9 | import com.quinn.hunter.transform.RunVariant; 10 | 11 | import org.gradle.api.Project; 12 | import org.zzy.largeimage.weaver.LargeImageWeaver; 13 | 14 | import java.io.IOException; 15 | import java.util.Collection; 16 | 17 | /** 18 | * ================================================ 19 | * 作 者:ZhouZhengyi 20 | * 创建日期:2020/3/31 21:05 21 | * 描 述:使用Hunter框架,不需用传统方式写Transform 22 | * 修订历史: 23 | * ================================================ 24 | */ 25 | public class LargeImageTransform extends HunterTransform { 26 | 27 | public LargeImageTransform(Project project) { 28 | super(project); 29 | this.bytecodeWeaver = new LargeImageWeaver(); 30 | } 31 | 32 | @Override 33 | public void transform(Context context, Collection inputs, Collection referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException { 34 | super.transform(context, inputs, referencedInputs, outputProvider, isIncremental); 35 | } 36 | 37 | @Override 38 | protected RunVariant getRunVariant() { 39 | return super.getRunVariant(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/transform/OkHttpTransform.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.transform; 2 | 3 | import com.android.build.api.transform.Context; 4 | import com.android.build.api.transform.TransformException; 5 | import com.android.build.api.transform.TransformInput; 6 | import com.android.build.api.transform.TransformOutputProvider; 7 | import com.quinn.hunter.transform.HunterTransform; 8 | import com.quinn.hunter.transform.RunVariant; 9 | 10 | import org.gradle.api.Project; 11 | import org.zzy.largeimage.weaver.OkHttpWeaver; 12 | 13 | import java.io.IOException; 14 | import java.util.Collection; 15 | 16 | /** 17 | * ================================================ 18 | * 作 者:ZhouZhengyi 19 | * 创建日期:2020/4/5 7:28 20 | * 描 述: 21 | * 修订历史: 22 | * ================================================ 23 | */ 24 | public class OkHttpTransform extends HunterTransform { 25 | 26 | public OkHttpTransform(Project project) { 27 | super(project); 28 | this.bytecodeWeaver = new OkHttpWeaver(); 29 | } 30 | 31 | @Override 32 | public void transform(Context context, Collection inputs, Collection referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException { 33 | super.transform(context, inputs, referencedInputs, outputProvider, isIncremental); 34 | } 35 | 36 | @Override 37 | protected RunVariant getRunVariant() { 38 | return super.getRunVariant(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/transform/UrlConnectionTransform.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.transform; 2 | 3 | import com.quinn.hunter.transform.HunterTransform; 4 | 5 | import org.gradle.api.Project; 6 | import org.zzy.largeimage.weaver.UrlConnectionWeaver; 7 | 8 | /** 9 | * ================================================ 10 | * 作 者:ZhouZhengyi 11 | * 创建日期:2020/4/5 16:22 12 | * 描 述: 13 | * 修订历史: 14 | * ================================================ 15 | */ 16 | public class UrlConnectionTransform extends HunterTransform { 17 | 18 | public UrlConnectionTransform(Project project) { 19 | super(project); 20 | this.bytecodeWeaver = new UrlConnectionWeaver(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/weaver/LargeImageWeaver.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.weaver; 2 | 3 | import com.quinn.hunter.transform.asm.BaseWeaver; 4 | 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | import org.zzy.largeimage.adapter.LargeImageClassAdapter; 8 | 9 | /** 10 | * ================================================ 11 | * 作 者:ZhouZhengyi 12 | * 创建日期:2020/3/31 21:24 13 | * 描 述: 14 | * 修订历史: 15 | * ================================================ 16 | */ 17 | public class LargeImageWeaver extends BaseWeaver { 18 | 19 | @Override 20 | protected ClassVisitor wrapClassWriter(ClassWriter classWriter) { 21 | return new LargeImageClassAdapter(classWriter); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/weaver/OkHttpWeaver.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.weaver; 2 | 3 | import com.quinn.hunter.transform.asm.BaseWeaver; 4 | 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | import org.zzy.largeimage.adapter.OkHttpClassAdapter; 8 | 9 | /** 10 | * ================================================ 11 | * 作 者:ZhouZhengyi 12 | * 创建日期:2020/4/5 7:29 13 | * 描 述: 14 | * 修订历史: 15 | * ================================================ 16 | */ 17 | public class OkHttpWeaver extends BaseWeaver { 18 | 19 | @Override 20 | protected ClassVisitor wrapClassWriter(ClassWriter classWriter) { 21 | return new OkHttpClassAdapter(classWriter); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/groovy/org/zzy/largeimage/weaver/UrlConnectionWeaver.java: -------------------------------------------------------------------------------- 1 | package org.zzy.largeimage.weaver; 2 | 3 | import com.quinn.hunter.transform.asm.BaseWeaver; 4 | 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | import org.zzy.largeimage.adapter.UrlConnectionClassAdapter; 8 | 9 | /** 10 | * ================================================ 11 | * 作 者:ZhouZhengyi 12 | * 创建日期:2020/4/5 16:24 13 | * 描 述: 14 | * 修订历史: 15 | * ================================================ 16 | */ 17 | public class UrlConnectionWeaver extends BaseWeaver { 18 | 19 | @Override 20 | protected ClassVisitor wrapClassWriter(ClassWriter classWriter) { 21 | return new UrlConnectionClassAdapter(classWriter); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /largeimage-plugin/src/main/resources/META-INF/gradle-plugins/largeimage-monitor.properties: -------------------------------------------------------------------------------- 1 | implementation-class=org.zzy.largeimage.LargeImageMonitorPlugin -------------------------------------------------------------------------------- /largeimage-plugin/upload.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.novoda.bintray-release' 2 | //gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=false 3 | publish { 4 | def groupProjectID = 'org.zzy.largeimage' 5 | def artifactProjectID = 'large-image-monitor-plugin' 6 | def publishVersionID = "${rootProject.ext.android["pluginVersionName"]}" 7 | 8 | userOrg = 'zzy' 9 | repoName = "imageMonitor" 10 | groupId = groupProjectID 11 | artifactId = artifactProjectID 12 | publishVersion = publishVersionID 13 | desc = 'Oh hi, this is a library which can be monitor large image.' 14 | website = 'https://github.com/121880399/LargeImageMonitor' 15 | } 16 | 17 | -------------------------------------------------------------------------------- /largeimage/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /largeimage/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: 'upload.gradle' 3 | 4 | android { 5 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 6 | 7 | defaultConfig { 8 | minSdkVersion rootProject.ext.android["minSdkVersion"] 9 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 10 | versionCode rootProject.ext.android["versionCode"] 11 | versionName rootProject.ext.android["versionName"] 12 | 13 | } 14 | lintOptions { 15 | abortOnError false 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(include: ['*.jar'], dir: 'libs') 29 | implementation rootProject.ext.dependencies["okhttp"] 30 | implementation rootProject.ext.dependencies["glide"] 31 | implementation rootProject.ext.dependencies["picasso"] 32 | implementation rootProject.ext.dependencies["fresco"] 33 | implementation rootProject.ext.dependencies["image-loader"] 34 | implementation rootProject.ext.dependencies["MMKV"] 35 | implementation (rootProject.ext.dependencies["FloatWindow"]){ 36 | exclude module: 'support-v4' 37 | exclude module: 'support-compat' 38 | } 39 | implementation rootProject.ext.dependencies["androidx"] 40 | implementation (rootProject.ext.dependencies["recylcerview"]){ 41 | exclude module: 'support-compat' 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /largeimage/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/consumer-rules.pro -------------------------------------------------------------------------------- /largeimage/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 22 | -------------------------------------------------------------------------------- /largeimage/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /largeimage/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/LargeImage.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage; 2 | 3 | import android.app.Application; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.view.animation.BounceInterpolator; 8 | import android.widget.ImageView; 9 | 10 | import androidx.core.util.Preconditions; 11 | 12 | import com.tencent.mmkv.MMKV; 13 | import com.yhao.floatwindow.FloatWindow; 14 | import com.yhao.floatwindow.MoveType; 15 | import com.yhao.floatwindow.Screen; 16 | 17 | import org.zzy.lib.largeimage.activity.LargeImageListActivity; 18 | import org.zzy.lib.largeimage.aop.okhttp.LargeImageInterceptor; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import okhttp3.Dns; 24 | import okhttp3.Interceptor; 25 | 26 | /** 27 | * ================================================ 28 | * 作 者:ZhouZhengyi 29 | * 创建日期:2020/4/4 20:58 30 | * 描 述:门面类,主要用于初始化和配置各项参数 31 | * 修订历史:2020/4/13 v1.0.0 32 | * github:https://github.com/121880399/LargeImageMonitor 33 | * ================================================ 34 | */ 35 | public class LargeImage { 36 | public static String TAG = "LargeImageMonitor"; 37 | 38 | public static Application APPLICATION; 39 | 40 | /** 41 | * 文件大小阈值 kb为单位 42 | */ 43 | private double fileSizeThreshold = 500.0; 44 | /** 45 | * 内存大小阈值 kb为单位 46 | */ 47 | private double memorySizeThreshold = 800.0; 48 | 49 | /** 50 | * 大图监控开关,这里只是存储数据的开关 51 | * 是否进行字节码修改需要在gradle文件中对插件进行设置 52 | */ 53 | private volatile boolean largeImageOpen = true; 54 | 55 | /** 56 | * Okhttp应用拦截器 57 | */ 58 | private List okHttpInterceptors = new ArrayList<>(); 59 | /** 60 | * Okhttp网络拦截器 61 | */ 62 | private List okHttpNetworkInterceptors = new ArrayList<>(); 63 | 64 | /** 65 | * 自定义DNS,实现全局HttpDNS 66 | */ 67 | private Dns mDns = Dns.SYSTEM; 68 | 69 | /** 70 | * 用来记录当前启动次数 71 | */ 72 | private MMKV mmkv; 73 | 74 | /** 75 | * 最大删除值,表示启动多少次以后开始清理mmkv记录的大图信息 76 | */ 77 | private int maxRemoveValue = 20; 78 | 79 | /** 80 | * 是否已经调用了install方法 81 | */ 82 | private boolean isCalled = false; 83 | 84 | /** 85 | * 当前删除值 86 | */ 87 | private int currentRemoveValue = 1; 88 | 89 | private LargeImage() { 90 | 91 | } 92 | 93 | private static class Holder { 94 | private static LargeImage INSTANCE = new LargeImage(); 95 | } 96 | 97 | public static LargeImage getInstance() { 98 | return Holder.INSTANCE; 99 | } 100 | 101 | public LargeImage install(Application app) { 102 | if(null == app){ 103 | throw new NullPointerException("You must set a applicatiion!"); 104 | } 105 | if (isCalled) { 106 | Log.e(TAG, "Don't call this method repeatedly!"); 107 | return this; 108 | } 109 | isCalled = true; 110 | APPLICATION = app; 111 | //默认添加拦截大图 112 | okHttpInterceptors.add(new LargeImageInterceptor()); 113 | //初始化MMKV 114 | MMKV.initialize(app); 115 | isRemoveMmkv(); 116 | ImageView ivIcon = new ImageView(app); 117 | ivIcon.setImageResource(R.drawable.ic_logo); 118 | FloatWindow.with(app) 119 | .setView(ivIcon) 120 | .setWidth(Screen.width, 0.2f) //设置悬浮控件宽高 121 | .setHeight(Screen.width, 0.2f) 122 | .setX(Screen.width, 0.8f) 123 | .setY(Screen.height, 0.3f) 124 | .setMoveType(MoveType.slide, 0, 0) 125 | .setMoveStyle(500, new BounceInterpolator()) 126 | .setDesktopShow(false) 127 | .build(); 128 | ivIcon.setOnClickListener(new View.OnClickListener() { 129 | @Override 130 | public void onClick(View v) { 131 | Intent intent = new Intent(); 132 | intent.setClass(APPLICATION, LargeImageListActivity.class); 133 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 134 | APPLICATION.startActivity(intent); 135 | } 136 | }); 137 | return this; 138 | } 139 | 140 | /** 141 | * 是否清理mmkv中的值 142 | * 作者: ZhouZhengyi 143 | * 创建时间: 2020/4/11 21:20 144 | */ 145 | private void isRemoveMmkv() { 146 | mmkv = MMKV.mmkvWithID("LargeImage"); 147 | if (!mmkv.containsKey("maxRemoveValue")) { 148 | mmkv.encode("maxRemoveValue", maxRemoveValue); 149 | } else { 150 | maxRemoveValue = mmkv.decodeInt("maxRemoveValue"); 151 | } 152 | if (!mmkv.containsKey("currentRemoveValue")) { 153 | mmkv.encode("currentRemoveValue", 1); 154 | } else { 155 | currentRemoveValue = mmkv.decodeInt("currentRemoveValue"); 156 | } 157 | //开启一个线程,给保存大图信息的mmkv中的每个entry的unUseCount加1 158 | // 当前启动次数已经达到了该清理mmkv的最大值,那么开启一个线程清理存放大图信息的mmkv 159 | Thread thread = new Thread(runnable); 160 | thread.start(); 161 | } 162 | 163 | private Runnable runnable = new Runnable() { 164 | @Override 165 | public void run() { 166 | try { 167 | MMKV largeImageMmkv = LargeImageManager.getInstance().getMmkv(); 168 | for (String key : largeImageMmkv.allKeys()) { 169 | LargeImageInfo largeImageInfo = largeImageMmkv.decodeParcelable(key, LargeImageInfo.class); 170 | //所有保存的信息未使用次数加一 171 | largeImageInfo.getUnUseCount().incrementAndGet(); 172 | largeImageMmkv.encode(key, largeImageInfo); 173 | } 174 | if (currentRemoveValue >= maxRemoveValue) { 175 | for (String key : largeImageMmkv.allKeys()) { 176 | LargeImageInfo largeImageInfo = largeImageMmkv.decodeParcelable(key, LargeImageInfo.class); 177 | //如果该大图信息未使用次数大于最大删除值,那么说明该信息已经很久不用了,直接删除 178 | if (largeImageInfo.getUnUseCount().get() >= maxRemoveValue) { 179 | largeImageMmkv.remove(key); 180 | } 181 | } 182 | //清理完后,当前启动次数清零 183 | currentRemoveValue = 0; 184 | } 185 | currentRemoveValue++; 186 | mmkv.encode("currentRemoveValue", currentRemoveValue); 187 | }catch (Exception e){ 188 | e.printStackTrace(); 189 | } 190 | } 191 | }; 192 | 193 | /** 194 | * 设置文件阈值 单位为kb 195 | * 196 | * @param fileSizeThreshold 文件阈值 单位kb 197 | * @return 198 | */ 199 | public LargeImage setFileSizeThreshold(double fileSizeThreshold) { 200 | this.fileSizeThreshold = fileSizeThreshold; 201 | return this; 202 | } 203 | 204 | /** 205 | * 设置内存阈值 单位为kb 206 | * 207 | * @param memorySizeThreshold 内存阈值 单位为kb 208 | * @return 209 | */ 210 | public LargeImage setMemorySizeThreshold(double memorySizeThreshold) { 211 | this.memorySizeThreshold = memorySizeThreshold; 212 | return this; 213 | } 214 | 215 | /** 216 | * 设置大图监控开关 true为开 false为关 217 | */ 218 | public LargeImage setLargeImageOpen(boolean largeImageOpen) { 219 | this.largeImageOpen = largeImageOpen; 220 | return this; 221 | } 222 | 223 | /** 224 | * 因为实现了Okhttp的全局插桩,所以提供一个可以添加拦截器的方法 225 | * 让用户可以自定义拦截器实现自己项目和三方库的OKhttp全局监听 226 | * 227 | * @param interceptor 应用拦截器 228 | */ 229 | public LargeImage addOkHttpInterceptor(Interceptor interceptor) { 230 | if (null != okHttpInterceptors) { 231 | okHttpInterceptors.add(interceptor); 232 | } 233 | return this; 234 | } 235 | 236 | /** 237 | * 添加Okhttp网络拦截器 238 | * 239 | * @param networkInterceptor 网络拦截器 240 | */ 241 | public LargeImage addOkHttpNetworkInterceptor(Interceptor networkInterceptor) { 242 | if (null != okHttpNetworkInterceptors) { 243 | okHttpNetworkInterceptors.add(networkInterceptor); 244 | } 245 | return this; 246 | } 247 | 248 | /** 249 | * 可以指定自己定义的HTTPDNS 250 | * 251 | * @param dns 自定义DNS 252 | * @return 253 | */ 254 | public LargeImage setDns(Dns dns) { 255 | this.mDns = dns; 256 | return this; 257 | } 258 | 259 | /** 260 | * 设置启动多少次,开始清除mmkv中的大图信息 261 | * 项目越大,该值可以适当调大 262 | */ 263 | public LargeImage setMaxRemoveValue(int value) { 264 | //不建议少于20次 265 | if(value <= 20){ 266 | value = 20; 267 | } 268 | //这里不设置this.maxRemoveValue的值是因为不想影响当前 269 | //的值,因为是否进行清理是在子线程中执行,只存放mmkv 270 | //那么只影响下次启动 271 | mmkv.encode("maxRemoveValue",value); 272 | return this; 273 | } 274 | 275 | 276 | public Dns getDns() { 277 | return mDns; 278 | } 279 | 280 | public List getOkHttpInterceptors() { 281 | return okHttpInterceptors; 282 | } 283 | 284 | public List getOkHttpNetworkInterceptors() { 285 | return okHttpNetworkInterceptors; 286 | } 287 | 288 | public double getFileSizeThreshold() { 289 | return fileSizeThreshold; 290 | } 291 | 292 | public double getMemorySizeThreshold() { 293 | return memorySizeThreshold; 294 | } 295 | 296 | public boolean isLargeImageOpen() { 297 | return largeImageOpen; 298 | } 299 | 300 | public int getMaxRemoveValue() { 301 | return maxRemoveValue; 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/LargeImageInfo.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | /** 9 | * ================================================ 10 | * 作 者:ZhouZhengyi 11 | * 创建日期:2020/4/4 8:09 12 | * 描 述:大图的信息 13 | * 修订历史: 14 | * ================================================ 15 | */ 16 | public class LargeImageInfo implements Parcelable { 17 | 18 | /** 19 | * 图片地址 20 | */ 21 | private String url; 22 | 23 | /** 24 | * 图片文件大小 25 | */ 26 | private double fileSize; 27 | 28 | /** 29 | * 图片所占内存大小 30 | */ 31 | private double memorySize; 32 | 33 | /** 34 | * 图片宽 35 | */ 36 | private int width; 37 | 38 | /** 39 | * 图片高 40 | */ 41 | private int height; 42 | 43 | /** 44 | * 加载图片所使用的框架 45 | */ 46 | private String framework; 47 | 48 | /** 49 | * View的宽 50 | */ 51 | private int targetWidth; 52 | /** 53 | * View的高 54 | */ 55 | private int targetHeight; 56 | 57 | /** 58 | * 标识该记录未使用次数 59 | * 该值只要使用一次,就为0.如果一直不使用就会持续增加, 60 | * 增加到最大清理值还未使用,就说明该信息很可能已经不用了 61 | * 那么就删除掉。 62 | * 不要拿这个值当做依据,该值只用于评判那些很久未使用的数据 63 | * 对于使用过的数据,不要在意他的值是否准确,只要使用过肯定会被设置 64 | * 为0 65 | */ 66 | private AtomicInteger unUseCount = new AtomicInteger(); 67 | 68 | public AtomicInteger getUnUseCount() { 69 | return unUseCount; 70 | } 71 | 72 | public int getTargetWidth() { 73 | return targetWidth; 74 | } 75 | 76 | public void setTargetWidth(int targetWidth) { 77 | this.targetWidth = targetWidth; 78 | } 79 | 80 | public int getTargetHeight() { 81 | return targetHeight; 82 | } 83 | 84 | public void setTargetHeight(int targetHeight) { 85 | this.targetHeight = targetHeight; 86 | } 87 | 88 | public String getUrl() { 89 | return url; 90 | } 91 | 92 | public void setUrl(String url) { 93 | this.url = url; 94 | } 95 | 96 | public double getFileSize() { 97 | return fileSize; 98 | } 99 | 100 | public void setFileSize(double fileSize) { 101 | this.fileSize = fileSize; 102 | } 103 | 104 | public double getMemorySize() { 105 | return memorySize; 106 | } 107 | 108 | public void setMemorySize(double memorySize) { 109 | this.memorySize = memorySize; 110 | } 111 | 112 | public int getWidth() { 113 | return width; 114 | } 115 | 116 | public void setWidth(int width) { 117 | this.width = width; 118 | } 119 | 120 | public int getHeight() { 121 | return height; 122 | } 123 | 124 | public void setHeight(int height) { 125 | this.height = height; 126 | } 127 | 128 | public String getFramework() { 129 | return framework; 130 | } 131 | 132 | public void setFramework(String framework) { 133 | this.framework = framework; 134 | } 135 | 136 | public LargeImageInfo() { 137 | } 138 | 139 | @Override 140 | public int describeContents() { 141 | return 0; 142 | } 143 | 144 | @Override 145 | public void writeToParcel(Parcel dest, int flags) { 146 | dest.writeString(this.url); 147 | dest.writeDouble(this.fileSize); 148 | dest.writeDouble(this.memorySize); 149 | dest.writeInt(this.width); 150 | dest.writeInt(this.height); 151 | dest.writeString(this.framework); 152 | dest.writeInt(this.targetWidth); 153 | dest.writeInt(this.targetHeight); 154 | dest.writeSerializable(this.unUseCount); 155 | } 156 | 157 | protected LargeImageInfo(Parcel in) { 158 | this.url = in.readString(); 159 | this.fileSize = in.readDouble(); 160 | this.memorySize = in.readDouble(); 161 | this.width = in.readInt(); 162 | this.height = in.readInt(); 163 | this.framework = in.readString(); 164 | this.targetWidth = in.readInt(); 165 | this.targetHeight = in.readInt(); 166 | this.unUseCount = (AtomicInteger) in.readSerializable(); 167 | } 168 | 169 | public static final Creator CREATOR = new Creator() { 170 | @Override 171 | public LargeImageInfo createFromParcel(Parcel source) { 172 | return new LargeImageInfo(source); 173 | } 174 | 175 | @Override 176 | public LargeImageInfo[] newArray(int size) { 177 | return new LargeImageInfo[size]; 178 | } 179 | }; 180 | } 181 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/Test.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage; 2 | 3 | 4 | import android.net.Uri; 5 | 6 | import com.bumptech.glide.request.RequestListener; 7 | import com.squareup.picasso.Transformation; 8 | 9 | import org.zzy.lib.largeimage.aop.glide.GlideHook; 10 | import org.zzy.lib.largeimage.aop.picasso.PicassoHook; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * ================================================ 16 | * 作 者:ZhouZhengyi 17 | * 创建日期:2020/4/1 19:14 18 | * 描 述:用来测试生成ASM码 19 | * 修订历史: 20 | * ================================================ 21 | */ 22 | public class Test { 23 | // private List requestListeners; 24 | //模拟glide 25 | // private void init(){ 26 | // GlideHook.process(requestListeners); 27 | // } 28 | 29 | //模拟picasso 30 | // private void init(Uri uri, List transformations, int resourceId, int targetWidth, int targetHeight){ 31 | // transformations = PicassoHook.process(uri,transformations,resourceId,targetWidth,targetHeight); 32 | // } 33 | 34 | //模拟fresco 35 | // private void init(ImageRequestBuilder builder){ 36 | // builder.setPostprocessor(FrescoHook.process(builder.getSourceUri(),builder.getPostprocessor(),builder.getResizeOptions())); 37 | // } 38 | // 39 | // public static class Builder { 40 | // private int a; 41 | // private Set mRequestListeners; 42 | // } 43 | 44 | //模拟imageloader 45 | // public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, 46 | // ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { 47 | // listener = ImageLoaderHook.process(listener); 48 | // } 49 | // final List interceptors = new ArrayList<>(); 50 | // final List networkInterceptors = new ArrayList<>(); 51 | // 52 | // private void init(){ 53 | // interceptors.addAll(LargeImage.getInstance().getOkHttpInterceptors()); 54 | // networkInterceptors.addAll(LargeImage.getInstance().getOkHttpNetworkInterceptors()); 55 | // } 56 | // Dns dns; 57 | // public void openConnection() { 58 | // dns = LargeImage.getInstance().getDns(); 59 | // } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/activity/LargeImageListActivity.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.activity; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.MenuItem; 6 | import android.view.View; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.appcompat.widget.Toolbar; 11 | import androidx.recyclerview.widget.DividerItemDecoration; 12 | import androidx.recyclerview.widget.LinearLayoutManager; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 15 | 16 | import org.zzy.lib.largeimage.LargeImageInfo; 17 | import org.zzy.lib.largeimage.LargeImageManager; 18 | import org.zzy.lib.largeimage.R; 19 | import org.zzy.lib.largeimage.adapter.LargeImageListAdapter; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * ================================================ 27 | * 作 者:ZhouZhengyi 28 | * 创建日期:2020/4/10 21:33 29 | * 描 述:用列表的形式显示超标图片,只显示本次APP启动以 30 | * 来超标图片,不会显示历史的。 31 | * 修订历史: 32 | * ================================================ 33 | */ 34 | public class LargeImageListActivity extends AppCompatActivity { 35 | private Toolbar mToolBar; 36 | private RecyclerView mRecyclerView; 37 | private LargeImageListAdapter mLargeImageAdapter; 38 | private List mLargeImageList; 39 | private SwipeRefreshLayout mRefresh; 40 | @Override 41 | protected void onCreate(@Nullable Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_largeimage_list); 44 | initView(); 45 | initData(); 46 | } 47 | 48 | private void initView(){ 49 | mToolBar = findViewById(R.id.toolbar); 50 | mRecyclerView = findViewById(R.id.rv_largeImage); 51 | mRefresh = findViewById(R.id.refresh); 52 | mToolBar.inflateMenu(R.menu.toolbar); 53 | mToolBar.setNavigationOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View v) { 56 | finish(); 57 | } 58 | }); 59 | mToolBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { 60 | @Override 61 | public boolean onMenuItemClick(MenuItem item) { 62 | if (item.getItemId() == R.id.action_setting) { 63 | Intent intent = new Intent(LargeImageListActivity.this,SettingActivity.class); 64 | startActivity(intent); 65 | } 66 | return true; 67 | } 68 | }); 69 | mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 70 | @Override 71 | public void onRefresh() { 72 | Map stringLargeImageInfoMap = LargeImageManager.getInstance().getmInfoCache(); 73 | if(mLargeImageList != null && mLargeImageList.size() > 0){ 74 | mLargeImageList.clear(); 75 | } 76 | for (Map.Entry entry : stringLargeImageInfoMap.entrySet()) { 77 | mLargeImageList.add(entry.getValue()); 78 | } 79 | mLargeImageAdapter.notifyDataSetChanged(); 80 | mRefresh.setRefreshing(false); 81 | } 82 | }); 83 | } 84 | 85 | private void initData(){ 86 | mLargeImageList = new ArrayList<>(); 87 | Map stringLargeImageInfoMap = LargeImageManager.getInstance().getmInfoCache(); 88 | for (Map.Entry entry : stringLargeImageInfoMap.entrySet()) { 89 | mLargeImageList.add(entry.getValue()); 90 | } 91 | mLargeImageAdapter = new LargeImageListAdapter(this,mLargeImageList); 92 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 93 | mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); 94 | mRecyclerView.setAdapter(mLargeImageAdapter); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/activity/SettingActivity.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.CheckBox; 6 | import android.widget.CompoundButton; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.appcompat.widget.Toolbar; 11 | 12 | import org.zzy.lib.largeimage.LargeImageManager; 13 | import org.zzy.lib.largeimage.R; 14 | 15 | /** 16 | * ================================================ 17 | * 作 者:ZhouZhengyi 18 | * 创建日期:2020/4/11 20:04 19 | * 描 述:设置界面 20 | * 修订历史: 21 | * ================================================ 22 | */ 23 | public class SettingActivity extends AppCompatActivity { 24 | private Toolbar mToolBar; 25 | private CheckBox mMenuSwitch; 26 | @Override 27 | protected void onCreate(@Nullable Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_setting); 30 | initView(); 31 | } 32 | 33 | private void initView(){ 34 | mToolBar = findViewById(R.id.toolbar); 35 | mMenuSwitch = findViewById(R.id.menu_switch); 36 | mToolBar.setNavigationOnClickListener(new View.OnClickListener() { 37 | @Override 38 | public void onClick(View v) { 39 | finish(); 40 | } 41 | }); 42 | mMenuSwitch.setChecked(LargeImageManager.getInstance().isOpenDialog()); 43 | mMenuSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 44 | @Override 45 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 46 | LargeImageManager.getInstance().setOpenDialog(!LargeImageManager.getInstance().isOpenDialog()); 47 | } 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/adapter/LargeImageListAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.adapter; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | import android.graphics.Bitmap; 7 | import android.text.TextUtils; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ImageView; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | import android.widget.Toast; 15 | 16 | 17 | import androidx.annotation.Nullable; 18 | import androidx.recyclerview.widget.RecyclerView; 19 | 20 | import org.zzy.lib.largeimage.LargeImage; 21 | import org.zzy.lib.largeimage.LargeImageInfo; 22 | import org.zzy.lib.largeimage.LargeImageManager; 23 | import org.zzy.lib.largeimage.R; 24 | import org.zzy.lib.largeimage.util.ResHelper; 25 | 26 | import java.text.DecimalFormat; 27 | import java.util.List; 28 | 29 | /** 30 | * ================================================ 31 | * 作 者:ZhouZhengyi 32 | * 创建日期:2020/4/11 9:39 33 | * 描 述: 34 | * 修订历史: 35 | * ================================================ 36 | */ 37 | public class LargeImageListAdapter extends RecyclerView.Adapter { 38 | 39 | private DecimalFormat mDecimalFormat = new DecimalFormat("0.00"); 40 | private List mData; 41 | private Context mContext; 42 | public LargeImageListAdapter(Context context,@Nullable List data) { 43 | this.mData = data; 44 | this.mContext = context; 45 | } 46 | 47 | @androidx.annotation.NonNull 48 | @Override 49 | public MyHolder onCreateViewHolder(@androidx.annotation.NonNull ViewGroup parent, int viewType) { 50 | View view = LayoutInflater.from(mContext).inflate(R.layout.dialog_custom,parent,false); 51 | MyHolder holder = new MyHolder(view); 52 | return holder; 53 | } 54 | 55 | @Override 56 | public void onBindViewHolder(@androidx.annotation.NonNull MyHolder holder, int position) { 57 | LargeImageInfo largeImageInfo = mData.get(position); 58 | //设置文件大小 59 | setFileSize(largeImageInfo.getFileSize(), holder.llFileSize,holder.tvFileSize, holder.ivFileSize); 60 | //设置内存大小 61 | setMemorySize(largeImageInfo.getMemorySize(), holder.llMemorySize, 62 | holder.tvMemorySize, holder.ivMemorySize); 63 | //设置图片尺寸 64 | setImageSize(largeImageInfo.getWidth(), largeImageInfo.getHeight(), holder.tvSize); 65 | //设置View尺寸 66 | setViewSize(largeImageInfo.getTargetWidth(), largeImageInfo.getTargetHeight(), holder.tvViewSize); 67 | //设置图片地址 68 | setImageUrl(largeImageInfo.getUrl(), holder.tvImageUrl); 69 | Bitmap bitmap = LargeImageManager.getInstance().getmBitmapCache().get(largeImageInfo.getUrl()); 70 | //设置图片 71 | holder.ivThumb.setImageBitmap(bitmap); 72 | } 73 | 74 | @Override 75 | public int getItemCount() { 76 | return mData == null ? 0 : mData.size(); 77 | } 78 | 79 | 80 | private void setImageUrl(final String url, TextView tvImageUrl) { 81 | if (TextUtils.isEmpty(url)) { 82 | tvImageUrl.setVisibility(View.GONE); 83 | } else { 84 | tvImageUrl.setVisibility(View.VISIBLE); 85 | if(!url.startsWith("http") && !url.startsWith("https")){ 86 | String tempUrl = url; 87 | if(url.contains("/")){ 88 | int index = url.lastIndexOf("/"); 89 | tempUrl =url.substring(index+1,url.length()); 90 | } 91 | try { 92 | final String resourceName = LargeImage.APPLICATION.getApplicationContext().getResources().getResourceName(Integer.parseInt(tempUrl)); 93 | if(TextUtils.isEmpty(resourceName)){ 94 | tvImageUrl.setText(ResHelper.getString(R.string.large_image_url, "本地图片")); 95 | }else { 96 | tvImageUrl.setText(ResHelper.getString(R.string.large_image_url, resourceName)); 97 | } 98 | tvImageUrl.setOnClickListener(new View.OnClickListener() { 99 | @Override 100 | public void onClick(View v) { 101 | copyToClipboard(resourceName); 102 | } 103 | }); 104 | } catch (NumberFormatException e){ 105 | //不是请求网络,也没用resId,统一显示为本地图片 106 | e.printStackTrace(); 107 | tvImageUrl.setText(ResHelper.getString(R.string.large_image_url, "本地图片")); 108 | } 109 | }else{ 110 | tvImageUrl.setText(ResHelper.getString(R.string.large_image_url, url)); 111 | tvImageUrl.setOnClickListener(new View.OnClickListener() { 112 | @Override 113 | public void onClick(View v) { 114 | copyToClipboard(url); 115 | } 116 | }); 117 | } 118 | } 119 | } 120 | 121 | private void copyToClipboard(String resourceName) { 122 | try { 123 | //获取剪贴板管理器 124 | ClipboardManager cm = 125 | (ClipboardManager) LargeImage.APPLICATION.getSystemService(Context.CLIPBOARD_SERVICE); 126 | //创建普通字符型ClipData 127 | ClipData clipData = ClipData.newPlainText("Label", resourceName); 128 | cm.setPrimaryClip(clipData); 129 | Toast.makeText(LargeImage.APPLICATION.getApplicationContext(), "复制成功!", Toast.LENGTH_SHORT).show(); 130 | } catch (Exception e) { 131 | Toast.makeText(LargeImage.APPLICATION.getApplicationContext(), "复制失败!", Toast.LENGTH_SHORT).show(); 132 | e.printStackTrace(); 133 | } 134 | } 135 | 136 | private void setViewSize(int targetWidth, int targetHeigh, TextView tvViewSize) { 137 | if (targetWidth <= 0 || targetHeigh <= 0) { 138 | tvViewSize.setVisibility(View.GONE); 139 | } else { 140 | tvViewSize.setVisibility(View.VISIBLE); 141 | StringBuilder sb = new StringBuilder(); 142 | sb.append(targetWidth).append("*").append(targetHeigh); 143 | tvViewSize.setText(ResHelper.getString(R.string.large_view_size, sb.toString())); 144 | } 145 | } 146 | 147 | private void setImageSize(int width, int height, TextView tvSize) { 148 | if (width <= 0 && height <= 0) { 149 | tvSize.setVisibility(View.GONE); 150 | } else { 151 | tvSize.setVisibility(View.VISIBLE); 152 | StringBuilder sb = new StringBuilder(); 153 | sb.append(width).append("*").append(height); 154 | tvSize.setText(ResHelper.getString(R.string.large_image_size, sb.toString())); 155 | } 156 | } 157 | 158 | private void setMemorySize(double memorySize, LinearLayout llMemorySize, TextView tvMemorySize, ImageView ivMemorySize) { 159 | if (memorySize <= 0) { 160 | llMemorySize.setVisibility(View.GONE); 161 | } else { 162 | llMemorySize.setVisibility(View.VISIBLE); 163 | if (memorySize > LargeImage.getInstance().getMemorySizeThreshold()) { 164 | ivMemorySize.setVisibility(View.VISIBLE); 165 | } else { 166 | ivMemorySize.setVisibility(View.GONE); 167 | } 168 | double size = memorySize / 1024; 169 | tvMemorySize.setText(ResHelper.getString(R.string.large_image_memory_size, mDecimalFormat.format(size))); 170 | } 171 | } 172 | 173 | private void setFileSize(double fileSize, LinearLayout llFileSize, TextView tvFileSize, ImageView ivFileSize) { 174 | if (fileSize <= 0) { 175 | llFileSize.setVisibility(View.GONE); 176 | } else { 177 | llFileSize.setVisibility(View.VISIBLE); 178 | if (fileSize > LargeImage.getInstance().getFileSizeThreshold()) { 179 | ivFileSize.setVisibility(View.VISIBLE); 180 | } else { 181 | ivFileSize.setVisibility(View.GONE); 182 | } 183 | tvFileSize.setText(ResHelper.getString(R.string.large_image_file_size, mDecimalFormat.format(fileSize))); 184 | } 185 | } 186 | 187 | 188 | class MyHolder extends RecyclerView.ViewHolder{ 189 | LinearLayout llMemorySize; 190 | TextView tvMemorySize; 191 | ImageView ivMemorySize; 192 | LinearLayout llFileSize; 193 | TextView tvFileSize; 194 | ImageView ivFileSize; 195 | TextView tvSize; 196 | TextView tvViewSize; 197 | TextView tvImageUrl; 198 | ImageView ivThumb; 199 | 200 | public MyHolder(@androidx.annotation.NonNull View itemView) { 201 | super(itemView); 202 | llMemorySize = itemView.findViewById(R.id.ll_memory_size); 203 | tvMemorySize = itemView.findViewById(R.id.tv_memory_size); 204 | ivMemorySize = itemView.findViewById(R.id.iv_memory_size); 205 | llFileSize = itemView.findViewById(R.id.ll_file_size); 206 | tvFileSize = itemView.findViewById(R.id.tv_file_size); 207 | ivFileSize = itemView.findViewById(R.id.iv_file_size); 208 | tvSize = itemView.findViewById(R.id.tv_size); 209 | tvViewSize = itemView.findViewById(R.id.tv_view_size); 210 | tvImageUrl = itemView.findViewById(R.id.tv_image_url); 211 | ivThumb = itemView.findViewById(R.id.iv_thumb); 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/fresco/FrescoHook.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.fresco; 2 | 3 | import android.net.Uri; 4 | 5 | import com.facebook.imagepipeline.common.ResizeOptions; 6 | import com.facebook.imagepipeline.core.ImagePipelineConfig; 7 | import com.facebook.imagepipeline.listener.RequestListener; 8 | import com.facebook.imagepipeline.request.Postprocessor; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * ================================================ 15 | * 作 者:ZhouZhengyi 16 | * 创建日期:2020/4/2 22:08 17 | * 描 述: 18 | * 修订历史: 19 | * ================================================ 20 | */ 21 | public class FrescoHook { 22 | 23 | public static Postprocessor process(Uri uri, Postprocessor postprocessor,ResizeOptions resizeOptions){ 24 | return new FrescoLargeImageProcessor(postprocessor,uri,resizeOptions); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/fresco/FrescoLargeImageProcessor.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.fresco; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Canvas; 5 | import android.net.Uri; 6 | 7 | import com.facebook.cache.common.CacheKey; 8 | import com.facebook.common.references.CloseableReference; 9 | import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory; 10 | import com.facebook.imagepipeline.common.ResizeOptions; 11 | import com.facebook.imagepipeline.nativecode.Bitmaps; 12 | import com.facebook.imagepipeline.request.Postprocessor; 13 | 14 | import org.zzy.lib.largeimage.LargeImageManager; 15 | 16 | import static com.facebook.imagepipeline.request.BasePostprocessor.FALLBACK_BITMAP_CONFIGURATION; 17 | 18 | /** 19 | * ================================================ 20 | * 作 者:ZhouZhengyi 21 | * 创建日期:2020/4/4 10:35 22 | * 描 述: 23 | * 修订历史: 24 | * ================================================ 25 | */ 26 | public class FrescoLargeImageProcessor implements Postprocessor { 27 | 28 | private Postprocessor mOriginProcessor; 29 | private Uri mUri; 30 | private ResizeOptions mResizeOptions; 31 | public FrescoLargeImageProcessor(Postprocessor mOriginProcessor, Uri mUri, ResizeOptions resizeOptions) { 32 | this.mOriginProcessor = mOriginProcessor; 33 | this.mUri = mUri; 34 | this.mResizeOptions = resizeOptions; 35 | } 36 | 37 | @Override 38 | public CloseableReference process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) { 39 | int width = 0; 40 | int height = 0; 41 | if(null != mResizeOptions){ 42 | width = mResizeOptions.width; 43 | height = mResizeOptions.height; 44 | } 45 | sourceBitmap = LargeImageManager.getInstance().transform(mUri.toString(),sourceBitmap,"Fresco",width,height); 46 | if(null != mOriginProcessor){ 47 | return mOriginProcessor.process(sourceBitmap,bitmapFactory); 48 | } 49 | final Bitmap.Config sourceBitmapConfig = sourceBitmap.getConfig(); 50 | CloseableReference destBitmapRef = 51 | bitmapFactory.createBitmapInternal( 52 | sourceBitmap.getWidth(), 53 | sourceBitmap.getHeight(), 54 | sourceBitmapConfig != null ? sourceBitmapConfig : FALLBACK_BITMAP_CONFIGURATION); 55 | try { 56 | process(destBitmapRef.get(),sourceBitmap); 57 | return CloseableReference.cloneOrNull(destBitmapRef); 58 | }finally { 59 | CloseableReference.closeSafely(destBitmapRef); 60 | } 61 | } 62 | 63 | public void process(Bitmap destBitmap, Bitmap sourceBitmap) { 64 | internalCopyBitmap(destBitmap, sourceBitmap); 65 | process(destBitmap); 66 | } 67 | 68 | public void process(Bitmap bitmap) { 69 | } 70 | 71 | private static void internalCopyBitmap(Bitmap destBitmap,Bitmap sourceBitmap){ 72 | if(destBitmap.getConfig() == sourceBitmap.getConfig()){ 73 | Bitmaps.copyBitmap(destBitmap, sourceBitmap); 74 | }else{ 75 | Canvas canvas = new Canvas(destBitmap); 76 | canvas.drawBitmap(sourceBitmap,0,0,null); 77 | } 78 | } 79 | 80 | @Override 81 | public String getName() { 82 | if(null != mOriginProcessor){ 83 | return mOriginProcessor.getName(); 84 | } 85 | return "LargeImage&Fresco&FrescoLargeImageProcessor"; 86 | } 87 | 88 | @Override 89 | public CacheKey getPostprocessorCacheKey() { 90 | if(null != mOriginProcessor){ 91 | return mOriginProcessor.getPostprocessorCacheKey(); 92 | } 93 | return null; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/glide/GlideHook.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.glide; 2 | 3 | import com.bumptech.glide.request.RequestListener; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * ================================================ 10 | * 作 者:ZhouZhengyi 11 | * 创建日期:2020/4/1 16:30 12 | * 描 述:将该类的process方法注入到 13 | * com.bumptech.glide.request.SingleRequest的构造方法中 14 | * 修订历史: 15 | * ================================================ 16 | */ 17 | public class GlideHook { 18 | 19 | public static List process(List requestListener){ 20 | if(requestListener == null ){ 21 | requestListener = new ArrayList<>(); 22 | } 23 | requestListener.add(new GlideLargeImageListener()); 24 | return requestListener; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/glide/GlideLargeImageListener.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.glide; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.drawable.BitmapDrawable; 5 | import android.view.View; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | import com.bumptech.glide.load.DataSource; 10 | import com.bumptech.glide.load.engine.GlideException; 11 | import com.bumptech.glide.request.Request; 12 | import com.bumptech.glide.request.RequestListener; 13 | import com.bumptech.glide.request.target.BitmapImageViewTarget; 14 | import com.bumptech.glide.request.target.Target; 15 | import com.bumptech.glide.request.target.ViewTarget; 16 | 17 | import org.zzy.lib.largeimage.LargeImageManager; 18 | 19 | /** 20 | * ================================================ 21 | * 作 者:ZhouZhengyi 22 | * 创建日期:2020/4/1 19:55 23 | * 描 述:当资源加载完成后,会回调这个类里面的onResourceReady方法 24 | * 在里面编写自己的逻辑 25 | * 修订历史: 26 | * ================================================ 27 | */ 28 | public class GlideLargeImageListener implements RequestListener { 29 | 30 | 31 | @Override 32 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { 33 | return false; 34 | } 35 | 36 | @Override 37 | public boolean onResourceReady(R resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { 38 | int width =0; 39 | int heigh =0; 40 | if(target instanceof ViewTarget){ 41 | View view = ((ViewTarget) target).getView(); 42 | width = view.getWidth(); 43 | heigh = view.getHeight(); 44 | } 45 | if(resource instanceof Bitmap){ 46 | LargeImageManager.getInstance().transform(model.toString(),(Bitmap)resource,"Glide",width,heigh); 47 | }else if(resource instanceof BitmapDrawable){ 48 | LargeImageManager.getInstance().transform(model.toString(),(BitmapDrawable) resource,"Glide",width,heigh); 49 | } 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/imageloader/ImageLoaderHook.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.imageloader; 2 | 3 | import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; 4 | 5 | /** 6 | * ================================================ 7 | * 作 者:ZhouZhengyi 8 | * 创建日期:2020/4/3 16:27 9 | * 描 述: 10 | * 修订历史: 11 | * ================================================ 12 | */ 13 | public class ImageLoaderHook { 14 | 15 | public static ImageLoadingListener process(ImageLoadingListener listener){ 16 | return new ImageLoaderLargeImageListener(listener); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/imageloader/ImageLoaderLargeImageListener.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.imageloader; 2 | 3 | import android.graphics.Bitmap; 4 | import android.view.View; 5 | 6 | import com.nostra13.universalimageloader.core.assist.FailReason; 7 | import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; 8 | import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; 9 | 10 | import org.zzy.lib.largeimage.LargeImageManager; 11 | 12 | /** 13 | * ================================================ 14 | * 作 者:ZhouZhengyi 15 | * 创建日期:2020/4/3 16:37 16 | * 描 述: 17 | * 修订历史: 18 | * ================================================ 19 | */ 20 | public class ImageLoaderLargeImageListener implements ImageLoadingListener { 21 | 22 | /** 23 | * 原始Listener 24 | */ 25 | private ImageLoadingListener mOriginListener; 26 | 27 | public ImageLoaderLargeImageListener(ImageLoadingListener listener){ 28 | if(null == listener){ 29 | mOriginListener = new SimpleImageLoadingListener(); 30 | }else { 31 | mOriginListener = listener; 32 | } 33 | } 34 | 35 | @Override 36 | public void onLoadingStarted(String imageUri, View view) { 37 | mOriginListener.onLoadingStarted(imageUri,view); 38 | } 39 | 40 | @Override 41 | public void onLoadingFailed(String imageUri, View view, FailReason failReason) { 42 | mOriginListener.onLoadingFailed(imageUri,view,failReason); 43 | } 44 | 45 | @Override 46 | public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { 47 | LargeImageManager.getInstance().transform(imageUri,loadedImage,"ImageLoader",view.getWidth(),view.getHeight()); 48 | mOriginListener.onLoadingComplete(imageUri, view, loadedImage); 49 | } 50 | 51 | @Override 52 | public void onLoadingCancelled(String imageUri, View view) { 53 | mOriginListener.onLoadingCancelled(imageUri, view); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/okhttp/LargeImageInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.okhttp; 2 | 3 | import android.text.TextUtils; 4 | 5 | import org.zzy.lib.largeimage.LargeImageManager; 6 | 7 | import java.io.IOException; 8 | 9 | import okhttp3.Interceptor; 10 | import okhttp3.Request; 11 | import okhttp3.Response; 12 | 13 | /** 14 | * ================================================ 15 | * 作 者:ZhouZhengyi 16 | * 创建日期:2020/4/5 8:43 17 | * 描 述: 18 | * 修订历史: 19 | * ================================================ 20 | */ 21 | public class LargeImageInterceptor implements Interceptor { 22 | 23 | 24 | @Override 25 | public Response intercept(Chain chain) throws IOException { 26 | //发出请求时不拦截 27 | Request request = chain.request(); 28 | Response response = chain.proceed(request); 29 | //拦截响应 30 | String header = response.header("Content-Type"); 31 | //如果是图片类型则拦截 32 | if(isImage(header)){ 33 | process(response); 34 | } 35 | return response; 36 | } 37 | 38 | private void process(Response response){ 39 | String header = response.header("Content-Length"); 40 | //判断是否有数据 41 | if(!TextUtils.isEmpty(header)){ 42 | try { 43 | //parseInt有可能抛出异常 44 | LargeImageManager.getInstance().saveImageInfo(response.request().url().toString(), Integer.parseInt(header)); 45 | }catch (Exception e){ 46 | e.printStackTrace(); 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * 判断是否是图片 53 | * 作者: ZhouZhengyi 54 | * 创建时间: 2020/4/5 9:45 55 | */ 56 | private boolean isImage(String contentType){ 57 | String mimeType = stripContentExtras(contentType); 58 | if(mimeType.startsWith("image")){ 59 | return true; 60 | }else{ 61 | return false; 62 | } 63 | } 64 | 65 | private String stripContentExtras(String contentType){ 66 | int index = contentType.indexOf(';'); 67 | return (index >= 0) ? contentType.substring(0, index) : contentType; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/picasso/PicassoHook.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.picasso; 2 | 3 | import android.net.Uri; 4 | 5 | import com.squareup.picasso.Transformation; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * ================================================ 12 | * 作 者:ZhouZhengyi 13 | * 创建日期:2020/4/2 10:47 14 | * 描 述:将process方法注入到 15 | * com.squareup.picasso.Request的构造方法中 16 | * 修订历史: 17 | * ================================================ 18 | */ 19 | public class PicassoHook { 20 | 21 | public static List process(Uri uri, List transformations,int resourceId,int targetWidth, int targetHeight){ 22 | if(null == transformations){ 23 | transformations = new ArrayList<>(); 24 | } 25 | transformations.add(new PicassoLargeImageTransformation(uri,resourceId,targetWidth,targetHeight)); 26 | return transformations; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/picasso/PicassoLargeImageTransformation.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.picasso; 2 | 3 | import android.graphics.Bitmap; 4 | import android.net.Uri; 5 | 6 | import com.squareup.picasso.Transformation; 7 | 8 | import org.zzy.lib.largeimage.LargeImageManager; 9 | 10 | /** 11 | * ================================================ 12 | * 作 者:ZhouZhengyi 13 | * 创建日期:2020/4/2 11:33 14 | * 描 述: 15 | * 修订历史: 16 | * ================================================ 17 | */ 18 | public class PicassoLargeImageTransformation implements Transformation { 19 | 20 | private String mUri; 21 | private int targetWidth; 22 | private int targetHeight; 23 | 24 | public PicassoLargeImageTransformation(Uri uri,int resourceId,int targetWidth, int targetHeight){ 25 | if(uri == null){ 26 | mUri = String.valueOf(resourceId); 27 | }else{ 28 | mUri = uri.toString(); 29 | } 30 | this.targetWidth=targetWidth; 31 | this.targetHeight=targetHeight; 32 | } 33 | 34 | @Override 35 | public Bitmap transform(Bitmap source) { 36 | return LargeImageManager.getInstance().transform(mUri,source,"Picasso",targetWidth,targetHeight); 37 | } 38 | 39 | @Override 40 | public String key() { 41 | return "LargeImage&Picasso&PicassoLargeImageTransformation"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/aop/urlconnection/UrlConnectionHook.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.aop.urlconnection; 2 | 3 | import org.zzy.lib.largeimage.aop.okhttp.LargeImageInterceptor; 4 | 5 | import java.net.MalformedURLException; 6 | import java.net.URL; 7 | import java.net.URLConnection; 8 | 9 | import okhttp3.HttpUrl; 10 | import okhttp3.Interceptor; 11 | import okhttp3.OkHttpClient; 12 | 13 | /** 14 | * ================================================ 15 | * 作 者:ZhouZhengyi 16 | * 创建日期:2020/4/5 16:36 17 | * 描 述: 18 | * 修订历史: 19 | * ================================================ 20 | */ 21 | public class UrlConnectionHook { 22 | 23 | public static URLConnection process (URLConnection urlConnection){ 24 | try { 25 | String host = HttpUrl.parse(urlConnection.getURL().toString()).host(); 26 | return createOkHttpUrlConnection(urlConnection); 27 | }catch (Exception e){ 28 | e.printStackTrace(); 29 | } 30 | return urlConnection; 31 | } 32 | 33 | /** 34 | * 构建一个OkhttpClient,并且判断当前是http还是https,返回相应的OkhttpUrlConnection 35 | * ObsoleteUrlFactory类在OKhttp3.14版本开始就被删掉了,不是因为有什么问题,只是不推荐使用了 36 | * 作者: ZhouZhengyi 37 | * 创建时间: 2020/4/5 20:47 38 | */ 39 | private static URLConnection createOkHttpUrlConnection(URLConnection urlConnection) throws MalformedURLException { 40 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 41 | addInterceptor(builder); 42 | OkHttpClient client = builder.build(); 43 | 44 | String strUrl = urlConnection.getURL().toString(); 45 | URL url = new URL(strUrl); 46 | String protocol = url.getProtocol().toLowerCase(); 47 | if(protocol.equalsIgnoreCase("http")){ 48 | return new ObsoleteUrlFactory.OkHttpURLConnection(url,client); 49 | } 50 | if (protocol.equalsIgnoreCase("https")){ 51 | return new ObsoleteUrlFactory.OkHttpsURLConnection(url,client); 52 | } 53 | return urlConnection; 54 | } 55 | 56 | private static void addInterceptor(OkHttpClient.Builder builder) { 57 | //判断当前是否已经添加过全局的Okhttp拦截器,如果添加了就返回 58 | for (Interceptor interceptor : builder.interceptors()) { 59 | if(interceptor instanceof LargeImageInterceptor){ 60 | return; 61 | } 62 | } 63 | builder.addInterceptor(new LargeImageInterceptor()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/util/ConvertUtils.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.util; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Canvas; 5 | import android.graphics.PixelFormat; 6 | import android.graphics.drawable.BitmapDrawable; 7 | import android.graphics.drawable.Drawable; 8 | 9 | import androidx.annotation.IntDef; 10 | 11 | import java.lang.annotation.Retention; 12 | import java.lang.annotation.RetentionPolicy; 13 | 14 | /** 15 | * ================================================ 16 | * 作 者:ZhouZhengyi 17 | * 创建日期:2020/4/4 8:58 18 | * 描 述:该工具类用于转换,里面的方法裁剪与AndroidUtilCode 19 | * 框架。 20 | * 修订历史: 21 | * ================================================ 22 | */ 23 | public class ConvertUtils { 24 | 25 | public static final int BYTE = 1; 26 | public static final int KB = 1024; 27 | public static final int MB = 1048576; 28 | public static final int GB = 1073741824; 29 | 30 | @IntDef({BYTE, KB, MB, GB}) 31 | @Retention(RetentionPolicy.SOURCE) 32 | public @interface Unit { 33 | } 34 | 35 | /** 36 | * Size of byte to size of memory in unit. 37 | * 38 | * @param byteSize Size of byte. 39 | * @param unit The unit of memory size. 40 | * @return size of memory in unit 41 | */ 42 | public static double byte2MemorySize(final long byteSize, 43 | @Unit final int unit) { 44 | if (byteSize < 0) return -1; 45 | return (double) byteSize / unit; 46 | } 47 | 48 | /** 49 | * Drawable to bitmap. 50 | * 51 | * @param drawable The drawable. 52 | * @return bitmap 53 | */ 54 | public static Bitmap drawable2Bitmap(final Drawable drawable) { 55 | if (drawable instanceof BitmapDrawable) { 56 | BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 57 | if (bitmapDrawable.getBitmap() != null) { 58 | return bitmapDrawable.getBitmap(); 59 | } 60 | } 61 | Bitmap bitmap; 62 | if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { 63 | bitmap = Bitmap.createBitmap(1, 1, 64 | drawable.getOpacity() != PixelFormat.OPAQUE 65 | ? Bitmap.Config.ARGB_8888 66 | : Bitmap.Config.RGB_565); 67 | } else { 68 | bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), 69 | drawable.getIntrinsicHeight(), 70 | drawable.getOpacity() != PixelFormat.OPAQUE 71 | ? Bitmap.Config.ARGB_8888 72 | : Bitmap.Config.RGB_565); 73 | } 74 | Canvas canvas = new Canvas(bitmap); 75 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 76 | drawable.draw(canvas); 77 | return bitmap; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/util/ImageUtil.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.util; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.os.Build; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.view.ViewTreeObserver; 9 | import android.widget.ImageView; 10 | 11 | import org.zzy.lib.largeimage.LargeImage; 12 | import org.zzy.lib.largeimage.R; 13 | 14 | import java.io.ByteArrayInputStream; 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.File; 17 | import java.io.FileOutputStream; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | 21 | import okhttp3.Call; 22 | import okhttp3.Callback; 23 | import okhttp3.OkHttpClient; 24 | import okhttp3.Request; 25 | import okhttp3.Response; 26 | 27 | /** 28 | * ================================================ 29 | * 作 者:ZhouZhengyi 30 | * 创建日期:2020/4/7 16:52 31 | * 描 述: 用来加载弹窗小图 32 | * 修订历史:1.为了防止二次hook,自定义该工具类加载图片 33 | * 2.目前已经不再使用,hook图片框架时能得到相应的bitmap对象 34 | * 可以直接使用ImageView加载。 35 | * ================================================ 36 | */ 37 | public class ImageUtil { 38 | 39 | /** 40 | * 从输入流中得到Bitmap 41 | * 作者: ZhouZhengyi 42 | * 创建时间: 2020/4/7 17:05 43 | */ 44 | private static Bitmap decodeBitmapFromStream(String filePath, int targetWidth, int targetHeight) { 45 | Log.d(LargeImage.TAG, "decodeBitmapFromStream Thread id:" + Thread.currentThread().getId()); 46 | BitmapFactory.Options options = new BitmapFactory.Options(); 47 | //只获取Bitmap原始宽高,不分配内存 48 | options.inJustDecodeBounds = true; 49 | BitmapFactory.decodeFile(filePath, options); 50 | //计算inSampleSize值 51 | options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight); 52 | options.inJustDecodeBounds = false; 53 | return BitmapFactory.decodeFile(filePath, options); 54 | } 55 | 56 | /** 57 | * 只要bitmap的宽或者高 大于ImageView的宽高 58 | * 那么就计算inSampleSize的值 59 | * 作者: ZhouZhengyi 60 | * 创建时间: 2020/4/7 17:14 61 | */ 62 | private static int calculateInSampleSize(BitmapFactory.Options options, int targetWidth, int targetHeight) { 63 | if (targetHeight == 0 || targetWidth == 0) { 64 | return 1; 65 | } 66 | Log.d(LargeImage.TAG, "calculateInSampleSize thread id:" + Thread.currentThread().getId()); 67 | int height = options.outHeight; 68 | int width = options.outWidth; 69 | int inSampleSize = 1; 70 | if (width > targetWidth || height > targetHeight) { 71 | int halfHeight = height / 2; 72 | int halfWidth = width / 2; 73 | while ((halfHeight / inSampleSize) >= targetHeight && 74 | (halfWidth / inSampleSize) >= targetWidth) { 75 | Log.d(LargeImage.TAG, "calculateInSampleSize thread id:" + Thread.currentThread().getId() + " inSampleSize:" + inSampleSize + " H"); 76 | inSampleSize *= 2; 77 | } 78 | 79 | } 80 | return inSampleSize; 81 | } 82 | 83 | /** 84 | * 如果使用四大图片框架,则会有循环引用的问题,所以只能利用OkHttp自己实现图片下载 85 | * 作者: ZhouZhengyi 86 | * 创建时间: 2020/4/7 17:17 87 | */ 88 | public static void downloadImage(final String url, final ImageView targetView) { 89 | OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); 90 | final Request request = new Request.Builder().url(url).build(); 91 | okHttpClient.newCall(request).enqueue(new Callback() { 92 | @Override 93 | public void onFailure(Call call, IOException e) { 94 | targetView.setImageResource(R.drawable.ic_failed); 95 | } 96 | 97 | @Override 98 | public void onResponse(Call call, Response response) throws IOException { 99 | String root = LargeImage.APPLICATION.getFilesDir().getAbsolutePath() + "/largeimage"; 100 | InputStream is = null; 101 | FileOutputStream fileOutputStream = null; 102 | File dir = new File(root); 103 | if(!dir.exists()){ 104 | dir.mkdirs(); 105 | } 106 | String imageUrl = url; 107 | int index = imageUrl.lastIndexOf("/"); 108 | String fileName = imageUrl.substring(index,imageUrl.length()); 109 | File file = new File(dir,fileName); 110 | try { 111 | Log.d(LargeImage.TAG, "onResponse thread id:" + Thread.currentThread().getId()); 112 | is = response.body().byteStream(); 113 | fileOutputStream = new FileOutputStream(file); 114 | byte [] bytes = new byte[2048]; 115 | int len = -1; 116 | while((len=is.read(bytes)) != -1){ 117 | fileOutputStream.write(bytes,0,len); 118 | } 119 | fileOutputStream.flush(); 120 | final Bitmap bitmap = decodeBitmapFromStream(file.getAbsolutePath(), targetView.getWidth(), targetView.getHeight()); 121 | targetView.post(new Runnable() { 122 | @Override 123 | public void run() { 124 | Log.d(LargeImage.TAG, "onResponse thread id:" + Thread.currentThread().getId()); 125 | targetView.setImageBitmap(bitmap); 126 | } 127 | }); 128 | } catch (Exception e) { 129 | e.printStackTrace(); 130 | } finally { 131 | if (is != null) { 132 | is.close(); 133 | } 134 | if(fileOutputStream != null){ 135 | fileOutputStream.close(); 136 | } 137 | } 138 | } 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/util/Reflector.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.util; 2 | 3 | 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.Field; 9 | import java.lang.reflect.InvocationTargetException; 10 | import java.lang.reflect.Member; 11 | import java.lang.reflect.Method; 12 | import java.lang.reflect.Modifier; 13 | 14 | /** 15 | * 16 | * 使用方法: 17 | * 1.反射类中某个方法并调用: 18 | * Reflector.on(factoryClass).method("create", Context.class).call(context); 19 | * 2.反射类中某个字段并取得值 20 | * Reflector.on(ActivityManagerNative.class).field("gDefault").get(); 21 | * 3.反射对象中某个字段并设置值 22 | * Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy); 23 | * 4.反射某个类并创建对象 24 | * Reflector.on("android.databinding.DataBinderMapperProxy").constructor().newInstance(); 25 | * 5.反射某个方法,并且为方法绑定调用对象 26 | * Reflector.on(AssetManager.class).method("addAssetPath", String.class); 27 | * assetManager = AssetManager.class.newInstance(); 28 | * reflector.bind(assetManager); 29 | * reflector.call(apk); 30 | * 31 | */ 32 | public class Reflector { 33 | 34 | public static final String LOG_TAG ="Reflector"; 35 | 36 | protected Class mType; 37 | protected Object mCaller; 38 | protected Constructor mConstructor; 39 | protected Field mField; 40 | protected Method mMethod; 41 | 42 | public static class ReflectedException extends Exception { 43 | 44 | public ReflectedException(String message) { 45 | super(message); 46 | } 47 | public ReflectedException(String message, Throwable cause) { 48 | super(message, cause); 49 | } 50 | 51 | } 52 | public static Reflector on(@NonNull String name) throws ReflectedException { 53 | return on(name, true, Reflector.class.getClassLoader()); 54 | } 55 | 56 | public static Reflector on(@NonNull String name, boolean initialize) throws ReflectedException { 57 | return on(name, initialize, Reflector.class.getClassLoader()); 58 | } 59 | 60 | public static Reflector on(@NonNull String name, boolean initialize, @Nullable ClassLoader loader) throws ReflectedException { 61 | try { 62 | return on(Class.forName(name, initialize, loader)); 63 | } catch (Throwable e) { 64 | throw new ReflectedException("Oops!", e); 65 | } 66 | } 67 | 68 | public static Reflector on(@NonNull Class type) { 69 | Reflector reflector = new Reflector(); 70 | reflector.mType = type; 71 | return reflector; 72 | } 73 | 74 | public static Reflector with(@NonNull Object caller) throws ReflectedException { 75 | return on(caller.getClass()).bind(caller); 76 | } 77 | 78 | protected Reflector() { 79 | 80 | } 81 | 82 | public Reflector constructor(@Nullable Class... parameterTypes) throws ReflectedException { 83 | try { 84 | mConstructor = mType.getDeclaredConstructor(parameterTypes); 85 | mConstructor.setAccessible(true); 86 | mField = null; 87 | mMethod = null; 88 | return this; 89 | } catch (Throwable e) { 90 | throw new ReflectedException("Oops!", e); 91 | } 92 | } 93 | 94 | @SuppressWarnings("unchecked") 95 | public R newInstance(@Nullable Object... initargs) throws ReflectedException { 96 | if (mConstructor == null) { 97 | throw new ReflectedException("Constructor was null!"); 98 | } 99 | try { 100 | return (R) mConstructor.newInstance(initargs); 101 | } catch (InvocationTargetException e) { 102 | throw new ReflectedException("Oops!", e.getTargetException()); 103 | } catch (Throwable e) { 104 | throw new ReflectedException("Oops!", e); 105 | } 106 | } 107 | 108 | protected Object checked(@Nullable Object caller) throws ReflectedException { 109 | if (caller == null || mType.isInstance(caller)) { 110 | return caller; 111 | } 112 | throw new ReflectedException("Caller [" + caller + "] is not a instance of type [" + mType + "]!"); 113 | } 114 | 115 | protected void check(@Nullable Object caller, @Nullable Member member, @NonNull String name) throws ReflectedException { 116 | if (member == null) { 117 | throw new ReflectedException(name + " was null!"); 118 | } 119 | if (caller == null && !Modifier.isStatic(member.getModifiers())) { 120 | throw new ReflectedException("Need a caller!"); 121 | } 122 | checked(caller); 123 | } 124 | 125 | public Reflector bind(@Nullable Object caller) throws ReflectedException { 126 | mCaller = checked(caller); 127 | return this; 128 | } 129 | 130 | public Reflector unbind() { 131 | mCaller = null; 132 | return this; 133 | } 134 | 135 | public Reflector field(@NonNull String name) throws ReflectedException { 136 | try { 137 | mField = findField(name); 138 | mField.setAccessible(true); 139 | mConstructor = null; 140 | mMethod = null; 141 | return this; 142 | } catch (Throwable e) { 143 | throw new ReflectedException("Oops!", e); 144 | } 145 | } 146 | 147 | protected Field findField(@NonNull String name) throws NoSuchFieldException { 148 | try { 149 | return mType.getField(name); 150 | } catch (NoSuchFieldException e) { 151 | for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { 152 | try { 153 | return cls.getDeclaredField(name); 154 | } catch (NoSuchFieldException ex) { 155 | // Ignored 156 | } 157 | } 158 | throw e; 159 | } 160 | } 161 | 162 | @SuppressWarnings("unchecked") 163 | public R get() throws ReflectedException { 164 | return get(mCaller); 165 | } 166 | 167 | @SuppressWarnings("unchecked") 168 | public R get(@Nullable Object caller) throws ReflectedException { 169 | check(caller, mField, "Field"); 170 | try { 171 | return (R) mField.get(caller); 172 | } catch (Throwable e) { 173 | throw new ReflectedException("Oops!", e); 174 | } 175 | } 176 | 177 | public Reflector set(@Nullable Object value) throws ReflectedException { 178 | return set(mCaller, value); 179 | } 180 | 181 | public Reflector set(@Nullable Object caller, @Nullable Object value) throws ReflectedException { 182 | check(caller, mField, "Field"); 183 | try { 184 | mField.set(caller, value); 185 | return this; 186 | } catch (Throwable e) { 187 | throw new ReflectedException("Oops!", e); 188 | } 189 | } 190 | 191 | public Reflector method(@NonNull String name, @Nullable Class... parameterTypes) throws ReflectedException { 192 | try { 193 | mMethod = findMethod(name, parameterTypes); 194 | mMethod.setAccessible(true); 195 | mConstructor = null; 196 | mField = null; 197 | return this; 198 | } catch (NoSuchMethodException e) { 199 | throw new ReflectedException("Oops!", e); 200 | } 201 | } 202 | 203 | protected Method findMethod(@NonNull String name, @Nullable Class... parameterTypes) throws NoSuchMethodException { 204 | try { 205 | return mType.getMethod(name, parameterTypes); 206 | } catch (NoSuchMethodException e) { 207 | for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { 208 | try { 209 | return cls.getDeclaredMethod(name, parameterTypes); 210 | } catch (NoSuchMethodException ex) { 211 | // Ignored 212 | } 213 | } 214 | throw e; 215 | } 216 | } 217 | 218 | public R call(@Nullable Object... args) throws ReflectedException { 219 | return callByCaller(mCaller, args); 220 | } 221 | 222 | @SuppressWarnings("unchecked") 223 | public R callByCaller(@Nullable Object caller, @Nullable Object... args) throws ReflectedException { 224 | check(caller, mMethod, "Method"); 225 | try { 226 | return (R) mMethod.invoke(caller, args); 227 | } catch (InvocationTargetException e) { 228 | throw new ReflectedException("Oops!", e.getTargetException()); 229 | } catch (Throwable e) { 230 | throw new ReflectedException("Oops!", e); 231 | } 232 | } 233 | 234 | public static class QuietReflector extends Reflector { 235 | 236 | protected Throwable mIgnored; 237 | 238 | public static QuietReflector on(@NonNull String name) { 239 | return on(name, true, QuietReflector.class.getClassLoader()); 240 | } 241 | 242 | public static QuietReflector on(@NonNull String name, boolean initialize) { 243 | return on(name, initialize, QuietReflector.class.getClassLoader()); 244 | } 245 | 246 | public static QuietReflector on(@NonNull String name, boolean initialize, @Nullable ClassLoader loader) { 247 | Class cls = null; 248 | try { 249 | cls = Class.forName(name, initialize, loader); 250 | return on(cls, null); 251 | } catch (Throwable e) { 252 | // Log.w(LOG_TAG, "Oops!", e); 253 | return on(cls, e); 254 | } 255 | } 256 | 257 | public static QuietReflector on(@Nullable Class type) { 258 | return on(type, (type == null) ? new ReflectedException("Type was null!") : null); 259 | } 260 | 261 | private static QuietReflector on(@Nullable Class type, @Nullable Throwable ignored) { 262 | QuietReflector reflector = new QuietReflector(); 263 | reflector.mType = type; 264 | reflector.mIgnored = ignored; 265 | return reflector; 266 | } 267 | 268 | public static QuietReflector with(@Nullable Object caller) { 269 | if (caller == null) { 270 | return on((Class) null); 271 | } 272 | return on(caller.getClass()).bind(caller); 273 | } 274 | 275 | protected QuietReflector() { 276 | 277 | } 278 | 279 | public Throwable getIgnored() { 280 | return mIgnored; 281 | } 282 | 283 | protected boolean skip() { 284 | return skipAlways() || mIgnored != null; 285 | } 286 | 287 | protected boolean skipAlways() { 288 | return mType == null; 289 | } 290 | 291 | @Override 292 | public QuietReflector constructor(@Nullable Class... parameterTypes) { 293 | if (skipAlways()) { 294 | return this; 295 | } 296 | try { 297 | mIgnored = null; 298 | super.constructor(parameterTypes); 299 | } catch (Throwable e) { 300 | mIgnored = e; 301 | // Log.w(LOG_TAG, "Oops!", e); 302 | } 303 | return this; 304 | } 305 | 306 | @Override 307 | public R newInstance(@Nullable Object... initargs) { 308 | if (skip()) { 309 | return null; 310 | } 311 | try { 312 | mIgnored = null; 313 | return super.newInstance(initargs); 314 | } catch (Throwable e) { 315 | mIgnored = e; 316 | // Log.w(LOG_TAG, "Oops!", e); 317 | } 318 | return null; 319 | } 320 | 321 | @Override 322 | public QuietReflector bind(@Nullable Object obj) { 323 | if (skipAlways()) { 324 | return this; 325 | } 326 | try { 327 | mIgnored = null; 328 | super.bind(obj); 329 | } catch (Throwable e) { 330 | mIgnored = e; 331 | // Log.w(LOG_TAG, "Oops!", e); 332 | } 333 | return this; 334 | } 335 | 336 | @Override 337 | public QuietReflector unbind() { 338 | super.unbind(); 339 | return this; 340 | } 341 | 342 | @Override 343 | public QuietReflector field(@NonNull String name) { 344 | if (skipAlways()) { 345 | return this; 346 | } 347 | try { 348 | mIgnored = null; 349 | super.field(name); 350 | } catch (Throwable e) { 351 | mIgnored = e; 352 | // Log.w(LOG_TAG, "Oops!", e); 353 | } 354 | return this; 355 | } 356 | 357 | @Override 358 | public R get() { 359 | if (skip()) { 360 | return null; 361 | } 362 | try { 363 | mIgnored = null; 364 | return super.get(); 365 | } catch (Throwable e) { 366 | mIgnored = e; 367 | // Log.w(LOG_TAG, "Oops!", e); 368 | } 369 | return null; 370 | } 371 | 372 | @Override 373 | public R get(@Nullable Object caller) { 374 | if (skip()) { 375 | return null; 376 | } 377 | try { 378 | mIgnored = null; 379 | return super.get(caller); 380 | } catch (Throwable e) { 381 | mIgnored = e; 382 | // Log.w(LOG_TAG, "Oops!", e); 383 | } 384 | return null; 385 | } 386 | 387 | @Override 388 | public QuietReflector set(@Nullable Object value) { 389 | if (skip()) { 390 | return this; 391 | } 392 | try { 393 | mIgnored = null; 394 | super.set(value); 395 | } catch (Throwable e) { 396 | mIgnored = e; 397 | // Log.w(LOG_TAG, "Oops!", e); 398 | } 399 | return this; 400 | } 401 | 402 | @Override 403 | public QuietReflector set(@Nullable Object caller, @Nullable Object value) { 404 | if (skip()) { 405 | return this; 406 | } 407 | try { 408 | mIgnored = null; 409 | super.set(caller, value); 410 | } catch (Throwable e) { 411 | mIgnored = e; 412 | // Log.w(LOG_TAG, "Oops!", e); 413 | } 414 | return this; 415 | } 416 | 417 | @Override 418 | public QuietReflector method(@NonNull String name, @Nullable Class... parameterTypes) { 419 | if (skipAlways()) { 420 | return this; 421 | } 422 | try { 423 | mIgnored = null; 424 | super.method(name, parameterTypes); 425 | } catch (Throwable e) { 426 | mIgnored = e; 427 | // Log.w(LOG_TAG, "Oops!", e); 428 | } 429 | return this; 430 | } 431 | 432 | @Override 433 | public R call(@Nullable Object... args) { 434 | if (skip()) { 435 | return null; 436 | } 437 | try { 438 | mIgnored = null; 439 | return super.call(args); 440 | } catch (Throwable e) { 441 | mIgnored = e; 442 | // Log.w(LOG_TAG, "Oops!", e); 443 | } 444 | return null; 445 | } 446 | 447 | @Override 448 | public R callByCaller(@Nullable Object caller, @Nullable Object... args) { 449 | if (skip()) { 450 | return null; 451 | } 452 | try { 453 | mIgnored = null; 454 | return super.callByCaller(caller, args); 455 | } catch (Throwable e) { 456 | mIgnored = e; 457 | // Log.w(LOG_TAG, "Oops!", e); 458 | } 459 | return null; 460 | } 461 | } 462 | } 463 | -------------------------------------------------------------------------------- /largeimage/src/main/java/org/zzy/lib/largeimage/util/ResHelper.java: -------------------------------------------------------------------------------- 1 | package org.zzy.lib.largeimage.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.ColorStateList; 5 | import android.content.res.Resources; 6 | import android.graphics.drawable.Drawable; 7 | 8 | import android.text.Html; 9 | import android.text.TextUtils; 10 | 11 | 12 | import androidx.annotation.ArrayRes; 13 | import androidx.annotation.ColorRes; 14 | import androidx.annotation.DimenRes; 15 | import androidx.annotation.DrawableRes; 16 | import androidx.annotation.RawRes; 17 | import androidx.annotation.StringRes; 18 | 19 | import org.zzy.lib.largeimage.LargeImage; 20 | import org.zzy.lib.largeimage.R; 21 | 22 | import java.io.BufferedOutputStream; 23 | import java.io.ByteArrayOutputStream; 24 | import java.io.File; 25 | import java.io.FileOutputStream; 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.io.OutputStream; 29 | 30 | 31 | /** 32 | * @author : Zhouzhengyi 33 | */ 34 | 35 | public class ResHelper { 36 | /** 37 | * 缓存区大小 38 | */ 39 | private static final int BUFFER_SIZE = 8192; 40 | 41 | /** 42 | * 获取全局的Context 43 | * 44 | * @return Context 45 | */ 46 | public static Context context() { 47 | return LargeImage.APPLICATION.getApplicationContext(); 48 | } 49 | 50 | /** 51 | * 获取字符串资源 52 | * 53 | * @param resId 字符串资源ID 54 | * @return 字符串 55 | */ 56 | public static String getString(@StringRes int resId) { 57 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 58 | if (null == resources || 0 == resId) { 59 | return ""; 60 | } 61 | return resources.getString(resId); 62 | } 63 | 64 | /** 65 | * 获取字符串资源 66 | * 67 | * @param resId 字符串资源ID 68 | * @param formatArgs 格式化 69 | * @return 字符串 70 | */ 71 | public static String getString(@StringRes int resId, Object... formatArgs) { 72 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 73 | if (null == resources || 0 == resId || null == formatArgs) { 74 | return ""; 75 | } 76 | return resources.getString(resId, formatArgs); 77 | } 78 | 79 | /** 80 | * 获取颜色资源 81 | * 82 | * @param resId 字颜色资源ID 83 | * @return 颜色值 84 | */ 85 | public static int getColor(@ColorRes int resId) { 86 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 87 | if (null == resources || 0 == resId) { 88 | resId = R.color.c999999; 89 | } 90 | return resources.getColor(resId); 91 | } 92 | 93 | /** 94 | * 获取颜色状态集合 95 | * 96 | * @param resId 颜色集合资源ID 97 | * @return 颜色集合 98 | */ 99 | public static ColorStateList getColorStateList(@ColorRes int resId) { 100 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 101 | if (null == resources || 0 == resId) { 102 | resId = R.color.c999999; 103 | } 104 | return resources.getColorStateList(resId); 105 | } 106 | 107 | /** 108 | * 获取Dimension 109 | * 110 | * @param resId Dimen资源ID 111 | * @return Dimen 112 | */ 113 | public static int getDimensionPixelOffset(@DimenRes int resId) { 114 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 115 | if (null == resources || 0 == resId) { 116 | return 0; 117 | } 118 | return resources.getDimensionPixelOffset(resId); 119 | } 120 | 121 | /** 122 | * 获取drawable资源 123 | * 124 | * @param resId drawable资源ID 125 | * @return Drawable 126 | */ 127 | public static Drawable getDrawable(@DrawableRes int resId) { 128 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 129 | if (null == resources || 0 == resId) { 130 | return null; 131 | } 132 | return resources.getDrawable(resId); 133 | } 134 | 135 | /** 136 | * 获取字符串数组资源 137 | * 138 | * @param resId 字符串数组资源ID 139 | * @return 字符串数组 140 | */ 141 | public static String[] getStringArray(@ArrayRes int resId) { 142 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 143 | if (null == resources || 0 == resId) { 144 | return new String[]{}; 145 | } 146 | return resources.getStringArray(resId); 147 | } 148 | 149 | 150 | /** 151 | * 从assets 中读取字符串 152 | * 153 | * @param assetsFilePath assets 文件路径 154 | * @return 内容 155 | */ 156 | public static String readAssetsString(final String assetsFilePath) { 157 | InputStream is; 158 | try { 159 | is = context().getAssets().open(assetsFilePath); 160 | } catch (IOException e) { 161 | e.printStackTrace(); 162 | return null; 163 | } 164 | byte[] bytes = is2Bytes(is); 165 | if (bytes == null) { return null; } 166 | return new String(bytes); 167 | } 168 | 169 | 170 | /** 171 | * 拷贝 raw 下的文件 172 | * 173 | * @param resId 资源ID 174 | * @param destFilePath 目标文件路径 175 | * @return {@code true}: 成功
{@code false}: 失败 176 | */ 177 | public static boolean copyFileFromRaw(@RawRes final int resId, final String destFilePath) { 178 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 179 | return writeFileFromIS(destFilePath, resources.openRawResource(resId), false 180 | ); 181 | } 182 | 183 | /** 184 | * 读取 raw 中的字符串 185 | * 186 | * @param resId 资源ID 187 | * @return 字符串内容 188 | */ 189 | public static String readRawString(@RawRes final int resId) { 190 | Resources resources = LargeImage.APPLICATION.getApplicationContext().getResources(); 191 | InputStream is = resources.openRawResource(resId); 192 | byte[] bytes = is2Bytes(is); 193 | if (bytes == null) { return null; } 194 | return new String(bytes); 195 | } 196 | 197 | /** 198 | * 从 InputStream 写入文件 199 | * 200 | * @param filePath 文件路径 201 | * @param is InputStream 输入流 202 | * @param append 是否追加写入 203 | * @return 写文件结果 204 | */ 205 | private static boolean writeFileFromIS(final String filePath, InputStream is, boolean append) { 206 | return writeFileFromIS(getFileByPath(filePath), is, append); 207 | } 208 | 209 | /** 210 | * 从 InputStream 写入文件 211 | * 212 | * @param file 文件 213 | * @param is InputStream 输入流 214 | * @param append 是否追加写入 215 | * @return 写文件结果 216 | */ 217 | private static boolean writeFileFromIS(final File file, final InputStream is, final boolean append) { 218 | if (!createOrExistsFile(file) || is == null) { return false; } 219 | OutputStream os = null; 220 | try { 221 | os = new BufferedOutputStream(new FileOutputStream(file, append)); 222 | byte[] data = new byte[BUFFER_SIZE]; 223 | int len; 224 | while ((len = is.read(data, 0, BUFFER_SIZE)) != -1) { 225 | os.write(data, 0, len); 226 | } 227 | return true; 228 | } catch (IOException e) { 229 | e.printStackTrace(); 230 | return false; 231 | } finally { 232 | try { 233 | is.close(); 234 | } catch (IOException e) { 235 | e.printStackTrace(); 236 | } 237 | try { 238 | if (os != null) { 239 | os.close(); 240 | } 241 | } catch (IOException e) { 242 | e.printStackTrace(); 243 | } 244 | } 245 | } 246 | 247 | /** 248 | * 创建或者已存在的文件 249 | * 250 | * @param file 文件 251 | * @return true 创建成功或者是文件已存在 252 | */ 253 | private static boolean createOrExistsFile(File file) { 254 | if (file == null) { return false; } 255 | if (file.exists()) { return file.isFile(); } 256 | if (!createOrExistsDir(file.getParentFile())) { return false; } 257 | try { 258 | return file.createNewFile(); 259 | } catch (IOException e) { 260 | e.printStackTrace(); 261 | return false; 262 | } 263 | } 264 | 265 | /** 266 | * 创建或者已存在的文件目录 267 | * 268 | * @param file 文件 269 | * @return true 创建成功或者是文件已存在 270 | */ 271 | private static boolean createOrExistsDir(File file) { 272 | return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); 273 | } 274 | 275 | /** 276 | * 通过路径获取文件 277 | * 278 | * @param filePath 文件路径 279 | * @return 文件 280 | */ 281 | private static File getFileByPath(String filePath) { 282 | return isSpace(filePath) ? null : new File(filePath); 283 | } 284 | 285 | /** 286 | * 是否是空 287 | * 288 | * @param s 字符串 289 | * @return true 空,false 非空 290 | */ 291 | private static boolean isSpace(String s) { 292 | if (s == null) { return true; } 293 | for (int i = 0, len = s.length(); i < len; ++i) { 294 | if (!Character.isWhitespace(s.charAt(i))) { 295 | return false; 296 | } 297 | } 298 | return true; 299 | } 300 | 301 | /** 302 | * 是否是字节数组 303 | * 304 | * @param is InputStream 输入流 305 | * @return 字节数组 306 | */ 307 | private static byte[] is2Bytes(final InputStream is) { 308 | if (is == null) { return null; } 309 | ByteArrayOutputStream os = null; 310 | try { 311 | os = new ByteArrayOutputStream(); 312 | byte[] b = new byte[BUFFER_SIZE]; 313 | int len; 314 | while ((len = is.read(b, 0, BUFFER_SIZE)) != -1) { 315 | os.write(b, 0, len); 316 | } 317 | return os.toByteArray(); 318 | } catch (IOException e) { 319 | e.printStackTrace(); 320 | return null; 321 | } finally { 322 | try { 323 | is.close(); 324 | } catch (IOException e) { 325 | e.printStackTrace(); 326 | } 327 | try { 328 | if (os != null) { 329 | os.close(); 330 | } 331 | } catch (IOException e) { 332 | e.printStackTrace(); 333 | } 334 | } 335 | } 336 | 337 | 338 | /** 339 | * @param content 内容 340 | * @param mode 模式 341 | * Html.FROM_HTML_MODE_COMPACT:html块元素之间使用一个换行符分隔 342 | * Html.FROM_HTML_MODE_LEGACY:html块元素之间使用两个换行符分隔 343 | */ 344 | public static CharSequence fromHtml(String content, int mode){ 345 | if(TextUtils.isEmpty(content)){ 346 | return ""; 347 | } 348 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { 349 | return Html.fromHtml(content,mode); 350 | } else { 351 | return Html.fromHtml(content); 352 | } 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable-xxhdpi/checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/drawable-xxhdpi/checkbox_checked.png -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable-xxhdpi/checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/drawable-xxhdpi/checkbox_unchecked.png -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable-xxhdpi/ic_default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/drawable-xxhdpi/ic_default.jpg -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable-xxhdpi/ic_failed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/drawable-xxhdpi/ic_failed.jpg -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable/ic_alarm.xml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable/ic_arrow_back_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable/ic_more_vert_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable/ic_satellite.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 | 51 | 52 | -------------------------------------------------------------------------------- /largeimage/src/main/res/drawable/switch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /largeimage/src/main/res/layout/activity_largeimage_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 21 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /largeimage/src/main/res/layout/activity_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 15 | 16 | 21 | 28 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /largeimage/src/main/res/layout/dialog_custom.xml: -------------------------------------------------------------------------------- 1 |  2 | 8 | 9 | 10 | 16 | 17 | 18 | 26 | 27 | 34 | 35 | 43 | 44 | 45 | 54 | 55 | 62 | 63 | 71 | 72 | 73 | 74 | 83 | 92 | 103 | -------------------------------------------------------------------------------- /largeimage/src/main/res/menu/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /largeimage/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/largeimage/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /largeimage/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF999999 4 | 5 | -------------------------------------------------------------------------------- /largeimage/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 内存大小:%sMB 3 | 文件大小:%sKB 4 | 图片尺寸:%s 5 | View尺寸:%s 6 | 图片地址:%s 7 | 8 | -------------------------------------------------------------------------------- /largeimage/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 12 | 18 | -------------------------------------------------------------------------------- /largeimage/upload.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.novoda.bintray-release' 2 | 3 | publish { 4 | 5 | def groupProjectID = 'org.zzy.lib.largeimage' 6 | def artifactProjectID = 'large-image-monitor' 7 | def publishVersionID = "${rootProject.ext.android["jcenterArchivesVersionName"]}" 8 | 9 | userOrg = 'zzy' 10 | repoName = "imageMonitor" 11 | groupId = groupProjectID 12 | artifactId = artifactProjectID 13 | publishVersion = publishVersionID 14 | desc = 'Oh hi, this is a library which can be monitor large image.' 15 | website = 'https://github.com/121880399/LargeImageMonitor' 16 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='ImageMonitor' 2 | include ':app' 3 | include ':largeimage' 4 | include ':largeimage-plugin' 5 | -------------------------------------------------------------------------------- /wiki/alipay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/wiki/alipay.jpg -------------------------------------------------------------------------------- /wiki/example1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/wiki/example1.jpg -------------------------------------------------------------------------------- /wiki/example2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/wiki/example2.jpg -------------------------------------------------------------------------------- /wiki/example3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/wiki/example3.jpg -------------------------------------------------------------------------------- /wiki/example4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/wiki/example4.jpg -------------------------------------------------------------------------------- /wiki/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/wiki/logo.png -------------------------------------------------------------------------------- /wiki/wxpay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/121880399/LargeImageMonitor/8db54dfff671c213d39ae06956d8cc4bed10f31e/wiki/wxpay.jpg --------------------------------------------------------------------------------