├── .gitignore ├── README.md └── android-as-blogs ├── Android Studio快捷键系列博客 └── README.md ├── Android-Animators-Blogs └── README.md ├── Android-Architectures └── README.md ├── Android-Bases └── README.md ├── Android-Complex-Blogs └── README.md ├── Android-Custom-Views └── README.md ├── Android-Data-Binding └── README.md ├── Android-Db-Nosql └── README.md ├── Android-Db-Orm └── README.md ├── Android-Debug-Tools └── README.md ├── Android-Di └── README.md ├── Android-Dynamical-Loading └── README.md ├── Android-Gradle-blogs └── README.md ├── Android-ImageLoaders-libs └── README.md ├── Android-Imageloaders ├── README.md └── blogs │ ├── images │ └── scrolling-slight.gif │ └── yelp_android_imageload_with_glide.md ├── Android-Java-Annotations ├── README.md └── blogs │ ├── annotation-processing.md │ └── java-annotations.md ├── Android-Kotlin-Blogs └── README.md ├── Android-Loaders └── README.md ├── Android-Net └── README.md ├── Android-Open-Source-Projects └── README.md ├── Android-Performance └── README.md ├── Android-Program-Specification └── README.md ├── Android-Pull-To-Refresh └── README.md ├── Android-ReactNative └── README.md ├── Android-RecycleView └── README.md ├── Android-Runtime-Permission └── README.md ├── Android-RxJava └── README.md ├── Android-Support-Design └── README.md ├── Android-Tools-Libs └── README.md ├── Android-UI-Tips └── README.md ├── Android-Views └── README.md ├── Material适配博客 └── README.md └── 使用不同的Theme实现主题切换系列两篇 └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android开发实践总结 2 | 收集Android开发中各种最佳实践的文章 3 | 4 | ## 最新推荐 5 | * [AndroidStudio 优秀插件汇总](https://github.com/dreamlivemeng/androidstudio-plugins) 6 | * [Android打包的那些事-(Gradle打包好文,强烈推荐!!!)](http://www.jayfeng.com/2015/11/07/Android%E6%89%93%E5%8C%85%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/) 7 | * [鸿洋个人博客,每一篇质量都很高](http://blog.csdn.net/lmj623565791) 8 | * [给 Android 开发者的 RxJava 详解](http://gank.io/post/560e15be2dca930e00da1083) 9 | * [Android 6.0 运行时权限处理完全解析](http://blog.csdn.net/lmj623565791/article/details/50709663) 10 | * [Android M 新的运行时权限开发者需要知道的一切](http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/) 11 | * [胡凯 Android开发最佳实践](http://hukai.me/android-dev-patterns/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io) 12 | * [Android开源框架分类](http://www.kuqin.com/shuoit/20150909/347960.html) 13 | * [Android 开发中,有哪些坑需要注意?](http://zhuanlan.zhihu.com/zmywly8866/20309921) 14 | * [Android批量打包极速版](http://ihongqiqu.com/blog/2015/07/16/android-mutiple-channel-build/) 15 | * [安卓开发中遇到的重难点解析,也包括平常的读书笔记和知识点整理](https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis) 16 | * [EventBus源码研读](http://kymjs.com/code/2015/12/12/01/) 17 | * [Android 开发实战经验总结](http://www.jianshu.com/p/4f152bc8f4f3#) 18 | * [浅谈Android自定义锁屏页的发车姿势](http://mp.weixin.qq.com/s?__biz=MzI1NjEwMTM4OA==&mid=2651231949&idx=1&sn=c5de2b2a719644392ddc756a6c6d5920) 19 | 20 | ## Android分类文章 21 | | 描述 | 地址 | 22 | |---------|--------| 23 | | Android基础 | [Android 基础系列](android-as-blogs/Android-Bases) | 24 | | Material适配博客 | [Material适配博客](android-as-blogs/Material适配博客) | 25 | | Android-Support-Design博客 | [Android-Support-Design博客](android-as-blogs/Android-Support-Design) | 26 | | Android Data Binding | [Android Data Binding](android-as-blogs/Android-Data-Binding) | 27 | | Android 性能优化 | [Android 性能优化](android-as-blogs/Android-Performance) | 28 | | Android 网络系列 | [Android 网络系列](android-as-blogs/Android-Net) | 29 | | Android RxJava系列 | [Android RxJava系列](android-as-blogs/Android-RxJava) | 30 | | Android ImageLoaders | [图片加载系列](android-as-blogs/Android-Imageloaders) | 31 | | Android Gradle | [Android Gradle相关文章](android-as-blogs/Android-Gradle-blogs) | 32 | | Android 动画 | [Android 动画相关文章](android-as-blogs/Android-Animators-Blogs) | 33 | | android 架构| [android 架构](android-as-blogs/Android-Architectures) | 34 | | android 无分类并值得一读的文章 | [android 无分类文章](android-as-blogs/Android-Complex-Blogs) | 35 | | 天之界线的Android最佳实践系列博文 | [Android最佳实践示例](https://github.com/tianzhijiexian/Android-Best-Practices) | 36 | | Android UI处理系列 | [Android UI处理小技巧](android-as-blogs/Android-UI-Tips) | 37 | | 自定义View | [自定义View](android-as-blogs/Android-Custom-Views) | 38 | | Loaders & LoaderManager | [Loaders & LoaderManager](android-as-blogs/Android-Loaders) | 39 | | Android编程规范专题 | [Android编程规范专题](android-as-blogs/Android-Program-Specification) | 40 | | kotlin专题 | [kotlin系列文章](android-as-blogs/Android-Kotlin-Blogs) | 41 | | Java Annotations | [Java Annotations专题](android-as-blogs/Android-Java-Annotations) | 42 | | Android热修复 | [Android动态加载技术](android-as-blogs/Android-Dynamical-Loading) | 43 | | ReactNative | [ReactNative](android-as-blogs/Android-ReactNative) | 44 | | Android依赖注入 | [Android依赖注入](android-as-blogs/Android-Di) | 45 | | 运行时权限适配 | [运行时权限适配](android-as-blogs/Android-Runtime-Permission) | 46 | 47 | ## Android开发轮子 48 | | 描述 | 地址 | 49 | |---------|--------| 50 | | 调试工具 | [调试工具集合](android-as-blogs/Android-Debug-Tools) | 51 | | NoSql数据库 | [NoSql数据库](android-as-blogs/Android-Db-Nosql) | 52 | | ORM数据库 | [ORM数据库](android-as-blogs/Android-Db-Orm) | 53 | | 下拉刷新 | [下拉刷新收集](android-as-blogs/Android-Pull-To-Refresh) | 54 | | ImageLoaders | [图片加载](android-as-blogs/Android-ImageLoaders-libs) | 55 | | 实用工具 | [实用工具集合](android-as-blogs/Android-Tools-Libs) | 56 | | 完整项目 | [完整开源项目](android-as-blogs/Android-Open-Source-Projects) | 57 | | views集合 | [view控件集合](android-as-blogs/Android-Views) | 58 | | square良心出品,必属精品 | [square项目集合](http://square.github.io/) | 59 | 60 | 61 | ## Android开发Tips 62 | | 描述 | 地址 | 63 | |---------|--------| 64 | | 判断APP是否在前台运行(前台和后台切换,支持4.0以上) | [APP前后台切换监听(实现ActivityLifecycleCallbacks接口)](http://www.600000300.com/2015/10/14/%E7%9B%91%E5%90%ACAndroid%E6%89%80%E6%9C%89Activity%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/) | 65 | | 判断APP是否在前台运行(前台和后台切换,通用实现) | [APP前后台切换监听通用实现](http://engineering.meetme.com/2015/04/android-determine-when-app-is-opened-or-closed/) | 66 | | Android平台免Root无侵入AOP框架Dexposed使用详解 | [Android平台免Root无侵入AOP框架Dexposed使用详解](http://www.jianshu.com/p/14edcb444c51) | 67 | | Android开发中那些让你觉得相见恨晚的方法、类或接口 | [Android开发中那些让你觉得相见恨晚的方法、类或接口](http://www.zhihu.com/question/33636939) | 68 | | ADB通过WiFi连接手机调试Android应用 | [ADB通过WiFi连接手机调试Android应用](http://segmentfault.com/a/1190000002727029) | 69 | 70 | 71 | ## Android Studio使用 72 | | 描述 | 地址 | 73 | |---------|--------| 74 | | Android Studio快捷键系列博客 | [Android Studio快捷键系列博客](android-as-blogs/Android Studio快捷键系列博客) | 75 | | 自定义Android 代码生成模版 | [打造更好用的 Android Studio:添加 Templates 以及改造 gradle-projects](http://www.jianshu.com/p/5d7b0d84f693) | 76 | | Android Studio调试 | [Android Studio你不知道的调试技巧](http://tianweishu.com/2015/12/21/android-studio-debug-tips-you-may-not-know/) | 77 | | Android Studio Jar、so、library项目依赖 | [Android Studio Jar、so、library项目依赖](http://rocko.xyz/2014/12/13/Android-Studio-jar%E3%80%81so%E3%80%81library%E9%A1%B9%E7%9B%AE%E4%BE%9D%E8%B5%96/) | 78 | 79 | ## Android Notifications 80 | | 描述 | 地址 | 81 | |---------|--------| 82 | | 正确的显示通知栏提示 | [Notification显示通知最佳实践](http://blog.csdn.net/xy_nyle/article/details/19853591) | 83 | 84 | ## Java基础 85 | * [深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)](http://www.cnblogs.com/figure9/p/java-8-lambdas-insideout-language-features.html) 86 | * [Android GC 那点事---QQ空间终端开发团队](http://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400021278&idx=1&sn=0e971807eb0e9dcc1a81853189a092f3#rd.) 87 | 88 | ## Git 89 | * [使用git和github进行协同开发流程](http://livoras.com/post/28) 90 | * [这些git技能够你用一年了](http://blog.jobbole.com/90061/) 91 | * [将Library上传到Jcenter](http://www.jianshu.com/p/0ba8960f80a9) 92 | * [常用 Git 命令清单](http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html) 93 | * [Git 撤销合并](http://blog.psjay.com/posts/git-revert-merge-commit/) 94 | * [Commit message 和 Change log 编写指南](http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html) 95 | * [如何使用Android Studio把自己的Android library分享到jCenter和Maven Central](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0623/3097.html) 96 | 97 | ## 主题 98 | [主题切换](android-as-blogs/使用不同的Theme实现主题切换系列两篇) 99 | 100 | ## HTML5 101 | * [H5 缓存机制浅析 移动端 Web 加载性能优化](http://segmentfault.com/a/1190000004132566) 102 | * [Android安全开发之浅谈网页打开APP](https://yq.aliyun.com/articles/57088?&utm_source=qq) 103 | -------------------------------------------------------------------------------- /android-as-blogs/Android Studio快捷键系列博客/README.md: -------------------------------------------------------------------------------- 1 | Android Studio快捷键系列博客 2 | --- 3 | 1. [开篇](http://www.developerphil.com/android-studio-tips-tricks-moving-around/) 4 | 2. [第一篇](http://www.developerphil.com/android-studio-tips-of-the-day-roundup-1/) 5 | 3. [第二篇](http://www.developerphil.com/android-studio-tips-of-the-day-roundup-2/) 6 | 4. [第三篇](http://www.developerphil.com/android-studio-tips-of-the-day-roundup-3/) 7 | 5. [第四篇](http://www.developerphil.com/android-studio-tips-of-the-day-roundup-4/) 8 | 6. [第五篇](http://www.developerphil.com/android-studio-tips-of-the-day-roundup-5/) 9 | 7. [第六篇](http://www.developerphil.com/android-studio-tips-of-the-day-roundup-6/) 10 | 11 | * [倍数提高工作效率的Android Studio奇技](http://zlv.me/posts/2015/07/13/14_android-studio-tips/) 12 | * [Android Studio for Experts:Edit](http://www.jianshu.com/p/c873441be31e) 13 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Animators-Blogs/README.md: -------------------------------------------------------------------------------- 1 | Android 动画相关文章 2 | --- 3 | 4 | * [Android应用开发之所有动画使用详解](http://blog.csdn.net/yanbober/article/details/46481171) 5 | * [Facebook Rebound 弹性动画库 源码分析](http://blog.zhaiyifan.cn/2015/09/10/Facebook-Rebound-%E5%BC%B9%E6%80%A7%E5%8A%A8%E7%94%BB%E5%BA%93-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) 6 | * [Android LayoutAnimation使用及扩展](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0619/3090.html) 7 | * [在RecyclerView上使用布局动画(Layout animation)](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3462.html) 8 | * [超详细的Material-Animations示例](https://github.com/lgvalle/Material-Animations) 9 | * [InstaMaterial:正确处理RecyclerView动画](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1217/3782.html) 10 | 11 | ## libs 12 | * [Animation for support-v4 Fragment transition.](https://github.com/kakajika/FragmentAnimations) 13 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Architectures/README.md: -------------------------------------------------------------------------------- 1 | Android 架构 2 | --- 3 | 4 | ### 文章 5 | * [安卓中的Flux架构](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0816/3311.html) 6 | * [被误解的 MVC 和被神化的 MVVM](http://blog.devtang.com/blog/2015/11/02/mvc-and-mvvm/) 7 | * [界面之下:还原真实的 MVC、MVP、MVVM 模式](http://www.lvsec.cn/1312.html) 8 | * [用MVP架构开发Android应用](http://kymjs.com/code/2015/11/09/01/) 9 | * [Android MVP模式 简单易懂的介绍方式](http://kaedea.com/2015/10/11/android-mvp-pattern/) 10 | * [重构 Plaid app - 响应式的MVP模式(一)](http://hanks.xyz/2015/12/08/refactoring-plaid-1/) 11 | * [Android应用架构-MVP](http://www.jianshu.com/p/8ca27934c6e6) 12 | * [Architecting Android with RxJava](http://www.jianshu.com/p/943ceaccfdff) 13 | * [【译】Android技术栈,1#架构](http://www.jianshu.com/p/1351f5b2f612) 14 | * [Android MVVM到底是啥?看完就明白了](http://mp.weixin.qq.com/s?__biz=MzA4MjU5NTY0NA==&mid=401410759&idx=1&sn=89f0e3ddf9f21f6a5d4de4388ef2c32f&scene=1&srcid=1221SAX6oElWkcvd58Q6B6lW&from=groupmessage&isappinstalled=0#wechat_redirect) 15 | * [MVC,MVP 和 MVVM 的图示](http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html) 16 | * [MVP模式在Android项目中的使用](http://www.liuling123.com/2015/12/mvp-pattern-android.html) 17 | * [Android Basic Project Architecture for MVP](https://medium.com/mobiwise-blog/android-basic-project-architecture-for-mvp-72f4b33252d0#.94z6877p3) 18 | * [MVVM_Android-CleanArchitecture](http://segmentfault.com/a/1190000003966281) 19 | * [Flux架构与Android](http://www.devtf.cn/?p=1193) 20 | * [Android的MVP设计模式](http://blog.waynell.com/2015/05/29/mvp-on-android/) 21 | * [Android应用架构 (Android Dev Summit 2015)](http://blog.zhaiyifan.cn/2016/01/29/android-app-architecture-2015/?from=groupmessage&isappinstalled=0) 22 | * [如何设计MVP中的Presentation层](http://blog.chengdazhi.com/index.php/115) 23 | * [Android架构文章合集](https://github.com/Juude/Awesome-Android-Architecture/) 24 | * [Android官方MVP架构示例项目解析](http://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&mid=403539764&idx=1&sn=d30d89e6848a8e13d4da0f5639100e5f#rd) 25 | 26 | ### 项目 27 | * [to MVVM using RxJava with new Android databinding](https://github.com/ffgiraldez/rx-mvvm-android) 28 | * [mr-mantou-android-MVVM](https://github.com/oxoooo/mr-mantou-android) 29 | * [GankDaily-MVP](https://github.com/maoruibin/GankDaily) 30 | * [phphub-android-MVP](https://github.com/CycloneAxe/phphub-android) 31 | * [KSimpleLibrary-MVP](https://github.com/kot32go/KSimpleLibrary) 32 | * [Material-Movies-MVP](https://github.com/saulmm/Material-Movies) 33 | * [SimpleNews-MVP](https://github.com/liuling07/SimpleNews) 34 | * [qualitymatters](https://github.com/artem-zinnatullin/qualitymatters) 35 | * [MVVM_Android-CleanArchitecture](https://github.com/zhengxiaopeng/MVVM_Android-CleanArchitecture) 36 | * [Gank.io-MVP](https://github.com/Panl/Gank.io) 37 | * [archi-MVP-MVVM](https://github.com/ivacf/archi) 38 | * [V2EXAndroidClient-MVVM](https://github.com/LenaYan/V2EXAndroidClient) 39 | * [MeiziAPP-MVP](https://github.com/xuyunqiang/MeiziAPP) 40 | * [Rosie](https://github.com/Karumi/Rosie) 41 | * [FluxyAndroidTodo](https://github.com/armueller/FluxyAndroidTodo) 42 | * [Idaily-MVVM知乎日报](https://github.com/liuguangqiang/Idaily) 43 | 44 | ### 工具 45 | * [nucleus-MVP](https://github.com/konmik/nucleus) 46 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Bases/README.md: -------------------------------------------------------------------------------- 1 | Android 基础系列 2 | --- 3 | 4 | 5 | * [如何选择 compileSdkVersion, minSdkVersion 和 targetSdkVersion](http://chinagdg.org/2016/01/picking-your-compilesdkversion-minsdkversion-targetsdkversion/) 6 | 7 | ## 控件 8 | * [Translucent System Bar 的最佳实践](http://www.jianshu.com/p/0acc12c29c1b) 9 | * [最详细的 Toolbar 开发实践总结](http://www.jianshu.com/p/79604c3ddcae) 10 | * [最详细的 NavigationDrawer 开发实践总结](http://www.jianshu.com/p/c8cbeb7ea43a) 11 | * [Android4.4及以上版本实现状态栏与顶栏同色](http://fanhongwei.github.io/blog/2015/03/04/android-statusbartint-and-swipebacklayout/) 12 | * [Android App 沉浸式状态栏解决方案](http://laobie.github.io/android/2016/02/15/status-bar-demo.html) 13 | 14 | ## Matrix 15 | * [深入理解 Android 中的 Matrix](http://www.jianshu.com/p/6aa6080373ab) 16 | * [Matrix原理](https://github.com/GcsSloop/AndroidNote/blob/master/CustomView/Advance/%5B09%5DMatrix_Basic.md) 17 | 18 | ## drawables 19 | * [Android drawable微技巧,你所不知道的drawable的那些细节](http://blog.csdn.net/guolin_blog/article/details/50727753) 20 | 21 | ## Path 22 | * [贝塞尔曲线开发的艺术](http://www.jianshu.com/p/55c721887568) 23 | * [PathMeasure之迷径追踪](http://www.jianshu.com/p/3efa5341abcc) 24 | 25 | ## 动画 26 | * [浅析Android动画(一),View动画高级实例探究](http://www.cnblogs.com/wondertwo/p/5295976.html) 27 | * [浅析Android动画(二),属性动画与高级实例探究](http://www.cnblogs.com/wondertwo/p/5312482.html) 28 | 29 | ## Dialog 30 | * [Android:我为何要封装DialogFragment?](http://www.jianshu.com/p/af6499abd5c2?utm_campaign=maleskine&utm_content=note&utm_me) 31 | 32 | ## Service 33 | * [Service学习笔记](http://www.jianshu.com/p/e3b7954b9c00) 34 | * [IntentService教给我什么](http://www.jianshu.com/p/85169ca537f7) 35 | 36 | ## AIDL 37 | * [你真的理解AIDL中的in,out,inout么?](http://blog.csdn.net/luoyanglizi/article/details/51958091) 38 | 39 | ## Fragment 40 | * [Fragment全解析系列(一):那些年踩过的坑](http://www.jianshu.com/p/d9143a92ad94) 41 | * [Fragment全解析系列(二):正确的使用姿势](http://www.jianshu.com/p/fd71d65f0ec6) 42 | * [Fragment之我的解决方案:Fragmentation](http://www.jianshu.com/p/38f7994faa6b) 43 | 44 | ## 图片选择&处理 45 | * [如何使用Android MediaStore裁剪大图片](http://my.oschina.net/ryanhoo/blog/86843) 46 | * [Android大图片裁剪终极解决方案(上:原理分析)](http://ryanhoo.github.io/blog/2014/05/26/the-ultimate-approach-to-crop-photos-on-android-1/) 47 | * [Android大图片裁剪终极解决方案(中:从相册截图)](http://ryanhoo.github.io/blog/2014/06/03/the-ultimate-approach-to-crop-photos-on-android-2/) 48 | * [Android大图片裁剪终极解决方案(下:拍照截图](http://ryanhoo.github.io/blog/2014/06/03/the-ultimate-approach-to-crop-photos-on-android-3/) 49 | * [图形编辑开源控件,支持自定义贴图 图片滤镜 图片旋转 以及图片剪裁操作](https://github.com/siwangqishiq/ImageEditor-Android) 50 | * [Image Cropping Library for Android](https://github.com/Yalantis/uCrop) 51 | * [你需要知道的 Android 拍照适配方案](http://diycode.cc/topics/101) 52 | * [滤镜相机,可拍照、录像、图片修改](https://github.com/wuhaoyu1990/MagicCamera) 53 | 54 | ## 音频处理 55 | * [Horizon - Simple visual equaliser for Android](https://github.com/Yalantis/Horizon) 56 | * [Live Audio Equalizer with wave effect](https://github.com/Cleveroad/WaveInApp) 57 | * [Awesome Audio Widget for any Android Music App](https://github.com/Cleveroad/MusicBobber) 58 | 59 | ## 奔溃处理 60 | * [Android平台的崩溃捕获机制及实现](http://geek.csdn.net/news/detail/50839) 61 | 62 | ## Notification 63 | * [通知工具类(高仿淘宝,网易新闻,微信,应用宝,环聊等等热门App的通知视图)](https://github.com/wenmingvs/NotifyUtil) 64 | * [Android自定义Notification并没有那么简单](http://sixwolf.net/blog/2016/04/18/Android%E8%87%AA%E5%AE%9A%E4%B9%89Notification%E5%B9%B6%E6%B2%A1%E6%9C%89%E9%82%A3%E4%B9%88%E7%AE%80%E5%8D%95/) 65 | 66 | ## 运行时权限 67 | * [聊一聊Android 6.0的运行时权限](http://droidyue.com/blog/2016/01/17/understanding-marshmallow-runtime-permission/) 68 | 69 | ## Fragments 70 | * [Fragment Recreate(1)](http://yanghui.name/blog/2015/09/27/fragment-recreate-1/) 71 | * [Fragment Recreate(2)](http://yanghui.name/blog/2015/09/28/fragment-recreate-2/) 72 | 73 | ## 综合 74 | * [判断指定App是否位于前台的方法](https://github.com/wenmingvs/AndroidProcess) 75 | * [那些你应该知道却不一定知道的——View坐标分析汇总](http://blog.csdn.net/mr_immortalz/article/details/51168278) 76 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Complex-Blogs/README.md: -------------------------------------------------------------------------------- 1 | Android 值得一读的文章 2 | --- 3 | 4 | * [Android输入框中加入清除按钮](http://segmentfault.com/a/1190000003040763) 5 | * [Tools 命名空间的使用与 Support Library Annotations 介绍](http://yanghui.name/blog/2015/08/31/tools-namespace-and-support-library-annotations/) 6 | * [获取NavBar高度的方法](http://seniorzhai.github.io/2015/08/05/%E8%8E%B7%E5%8F%96NavBar%E9%AB%98%E5%BA%A6%E7%9A%84%E6%96%B9%E6%B3%95/) 7 | * [使用硬件层来实现灰度视图](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1031/3642.html) 8 | * [使用moveTaskToBack()方法实现手动隐藏当前Activity](http://blog.csdn.net/yelangjueqi/article/details/9082977) 9 | * [强迫症的研究——MediaPlayer播放进度条的优化](http://www.jianshu.com/p/56f37988428b) 10 | * [Android Launcher 设置壁纸](http://blog.csdn.net/wangjinyu501/article/details/41518465) 11 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Custom-Views/README.md: -------------------------------------------------------------------------------- 1 | Android 自定义View 2 | --- 3 | 4 | * [Android CustomView](http://wuxiaolong.me/2016/01/01/CustomView/) 5 | 6 | ## Path & 贝塞尔曲线 7 | * [【Android开源项目解析】QQ“一键下班”功能实现解析——学习Path及贝塞尔曲线的基本使用](http://www.jianshu.com/p/dce9794ed07e) 8 | * [实践自定UI—View](http://www.jianshu.com/p/11210b14f743) 9 | * [实践自定义UI—RLF...(RelativeLayout LinearLayout FrameLayout....)](http://www.jianshu.com/p/ff8dcefce371) 10 | * [实践自定义UI-ViewGroup](http://www.jianshu.com/p/525ccf61db94) 11 | 12 | ## 触摸事件相关 13 | * [Viewdraghelper解析](http://souly.cn/%E6%8A%80%E6%9C%AF%E5%8D%9A%E6%96%87/2015/09/23/viewDragHelper%E8%A7%A3%E6%9E%90/) 14 | * [Android中View的触摸事件涉及到哪些方法?他们之间有什么关系?](https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/5.Android%E4%B8%ADView%E7%9A%84%E8%A7%A6%E6%91%B8%E4%BA%8B%E4%BB%B6%E6%B6%89%E5%8F%8A%E5%88%B0%E5%93%AA%E4%BA%9B%E6%96%B9%E6%B3%95%EF%BC%9F%E4%BB%96%E4%BB%AC%E4%B9%8B%E9%97%B4%E6%9C%89%E4%BB%80%E4%B9%88%E5%85%B3%E7%B3%BB%EF%BC%9F.md) 15 | 16 | ## Canvas 17 | * [二十多行代码画太极(Android中的Canvas)](http://blog.csdn.net/u013831257/article/details/50407282) 18 | * [安卓自定义View进阶-Canvas(4) Path1](http://blog.csdn.net/u013831257/article/details/50784565) 19 | * [Path相关方法讲解(二)](http://blog.csdn.net/tianjian4592/article/details/46955833) 20 | * [仿支付宝付款成功及"天女散花"效果实现——看PathMeasure大展身手](http://www.jianshu.com/p/610a46fabd1d) 21 | * [Android自定义View之高仿QQ健康](http://www.jianshu.com/p/740c64ba15ac) 22 | 23 | ## 相机 24 | * [Android自定义相机拍照、图片裁剪的实现](http://liuling123.com/2015/10/custom-camera.html) 25 | * [Android相机开发那些坑](https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=401454605&idx=1&sn=d5a16f6dc13e7581fec08a4e704cd5d0) 26 | 27 | ## Like动画按钮 28 | * [twitter like animation for any view](https://github.com/hanks-zyh/SmallBang) 29 | * [Twitter's heart animation for Android](https://github.com/jd-alexander/LikeButton) 30 | * [LikeAnimation](https://github.com/frogermcs/LikeAnimation) 31 | 32 | ## 3D View 33 | * [3d旋转切换view,类似旋转木马效果](https://github.com/dalong982242260/LoopRotarySwitch) 34 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Data-Binding/README.md: -------------------------------------------------------------------------------- 1 | * [Android DataBinding:再见Presenter,你好ViewModel!](http://www.devtf.cn/?p=955) 2 | * [Two-way Android Data Binding](https://medium.com/@fabioCollini/android-data-binding-f9f9d3afc761) 3 | * [Data Bingding Demo 示例](https://github.com/sys1yagi/data-binding-sample) 4 | * [Data Bingding RecyclerView](https://github.com/radzio/android-data-binding-recyclerview) 5 | * [巧妙、灵活的databinding改造库](https://github.com/tianzhijiexian/DBinding) 6 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Db-Nosql/README.md: -------------------------------------------------------------------------------- 1 | Android NoSql数据库 2 | --- 3 | 4 | * [Paper](https://github.com/pilgr/Paper) 5 | * [RxPaper](https://github.com/cesarferreira/RxPaper) 6 | * [AndroidKeyValueStore-基于Sqlite](https://github.com/lusfold/AndroidKeyValueStore) 7 | * [SnappyDB](https://github.com/nhachicha/SnappyDB) 8 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Db-Orm/README.md: -------------------------------------------------------------------------------- 1 | Android ORM数据库 2 | --- 3 | 4 | * [yahoo squidb](https://github.com/yahoo/squidb) 5 | * [greenrobot greenDAO](https://github.com/greenrobot/greenDAO) 6 | * [LitePal](https://github.com/LitePalFramework/LitePal) 7 | * [storio](https://github.com/pushtorefresh/storio) 8 | * [ActiveAndroid](https://github.com/pardom/ActiveAndroid) 9 | 10 | ## about database helper 11 | * [GreenDaoUpgradeHelper-号称一行代码解决GreenDao数据库升级](https://github.com/yuweiguocn/GreenDaoUpgradeHelper) 12 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Debug-Tools/README.md: -------------------------------------------------------------------------------- 1 | Android 调试工具 2 | --- 3 | 4 | * [Facebook出品的stetho](http://facebook.github.io/stetho/) 5 | * [Square 内存泄漏检测](https://github.com/square/leakcanary) 6 | * [dbinspector免Root查看数据库](https://github.com/infinum/android_dbinspector) 7 | * [TinyDancer查看界面每秒的帧数](https://github.com/brianPlummer/TinyDancer) 8 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Di/README.md: -------------------------------------------------------------------------------- 1 | Android依赖注入 2 | --- 3 | 4 | * [awesome-dagger](https://github.com/andyiac/awesome-dagger) 5 | * [聊聊 Android 中的依赖注入](http://android.jobbole.com/82386/) 6 | * [Android:dagger2让你爱不释手-基础依赖注入框架篇](http://www.jianshu.com/p/cd2c1c9f68d4) 7 | * [依赖注入学习指南](https://lber19535.github.io/2016/04/22/%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/) 8 | * [dagger 2 详解](http://www.jianshu.com/p/269c3f70ec1e) 9 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Dynamical-Loading/README.md: -------------------------------------------------------------------------------- 1 | Android动态加载技术 2 | --- 3 | 4 | * [Android动态加载技术 简单易懂的介绍方式](http://segmentfault.com/a/1190000004062866) 5 | * [Android动态加载基础 ClassLoader工作机制](http://segmentfault.com/a/1190000004062880) 6 | * [Android动态加载补充 加载SD卡中的SO库](http://segmentfault.com/a/1190000004062899) 7 | * [Android动态加载入门 简单加载模式](http://segmentfault.com/a/1190000004062952) 8 | * [Android动态加载进阶 代理Activity模式](http://segmentfault.com/a/1190000004062972) 9 | * [Android 热补丁动态修复框架小结](http://blog.csdn.net/lmj623565791/article/details/49883661) 10 | * [关于MultiDex方案的一点研究与思考](http://blog.csdn.net/zhaokaiqiang1992/article/details/50412975) 11 | * [深入理解Android(三):Xposed详解](http://www.infoq.com/cn/articles/android-in-depth-xposed) 12 | 13 | ## 框架 14 | * [Small](https://github.com/wequick/Small) 15 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Gradle-blogs/README.md: -------------------------------------------------------------------------------- 1 | Android Gradle相关博客 2 | --- 3 | 4 | * [Android项目中如何用好构建神器Gradle?](http://www.csdn.net/article/2015-08-10/2825420) 5 | * [Android Gradle实战](http://www.csdn.net/article/2015-08-10/2825420/2) 6 | * [美团Android自动化之旅—适配渠道包](http://tech.meituan.com/mt-apk-adaptation.html) 7 | * [用Gradle构建时,将密码等敏感信息放在build.gradle之外](http://www.cnblogs.com/tianzhijiexian/p/4493109.html) 8 | * [构建神器Gradle](http://jiajixin.cn/2015/08/07/gradle-android/) 9 | * [Android Gradle多渠道打包plugin](https://github.com/mcxiaoke/gradle-packer-plugin) 10 | * [在Android调试模式中使用Stetho](https://github.com/bboyfeiyu/android-tech-frontier/tree/master/androidweekly/%E5%9C%A8Android%E8%B0%83%E8%AF%95%E6%A8%A1%E5%BC%8F%E4%B8%AD%E4%BD%BF%E7%94%A8Stetho) 11 | * [导入一个AndroidStudio工程作为一个Library Module](http://blog.csdn.net/growth58/article/details/47441245) 12 | * [适合新手的 gradle 自学教程合集](https://testerhome.com/topics/1867) 13 |
14 | 我错过了ant时代,我也错过了maven时代,但我不能错过gradle时代! 15 | * [发布lib到jcenter](https://github.com/andforce/release-android-lib-to-jcenter/blob/master/README.md) 16 | * [manifestPlaceholders的一些坑](http://blog.saymagic.cn/2015/04/18/build-gradle.html) 17 | * [Android打包的那些事-(Gradle打包好文,强烈推荐!!!)](http://www.jayfeng.com/2015/11/07/Android%E6%89%93%E5%8C%85%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/) 18 | * [深入理解Android之Gradle](http://blog.csdn.net/innost/article/details/48228651) 19 | * [大型项目 Gradle 的常用库和版本管理](http://www.wangchenlong.org/2016/03/15/manage-gradle-lib-version/) 20 | 21 | ## Gradle for android翻译 22 | * [Gradle for Android 第一篇( 从 Gradle 和 AS 开始 )](http://segmentfault.com/a/1190000004229002) 23 | * [Gradle for Android 第二篇( Build.gradle入门 )](http://segmentfault.com/a/1190000004234712?_ea=538654) 24 | * [Gradle for Android 第三篇( 依赖管理 )](http://segmentfault.com/a/1190000004237922) 25 | * [Gradle for Android 第四篇( 构建变体 )](http://segmentfault.com/a/1190000004241503) 26 | * [Gradle for Android 第五篇( 多模块构建 )](http://segmentfault.com/a/1190000004247809) 27 | * [Gradle for Android 第六篇( 测试)](http://segmentfault.com/a/1190000004260141) 28 | * [Gradle for Android 第七篇( Groovy入门 )](http://segmentfault.com/a/1190000004276167) 29 | -------------------------------------------------------------------------------- /android-as-blogs/Android-ImageLoaders-libs/README.md: -------------------------------------------------------------------------------- 1 | Android 图片加载 2 | --- 3 | 4 | * [Glide](https://github.com/bumptech/glide) 5 | * [Facebook Fresco](https://github.com/facebook/fresco) 6 | * [Square Picasso](https://github.com/square/picasso) 7 | * [Android-Universal-Image-Loader](https://github.com/nostra13/Android-Universal-Image-Loader) 8 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Imageloaders/README.md: -------------------------------------------------------------------------------- 1 | Android 图片加载系列文章 2 | --- 3 | 4 | * [Picasso v/s Imageloader v/s Fresco vs Glide](http://stackoverflow.com/questions/29363321/picasso-v-s-imageloader-v-s-fresco-vs-glide) 5 | * [Yelp是如何通过Glide优化图片加载的](blogs/yelp_android_imageload_with_glide.md) 6 | * [Google推荐的图片加载库Glide介绍](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0327/2650.html) 7 | * [Introduction to Glide, Image Loader Library for Android, recommended by Google](http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en) 8 | * [Android的媒体管理框架:Glide 3.0发布](http://www.infoq.com/cn/news/2014/09/android-glide) 9 | * [picasso-强大的Android图片下载缓存库](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0731/1639.html) 10 | * [FaceBook推出的Android图片加载库-Fresco](https://github.com/bboyfeiyu/android-tech-frontier/tree/master/others/FaceBook%E6%8E%A8%E5%87%BA%E7%9A%84Android%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E5%BA%93-Fresco) 11 | * [glide-transformations](https://github.com/wasabeef/glide-transformations) 12 | * [开源选型之 Android 三大图片缓存原理、特性对比](http://mp.weixin.qq.com/s?__biz=MzAxNjI3MDkzOQ==&mid=400056342&idx=1&sn=894325d70f16a28bfe8d6a4da31ec304&scene=0#rd) 13 | * [支持gif的图片预览控件](http://kymjs.com/code/2015/10/18/01/) 14 | * [Glide 一个专注于平滑滚动的图片加载和缓存库](http://www.jianshu.com/p/4a3177b57949) 15 | * [Glide学习](http://blog.csdn.net/fandong12388/article/details/46372255) 16 | * [Glide使用系列](http://mrfu.me/2016/02/27/Glide_Getting_Started/) 17 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Imageloaders/blogs/images/scrolling-slight.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeng1990java/android-practice-tips/2cce026733b60aebb76547cd3cafca4dce60c99b/android-as-blogs/Android-Imageloaders/blogs/images/scrolling-slight.gif -------------------------------------------------------------------------------- /android-as-blogs/Android-Imageloaders/blogs/yelp_android_imageload_with_glide.md: -------------------------------------------------------------------------------- 1 | Yelp是如何通过Glide优化图片加载的 2 | --- 3 | * 原文链接 : [Glide – How Yelp’s Android App Loads Images](http://engineeringblog.yelp.com/2015/07/glide-how-yelps-android-app-loads-images.html) 4 | * 译文出自 : [开发技术前线 www.devtf.cn。未经允许,不得转载!](www.devtf.cn) 5 | * 译者 : [tiiime](https://github.com/tiiime) 6 | 7 | 8 | 动态加载图片是是很多应用的基础。在yelp,图片是连接消费者和企业的关键。 9 | 随着网络和硬件设备的发展,用户对图片质量要求越来越高,数量越来越大。图片很容就 10 | 消耗了大量的硬件资源和网络流量,下载和管理这些数据变成了一个很麻烦的问题。 11 | 我们尝试了一些现有的解决方案,最终选择了 Glide 这个项目。 12 | 13 | 在最简单的使用场景中,Glide 会将来自服务器或本地文件的图片加载到磁盘和内存的缓存中, 14 | 然后再把它们载入到 view 里。虽然 Glide 适用场景很多, 但它主要致力于优化 15 | 带有图片的 scrolling list ,使其尽可能流畅。 16 | 17 | ### 对象池 18 | 19 | Glide 的核心部分就是维护一个 bitmap 的对象池。对象池通过减少大块对象分配 20 | 来提升性能,而不是重用对象。(关于对象池的介绍:[this Android performance pattern video](https://www.youtube.com/watch?v=bSOREVMEFnM) )。 21 | 22 | 目前为止,Dalvik 和 ART 都没有使用压缩垃圾回收机制 23 | ([compacting garbage collector](https://en.wikipedia.org/wiki/Mark-compact_algorithm)),压缩垃圾回收算法会检查 heap, 24 | 然后将内存中所有存活对象移到相邻的位置,把大块的可用空间空出来以备后用。 25 | 因为 Android 没有采用这套机制,所以内存有可能被各种对象占满,对象间的可用 26 | 空间很小。如果应用在这时申请一个比内存里现在最大的空闲块还大的对象, 27 | 就会 OutOfMemoryError ,即使剩余内存总和远大于分配这个对象所需的空间。 28 | 29 | 使用对象池也提升了 list 滚动时的性能,因为重用 bitmaps 就意味着减少了创建和回收对象。 30 | 当系统执行垃圾回收时会让整个系统”时间静止”,回收器运行时所有的线程(包括 UI 线程) 31 | 都会被暂停。这段时间里,动画帧不能绘制,UI 会卡顿,这在滚动动画的过程中会更加明显明显。 32 | 33 | ![gif](images/scrolling-slight.gif) 34 | 35 | --- 36 | 37 | ### 使用Glide 38 | 39 | Glide 上手很容易,不需要任何配置,默认就会使用 bitmap 对象池。 40 | ```java 41 | DrawableRequestBuilder requestBuilder = Glide.with(context).load(imageUrl); 42 | requestBuilder.into(imageView); 43 | ``` 44 | 很简单的两步图片就载入完成了。在 Android 中, with() 方法里传递的 context 45 | 可能是 Application 的 context ,也可能是 Activity 的 context。区别这一点很重要, 46 | 如果传递的是 Activity 的 context,Glide 会监测 activity 的生命周期, 47 | 当 Glide 检测到 Activity 被销毁时, 会自动取消等待中的请求。如果你传递的是一个 48 | Application context,Glide 就不能对其进行优化。 49 | 50 | ### 优化特性 51 | 和上面一样,当图片从 ListView 中移出屏幕时, Glide 也会取消其对应的请求。 52 | 由于大多数开发者在 adapter 中重用 View,Glide 会给在请求数据时给对应的 53 | ImageView 附加一个 tag,然后再载入其他图片时检查这个 tag, 54 | 如果存在的话取消第一个请求。 55 | 56 | Glide 提供一些特性,可以提升图片加载速度。首先是在图片展示前预读取数据, 57 | 它提供了一个 ListPreloader,通过预加载 item 的数量初始化。接着通过 58 | setOnScrollListener(OnScrollListener) 把 [ListPreloader](https://github.com/bumptech/glide/blob/30c92551ee75c2109955ee653e8795c7c1d60bf8/library/src/main/java/com/bumptech/glide/ListPreloader.java) 设置给 ListView。如果你想在 ListView 之外预载图片,只要调用上面 DrawableRequestBuilder 59 | 对象的 downloadOnly() 方法就好,像这样 builder.downloadOnly();。 60 | 61 | 总之,我们发现了一个强大的工具 Glide。后面都是各种夸 Glide 的话啦~ 62 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Java-Annotations/README.md: -------------------------------------------------------------------------------- 1 | Java Annotations专题 2 | --- 3 | * [Java注解处理器使用详解](blogs/annotation-processing.md) 4 | * [Java 注解指导手册 – 终极向导](http://www.importnew.com/14227.html) 5 | * [android 中运用apt自定义一个AbstractProcessor](http://yzx41099298.github.io/2015/03/26/apt/) 6 | * [Java Annotation 及几个常用开源项目注解原理简析](http://www.trinea.cn/android/java-annotation-android-open-source-analysis/)--->[公共技术点之 Java 注解 Annotation ](http://a.codekk.com/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20Java%20%E6%B3%A8%E8%A7%A3%20Annotation) 7 | * [Android 打造编译时注解解析框架 这只是一个开始](http://blog.csdn.net/lmj623565791/article/details/43452969) 8 | * [Dagger2入坑指南(主要是看provided和apt的区别)](http://www.jianshu.com/p/b5cc2418a712) 9 | * [Google官方MVP+Dagger2架构详解](http://www.jianshu.com/p/01d3c014b0b1) 10 | 11 | 12 | ### 相关项目 13 | * [Treasure](https://github.com/baoyongzhang/Treasure) 14 | * [annotationprocessing101](https://github.com/sockeqwe/annotationprocessing101) 15 | * [butterknife](https://github.com/JakeWharton/butterknife) 16 | * [esperandro-Easy SharedPreference Engine for Android](https://github.com/dkunzler/esperandro) 17 | 18 | 19 | ### 相关工具库 20 | * [icepick-状态保存与恢复](https://github.com/frankiesardo/icepick) 21 | * [fragmentargs-fragment参数传递](https://github.com/sockeqwe/fragmentargs) 22 | * [IntentBuilder-Activity参数传递](https://github.com/emilsjolander/IntentBuilder) 23 | * [AutoBundle-fragmentargs和IntentBuilder的结合](https://github.com/yatatsu/AutoBundle) 24 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Java-Annotations/blogs/annotation-processing.md: -------------------------------------------------------------------------------- 1 | Java注解处理器使用详解 2 | --- 3 | 在这篇文章中,我将阐述怎样写一个注解处理器(Annotation Processor)。在这篇教程中,首先,我将向您解释什么是注解器,你可以利用这个强大的工具做什么以及不能做什么;然后,我将一步一步实现一个简单的注解器。 4 | 5 | ### 一些基本概念 6 | 在开始之前,我们首先申明一个非常重要的问题:我们并不讨论那些在运行时(Runtime)通过反射机制运行处理的注解,而是讨论在编译时(Compile time)处理的注解。 7 | 8 | 注解处理器是一个在javac中的,用来编译时扫描和处理的注解的工具。你可以为特定的注解,注册你自己的注解处理器。到这里,我假设你已经知道什么是注解,并且知道怎么申明的一个注解类型。如果你不熟悉注解,你可以在这[官方文档](http://docs.oracle.com/javase/tutorial/java/annotations/index.html)中得到更多信息。注解处理器在Java 5开始就有了,但是从Java 6(2006年12月发布)开始才有可用的API。过了一些时间,Java世界才意识到注解处理器的强大作用,所以它到最近几年才流行起来。 9 | 10 | 一个注解的注解处理器,以Java代码(或者编译过的字节码)作为输入,生成文件(通常是.java文件)作为输出。这具体的含义是什么呢?你可以生成Java代码!这些生成的Java代码是在生成的.java文件中,所以你不能修改已经存在的Java类,例如向已有的类中添加方法。这些生成的Java文件,会同其他普通的手动编写的Java源代码一样被javac编译。 11 | 12 | ### AbstractProcessor 13 | 我们首先看一下处理器的API。每一个处理器都是继承于AbstractProcessor,如下所示: 14 | ```java 15 | package com.example; 16 | 17 | public class MyProcessor extends AbstractProcessor { 18 | 19 | @Override 20 | public synchronized void init(ProcessingEnvironment env){ } 21 | 22 | @Override 23 | public boolean process(Set annoations, RoundEnvironment env) { } 24 | 25 | @Override 26 | public Set getSupportedAnnotationTypes() { } 27 | 28 | @Override 29 | public SourceVersion getSupportedSourceVersion() { } 30 | 31 | } 32 | ``` 33 | * init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer。后面我们将看到详细的内容。 34 | * process(Set annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。后面我们将看到详细的内容。 35 | * getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。 36 | * getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果你有足够的理由只支持Java 6的话,你也可以返回SourceVersion.RELEASE_6。我推荐你使用前者。 37 | 38 | 在Java 7中,你也可以使用注解来代替getSupportedAnnotationTypes()和getSupportedSourceVersion(),像这样: 39 | ```java 40 | @SupportedSourceVersion(SourceVersion.latestSupported()) 41 | @SupportedAnnotationTypes({ 42 | // 合法注解全名的集合 43 | }) 44 | public class MyProcessor extends AbstractProcessor { 45 | 46 | @Override 47 | public synchronized void init(ProcessingEnvironment env){ } 48 | 49 | @Override 50 | public boolean process(Set annoations, RoundEnvironment env) { } 51 | } 52 | ``` 53 | 54 | 因为兼容的原因,特别是针对Android平台,我建议使用重载getSupportedAnnotationTypes()和getSupportedSourceVersion()方法代替@SupportedAnnotationTypes和@SupportedSourceVersion。 55 | 56 | 接下来的你必须知道的事情是,注解处理器是运行在它自己的虚拟机JVM中的。是的,你没有看错,javac启动一个完整Java虚拟机来运行注解处理器。这对你意味着什么?你可以使用任何你在其他java应用中使用的的东西。使用guava。如果你愿意,你可以使用依赖注入工具,例如dagger或者其他你想要的类库。但是不要忘记,即使是一个很小的处理,你也要像其他Java应用一样,注意算法效率,以及[设计模式](http://www.codeceo.com/article/category/develop/design-patterns)。 57 | 58 | ### 注册你的处理器 59 | 你可能会问,我怎样注册处理器MyProcessor到javac中。你必须提供一个.jar文件。就像其他.jar文件一样,打包你的注解处理器到此文件中。并且,在你的jar中,你需要打包一个特定的文件javax.annotation.processing.Processor到META-INF/services路径下。所以,你的.jar文件看起来就像下面这样: 60 | ```java 61 | MyProcessor.jar 62 | - com 63 | - example 64 | - MyProcessor.class 65 | 66 | - META-INF 67 | - services 68 | - javax.annotation.processing.Processor 69 | ``` 70 | 打包进MyProcessor.jar中的javax.annotation.processing.Processor的内容是,注解处理器的合法的全名列表,每一个元素换行分割: 71 | ```java 72 | com.example.MyProcessor 73 | com.foo.OtherProcessor 74 | net.blabla.SpecialProcessor 75 | ``` 76 | 把MyProcessor.jar放到你的builpath中,javac会自动检查和读取javax.annotation.processing.Processor中的内容,并且注册MyProcessor作为注解处理器。 77 | 78 | ### 例子:工厂模式 79 | 是时候来说一个实际的例子了。我们将使用maven工具来作为我们的编译系统和依赖管理工具。如果你不熟悉maven,不用担心,因为maven不是必须的。本例子的完成代码在[Github](https://github.com/sockeqwe/annotationprocessing101)上。 80 | 81 | 开始之前,我必须说,要为这个教程找到一个需要用注解处理器解决的简单问题,实在并不容易。这里我们将实现一个非常简单的工厂模式(不是抽象工厂模式)。这将对注解处理器的API做一个非常简明的介绍。所以,这个问题的程序并不是那么有用,也不是一个真实世界的例子。所以在此申明,你将学习关于注解处理过程的相关内容,而不是设计模式。 82 | 83 | 我们将要解决的问题是:我们将实现一个披萨店,这个披萨店给消费者提供两种披萨(“Margherita”和“Calzone”)以及提拉米苏甜点(Tiramisu)。 84 | 85 | 看一下如下的代码,不需要做任何更多的解释: 86 | ```java 87 | public interface Meal { 88 | public float getPrice(); 89 | } 90 | 91 | public class MargheritaPizza implements Meal { 92 | 93 | @Override public float getPrice() { 94 | return 6.0f; 95 | } 96 | } 97 | 98 | public class CalzonePizza implements Meal { 99 | 100 | @Override public float getPrice() { 101 | return 8.5f; 102 | } 103 | } 104 | 105 | public class Tiramisu implements Meal { 106 | 107 | @Override public float getPrice() { 108 | return 4.5f; 109 | } 110 | } 111 | ``` 112 | 为了在我们的披萨店PizzsStore下订单,消费者需要输入餐(Meal)的名字。 113 | ```java 114 | public class PizzaStore { 115 | 116 | public Meal order(String mealName) { 117 | 118 | if (mealName == null) { 119 | throw new IllegalArgumentException("Name of the meal is null!"); 120 | } 121 | 122 | if ("Margherita".equals(mealName)) { 123 | return new MargheritaPizza(); 124 | } 125 | 126 | if ("Calzone".equals(mealName)) { 127 | return new CalzonePizza(); 128 | } 129 | 130 | if ("Tiramisu".equals(mealName)) { 131 | return new Tiramisu(); 132 | } 133 | 134 | throw new IllegalArgumentException("Unknown meal '" + mealName + "'"); 135 | } 136 | 137 | public static void main(String[] args) throws IOException { 138 | PizzaStore pizzaStore = new PizzaStore(); 139 | Meal meal = pizzaStore.order(readConsole()); 140 | System.out.println("Bill: $" + meal.getPrice()); 141 | } 142 | } 143 | ``` 144 | 正如你所见,在order()方法中,我们有很多的if语句,并且如果我们每添加一种新的披萨,我们都要添加一条新的if语句。但是等一下,使用注解处理和工厂模式,我们可以让注解处理器来帮我们自动生成这些if语句。如此以来,我们期望的是如下的代码: 145 | ```java 146 | public class PizzaStore { 147 | 148 | private MealFactory factory = new MealFactory(); 149 | 150 | public Meal order(String mealName) { 151 | return factory.create(mealName); 152 | } 153 | 154 | public static void main(String[] args) throws IOException { 155 | PizzaStore pizzaStore = new PizzaStore(); 156 | Meal meal = pizzaStore.order(readConsole()); 157 | System.out.println("Bill: $" + meal.getPrice()); 158 | } 159 | } 160 | ``` 161 | MealFactory应该是如下的样子: 162 | ```java 163 | public class MealFactory { 164 | 165 | public Meal create(String id) { 166 | if (id == null) { 167 | throw new IllegalArgumentException("id is null!"); 168 | } 169 | if ("Calzone".equals(id)) { 170 | return new CalzonePizza(); 171 | } 172 | 173 | if ("Tiramisu".equals(id)) { 174 | return new Tiramisu(); 175 | } 176 | 177 | if ("Margherita".equals(id)) { 178 | return new MargheritaPizza(); 179 | } 180 | 181 | throw new IllegalArgumentException("Unknown id = " + id); 182 | } 183 | } 184 | ``` 185 | 186 | ### @Factory注解 187 | 你能猜到么:我们想用注解处理器自动生成MealFactory。更一般的说,我们将想要提供一个注解和一个处理器来生成工厂类。 188 | 189 | 我们先来看一下@Factory注解: 190 | ```java 191 | @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) 192 | public @interface Factory { 193 | 194 | /** 195 | * 工厂的名字 196 | */ 197 | Class type(); 198 | 199 | /** 200 | * 用来表示生成哪个对象的唯一id 201 | */ 202 | String id(); 203 | } 204 | ``` 205 | 想法是这样的:我们将使用同样的type()注解那些属于同一个工厂的类,并且用注解的id()做一个映射,例如从"Calzone"映射到"ClzonePizza"类。我们应用@Factory注解到我们的类中,如下: 206 | ```java 207 | @Factory( 208 | id = "Margherita", 209 | type = Meal.class 210 | ) 211 | public class MargheritaPizza implements Meal { 212 | 213 | @Override public float getPrice() { 214 | return 6f; 215 | } 216 | } 217 | ``` 218 | ```java 219 | @Factory( 220 | id = "Calzone", 221 | type = Meal.class 222 | ) 223 | public class CalzonePizza implements Meal { 224 | 225 | @Override public float getPrice() { 226 | return 8.5f; 227 | } 228 | } 229 | ``` 230 | ```java 231 | @Factory( 232 | id = "Tiramisu", 233 | type = Meal.class 234 | ) 235 | public class Tiramisu implements Meal { 236 | 237 | @Override public float getPrice() { 238 | return 4.5f; 239 | } 240 | } 241 | ``` 242 | 243 | 你可能会问你自己,我们是否可以只把@Factory注解应用到我们的Meal接口上?答案是,注解是不能继承的。一个类class X被注解,并不意味着它的子类class Y extends X会自动被注解。在我们开始写处理器的代码之前,我们先规定如下一些规则: 244 | 245 | * 只有类可以被@Factory注解,因为接口或者抽象类并不能用new操作实例化; 246 | * 被@Factory注解的类,必须至少提供一个公开的默认构造器(即没有参数的构造函数)。否者我们没法实例化一个对象。 247 | * 被@Factory注解的类必须直接或者间接的继承于type()指定的类型; 248 | * 具有相同的type的注解类,将被聚合在一起生成一个工厂类。这个生成的类使用Factory后缀,例如type = Meal.class,将生成MealFactory工厂类; 249 | * id只能是String类型,并且在同一个type组中必须唯一。 250 | 251 | ### 处理器 252 | 我将通过添加代码和一段解释的方法,一步一步的引导你来构建我们的处理器。省略号(...)表示省略那些已经讨论过的或者将在后面的步骤中讨论的代码,目的是为了让我们的代码有更好的可读性。正如我们前面说的,我们完整的代码可以在[Github](https://github.com/sockeqwe/annotationprocessing101)上找到。好了,让我们来看一下我们的处理器类FactoryProcessor的骨架: 253 | ```java 254 | @AutoService(Processor.class) 255 | public class FactoryProcessor extends AbstractProcessor { 256 | 257 | private Types typeUtils; 258 | private Elements elementUtils; 259 | private Filer filer; 260 | private Messager messager; 261 | private Map factoryClasses = new LinkedHashMap(); 262 | 263 | @Override 264 | public synchronized void init(ProcessingEnvironment processingEnv) { 265 | super.init(processingEnv); 266 | typeUtils = processingEnv.getTypeUtils(); 267 | elementUtils = processingEnv.getElementUtils(); 268 | filer = processingEnv.getFiler(); 269 | messager = processingEnv.getMessager(); 270 | } 271 | 272 | @Override 273 | public Set getSupportedAnnotationTypes() { 274 | Set annotataions = new LinkedHashSet(); 275 | annotataions.add(Factory.class.getCanonicalName()); 276 | return annotataions; 277 | } 278 | 279 | @Override 280 | public SourceVersion getSupportedSourceVersion() { 281 | return SourceVersion.latestSupported(); 282 | } 283 | 284 | @Override 285 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 286 | ... 287 | } 288 | } 289 | ``` 290 | 你看到在代码的第一行是@AutoService(Processor.class),这是什么?这是一个其他注解处理器中引入的注解。AutoService注解处理器是Google开发的,用来生成 291 | META-INF/services/javax.annotation.processing.Processor文件的。是的,你没有看错,我们可以在注解处理器中使用注解。非常方便,难道不是么?在getSupportedAnnotationTypes()中,我们指定本处理器将处理@Factory注解。 292 | 293 | ### Elements和TypeMirrors 294 | 在init()中我们获得如下引用: 295 | * Elements:一个用来处理Element的工具类(后面将做详细说明); 296 | * Types:一个用来处理TypeMirror的工具类(后面将做详细说明); 297 | * Filer:正如这个名字所示,使用Filer你可以创建文件。 298 | 299 | 在注解处理过程中,我们扫描所有的Java源文件。源代码的每一个部分都是一个特定类型的Element。换句话说:Element代表程序的元素,例如包、类或者方法。每个Element代表一个静态的、语言级别的构件。在下面的例子中,我们通过注释来说明这个 300 | ```java 301 | package com.example; // PackageElement 302 | 303 | public class Foo { // TypeElement 304 | 305 | private int a; // VariableElement 306 | private Foo other; // VariableElement 307 | 308 | public Foo () {} // ExecuteableElement 309 | 310 | public void setA ( // ExecuteableElement 311 | int newA // TypeElement 312 | ) {} 313 | } 314 | ``` 315 | 你必须换个角度来看源代码,它只是结构化的文本,他不是可运行的。你可以想象它就像你将要去解析的XML文件一样(或者是编译器中抽象的语法树)。就像XML解释器一样,有一些类似DOM的元素。你可以从一个元素导航到它的父或者子元素上。 316 | 317 | 举例来说,假如你有一个代表public class Foo类的TypeElement元素,你可以遍历它的孩子,如下: 318 | ```java 319 | TypeElement fooClass = ... ; 320 | for (Element e : fooClass.getEnclosedElements()){ // iterate over children 321 | Element parent = e.getEnclosingElement(); // parent == fooClass 322 | } 323 | ``` 324 | 正如你所见,Element代表的是源代码。TypeElement代表的是源代码中的类型元素,例如类。然而,TypeElement并不包含类本身的信息。你可以从TypeElement中获取类的名字,但是你获取不到类的信息,例如它的父类。这种信息需要通过TypeMirror获取。你可以通过调用elements.asType()获取元素的TypeMirror。 325 | 326 | ### 搜索@Factory注解 327 | 我们来一步一步实现process()方法。首先,我们从搜索被注解了@Factory的类开始: 328 | ```java 329 | @AutoService(Processor.class) 330 | public class FactoryProcessor extends AbstractProcessor { 331 | 332 | private Types typeUtils; 333 | private Elements elementUtils; 334 | private Filer filer; 335 | private Messager messager; 336 | private Map factoryClasses = new LinkedHashMap(); 337 | ... 338 | 339 | @Override 340 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 341 | 342 | // 遍历所有被注解了@Factory的元素 343 | for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Factory.class)) { 344 | ... 345 | } 346 | } 347 | ... 348 | } 349 | ``` 350 | 这里并没有什么高深的技术。roundEnv.getElementsAnnotatedWith(Factory.class))返回所有被注解了@Factory的元素的列表。你可能已经注意到,我们并没有说“所有被注解了@Factory的类的列表”,因为它真的是返回Element的列表。请记住:Element可以是类、方法、变量等。所以,接下来,我们必须检查这些Element是否是一个类: 351 | ```java 352 | @Override 353 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 354 | 355 | for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Factory.class)) { 356 | 357 | // 检查被注解为@Factory的元素是否是一个类 358 | if (annotatedElement.getKind() != ElementKind.CLASS) { 359 | // ... 360 | } 361 | } 362 | ... 363 | } 364 | ``` 365 | 为什么要这么做?我们要确保只有class元素被我们的处理器处理。前面我们已经学习到类是用TypeElement表示。我们为什么不这样判断呢 366 | ```java 367 | // 检查被注解为@Factory的元素是否是一个类 368 | if (! (annotatedElement instanceof TypeElement) ){ 369 | // ... 370 | } 371 | ``` 372 | 这是错误的,因为接口(interface)类型也是TypeElement。所以在注解处理器中,我们要避免使用instanceof,而是配合TypeMirror使用EmentKind或者TypeKind。 373 | 374 | ### 错误处理 375 | 在init()中,我们也获得了一个Messager对象的引用。Messager提供给注解处理器一个报告错误、警告以及提示信息的途径。它不是注解处理器开发者的日志工具,而是用来写一些信息给使用此注解器的第三方开发者的。在[官方文档](http://docs.oracle.com/javase/7/docs/api/javax/tools/Diagnostic.Kind.html)中描述了消息的不同级别。非常重要的是Kind.ERROR,因为这种类型的信息用来表示我们的注解处理器处理失败了。很有可能是第三方开发者错误的使用了@Factory注解(例如,给接口使用了@Factory注解)。这个概念和传统的Java应用有点不一样,在传统Java应用中我们可能就抛出一个异常Exception。如果你在process()中抛出一个异常,那么运行注解处理器的JVM将会崩溃(就像其他Java应用一样),使用我们注解处理器FactoryProcessor的第三方开发者将会从javac中得到非常难懂的出错信息,因为它包含FactoryProcessor的堆栈跟踪(Stacktace)信息。因此,注解处理器就有一个Messager类,它能够打印非常优美的错误信息。除此之外,你还可以连接到出错的元素。在像IntelliJ这种现代的IDE(集成开发环境)中,第三方开发者可以直接点击错误信息,IDE将会直接跳转到第三方开发者项目中出错的源文件的相应的行。 376 | 377 | 我们重新回到process()方法的实现。如果遇到一个非类类型被注解@Factory,我们发出一个出错信息: 378 | ```java 379 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 380 | 381 | for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Factory.class)) { 382 | 383 | // 检查被注解为@Factory的元素是否是一个类 384 | if (annotatedElement.getKind() != ElementKind.CLASS) { 385 | error(annotatedElement, "Only classes can be annotated with @%s", 386 | Factory.class.getSimpleName()); 387 | return true; // 退出处理 388 | } 389 | ... 390 | } 391 | 392 | private void error(Element e, String msg, Object... args) { 393 | messager.printMessage( 394 | Diagnostic.Kind.ERROR, 395 | String.format(msg, args), 396 | e); 397 | } 398 | 399 | } 400 | ``` 401 | 让Messager显示相关出错信息,更重要的是注解处理器程序必须完成运行而不崩溃,这就是为什么在调用error()后直接return。如果我们不直接返回,process()将继续运行,因为messager.printMessage( Diagnostic.Kind.ERROR)不会停止此进程。因此,如果我们在打印错误信息以后不返回的话,我们很可能就会运行到一个NullPointerException等。就像我们前面说的,如果我们继续运行process(),问题是如果在process()中抛出一个未处理的异常,javac将会打印出内部的NullPointerException,而不是Messager中的错误信息。 402 | 403 | ### 数据模型 404 | 在继续检查被注解@Fractory的类是否满足我们上面说的5条规则之前,我们将介绍一个让我们更方便继续处理的数据结构。有时候,一个问题或者解释器看起来如此简单,以至于程序员倾向于用一个面向过程方式来写整个处理器。但是你知道吗?一个注解处理器任然是一个Java程序,所以我们需要使用面向对象编程、接口、设计模式,以及任何你在其他普通Java程序中使用的技巧。 405 | 406 | 我们的FactoryProcessor非常简单,但是我们仍然想要把一些信息存为对象。在FactoryAnnotatedClass中,我们保存被注解类的数据,比如合法的类的名字,以及@Factory注解本身的一些信息。所以,我们保存TypeElement和处理过的@Factory注解: 407 | ```java 408 | public class FactoryAnnotatedClass { 409 | 410 | private TypeElement annotatedClassElement; 411 | private String qualifiedSuperClassName; 412 | private String simpleTypeName; 413 | private String id; 414 | 415 | public FactoryAnnotatedClass(TypeElement classElement) throws IllegalArgumentException { 416 | this.annotatedClassElement = classElement; 417 | Factory annotation = classElement.getAnnotation(Factory.class); 418 | id = annotation.id(); 419 | 420 | if (StringUtils.isEmpty(id)) { 421 | throw new IllegalArgumentException( 422 | String.format("id() in @%s for class %s is null or empty! that's not allowed", 423 | Factory.class.getSimpleName(), classElement.getQualifiedName().toString())); 424 | } 425 | 426 | // Get the full QualifiedTypeName 427 | try { 428 | Class clazz = annotation.type(); 429 | qualifiedSuperClassName = clazz.getCanonicalName(); 430 | simpleTypeName = clazz.getSimpleName(); 431 | } catch (MirroredTypeException mte) { 432 | DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror(); 433 | TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement(); 434 | qualifiedSuperClassName = classTypeElement.getQualifiedName().toString(); 435 | simpleTypeName = classTypeElement.getSimpleName().toString(); 436 | } 437 | } 438 | 439 | /** 440 | * 获取在{@link Factory#id()}中指定的id 441 | * return the id 442 | */ 443 | public String getId() { 444 | return id; 445 | } 446 | 447 | /** 448 | * 获取在{@link Factory#type()}指定的类型合法全名 449 | * 450 | * @return qualified name 451 | */ 452 | public String getQualifiedFactoryGroupName() { 453 | return qualifiedSuperClassName; 454 | } 455 | 456 | /** 457 | * 获取在{@link Factory#type()}{@link Factory#type()}指定的类型的简单名字 458 | * 459 | * @return qualified name 460 | */ 461 | public String getSimpleFactoryGroupName() { 462 | return simpleTypeName; 463 | } 464 | 465 | /** 466 | * 获取被@Factory注解的原始元素 467 | */ 468 | public TypeElement getTypeElement() { 469 | return annotatedClassElement; 470 | } 471 | } 472 | ``` 473 | 代码很多,但是最重要的部分是在构造函数中。其中你能找到如下的代码: 474 | ```java 475 | Factory annotation = classElement.getAnnotation(Factory.class); 476 | id = annotation.id(); // Read the id value (like "Calzone" or "Tiramisu") 477 | 478 | if (StringUtils.isEmpty(id)) { 479 | throw new IllegalArgumentException( 480 | String.format("id() in @%s for class %s is null or empty! that's not allowed", 481 | Factory.class.getSimpleName(), classElement.getQualifiedName().toString())); 482 | } 483 | ``` 484 | 这里我们获取@Factory注解,并且检查id是否为空?如果为空,我们将抛出IllegalArgumentException异常。你可能感到疑惑的是,前面我们说了不要抛出异常,而是使用Messager。这里仍然不矛盾。我们抛出内部的异常,你在将在后面看到会在process()中捕获这个异常。我这样做出于一下两个原因: 485 | * 我想示意我们应该像普通的Java程序一样编码。抛出和捕获异常是非常好的Java编程实践; 486 | * 如果我们想要在FactoryAnnotatedClass中打印信息,我需要也传入Messager对象,并且我们在错误处理一节中已经提到,为了打印Messager信息,我们必须成功停止处理器运行。如果我们使用Messager打印了错误信息,我们怎样告知process()出现了错误呢?最容易,并且我认为最直观的方式就是抛出一个异常,然后让process()捕获之。 487 | 接下来,我们将获取@Fractory注解中的type成员。我们比较关心的是合法的全名: 488 | ```java 489 | try { 490 | Class clazz = annotation.type(); 491 | qualifiedGroupClassName = clazz.getCanonicalName(); 492 | simpleFactoryGroupName = clazz.getSimpleName(); 493 | } catch (MirroredTypeException mte) { 494 | DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror(); 495 | TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement(); 496 | qualifiedGroupClassName = classTypeElement.getQualifiedName().toString(); 497 | simpleFactoryGroupName = classTypeElement.getSimpleName().toString(); 498 | } 499 | ``` 500 | 这里有一点小麻烦,因为这里的类型是一个java.lang.Class。这就意味着,他是一个真正的Class对象。因为注解处理是在编译Java源代码之前。我们需要考虑如下两种情况: 501 | * 这个类已经被编译:这种情况是:如果第三方.jar包含已编译的被@Factory注解.class文件。在这种情况下,我们可以像try中那块代码中所示直接获取Class。 502 | * 这个还没有被编译:这种情况是我们尝试编译被@Fractory注解的源代码。这种情况下,直接获取Class会抛出MirroredTypeException异常。幸运的是,MirroredTypeException包含一个TypeMirror,它表示我们未编译类。因为我们已经知道它必定是一个类类型(我们已经在前面检查过),我们可以直接强制转换为DeclaredType,然后读取TypeElement来获取合法的名字。 503 | 504 | 好了,我们现在还需要一个数据结构FactoryGroupedClasses,它将简单的组合所有的FactoryAnnotatedClasses到一起。 505 | ```java 506 | public class FactoryGroupedClasses { 507 | 508 | private String qualifiedClassName; 509 | 510 | private Map itemsMap = 511 | new LinkedHashMap(); 512 | 513 | public FactoryGroupedClasses(String qualifiedClassName) { 514 | this.qualifiedClassName = qualifiedClassName; 515 | } 516 | 517 | public void add(FactoryAnnotatedClass toInsert) throws IdAlreadyUsedException { 518 | 519 | FactoryAnnotatedClass existing = itemsMap.get(toInsert.getId()); 520 | if (existing != null) { 521 | throw new IdAlreadyUsedException(existing); 522 | } 523 | 524 | itemsMap.put(toInsert.getId(), toInsert); 525 | } 526 | 527 | public void generateCode(Elements elementUtils, Filer filer) throws IOException { 528 | ... 529 | } 530 | } 531 | ``` 532 | 正如你所见,这是一个基本的Map,这个映射表用来映射@Factory.id()到FactoryAnnotatedClass。我们选择Map这个数据类型,是因为我们要确保每个id是唯一的,我们可以很容易通过map查找实现。generateCode()方法将被用来生成工厂类代码(将在后面讨论)。 533 | 534 | ### 匹配标准 535 | 我们继续实现process()方法。接下来我们想要检查被注解的类必须有只要一个公开的构造函数,不是抽象类,继承于特定的类型,以及是一个公开类: 536 | ```java 537 | public class FactoryProcessor extends AbstractProcessor { 538 | 539 | @Override 540 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 541 | 542 | for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Factory.class)) { 543 | 544 | ... 545 | 546 | // 因为我们已经知道它是ElementKind.CLASS类型,所以可以直接强制转换 547 | TypeElement typeElement = (TypeElement) annotatedElement; 548 | 549 | try { 550 | FactoryAnnotatedClass annotatedClass = 551 | new FactoryAnnotatedClass(typeElement); // throws IllegalArgumentException 552 | 553 | if (!isValidClass(annotatedClass)) { 554 | return true; // 已经打印了错误信息,退出处理过程 555 | } 556 | } catch (IllegalArgumentException e) { 557 | // @Factory.id()为空 558 | error(typeElement, e.getMessage()); 559 | return true; 560 | } 561 | ... 562 | } 563 | 564 | private boolean isValidClass(FactoryAnnotatedClass item) { 565 | 566 | // 转换为TypeElement, 含有更多特定的方法 567 | TypeElement classElement = item.getTypeElement(); 568 | 569 | if (!classElement.getModifiers().contains(Modifier.PUBLIC)) { 570 | error(classElement, "The class %s is not public.", 571 | classElement.getQualifiedName().toString()); 572 | return false; 573 | } 574 | 575 | // 检查是否是一个抽象类 576 | if (classElement.getModifiers().contains(Modifier.ABSTRACT)) { 577 | error(classElement, "The class %s is abstract. You can't annotate abstract classes with @%", 578 | classElement.getQualifiedName().toString(), Factory.class.getSimpleName()); 579 | return false; 580 | } 581 | 582 | // 检查继承关系: 必须是@Factory.type()指定的类型子类 583 | TypeElement superClassElement = 584 | elementUtils.getTypeElement(item.getQualifiedFactoryGroupName()); 585 | if (superClassElement.getKind() == ElementKind.INTERFACE) { 586 | // 检查接口是否实现了 587 | if(!classElement.getInterfaces().contains(superClassElement.asType())) { 588 | error(classElement, "The class %s annotated with @%s must implement the interface %s", 589 | classElement.getQualifiedName().toString(), Factory.class.getSimpleName(), 590 | item.getQualifiedFactoryGroupName()); 591 | return false; 592 | } 593 | } else { 594 | // 检查子类 595 | TypeElement currentClass = classElement; 596 | while (true) { 597 | TypeMirror superClassType = currentClass.getSuperclass(); 598 | 599 | if (superClassType.getKind() == TypeKind.NONE) { 600 | // 到达了基本类型(java.lang.Object), 所以退出 601 | error(classElement, "The class %s annotated with @%s must inherit from %s", 602 | classElement.getQualifiedName().toString(), Factory.class.getSimpleName(), 603 | item.getQualifiedFactoryGroupName()); 604 | return false; 605 | } 606 | 607 | if (superClassType.toString().equals(item.getQualifiedFactoryGroupName())) { 608 | // 找到了要求的父类 609 | break; 610 | } 611 | 612 | // 在继承树上继续向上搜寻 613 | currentClass = (TypeElement) typeUtils.asElement(superClassType); 614 | } 615 | } 616 | 617 | // 检查是否提供了默认公开构造函数 618 | for (Element enclosed : classElement.getEnclosedElements()) { 619 | if (enclosed.getKind() == ElementKind.CONSTRUCTOR) { 620 | ExecutableElement constructorElement = (ExecutableElement) enclosed; 621 | if (constructorElement.getParameters().size() == 0 && constructorElement.getModifiers() 622 | .contains(Modifier.PUBLIC)) { 623 | // 找到了默认构造函数 624 | return true; 625 | } 626 | } 627 | } 628 | 629 | // 没有找到默认构造函数 630 | error(classElement, "The class %s must provide an public empty default constructor", 631 | classElement.getQualifiedName().toString()); 632 | return false; 633 | } 634 | } 635 | ``` 636 | 我们这里添加了isValidClass()方法,来检查是否我们所有的规则都被满足了: 637 | * 必须是公开类:classElement.getModifiers().contains(Modifier.PUBLIC) 638 | * 必须是非抽象类:classElement.getModifiers().contains(Modifier.ABSTRACT) 639 | * 必须是@Factoy.type()指定的类型的子类或者接口的实现:首先我们使用elementUtils.getTypeElement(item.getQualifiedFactoryGroupName())创建一个传入的Class(@Factoy.type())的元素。是的,你可以仅仅通过已知的合法类名来直接创建TypeElement(使用TypeMirror)。接下来我们检查它是一个接口还是一个类:superClassElement.getKind() == ElementKind.INTERFACE。所以我们这里有两种情况:如果是接口,就判断classElement.getInterfaces().contains(superClassElement.asType());如果是类,我们就* 必须使用currentClass.getSuperclass()扫描继承层级。注意,整个检查也可以使用typeUtils.isSubtype()来实现。 640 | * 类必须有一个公开的默认构造函数:我们遍历所有的闭元素classElement.getEnclosedElements(),然后检查ElementKind.CONSTRUCTOR、Modifier.PUBLIC以及constructorElement.getParameters().size() == 0。 641 | 如果所有这些条件都满足,isValidClass()返回true,否者就打印错误信息,否则返回false。 642 | 643 | ### 组合被注解的类 644 | 一旦我们检查isValidClass()成功,我们将添加FactoryAnnotatedClass到对应的FactoryGroupedClasses中,如下: 645 | ```java 646 | public class FactoryProcessor extends AbstractProcessor { 647 | 648 | private Map factoryClasses = 649 | new LinkedHashMap(); 650 | 651 | @Override 652 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 653 | ... 654 | try { 655 | FactoryAnnotatedClass annotatedClass = 656 | new FactoryAnnotatedClass(typeElement); // throws IllegalArgumentException 657 | 658 | if (!isValidClass(annotatedClass)) { 659 | return true; // 错误信息被打印,退出处理流程 660 | } 661 | 662 | // 所有检查都没有问题,所以可以添加了 663 | FactoryGroupedClasses factoryClass = 664 | factoryClasses.get(annotatedClass.getQualifiedFactoryGroupName()); 665 | if (factoryClass == null) { 666 | String qualifiedGroupName = annotatedClass.getQualifiedFactoryGroupName(); 667 | factoryClass = new FactoryGroupedClasses(qualifiedGroupName); 668 | factoryClasses.put(qualifiedGroupName, factoryClass); 669 | } 670 | 671 | // 如果和其他的@Factory标注的类的id相同冲突, 672 | // 抛出IdAlreadyUsedException异常 673 | factoryClass.add(annotatedClass); 674 | } catch (IllegalArgumentException e) { 675 | // @Factory.id()为空 --> 打印错误信息 676 | error(typeElement, e.getMessage()); 677 | return true; 678 | } catch (IdAlreadyUsedException e) { 679 | FactoryAnnotatedClass existing = e.getExisting(); 680 | // 已经存在 681 | error(annotatedElement, 682 | "Conflict: The class %s is annotated with @%s with id ='%s' but %s already uses the same id", 683 | typeElement.getQualifiedName().toString(), Factory.class.getSimpleName(), 684 | existing.getTypeElement().getQualifiedName().toString()); 685 | return true; 686 | } 687 | } 688 | ... 689 | } 690 | ``` 691 | 692 | ### 代码生成 693 | 我们已经收集了所有被@Factory注解的类保存为FactoryAnnotatedClass,并且组合到了FactoryGroupedClasses。现在我们将为每个工厂生成Java文件了: 694 | ```java 695 | @Override 696 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 697 | ... 698 | try { 699 | for (FactoryGroupedClasses factoryClass : factoryClasses.values()) { 700 | factoryClass.generateCode(elementUtils, filer); 701 | } 702 | } catch (IOException e) { 703 | error(null, e.getMessage()); 704 | } 705 | 706 | return true; 707 | } 708 | ``` 709 | 写Java文件,和写其他普通文件没有什么两样。使用Filer提供的Writer对象,我们可以连接字符串来写我们生成的Java代码。幸运的是,Square公司(因为提供了许多非常优秀的开源项目而非常有名)给我们提供了JavaWriter,这是一个高级的生成Java代码的库: 710 | ```java 711 | @Override 712 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 713 | ... 714 | try { 715 | for (FactoryGroupedClasses factoryClass : factoryClasses.values()) { 716 | factoryClass.generateCode(elementUtils, filer); 717 | } 718 | } catch (IOException e) { 719 | error(null, e.getMessage()); 720 | } 721 | 722 | return true; 723 | } 724 | ``` 725 | 写Java文件,和写其他普通文件没有什么两样。使用Filer提供的Writer对象,我们可以连接字符串来写我们生成的Java代码。幸运的是,Square公司(因为提供了许多非常优秀的开源项目二非常有名)给我们提供了JavaWriter,这是一个高级的生成Java代码的库: 726 | 727 | ```java 728 | public class FactoryGroupedClasses { 729 | 730 | /** 731 | * 将被添加到生成的工厂类的名字中 732 | */ 733 | private static final String SUFFIX = "Factory"; 734 | 735 | private String qualifiedClassName; 736 | 737 | private Map itemsMap = 738 | new LinkedHashMap(); 739 | ... 740 | 741 | public void generateCode(Elements elementUtils, Filer filer) throws IOException { 742 | 743 | TypeElement superClassName = elementUtils.getTypeElement(qualifiedClassName); 744 | String factoryClassName = superClassName.getSimpleName() + SUFFIX; 745 | 746 | JavaFileObject jfo = filer.createSourceFile(qualifiedClassName + SUFFIX); 747 | Writer writer = jfo.openWriter(); 748 | JavaWriter jw = new JavaWriter(writer); 749 | 750 | // 写包名 751 | PackageElement pkg = elementUtils.getPackageOf(superClassName); 752 | if (!pkg.isUnnamed()) { 753 | jw.emitPackage(pkg.getQualifiedName().toString()); 754 | jw.emitEmptyLine(); 755 | } else { 756 | jw.emitPackage(""); 757 | } 758 | 759 | jw.beginType(factoryClassName, "class", EnumSet.of(Modifier.PUBLIC)); 760 | jw.emitEmptyLine(); 761 | jw.beginMethod(qualifiedClassName, "create", EnumSet.of(Modifier.PUBLIC), "String", "id"); 762 | 763 | jw.beginControlFlow("if (id == null)"); 764 | jw.emitStatement("throw new IllegalArgumentException(\"id is null!\")"); 765 | jw.endControlFlow(); 766 | 767 | for (FactoryAnnotatedClass item : itemsMap.values()) { 768 | jw.beginControlFlow("if (\"%s\".equals(id))", item.getId()); 769 | jw.emitStatement("return new %s()", item.getTypeElement().getQualifiedName().toString()); 770 | jw.endControlFlow(); 771 | jw.emitEmptyLine(); 772 | } 773 | 774 | jw.emitStatement("throw new IllegalArgumentException(\"Unknown id = \" + id)"); 775 | jw.endMethod(); 776 | jw.endType(); 777 | jw.close(); 778 | } 779 | } 780 | ``` 781 | * 注意:因为JavaWriter非常非常的流行,所以很多处理器、库、工具都依赖于JavaWriter。如果你使用依赖管理工具,例如maven或者gradle,假如一个库依赖的JavaWriter的版本比其他的库新,这将会导致一些问题。所以我建议你直接拷贝重新打包JavaWiter到你的注解处理器代码中(实际它只是一个Java文件)。 782 | 更新:JavaWrite现在已经被[JavaPoet](https://github.com/square/javapoet)取代了 783 | 784 | ### 处理循环 785 | 注解处理过程可能会多于一次。官方javadoc定义处理过程如下: 786 | 787 | * 注解处理过程是一个有序的循环过程。在每次循环中,一个处理器可能被要求去处理那些在上一次循环中产生的源文件和类文件中的注解。第一次循环的输入是运行此工具的初始输入。这些初始输入,可以看成是虚拟的第0次的循环的输出。 788 | 789 | 一个简单的定义:一个处理循环是调用一个注解处理器的process()方法。对应到我们的工厂模式的例子中:FactoryProcessor被初始化一次(不是每次循环都会新建处理器对象),然而,如果生成了新的源文件process()能够被调用多次。听起来有点奇怪不是么?原因是这样的,这些生成的文件中也可能包含@Factory注解,它们还将会被FactoryProcessor处理。 790 | 791 | 例如我们的PizzaStore的例子中将会经过3次循环处理: 792 | 793 | 794 | | Round | Input | Output | 795 | |-------|-------|--------| 796 | | 1 | CalzonePizza.java,Tiramisu.java,MargheritaPizza.java,Meal.java,PizzaStore.java | MealFactory.java | 797 | | 2 | MealFactory.java | none | 798 | | 3 | none | none | 799 | 800 | 801 | 我解释处理循环还有另外一个原因。如果你看一下我们的FactoryProcessor代码你就能注意到,我们收集数据和保存它们在一个私有的域中Map factoryClasses。在第一轮中,我们检测到了MagheritaPizza, CalzonePizza和Tiramisu,然后生成了MealFactory.java。在第二轮中把MealFactory作为输入。因为在MealFactory中没有检测到@Factory注解,我们预期并没有错误,然而我们得到如下的信息: 802 | ```java 803 | Attempt to recreate a file for type com.hannesdorfmann.annotationprocessing101.factory.MealFactory 804 | ``` 805 | 这个问题是因为我们没有清除factoryClasses,这意味着,在第二轮的process()中,任然保存着第一轮的数据,并且会尝试生成在第一轮中已经生成的文件,从而导致这个错误的出现。在我们的这个场景中,我们知道只有在第一轮中检查@Factory注解的类,所以我们可以简单的修复这个问题,如下: 806 | ```java 807 | @Override 808 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 809 | try { 810 | for (FactoryGroupedClasses factoryClass : factoryClasses.values()) { 811 | factoryClass.generateCode(elementUtils, filer); 812 | } 813 | 814 | // 清除factoryClasses 815 | factoryClasses.clear(); 816 | 817 | } catch (IOException e) { 818 | error(null, e.getMessage()); 819 | } 820 | ... 821 | return true; 822 | } 823 | ``` 824 | 我知道这有其他的方法来处理这个问题,例如我们也可以设置一个布尔值标签等。关键的点是:我们要记住注解处理过程是需要经过多轮处理的,并且你不能重载或者重新创建已经生成的源代码。 825 | ### 分离处理器和注解 826 | 如果你已经看了我们的[代码库](https://github.com/sockeqwe/annotationprocessing101),你将发现我们组织我们的代码到两个maven模块中了。我们这么做是因为,我们想让我们的工厂模式的例子的使用者,在他们的工程中只编译注解,而包含处理器模块只是为了编译。有点晕?我们举个例子,如果我们只有一个包。如果另一个开发者想要把我们的工厂模式处理器用于他的项目中,他就必须包含@Factory注解和整个FactoryProcessor的代码(包括FactoryAnnotatedClass和FactoryGroupedClasses)到他们项目中。我非常确定的是,他并不需要在他已经编译好的项目中包含处理器相关的代码。如果你是一个Android的开发者,你肯定听说过65k个方法的限制(即在一个.dex文件中,只能寻址65000个方法)。如果你在FactoryProcessor中使用guava,并且把注解和处理器打包在一个包中,这样的话,Android APK安装包中不只是包含FactoryProcessor的代码,而也包含了整个guava的代码。Guava有大约20000个方法。所以分开注解和处理器是非常有意义的。 827 | ### 生成的类的实例化 828 | 你已经看到了,在这个PizzaStore的例子中,生成了MealFactory类,它和其他手写的Java类没有任何区别。进而,你需要的就是像其他Java对象,手动实例化它: 829 | ```java 830 | public class PizzaStore { 831 | 832 | private MealFactory factory = new MealFactory(); 833 | 834 | public Meal order(String mealName) { 835 | return factory.create(mealName); 836 | } 837 | ... 838 | } 839 | ``` 840 | * 注意:使用apt的方式而不是provided的方式使用注解库。具体原因[请看](http://www.jianshu.com/p/b5cc2418a712)。感谢[鲍永章](https://github.com/baoyongzhang/)的指导 841 | 842 | 如果你是一个Android的开发者,你应该也非常熟悉一个叫做[ButterKnife](https://github.com/JakeWharton/butterknife)的注解处理器。在ButterKnife中,你使用@InjectView注解Android的View。ButterKnifeProcessor生成一个MyActivity$$ViewInjector,但是在ButterKnife你不需要手动调用new MyActivity$$ViewInjector()实例化一个ButterKnife注入的对象,而是使用Butterknife.inject(activity)。ButterKnife内部使用反射机制来实例化MyActivity$$ViewInjector()对象: 843 | ```java 844 | try { 845 | Class injector = Class.forName(clsName + "$$ViewInjector"); 846 | } catch (ClassNotFoundException e) { ... } 847 | ``` 848 | 但是反射机制不是很慢么,我们使用注解处理来生成本地代码,会不会导致很多的反射性能的问题?的确,反射机制的性能确实是一个问题。然而,它不需要手动去创建对象,确实提高了开发者的开发速度。ButterKnife中有一个哈希表HashMap来缓存实例化过的对象。所以MyActivity$$ViewInjector只是使用反射机制实例化一次,第二次需要MyActivity$$ViewInjector的时候,就直接冲哈希表中获得。 849 | 850 | [FragmentArgs](https://github.com/sockeqwe/fragmentargs)非常类似于[ButterKnife](https://github.com/JakeWharton/butterknife)。它使用反射机制来创建对象,而不需要开发者手动来做这些。[FragmentArgs](https://github.com/sockeqwe/fragmentargs)在处理注解的时候生成一个特别的查找表类,它其实就是一种哈希表,所以整个[FragmentArgs](https://github.com/sockeqwe/fragmentargs)库只是在第一次使用的时候,执行一次反射调用,一旦整个Class.forName()的Fragemnt的参数对象被创建,后面的都是本地代码运行了。 851 | 852 | 作为一个注解注解处理器的开发者,这些都由你来决定,为其他的注解器使用者,在反射和可用性上找到一个好的平衡。 853 | 854 | ### 总结 855 | 到此,我希望你对注解处理过程有一个非常深刻的理解。我必须再次说明一下:注解处理器是一个非常强大的工具,减少了很多无聊的代码的编写。我也想提醒的是,注解处理器可以做到比我上面提到的工厂模式的例子复杂很多的事情。例如,泛型的类型擦除,因为注解处理器是发生在类型擦除(type erasure)之前的(译者注:类型擦除可以参考[这里](http://justjavac.iteye.com/blog/1741638))。就像你所看到的,你在写注解处理的时候,有两个普遍的问题你需要处理:第一问题, 如果你想在其他类中使用ElementUtils, TypeUtils和Messager,你就必须把他们作为参数传进去。在我为Android开发的注解器[AnnotatedAdapter](https://github.com/sockeqwe/AnnotatedAdapter)中,我尝试使用Dagger(一个依赖注入库)来解决这个问题。在这个简单的处理中使用它听起来有点过头了,但是它确实很好用;第二个问题,你必须做查询Elements的操作。就像我之前提到的,处理Element就解析XML或者HTML一样。对于HTML你可以是用jQuery,如果在注解处理器中,有类似于jQuery的库那那绝对是酷毙了。如果你知道有类似的库,请在下面的评论告诉我。 856 | 857 | 请注意的是,在FactoryProcessor代码中有一些缺陷和陷阱。这些“错误”是我故意放进去的,是为了演示一些在开发过程中的常见错误(例如“Attempt to recreate a file”)。如果你想基于FactoryProcessor写你自己注解处理器,请不要直接拷贝粘贴这些陷阱过去,你应该从最开始就避免它们。 858 | 859 | 作者在youtube上关于Annotation Processing的演讲[Annotation Processing 101](https://www.youtube.com/watch?v=43FFfTyDYEg) 860 | 861 | ### 版权 862 | * [翻译原文出处](http://www.codeceo.com/article/java-annotation-processor.html) 863 | * [英文原文](http://hannesdorfmann.com/annotation-processing/annotationprocessing101/) 864 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Java-Annotations/blogs/java-annotations.md: -------------------------------------------------------------------------------- 1 | Java 注解指导手册 – 终极向导 2 | --- 3 | * [Java 注解指导手册 – 终极向导](blogs/java-annotations.md) 4 | 5 | 6 | * 翻译 by [Toien Liu](http://ifeve.com/java-annotations-tutorial/) 7 | * 原文 by [Dani Buiza](http://www.javacodegeeks.com/2014/11/java-annotations-tutorial.html) 8 | 9 | 编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它 10 | 11 | 我们已经在Java Code Geeks提供了丰富的教程, 如[Creating Your Own Java Annotations](http://www.javacodegeeks.com/2014/07/creating-your-own-java-annotations.html), [Java Annotations Tutorial with Custom Annotation](http://www.javacodegeeks.com/2012/11/java-annotations-tutorial-with-custom-annotation.html) 和 [Java Annotations: Explored & Explained](http://www.javacodegeeks.com/2012/08/java-annotations-explored-explained.html). 12 | 13 | 我们也有些文章是关于注解在不同类库中的应用,包括 [Make your Spring Security @Secured annotations more DRY](http://www.javacodegeeks.com/2012/06/make-your-spring-security-secured.html)和 [Java Annotations & A Real World Spring Example](http://www.javacodegeeks.com/2012/01/java-annotations-real-world-spring.html). 14 | 15 | 现在,是时候汇总这些和注解相关的信息到一篇文章了,祝大家阅读愉快。 16 | * 什么是注解 17 | * 介绍 18 | * 消费器 19 | * 注解语法和注解元素 20 | * 在什么地方使用 21 | * 使用案例 22 | * 内建注解 23 | * Java 8 与注解 24 | * 自定义注解 25 | * 提取注解 26 | * 注解集成 27 | * 使用注解的知名类库 28 | * 小结 29 | * 下载 30 | * 资料 31 | 在这篇文章中我们将阐述什么是Java注解,它们如何工作,怎么使用它们。 32 | 33 | 我们将揭开Java注解的面纱,包括内建注解或称元注解,还将讨论Java8中与之相关的的新特性。 34 | 35 | 最后,我们将实现自定义的注解,编写一个使用注解的处理程序(消费器),它通过java反射使用注解。 36 | 37 | 我们还会列出一些基于注解,知名且被广泛应用的第三方类库如:Junit,JAXB,Spring,Hibernate。 38 | 39 | 在文章的最后,会有一个压缩文件包含了文章中的所有示例,实现这些例子使用的软件版本如下所示: 40 | 41 | * Eclipse Luna 4.4 42 | * JRE Update 8.20 43 | * Junit 4 44 | * Hibernate 4.3.6 45 | * FindBugs 3.0.0 46 | 47 | ### 什么是注解? 48 | 注解早在J2SE1.5就被引入到Java中,主要提供一种机制,这种机制允许程序员在编写代码的同时可以直接编写元数据。 49 | 50 | 在引入注解之前,程序员们描述其代码的形式尚未标准化,每个人的做法各异:transient关键字、注释、接口等。这显然不是一种优雅的方式,随之而来的一种崭新的记录元数据的形式——注解被引入到Java中。 51 | 52 | 其它因素也促成了这个决定:当时不同类型的应用程序使用XML作为标准的代码配置机制,这其实并不是最佳方式,因为代码和XML的解耦以及未来对这种解耦应用的维护并不低廉。另外,由于非保留字的使用,例如“@deprecated”自从Java1.4便开始在Java文档中使用。我非常确定这是一个现在在注解中使用“@”原因。 53 | 54 | 包含注解的设计和开发的Java规范主要有以下两篇: 55 | 56 | * [JSR 175 A metadata facility for the Java programming Language](https://www.jcp.org/aboutJava/communityprocess/final/jsr175/index.html) 57 | * [JSR 250 Common Annotations for the Java Platform](https://jcp.org/en/jsr/detail?id=250) 58 | 59 | ### 介绍 60 | 解释何为注解的最佳方式就是元数据这个词:描述数据自身的数据。注解就是代码的元数据,他们包含了代码自身的信息。 61 | 62 | 注解可以被用在包,类,方法,变量,参数上。自Java8起,有一种注解几乎可以被放在代码的任何位置,叫做类型注解。我们将会在后面谈到具体用法。 63 | 64 | 被注解的代码并不会直接被注解影响。这只会向第三系统提供关于自己的信息以用于不同的需求。 65 | 66 | 注解会被编译至class文件中,而且会在运行时被处理程序提取出来用于业务逻辑。当然,创建在运行时不可用的注解也是可能的,甚至可以创建只在源文件中可用,在编译时不可用的注解。 67 | 68 | ### 消费器 69 | 理解注解的目的以及如何使用它都会带来困难,因为注解本身并不包含任何功能逻辑,它们也不会影响自己注解的代码,那么,它们到底为什么而存在呢? 70 | 71 | 这个问题的解释就是我所称的注解消费器。它们是利用被注解代码并根据注解信息产生不同行为的系统或者应用程序。 72 | 73 | 例如,在Java自带的内建注解(元注解)中,消费器是执行被注解代码的JVM。还有其他稍后谈到的其他例子,例如JUnit,消费器是读取,分析被注解代码的JUnit处理程序,它还可以决定测试单元和方法执行顺序。我们会在JUnit章节更深入。 74 | 75 | 消费器使用Java中的反射机制来读取和分析被注解的源代码。使用的主要的包有:java.lang, java.lang.reflect。我们将会在本篇指南中介绍如何用反射从头开始创建一个自定义的消费器。 76 | 77 | ### 注解语法和元素 78 | 声明一个注解需要使用“@”作为前缀,这便向编译器说明,该元素为注解。例如: 79 | ```java 80 | @Annotation 81 | public void annotatedMehod() { 82 | ... 83 | } 84 | ``` 85 | 上述的注解名称为Annotation,它正在注解annotatedMethod方法。编译器会处理它。注解可以以键值对的形式持有有很多元素,即注解的属性。 86 | ```java 87 | @Annotation( 88 | info = "I am an annotation", 89 | counter = "55" 90 | ) 91 | public void annotatedMehod() { 92 | ... 93 | } 94 | ``` 95 | 如果注解只包含一个元素(或者只需要指定一个元素的值,其它则使用默认值),可以像这样声明 96 | ```java 97 | @Annotation("I am an annotation") 98 | public void annotatedMehod() { 99 | ... 100 | } 101 | ``` 102 | 就像我们看到的一样,如果没有元素需要被指定,则不需要括号。多个注解可以使用在同一代码上,例如类: 103 | ```java 104 | @ Annotation (info = "U a u O") 105 | @ Annotation2 106 | class AnnotatedClass { ... } 107 | ``` 108 | 一些java本身提供的开箱即用的注解,我们称之为内建注解。也可以定义你自己的注解,称之为自定义注解。我们会在下一章讨论。 109 | 110 | ### 在什么地方使用 111 | 注解基本上可以在Java程序的每一个元素上使用:类,域,方法,包,变量,等等。 112 | 113 | 自Java8,诞生了通过类型注解的理念。在此之前,注解是限于在前面讨论的元素的声明上使用。从此,无论是类型还是声明都可以使用注解,就像: 114 | 115 | ```java 116 | @MyAnnotation String str = "danibuiza"; 117 | ``` 118 | 119 | 我们将会在Java8关联章节看到这种机制的更多细节。 120 | ### 使用案例 121 | 122 | 注解可以满足许多要求,最普遍的是: 123 | 124 | * 向编译器提供信息:注解可以被编译器用来根据不同的规则产生警告,甚至错误。一个例子是Java8中@FunctionalInterface注解,这个注解使得编译器校验被注解的类,检查它是否是一个正确的函数式接口。 125 | * 文档:注解可以被软件应用程序计算代码的质量例如:FindBugs,PMD或者自动生成报告,例如:用来Jenkins, Jira,Teamcity。 126 | * 代码生成:注解可以使用代码中展现的元数据信息来自动生成代码或者XML文件,一个不错的例子是JAXB。 127 | * 运行时处理:在运行时检查的注解可以用做不同的目的,像单元测试(JUnit),依赖注入(Spring),校验,日志(Log4j),数据访问(Hibernate)等等。 128 | 129 | 在这篇手册中我们将展现几种注解可能的用法,包括流行的Java类库是如何使用它们的。 130 | 131 | ### 内建注解 132 | 133 | Java语言自带了一系列的注解。在本章中我们将阐述最重要的一部分。这个清单只涉及了Java语言最核心的包,未包含标准JRE中所有包和库如JAXB或Servlet规范。 134 | 135 | 以下讨论到的注解中有一些被称之为Meta注解,它们的目的注解其他注解,并且包含关于其它注解的信息。 136 | 137 | * @Retention:这个注解注在其他注解上,并用来说明如何存储已被标记的注解。这是一种元注解,用来标记注解并提供注解的信息。可能的值是: 138 | * SOURCE:表明这个注解会被编译器忽略,并只会保留在源代码中。 139 | * CLASS:表明这个注解会通过编译驻留在CLASS文件,但会被JVM在运行时忽略,正因为如此,其在运行时不可见。 140 | * RUNTIME:表示这个注解会被JVM获取,并在运行时通过反射获取。 141 | 142 | 我们会在稍后展开几个例子。 143 | 144 | 145 | * @Target:这个注解用于限制某个元素可以被注解的类型。例如: 146 | * ANNOTATION_TYPE 表示该注解可以应用到其他注解上 147 | * CONSTRUCTOR 表示可以使用到构造器上 148 | * FIELD 表示可以使用到域或属性上 149 | * LOCAL_VARIABLE表示可以使用到局部变量上。 150 | * METHOD可以使用到方法级别的注解上。 151 | * PACKAGE可以使用到包声明上。 152 | * PARAMETER可以使用到方法的参数上 153 | * TYPE可以使用到一个类的任何元素上。 154 | 155 | * @Documented:被注解的元素将会作为Javadoc产生的文档中的内容。注解都默认不会成为成为文档中的内容。这个注解可以对其它注解使用。 156 | * @Inherited:在默认情况下,注解不会被子类继承。被此注解标记的注解会被所有子类继承。这个注解可以对类使用。 157 | * @Deprecated:说明被标记的元素不应该再度使用。这个注解会让编译器产生警告消息。可以使用到方法,类和域上。相应的解释和原因,包括另一个可取代的方法应该同时和这个注解使用。 158 | * @SuppressWarnings:说明编译器不会针对指定的一个或多个原因产生警告。例如:如果我们不想因为存在尚未使用的私有方法而得到警告可以这样做: 159 | ```java 160 | @SuppressWarnings( "unused") 161 | private String myNotUsedMethod(){ 162 | ... 163 | } 164 | ``` 165 | 通常,编译器会因为没调用该方而产生警告; 用了注解抑制了这种行为。该注解需要一个或多个参数来指定抑制的警告类型。 166 | 167 | * @Override:向编译器说明被注解元素是重写的父类的一个元素。在重写父类元素的时候此注解并非强制性的,不过可以在重写错误时帮助编译器产生错误以提醒我们。比如子类方法的参数和父类不匹配,或返回值类型不同。 168 | * @SafeVarargs:断言方法或者构造器的代码不会对参数进行不安全的操作。在Java的后续版本中,使用这个注解时将会令编译器产生一个错误在编译期间防止潜在的不安全操作。 169 | 170 | 更多信息请参考:http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html 171 | 172 | ### Java 8 与注解 173 | 174 | Java8带来了一些优势,同样注解框架的能力也得到了提升。在本章我们将会阐述,并就java8带来的3个注解做专题说明和举例: 175 | 176 | * @Repeatable注解,关于类型注解的声明,函数式接口注解@FunctionalInterface(与Lambdas结合使用)。 177 | 178 | @Repeatable:说明该注解标识的注解可以多次使用到同一个元素的声明上。 179 | 看一个使用的例子。首先我们创造一个能容纳重复的注解的容器: 180 | ```java 181 | /** 182 | * Container for the {@link CanBeRepeated} Annotation containing a list of values 183 | */ 184 | @Retention( RetentionPolicy.RUNTIME ) 185 | @Target( ElementType.TYPE_USE ) 186 | public @interface RepeatedValues 187 | { 188 | CanBeRepeated[] value(); 189 | } 190 | ``` 191 | 接着,创建注解本身,然后标记@Repeatable 192 | ```java 193 | @Retention( RetentionPolicy.RUNTIME ) 194 | @Target( ElementType.TYPE_USE ) 195 | @Repeatable( RepeatedValues.class ) 196 | public @interface CanBeRepeated 197 | { 198 | String value(); 199 | } 200 | ``` 201 | 最后,我们可以这样重复地使用: 202 | ```java 203 | @CanBeRepeated( "the color is green" ) 204 | @CanBeRepeated( "the color is red" ) 205 | @CanBeRepeated( "the color is blue" ) 206 | public class RepeatableAnnotated 207 | { 208 | 209 | } 210 | ``` 211 | 如果我们尝试去掉@Repeatable 212 | ```java 213 | @Retention( RetentionPolicy.RUNTIME ) 214 | @Target( ElementType.TYPE_USE ) 215 | public @interface CannotBeRepeated 216 | { 217 | 218 | String value(); 219 | } 220 | 221 | @CannotBeRepeated( "info" ) 222 | /* 223 | * if we try repeat the annotation we will get an error: Duplicate annotation of non-repeatable type 224 | * 225 | * @CannotBeRepeated. Only annotation types marked 226 | * 227 | * @Repeatable can be used multiple times at one target. 228 | */ 229 | // @CannotBeRepeated( "more info" ) 230 | public class RepeatableAnnotatedWrong 231 | { 232 | 233 | } 234 | ``` 235 | 我们会得到编译器的错误信息: 236 | ```java 237 | Duplicate annotation of non-repeatable type 238 | ``` 239 | 240 | * 自Java8开始,我们可以在类型上使用注解。由于我们在任何地方都可以使用类型,包括 new操作符,casting,implements,throw等等。注解可以改善对Java代码的分析并且保证更加健壮的类型检查。这个例子说明了这一点: 241 | ```java 242 | @SuppressWarnings( "unused" ) 243 | public static void main( String[] args ) 244 | { 245 | // type def 246 | @TypeAnnotated 247 | String cannotBeEmpty = null; 248 | 249 | // type 250 | List<@TypeAnnotated String> myList = new ArrayList(); 251 | 252 | // values 253 | String myString = new @TypeAnnotated String( "this is annotated in java 8" ); 254 | 255 | } 256 | // in method params 257 | public void methodAnnotated( @TypeAnnotated int parameter ) 258 | { 259 | System.out.println( "do nothing" ); 260 | } 261 | ``` 262 | 所有的这些在Java8之前都是不可能的。 263 | 264 | * @FunctionalInterface:这个注解表示一个函数式接口元素。函数式接口是一种只有一个抽象方法(非默认)的接口。编译器会检查被注解元素,如果不符,就会产生错误。例子如下 265 | ```java 266 | // implementing its methods 267 | @SuppressWarnings( "unused" ) 268 | MyCustomInterface myFuncInterface = new MyCustomInterface() 269 | { 270 | 271 | @Override 272 | public int doSomething( int param ) 273 | { 274 | return param * 10; 275 | } 276 | }; 277 | 278 | // using lambdas 279 | @SuppressWarnings( "unused" ) 280 | MyCustomInterface myFuncInterfaceLambdas = ( x ) -> ( x * 10 ); 281 | } 282 | 283 | @FunctionalInterface 284 | interface MyCustomInterface 285 | { 286 | /* 287 | * more abstract methods will cause the interface not to be a valid functional interface and 288 | * the compiler will thrown an error:Invalid '@FunctionalInterface' annotation; 289 | * FunctionalInterfaceAnnotation.MyCustomInterface is not a functional interface 290 | */ 291 | // boolean isFunctionalInterface(); 292 | 293 | int doSomething( int param ); 294 | } 295 | ``` 296 | 这个注解可以被使用到类,接口,枚举和注解本身。它的被JVM保留并在runtime可见,这个是它的声明: 297 | ```java 298 | @Documented 299 | @Retention(value=RUNTIME) 300 | @Target(value=TYPE) 301 | public @interface FunctionalInterface 302 | ``` 303 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Kotlin-Blogs/README.md: -------------------------------------------------------------------------------- 1 | kotlin专题系列文章 2 | --- 3 | 4 | * [Kotlin在Android中的应用](http://qq157755587.github.io/2015/11/14/kotlin-in-android/) 5 | * [Android开发必备知识:为什么说Kotlin值得一试](http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=624&fromuid=6) 6 | 7 | ## 项目&类库 8 | * [KBinding-MVVM框架](https://github.com/BennyWang/KBinding) 9 | * [DailyGank](https://github.com/zzhoujay/DailyGank) 10 | * [Fuel-HTTP networking library](https://github.com/kittinunf/Fuel) 11 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Loaders/README.md: -------------------------------------------------------------------------------- 1 | Loaders & LoaderManager 2 | --- 3 | 4 | * [LoaderManager使用详解(一)---没有Loader之前的世界](http://blog.csdn.net/murphykwu/article/details/35287303) 5 | * [LoaderManager使用详解(二)---了解LoaderManager](http://blog.csdn.net/murphykwu/article/details/35287883) 6 | * [LoaderManager使用详解(三)---实现Loaders](http://blog.csdn.net/murphykwu/article/details/35288477) 7 | * [LoaderManager使用详解(四)---实例:AppListLoader](http://blog.csdn.net/murphykwu/article/details/35288753) 8 | 9 | * [Android应用Loaders全面详解及源码浅析](http://blog.csdn.net/yanbober/article/details/48861457) 10 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Net/README.md: -------------------------------------------------------------------------------- 1 | Android 网络系列 2 | --- 3 | 4 | * [Android 网络--我是怎么做的: Volley+OkHttp+Https](http://gold.xitu.io/entry/55b8db8200b0196faa293e72) 5 | * [深入剖析Android网络开发库-part1: OkHttp, Volley and Gson](http://www.devtf.cn/?p=985) 6 | * [Android OkHttp文件上传与下载的进度监听扩展](http://blog.csdn.net/sbsujjbcy/article/details/48194701) 7 | * [a-smart-way-to-use-retrofit](http://blog.robinchutaux.com/blog/a-smart-way-to-use-retrofit/) 8 | * [Retrofit 2.0:有史以来最大的改进](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html) 9 | * [Android 一个改善的okHttp封装库](http://blog.csdn.net/lmj623565791/article/details/49734867) 10 | * [用 Retrofit 2 简化 HTTP 请求](https://realm.io/cn/news/droidcon-jake-wharton-simple-http-retrofit-2/) 11 | * [Retrofit — Getting Started and Create an Android Client](https://futurestud.io/blog/retrofit-getting-started-and-android-client) 12 | * [Android Retrofit 2.0使用](http://wuxiaolong.me/2016/01/15/retrofit/) 13 | * [RxJava 与 Retrofit 结合的最佳实践](http://gank.io/post/56e80c2c677659311bed9841) 14 | * [Retrofit 2.0 + OkHttp 3.0 配置](https://drakeet.me/retrofit-2-0-okhttp-3-0-config) 15 | * [网络服务库 Retrofit2 的使用方式](http://www.wangchenlong.org/2016/03/16/1602/use-retrofit-first/) 16 | * [你真的会用Retrofit2吗?Retrofit2完全教程](http://www.jianshu.com/p/308f3c54abdd) 17 | * [深入浅出 Retrofit](http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1117#rd) 18 | * [Retrofit + RxAndroid 实践总结](http://www.jianshu.com/p/f48f6d31314b) 19 | 20 | ### 网络工具相关 21 | * [如何利用mitmproxy来批量修改Android中HTTP流量](https://greenrobot.me/devpost/how-to-use-mitmproxy-custom-android-api-call/) 22 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Open-Source-Projects/README.md: -------------------------------------------------------------------------------- 1 | 完整开源项目 2 | --- 3 | 4 | * [AmazeFileManager 一款Material Design风格的文件管理器](https://github.com/arpitkh96/AmazeFileManager) 5 | * [Material Design 短信开源App](https://github.com/qklabs/qksms) 6 | * [SimplifyReader](https://github.com/SkillCollege/SimplifyReader) 7 |
8 | 一款基于Google Material Design设计开发的Android客户端,包括新闻简读,图片浏览,视频爽看 ,音乐轻听以及二维码扫描五个子模块。项目采取的是MVP架构开发。 9 | * [新浪微博第三方Android客户端 AisenWeiBo](https://github.com/wangdan/AisenWeiBo) 10 | * [MVVM_Hacker_News 使用官方Data Binding实现](https://github.com/hitherejoe/MVVM_Hacker_News) 11 | * [Coding Android客户端源码](https://coding.net/u/coding/p/Coding-Android/git) 12 | * [更纯净的知乎日报](https://github.com/izzyleung/ZhihuDailyPurify) 13 | * [一款基于Material Design 的开源笔记本应用](https://github.com/lguipeng/Notes) 14 | * [iBeebo微博客户端](https://github.com/andforce/iBeebo) 15 | * [a android client for news feed](https://github.com/miao1007/nmid-headline) 16 |
17 | ##### Features 18 | Full Material Design 19 |
RESTful API Design 20 |
Translucent mode for kitkat or higher 21 |
Local CSS in WebView 22 |
Night mode supported 23 | * [一个简单的记事本app,具有多色彩主题,自带日历视图,记事可以是列表显示也可以是网格显示,提醒功能,隐私保护功能等。](https://github.com/channguyen/pocket-note-android) 24 | * [湘潭大学三翼校园"四季电台" Android客户端](https://github.com/linroid/Sky31Radio/blob/master/app/src/main/AndroidManifest.xml) 25 | * [Timber-Material Design Music Player](https://github.com/naman14/Timber) 26 | * [一个看妹纸与开发资讯的Android APP](https://github.com/IvorHu/RealStuff) 27 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Performance/README.md: -------------------------------------------------------------------------------- 1 | Android 性能优化 2 | --- 3 | 4 | * [androidperformance 博客](http://androidperformance.com/) 5 | * [TextView预渲染研究](http://www.devtf.cn/?p=957) 6 | * [MAT - Memory Analyzer Tool 使用进阶](http://www.lightskystreet.com/2015/09/01/mat_usage/) 7 | * [一个只关注安卓性能优化以及最佳实践的Blog](http://android-performance.com/) 8 | * [性能优化总结](https://github.com/android-cn/android-discuss/issues/254) 9 | * [Android应用开发性能优化完全分析](http://blog.csdn.net/yanbober/article/details/48394201) 10 | * [Android应用启动优化:一种DelayLoad的实现和原理(上篇)](http://androidperformance.com/2015/11/18/Android-app-lunch-optimize-delay-load.html) 11 | * [Android应用启动优化:一种DelayLoad的实现和原理(下篇)](http://androidperformance.com/2015/12/29/Android%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96-%E4%B8%80%E7%A7%8DDelayLoad%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%92%8C%E5%8E%9F%E7%90%86-%E4%B8%8B%E7%AF%87.html) 12 | * [【凯子哥带你学Android】Andriod性能优化之列表卡顿——以“简书”APP为例](http://blog.csdn.net/zhaokaiqiang1992/article/details/49951095) 13 | * [Android冷启动时间优化](http://blog.waynell.com/android/2015/11/17/android-cold-start.html) 14 | * [关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析](http://blog.csdn.net/zhaokaiqiang1992/article/details/49787117) 15 | * [Android App 性能优化实践](http://www.imooc.com/article/2607) 16 | * [Android 中 SQLite 性能优化](http://droidyue.com/blog/2015/12/13/android-sqlite-tuning/) 17 | * [Android内存优化杂谈](https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=400656149&idx=1&sn=122b4f4965fafebf78ec0b4fce2ef62a&scene=0&key=ac89cba618d2d9769bc3006ae6f052433e9addebbf0099674296838d889f7d2c4098d859550e3bf2f6b53b3483336fec&ascene=7&uin=MTgxNDQ2NzkyMg%3D%3D&devicetype=android-19&version=26030832&nettype=ctlte&pass_ticket=LuET1X%2BVwvmFOKoypeaRevizrPu8nav3c69WqE2ynS2F%2BUBstsQr9bD%2FYhH3gEAj) 18 | * [性能优化模式](http://tech.meituan.com/performance_tuning_pattern.html) 19 | * [记一次 ListView 性能优化过程](http://kymjs.com/code/2015/11/26/01/) 20 | * [Android性能优化典范 - 第4季](http://hukai.me/android-performance-patterns-season-4/) 21 | * [Android UI性能优化详解](http://music4kid.github.io//android/2016/01/11/android-performance-ui/) 22 | * [Android 开发绕不过的坑:你的 Bitmap 究竟占多大内存](http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=498&fromuid=6) 23 | * [Android中导致内存泄漏的竟然是它----Dialog](http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=516&fromuid=6) 24 | * [关于Android中工作者线程的思考](http://droidyue.com/blog/2015/12/20/worker-thread-in-android/) 25 | * [Android UI性能优化详解](http://mrpeak.cn/android/2016/01/11/android-performance-ui) 26 | 27 | ## 工具 28 | * [GT-性能测试工具](https://github.com/TencentOpen/GT) 29 | * [Android性能分析工具整理汇总](http://www.jianshu.com/p/8b77d394b2a6) 30 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Program-Specification/README.md: -------------------------------------------------------------------------------- 1 | Android编程规范专题 2 | --- 3 | 4 | * [利用 Android Annotations 来玩玩契约编程](http://blog.csdn.net/FeeLang/article/details/49000203) 5 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Pull-To-Refresh/README.md: -------------------------------------------------------------------------------- 1 | Android 下拉刷新收集 2 | --- 3 | 4 | * [liaohuqiu/android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh) 5 | * [SuperSwipeRefreshLayout](https://github.com/nuptboyzhb/SuperSwipeRefreshLayout) 6 | ###Feature 7 | - 非侵入式,对原来的ListView、RecyclerView没有任何影响,用法和SwipeRefreshLayout类似。 8 | - 可自定义头部View的样式,调用setHeaderView方法即可 9 | - 支持RecyclerView,ListView,ScrollView,GridView等等。 10 | - 被包含的View(RecyclerView,ListView etc.)可跟随手指的滑动而滑动
11 | 默认是跟随手指的滑动而滑动,也可以设置为不跟随:setTargetScrollWithLayout(false) 12 | - 回调方法更多
13 | 比如:onRefresh() onPullDistance(int distance)和onPullEnable(boolean enable)
14 | 开发人员可以根据下拉过程中distance的值做一系列动画。 15 |
16 | 17 | * [WaveSwipeRefreshLayout](https://github.com/recruit-lifestyle/WaveSwipeRefreshLayout) 18 | -------------------------------------------------------------------------------- /android-as-blogs/Android-ReactNative/README.md: -------------------------------------------------------------------------------- 1 | ReactNative专题 2 | --- 3 | 4 | * [ReactNative 开发实践](http://www.jianshu.com/p/6d4cce9d914f) 5 | * [ReactNativeAndroid源码分析-Js如何调用Native的代码](http://zhuanlan.zhihu.com/program-life/20464825) 6 | -------------------------------------------------------------------------------- /android-as-blogs/Android-RecycleView/README.md: -------------------------------------------------------------------------------- 1 | Android-RecycleView 相关 2 | --- 3 | 4 | * [SuperSLiM](https://github.com/TonicArtos/SuperSLiM) 5 | * [Drag and Swipe with RecyclerView](https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd?source=latest---------2) 6 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Runtime-Permission/README.md: -------------------------------------------------------------------------------- 1 | Android 运行时权限适配 2 | --- 3 | 4 | * [permissions-dispatcher-plugin-自动生成代码的插件](https://github.com/shiraji/permissions-dispatcher-plugin) 5 | -------------------------------------------------------------------------------- /android-as-blogs/Android-RxJava/README.md: -------------------------------------------------------------------------------- 1 | Android RxJava系列 2 | --- 3 | 4 | ## 强烈推荐 5 | * [给 Android 开发者的 RxJava 详解](http://gank.io/post/560e15be2dca930e00da1083) 6 | 7 | * [大头鬼的RxJava收集](https://github.com/lzyzsd/Awesome-RxJava) 8 | * [【译】Don't break the chain: use RxJava's compose() operator](http://www.jianshu.com/p/e9e03194199e?utm_campaign=maleskine&utm_content=note&utm_medium=writer_share&utm_source=weibo) 9 | * [Don't break the chain: use RxJava's compose() operator](http://blog.danlew.net/2015/03/02/dont-break-the-chain/) 10 | * [Loading data from multiple sources with RxJava](http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/) 11 | * [Deferring Observable code until subscription in RxJava](http://blog.danlew.net/2015/07/23/deferring-observable-code-until-subscription-in-rxjava/) 12 | * [ReactiveX文档中文翻译](http://mcxiaoke.gitbooks.io/rxdocs/content/index.html) 13 | * [RxDocs](https://github.com/mcxiaoke/RxDocs) 14 | * [Using Realm with RxJava](https://realm.io/news/using-realm-with-rxjava/) 15 | * [周末终于去学了下RxJava,感觉屌爆了,相见恨晚啊](http://www.licheedev.com/2015/10/26/rxjava-diao-bao-le/) 16 | * [Architecting Android with RxJava](http://www.jianshu.com/p/943ceaccfdff) 17 | * [可能是东半球最全的RxJava使用场景小结](http://blog.csdn.net/theone10211024/article/details/50435325) 18 | * [RxJava中的错误处理](http://www.jianshu.com/p/916b72778145) 19 | * [图片Rx操作符](http://rxmarbles.com/) 20 | * [使用RxJava 提升用户体验](http://www.jianshu.com/p/33c548bce571) 21 | * [当EventBus遇上RxJava](http://www.easydone.cn/2016/03/29/) 22 | * [带你学开源项目:RxLifecycle - 当 Activity 被 destory 时自动暂停网络请求](https://gold.xitu.io/entry/5787a3fe5bbb500064916212) 23 | * [不该使用 RxJava 的一些情况](http://blog.chengyunfeng.com/?p=1009) 24 | 25 | --- 26 | * [谜之RxJava (一) —— 最基本的观察者模式](http://segmentfault.com/a/1190000004049490) 27 | * [谜之RxJava (二) —— Magic Lift](http://segmentfault.com/a/1190000004049841) 28 | * [迷之RxJava (三)—— 线程切换](http://segmentfault.com/a/1190000004051191) 29 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Support-Design/README.md: -------------------------------------------------------------------------------- 1 | Android-Support-Design相关博客 2 | --- 3 | 4 | * [Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏](http://www.jianshu.com/p/1078568e859f) 5 | * [TabLayout of design support library](http://chenfuduo.me/2015/07/30/TabLayout-of-design-support-library/) 6 | * [CoordinatorLayoutExample](https://github.com/saulmm/CoordinatorLayoutExample) 7 | * [CoordinatorLayout与快速返回的实现](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0818/3315.html) 8 | * [Android NestedScrolling 实战](http://www.race604.com/android-nested-scrolling/) 9 | * [如何为drawable着色,试用主题切换等](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0824/3356.html) 10 | * [CoordinatorBehaviorLayoutExample](https://github.com/saulmm/CoordinatorLayoutExample) 11 | * [A demo of the Android Material Design Support libraries](https://github.com/mwolfson/android-historian) 12 | * [SwipeDismissBehavior用法及实现原理](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1103/3650.html) 13 | * [AppBarLayout的越界滚动行为](http://liuling123.com/2016/01/overscroll-appBarLayout-behavior.html) 14 | * [拦截一切的CoordinatorLayout Behavior](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0224/3991.html) 15 | * [Android Support Library 23.2有哪些新东西(官网博客翻译)](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0226/3996.html) 16 | * [看,这个工具栏能伸缩折叠——Android CollapsingToolbarLayout使用介绍](http://www.jianshu.com/p/06c0ae8d9a96) 17 | 18 | 19 | ## libs 20 | * [TabbedCoordinatorLayout](https://github.com/vitovalov/TabbedCoordinatorLayout) 21 | 22 | ## 兼容库 23 | * [smooth-app-bar-layout](https://github.com/henrytao-me/smooth-app-bar-layout) 24 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Tools-Libs/README.md: -------------------------------------------------------------------------------- 1 | 实用工具集合 2 | --- 3 | 4 | * [Akatsuki 使用注解的方式来保存和恢复状态](https://github.com/tom91136/Akatsuki) 5 | * [icepick: Android Instance State made easy(使用注解的方式来保存和恢复状态)](https://github.com/frankiesardo/icepick) 6 | * [prefs 使用注解的方式来简化对Android SharePreferences 的使用](https://github.com/BoD/android-prefs) 7 | * [json字符串转model插件](https://github.com/zzz40500/GsonFormat) 8 | * [网易开源的监控应用内存,网络,电量等的工具](https://github.com/NetEase/Emmagee) 9 | * [Android中的layout文件分目录管理插件](https://github.com/dmytrodanylyk/folding-plugin) 10 | * [square: Clean, lightweight protocol buffers for Android and Java.](https://github.com/square/wire) 11 | * [java反射调用简化库](https://github.com/jOOQ/jOOR) 12 | * [square出品,手机摇一摇检测](https://github.com/square/seismic) 13 | * [bugtags为移动测试而生](https://github.com/bugtags/Bugtags-Android) 14 | * [IntelliJ Plugin for Android Parcelable boilerplate code generation.](https://github.com/mcharmas/android-parcelable-intellij-plugin) 15 | * [eventbus 事件追踪plugin](https://github.com/kgmyshin/eventbus-intellij-plugin) 16 | * [otto 事件追踪plugin](https://github.com/square/otto-intellij-plugin) 17 | * [methodscount(一个计算lib方法数的插件)](http://www.methodscount.com/) 18 | * [innerbuilder(一个用于生成内部Builder类的AS插件)](https://github.com/analytically/innerbuilder) 19 | * [知乎问答-Android Studio有哪些非常好用的插件](http://www.zhihu.com/question/28527388) 20 | * [octotree-浏览github&gitlab资源的浏览器插件](https://github.com/buunguyen/octotree) 21 | * [下一代Android打包工具,1000个渠道包只需要5秒钟](https://github.com/mcxiaoke/packer-ng-plugin) 22 | * [dart Extras "injection" library for Android](https://github.com/f2prateek/dart) 23 | * [统计gradle task耗时的插件](https://github.com/passy/build-time-tracker-plugin) 24 | * [ProcessPhoenix重启应用进程](https://github.com/JakeWharton/ProcessPhoenix) 25 | * [AndroidProguardPlugin](https://github.com/zhonghanwen/AndroidProguardPlugin) 26 | * [那些值得你试试的Android竞品分析工具](http://www.jianshu.com/p/ba2d9eca47a2) 27 | 28 | * [FileDownloader文件下载工具](https://github.com/lingochamp/FileDownloader) 29 | * [可任意定制的app更新组件(umeng更新替换方案)](https://github.com/yjfnypeu/UpdatePlugin) 30 | -------------------------------------------------------------------------------- /android-as-blogs/Android-UI-Tips/README.md: -------------------------------------------------------------------------------- 1 | Android UI处理小技巧 2 | --- 3 | 4 | * [使用LayerDrawable在ActionBar(Toolbar) Item中增加未读数badge](http://www.jmhend.me/layerdrawable-menuitems) 5 | * [如何在安卓app中使用FontAwesome图标](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0925/3518.html)([英语原文](http://code.tutsplus.com/tutorials/how-to-use-fontawesome-in-an-android-app--cms-24167)) 6 | * [ListView单条刷新的方法](http://www.cnblogs.com/tianzhijiexian/p/4278546.html) 7 | * [ViewDragHelper解析](http://gold.xitu.io/entry/5642b95e60b28045db01c13f) 8 | * [Android4.4及以上版本实现状态栏与顶栏同色](http://fanhongwei.github.io/blog/2015/03/04/android-statusbartint-and-swipebacklayout/) 9 | * [Android View.OnTouchListener 的子类](http://rocko.xyz/2015/04/26/Android-View-OnTouchListener-%E7%9A%84%E5%AD%90%E7%B1%BB/) 10 | * [修复Android中Navigation Bar遮挡PopupWindow的问题](http://droidyue.com/blog/2016/01/10/android-navigation-bar-popupwindow-issue/) 11 | * [SystemUiHelper-Helper for dealing with Android System UI visibility](https://github.com/DreaminginCodeZH/SystemUiHelper) 12 | 13 | ## RecyclerView 14 | * [RecyclerView: 带头部的 Grid 布局](http://blog.sqisland.com/2014/12/recyclerview-grid-with-header.html) 15 | 16 | ## 魅族相关 17 | * [浅谈NavigationBar高度的获取](http://faywong.github.io/posts/2015-11-26-01.html) 18 | -------------------------------------------------------------------------------- /android-as-blogs/Android-Views/README.md: -------------------------------------------------------------------------------- 1 | Android view控件集合 2 | --- 3 | 4 | # PhotoPicker图片选自 5 | * [PhotoPicker-类似微信的图片选择、预览组件](https://github.com/donglua/PhotoPicker) 6 | 7 | ## drawer 8 | * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) 9 | 10 | ## ToolBar 11 | * [CollapsingAvatarToolbar](https://github.com/Sloy/CollapsingAvatarToolbar) 12 | * [Android-ActionItemBadge](https://github.com/mikepenz/Android-ActionItemBadge) 13 |
14 | ![Image](https://raw.githubusercontent.com/mikepenz/Android-ActionItemBadge/develop/DEV/screenshot/screenshot1_small.png) 15 | ![Image](https://raw.githubusercontent.com/mikepenz/Android-ActionItemBadge/develop/DEV/screenshot/screenshot2_small.png) 16 | 17 | ## drawable 18 | * [用ClipDrawable实现音频录制麦克风讲话效果](http://www.jianshu.com/p/06eca50ddda4) 19 | * [用RotateDrawable实现网易云音乐唱片机效果](http://www.jianshu.com/p/0e0de2cdd2bb) 20 | 21 | ## ProgressViews 22 | * [水波纹&浪花WashingMachineView](https://github.com/naman14/WashingMachineView) 23 | * [水波纹WaveView](https://github.com/gelitenight/WaveView) 24 | 25 | ## TextView 26 | * [模仿微信朋友圈、QQ说说的评论的人,点赞人可点击效果 ](https://github.com/nimengbo/TextViewSpanClickable) 27 | * [A label view. Extends TextView](https://github.com/Frank-Zhu/TriangleRectangleLabelView) 28 |
29 | ![](https://raw.githubusercontent.com/Frank-Zhu/TriangleRectangleLabelView/master/art/art.png) 30 | * [labelview](https://github.com/linger1216/labelview) 31 |
32 | ![](https://github.com/linger1216/labelview/blob/master/img/img1.png) 33 | 34 | ## EditText 35 | * [MaterialTextField](https://github.com/florent37/MaterialTextField) 36 | 37 | ## WebView 38 | * [史上最全WebView使用,附送Html5Activity一份](http://www.jianshu.com/p/3fcf8ba18d7f) 39 | * [好好和h5沟通!几种常见的hybrid通信方式](http://zjutkz.net/2016/04/17/%E5%A5%BD%E5%A5%BD%E5%92%8Ch5%E6%B2%9F%E9%80%9A%EF%BC%81%E5%87%A0%E7%A7%8D%E5%B8%B8%E8%A7%81%E7%9A%84hybrid%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F/) 40 | 41 | ## ViewPager 42 | * [实现上滑滚动列表以及item左右滑动的库](https://github.com/florent37/MaterialLeanBack) 43 | 44 | ## Recyclerview 45 | * [sticky-headers-recyclerview](https://github.com/timehop/sticky-headers-recyclerview) 46 | * [Dividers is a simple Android library to create easy separators for your RecyclerViews](https://github.com/Karumi/Dividers) 47 | * [XRecyclerView-(集成下拉刷新,加载更多,Header, Footer)](https://github.com/jianghejie/XRecyclerView) 48 | * [RecyclerView的使用与提升](http://lovinghuan.com/2015/11/26/RecyclerView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%92%8C%E6%8F%90%E5%8D%87/) 49 | * [Paginate-简化加载更多](https://github.com/MarkoMilos/Paginate) 50 | 51 | ## RefreshLayout 52 | [MaterialRefreshLayout](https://github.com/android-cjj/Android-MaterialRefreshLayout) 53 | 54 | ## ScrollBar 55 | * [MaterialScrollBar](https://github.com/krimin-killr21/MaterialScrollBar) 56 |
57 | An Android library that brings the Material Design 5.1 scrollbar to pre-5.1 devices. Designed for recyclerViews. 58 |
[Video](https://youtu.be/CmcPsJYuzME) 59 | ![](http://i.imgur.com/8DNLqkn.png) 60 | * [SnailBar](https://github.com/android-cjj/SnailBar) 61 | ![](http://www.apkbus.com/data/attachment/forum/201509/14/152642n3a5kvn36a6v3m3a.gif) 62 | 63 | ## filter view 64 | * [一个实用的多条件筛选菜单](https://github.com/dongjunkun/DropDownMenu) 65 | * [DropDownMenu](https://github.com/JayFang1993/DropDownMenu) 66 | 67 | ## complex views 68 | * [MaterialRecents](https://github.com/ZieIony/MaterialRecents) 69 |
70 | ![gif](https://github.com/ZieIony/MaterialRecents/blob/master/images/recents.gif) 71 | * [android粒子系统](https://github.com/plattysoft/Leonids) 72 | * [StackOverView](https://github.com/Bossyao168/StackOverView) 73 |
74 | ![gif](http://i1060.photobucket.com/albums/t444/bossyao168/2015-04-03%2020_51_50_zpscduimbta.gif) 75 | * [Android-ObservableScrollView](https://github.com/ksoichiro/Android-ObservableScrollView) 76 |
77 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo12.gif) 78 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo10.gif) 79 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo11.gif) 80 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo13.gif) 81 | 82 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo1.gif) 83 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo2.gif) 84 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo3.gif) 85 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo4.gif) 86 | 87 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo5.gif) 88 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo6.gif) 89 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo7.gif) 90 | ![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo8.gif) 91 | -------------------------------------------------------------------------------- /android-as-blogs/Material适配博客/README.md: -------------------------------------------------------------------------------- 1 | Material适配相关博客 2 | --- 3 | 4 | ## Material Design github收集库 5 | [MaterialDesignLibrary](https://github.com/navasmdc/MaterialDesignLibrary) 6 | 7 | --- 8 | 9 | * [Material Design 中文版](http://wiki.jikexueyuan.com/project/material-design/) 10 | * [Material适配1 - 入门篇](http://www.cnblogs.com/ct2011/p/4493384.html) 11 | * [Material适配2 - 高级篇](http://www.cnblogs.com/ct2011/p/4493439.html) 12 | * [谈谈Android Material Design 中的Tint(着色)](http://segmentfault.com/a/1190000003038675?utm_source=Weibo&utm_medium=shareLink&utm_campaign=socialShare) 13 | * [awesome-android-ui Android UI集合](https://github.com/wasabeef/awesome-android-ui) 14 | * [material-sheet-fab](https://github.com/gowong/material-sheet-fab) 15 | * [薄荷Toolbar(ActionBar)的适配方案](http://www.stormzhang.com/android/2015/08/16/boohee-toolbar/) 16 | * [解决共享元素在系统UI上绘制的问题](http://rocko.xyz/2015/06/19/%E3%80%90%E8%AF%91%E3%80%91%E8%A7%A3%E5%86%B3%E5%85%B1%E4%BA%AB%E5%85%83%E7%B4%A0%E5%9C%A8%E7%B3%BB%E7%BB%9FUI%E4%B8%8A%E7%BB%98%E5%88%B6%E7%9A%84%E9%97%AE%E9%A2%98/) 17 | * [Android Toolbar ActionItemBadge](https://github.com/mikepenz/Android-ActionItemBadge) 18 |
19 | ## Screenshots 20 |
![Image](https://raw.githubusercontent.com/mikepenz/Android-ActionItemBadge/develop/DEV/screenshot/screenshot1_small.png) ![Image](https://raw.githubusercontent.com/mikepenz/Android-ActionItemBadge/develop/DEV/screenshot/screenshot2_small.png) 21 | * [A easy helper library for Android to manage your applications StatusBarShadow, FullScreen behavior and much more over API Levels down to 10](https://github.com/mikepenz/Materialize) 22 | * [Ripple effect for Android 14+](https://github.com/ozodrukh/RippleDrawable) 23 | * [关于使用 CardView 开发过程中要注意的细节](http://blog.feng.moe/2015/10/24/something-about-cardview-development/) 24 | * [Android Tinting drawables](http://www.devtf.cn/?p=1123) 25 | * [结合motion和Transition实现共享元素的酷炫动画](https://github.com/hehonghui/android-tech-frontier/blob/master/issue-43/%E7%BB%93%E5%90%88motion%E5%92%8CTransition%E5%AE%9E%E7%8E%B0%E5%85%B1%E4%BA%AB%E5%85%83%E7%B4%A0%E7%9A%84%E9%85%B7%E7%82%AB%E5%8A%A8%E7%94%BB.md) 26 | 27 | ## Toolbar 28 | * [replaces the stock contextual action bar to allow more customization](https://github.com/afollestad/material-cab) 29 | * [AppBar 布局的使用方式](http://www.wangchenlong.org/2016/03/22/1603/227-app-bar-first/) 30 | 31 | ## BottomBar 32 | * [BottomBar](https://github.com/roughike/BottomBar) 33 | * [BottomNavigation](https://github.com/Ashok-Varma/BottomNavigation) 34 | * [Material-BottomNavigation](https://github.com/sephiroth74/Material-BottomNavigation) 35 | * [底部导航栏(Bottom navigation)规范指南](https://github.com/LittleFriendsGroup/BottomNavigation) 36 | 37 | ## Camera 38 | * [material-camera](https://github.com/afollestad/material-camera) 39 | 40 | ## Dialog 41 | * [material-dialogs](https://github.com/afollestad/material-dialogs) 42 | 43 | ## Ripples 44 | * [Ripples – Part 1](https://blog.stylingandroid.com/ripples-part-1/) 45 | * [Ripples – Part 2](https://blog.stylingandroid.com/ripples-part-2/) 46 | * [Ripples – Part 3](https://blog.stylingandroid.com/ripples-part-3/) 47 | 48 | ## Transition Animators 49 | * [理论加配合结合,讲的非常好](http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html) 50 | * [这个博客里面有几篇例子,代码值得学习](https://halfthought.wordpress.com/) 51 | 52 | ## Projects 53 | * [a showcase of material design](https://github.com/nickbutcher/plaid) 54 | -------------------------------------------------------------------------------- /android-as-blogs/使用不同的Theme实现主题切换系列两篇/README.md: -------------------------------------------------------------------------------- 1 | Android Studio快捷键系列博客 2 | --- 3 | 1. [让你的Android应用能使用多种主题-Part-1](https://github.com/bboyfeiyu/android-tech-frontier/tree/master/androidweekly/%E8%AE%A9%E4%BD%A0%E7%9A%84Android%E5%BA%94%E7%94%A8%E8%83%BD%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%A7%8D%E4%B8%BB%E9%A2%98-Part-1) 4 | 2. [让你的Android应用能使用多种主题-Part-2](https://github.com/bboyfeiyu/android-tech-frontier/tree/master/androidweekly/%E8%AE%A9%E4%BD%A0%E7%9A%84Android%E5%BA%94%E7%94%A8%E8%83%BD%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%A7%8D%E4%B8%BB%E9%A2%98-Part-2) 5 | 6 | [具体实现项目](https://github.com/hidroh/materialistic) 7 | 8 | --- 9 | * [android 实现【夜晚模式】的另外一种思路](https://segmentfault.com/a/1190000005736047) 10 | * [Bilibili Android 多主题框架](https://github.com/Bilibili/MagicaSakura) 11 | 12 | ## Android主题切换 13 | | 描述 | 地址 | 14 | |---------|--------| 15 | | Android中插件开发篇之----类加载器 | [Android中插件开发篇之----类加载器](http://blog.csdn.net/jiangwei0910410003/article/details/41384667) | 16 | | Android中插件开发篇之----应用换肤原理解析 | [Android中插件开发篇之----应用换肤原理解析](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0819/3328.html) | 17 | | Android更换主题 | [Android更换主题](http://wuxiaolong.me/2015/08/19/ChangeTheme/) | 18 | | 自定义属性方法实现日间,夜间切换 | [Colorful](https://github.com/bboyfeiyu/Colorful) | 19 | | 日夜间模式切换 | [日夜间模式切换](https://github.com/zzz40500/ThemeDemo) | 20 | | Android 主题动态切换框架:Prism | [Android 主题动态切换框架:Prism](https://blog.leancloud.cn/3612/) | 21 | | 一种完全无侵入的换肤方式,支持插件式和应用内,无需重启Activity. | [AndroidChangeSkin](https://github.com/hongyangAndroid/AndroidChangeSkin) | 22 | | 日间夜间主题切换 | [NightOwl](https://github.com/ashqal/NightOwl) | 23 | | 颜色主题切换 | [app-theme-engine](https://github.com/afollestad/app-theme-engine) | 24 | | 一种Android换肤机制的实现 | [一种Android换肤机制的实现](http://eastmoneyandroid.github.io/2016/01/22/android-reskin/#more) | 25 | | 夜晚的故事(android夜间模式实现) | [夜晚的故事(android夜间模式实现)](http://www.jianshu.com/p/60608820bb71) | 26 | --------------------------------------------------------------------------------