├── API简析
└── LruCache.md
├── Kotlin
├── Kotlin.md
└── res
│ ├── QQ截图20190303112453.png
│ └── QQ截图20190303112506.png
├── README.md
├── 其他
├── Android知识点随记.md
├── MarkNote版本1的.md
├── MarkNote版本2.md
├── res
│ ├── Compressor.png
│ ├── QQ截图20190718000949.png
│ ├── Screenshot_20190718-003619.jpg
│ ├── easymark.png
│ ├── shortcuts.jpg
│ └── themes.jpg
└── 计算机视觉与Android.md
├── 响应式编程
├── RxJava系列-4:RxJava源码分析.md
├── RxJava系列(1):一篇的比较全面的RxJava2方法总结.md
├── RxJava系列(2):Flowable和背压.md
├── RxJava系列(3):用RxJava打造EventBus.md
└── res
│ ├── RxJava.png
│ ├── RxJava_Scheduler.png
│ ├── RxJava_Switch.png
│ ├── RxJava_Switch2.png
│ └── combineLatest.png
├── 四大组件
├── Activity.md
├── Broadcast.md
├── Fragment.md
├── Service.md
└── res
│ ├── ActivityThread示意图.png
│ ├── activity_fragment_lifecycle.png
│ ├── activity_life.png
│ ├── activity_life2.png
│ ├── fragment_lifecycle.png
│ └── service_life.png
├── 图片加载
├── Android相机最佳实践.md
├── Glide系列:Glide主流程源码分析.md
├── Glide系列:Glide的缓存的实现原理.md
├── Glide系列:Glide的配置和使用方式.md
├── res
│ ├── QQ图片20190423230233.png
│ ├── QQ截图20190312223345.png
│ ├── glide_configuration.png
│ ├── glide_into_stage1.jpg
│ ├── glide_into_stage2.jpg
│ ├── glide_into_stage3.jpg
│ ├── glide_into_stage4.jpg
│ ├── glide_load.jpg
│ ├── glide_with.jpg
│ └── 开发.png
└── 图片压缩框架封装.md
├── 工作空间
├── OOM优化.md
├── Tinker.md
├── URL编码问题.md
├── res
│ ├── 2017-11-19 17-32-47.png
│ └── 2017-11-19 17-33-49.png
├── 文章暂时存放.md
├── 百度定位API.md
└── 第三方库整理.md
├── 开发工具
├── ADB_常见的ADB指令总结.md
├── Gradle_常见的指令和配置总结.md
├── Keytool_常用的指令.md
└── res
│ ├── keytool_alias_name.jpg
│ ├── keytool_alias_psd.jpg
│ ├── keytool_list.jpg
│ ├── keytool_list_after.jpg
│ └── keytool_password.jpg
├── 异步编程
├── Android多线程编程:IntentService和HandlerThread.md
└── AsyncTask的使用和源码分析.md
├── 性能优化
├── Android性能优化-ANR.md
├── Android性能优化-内存优化.md
├── Android性能优化-启动优化.md
├── Android性能优化-布局优化.md
├── Android相机Camera1资料.md
├── Android相机Camera2资料.md
└── Android进程保活.md
├── 消息机制
├── EventBus的源码分析.md
├── res
│ ├── AIDL.png
│ ├── AIDL_Location.png
│ ├── AIDL_Manager.png
│ ├── AMS.png
│ ├── Binder.png
│ ├── BinderAMS.png
│ ├── BinderTerminology.png
│ ├── Binder_流程.png
│ ├── Binder内存映射.png
│ ├── Full_IPC.png
│ ├── Handler_Looper_Message.png
│ ├── Handler_handle_message.png
│ ├── Handler_send_message.png
│ ├── binder_diver.jpg
│ ├── binder_model.png
│ ├── launcher.png
│ ├── mmap.jpg
│ └── 包结构.png
├── 线程通信:Handler、MessageQueue和Looper.md
└── 跨进程通信:Binder机制.md
├── 混合开发
├── ReactNative.md
└── res
│ ├── align-content.jpg
│ ├── align-items.jpg
│ ├── flex-direction.jpg
│ ├── flex-wrap.jpg
│ └── justify-content.jpg
├── 笔试面试
├── Android高级软件工程师2017.md
├── Android高级面试_10_跨平台开发.md
├── Android高级面试_11_JNINDK.md
├── Android高级面试_12_各种三方库分析.md
├── Android高级面试_12_算法.md
├── Android高级面试_12_项目经验梳理.md
├── Android高级面试_1_Handler相关.md
├── Android高级面试_2_IPC相关.md
├── Android高级面试_3_语言相关.md
├── Android高级面试_4_虚拟机相关.md
├── Android高级面试_5_四大组件、系统源码等.md
├── Android高级面试_6_性能优化.md
├── Android高级面试_7_网络相关.md
├── Android高级面试_8_热修补插件化等.md
├── Android高级面试_9_网络基础.md
├── README.md
├── java
│ ├── ArrayList、LinkedList、Vector.md
│ ├── Collection包结构,与Collections的区别.md
│ ├── HashMap和ConcurrentHashMap的区别,HashMap的底层源码.md
│ ├── HashMap和HashTable的区别.md
│ ├── Hashcode的作用.md
│ ├── Java1.7与1.8新特性.md
│ ├── Java的四种引用,强弱软虚,用到的场景.md
│ ├── Map、Set、List、Queue、Stack的特点与用法.md
│ ├── Object有哪些公用方法.md
│ ├── Override和Overload的含义去区别.md
│ ├── Static class 与 non static class的区别.md
│ ├── String、StringBuffer与StringBuilder的区别.md
│ ├── Switch能否用string做参数.md
│ ├── TreeMap、HashMap、LindedHashMap.md
│ ├── equals与==的区别.md
│ ├── jvm-java 内存模型 以及各个分区具体内容.md
│ ├── throw和throws有什么区别.md
│ ├── wait()和sleep()的区别.md
│ ├── 九种基本数据类型的大小以及他们的封装类.md
│ ├── 内存溢出和内存泄露的区别.md
│ └── 解析XML的几种方式的原理与特点:DOM、SAX、PULL.md
├── res
│ ├── 687472.webp
│ ├── 940884-20180423141951735-912699213.png
│ ├── QQ图片20190425213636.jpg
│ ├── QQ截图20190225213434.png
│ ├── QQ截图20190226123631.png
│ ├── bg2014092004.png
│ ├── ipc.png
│ ├── jvm_heap_region.jpg
│ ├── tcp_3_hello.png
│ └── tcp_4_bye.png
├── 今日头条Android面试.md
└── 初级工程师.md
├── 系统架构
├── Android应用启动过程.md
├── Android应用安装过程.md
├── Android打包过程.md
├── Android系统启动过程.md
├── Android系统架构.md
├── SurefaceView_and_TextureView.md
├── res
│ └── Andriod系统架构图.jpg
├── 控件体系
│ ├── RV.md
│ ├── RV各种效果实现.md
│ ├── View体系详解:View的工作流程.md
│ ├── View体系详解:坐标系、滑动事件和分发机制.md
│ ├── View体系详解:自定义控件.md
│ ├── resources
│ │ ├── View动画属性详解.png
│ │ ├── View动画框架.png
│ │ ├── 事件分发机制.png
│ │ ├── 事件分发机制原理.png
│ │ ├── 坐标系.png
│ │ ├── 属性动画.png
│ │ └── 属性动画XML.png
│ └── 动画体系详解.md
└── 窗口机制
│ └── Android的Window管理机制.md
├── 网络访问
├── OKHttp源码阅读.md
├── OkHttp异步请求.png
├── OkHttp请求时序图.png
├── Retrofit源码阅读.md
├── Retrofit的执行过程.png
├── Retrofit类图.png
└── 责任链执行过程.png
└── 高阶技术
├── Android插件化.md
├── Dagger从集成到源码.md
├── JNI技术总结.md
├── res
├── 20140507203312765.jpg
├── Component.png
├── QQ截图20190227124949.png
├── QQ截图20190227125150.png
├── QQ截图20190302004456.png
├── QQ截图20190302122352.png
├── knife_gen.png
├── live_data.png
├── mvp_package.png
├── mvvm_package.png
└── viewmodel-lifecycle.png
├── 探索Android架构设计.md
├── 注解在Android中的应用.md
├── 浅谈LiveData的通知过程.md
└── 浅谈ViewModel生命周期控制.md
/Kotlin/res/QQ截图20190303112453.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/Kotlin/res/QQ截图20190303112453.png
--------------------------------------------------------------------------------
/Kotlin/res/QQ截图20190303112506.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/Kotlin/res/QQ截图20190303112506.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android [DEPRECATED]
2 |
3 | ## 1、目录
4 |
5 | ### 基础开发
6 |
7 | - 基础回顾
8 | - [Android 基础回顾:Activity 基础](四大组件/Activity.md)
9 | - [Android 基础回顾:Fragment 基础](四大组件/Fragment.md)
10 | - [Android 基础回顾:Service 基础](四大组件/Service.md)
11 | - [Android 基础回顾:Broadcast 基础](四大组件/Broadcast.md)
12 |
13 | - 开发语言
14 | - [Java 注解在 Android 中的应用](注解和依赖注入/注解在Android中的应用.md)
15 | - [Kotlin 基础知识梳理](Kotlin/Kotlin.md)
16 | - [在 Android 中使用 JNI 的总结](高阶技术/JNI技术总结.md)
17 |
18 | - 架构设计
19 | - [Android 应用架构设计探索:MVC、MVP、MVVM和组件化](结构设计/探索Android架构设计.md)
20 | - [浅谈 ViewModel 的生命周期控制](高阶技术/浅谈ViewModel生命周期控制.md)
21 | - [浅谈 LiveData 的通知机制](高阶技术/浅谈LiveData的通知过程.md)
22 |
23 | - 性能优化
24 | - [ANR](性能优化/Android性能优化-ANR.md)
25 | - [布局优化](性能优化/Android性能优化-布局优化.md)
26 | - [进程保活](性能优化/Android进程保活.md)
27 | - [启动优化](性能优化/Android性能优化-启动优化.md)
28 | - [内存优化](性能优化/Android性能优化-内存优化.md)
29 |
30 | - 开发环境
31 | - [常见的 ADB 指令总结](开发工具/ADB_常见的ADB指令总结.md)
32 | - [常见的 Gradle 指令和配置总结](开发工具/Gradle_常见的指令和配置总结.md)
33 | - [常见的 Keytool 指令总结](开发工具/Keytool_常用的指令.md)
34 |
35 | ### 系统源码
36 |
37 | - 核心流程
38 | - [Android 系统架构](系统架构/Android系统架构.md)
39 | - [Android 系统启动流程源码分析](系统架构/Android系统启动过程.md)
40 | - [Android 应用打包过程](系统架构/Android打包过程.md)
41 | - [Android 应用安装过程](系统架构/Android应用安装过程.md)
42 |
43 | - 消息机制
44 | - [Android 消息机制:Handler、MessageQueue 和 Looper](消息机制/线程通信:Handler、MessageQueue和Looper.md.md)
45 | - [Android IPC 机制:Binder 机制](消息机制/跨进程通信:Binder机制.md)
46 |
47 | - 异步编程
48 | - [AsyncTask 的使用和源码分析](异步编程/AsyncTask源码分析.md)
49 | - [Android 多线程编程:IntentService 和 HandlerThread](异步编程/Android多线程编程:IntentService和HandlerThread.md)
50 |
51 | - 窗口机制
52 | - [Android 的窗口管理机制](系统架构/窗口机制/Android的Window管理机制.md)(编辑中)
53 |
54 | - 控件体系
55 | - [View 体系详解:View的工作流程](系统架构/控件体系/View体系详解:View的工作流程.md)
56 | - [View 体系详解:坐标系、滑动事件和分发机制](系统架构/控件体系/View体系详解:坐标系、滑动事件和分发机制.md)
57 | - [Android 动画体系详解](系统架构/控件体系/动画体系详解.md)
58 | - [SurfaceView 与 TextureView 的区别](系统架构/SurefaceView_and_TextureView.md)
59 |
60 | - 部分 API 源码
61 | - [LruCache 的使用和源码分析](API简析/LruCache.md)
62 |
63 | ### 三方库源码
64 |
65 | - 网络框架
66 | - [网络框架 OkHttp 源码解析](网络访问/OKHttp源码阅读.md)
67 | - [网络框架 Retrofit 源码解析](网络访问/Retrofit源码阅读.md)
68 |
69 | - 图片加载框架
70 | - [Glide 系列-1:预热、Glide 的常用配置方式及其原理](图片加载/Glide系列:Glide的配置和使用方式.md)
71 | - [Glide 系列-2:主流程源码分析](图片加载/Glide系列:Glide主流程源码分析.md)
72 | - [Glide 系列-3:Glide 缓存的实现原理](图片加载/Glide系列:Glide的缓存的实现原理.md)
73 |
74 | - RxJava
75 | - [RxJava2 系列-1:一篇的比较全面的 RxJava2 方法总结](响应式编程/RxJava2系列·_一篇的比较全面的RxJava2方法总结.md)
76 | - [RxJava2 系列-2:Flowable 和背压](响应式编程/Flowable和背压.md)
77 | - [RxJava2 系列-3:使用 Subject](响应式编程/用RxJava打造EventBus.md)
78 | - [RxJava2 系列-4:RxJava 源码分析](响应式编程/RxJava系列-4:RxJava源码分析.md)
79 |
80 | - 其他框架
81 | - [消息机制 EventBus 源码解析](消息机制/EventBus的源码分析.md)
82 | - [Dagger 从集成到源码带你理解依赖注入框架](高阶技术/Dagger从集成到源码.md)
83 |
84 | ### Java 相关
85 |
86 | - 并发编程
87 | - [Java 并发编程:ThreadLocal 的使用及其源码实现](https://blog.csdn.net/github_35186068/article/details/83858944)
88 |
89 | - 设计模式
90 | - [观察者模式](https://blog.csdn.net/github_35186068/article/details/83754026)
91 |
92 | - 虚拟机
93 | - [内存管理](https://juejin.im/post/5b475e976fb9a04fa8671a45)
94 | - [虚拟机执行子系统](https://juejin.im/post/5b4a1fb7e51d4519213fd374)
95 | - [虚拟机内存模型与高效并发](https://juejin.im/post/5b4f48e75188251b1b448aa0)
96 |
97 | - 三方库
98 | - [时间库 JodaTime](https://blog.csdn.net/github_35186068/article/details/83754146)
99 |
100 | ### UI 相关
101 |
102 | - [自定义控件](系统架构/控件体系/View体系详解:自定义控件.md)(编辑中)
103 |
104 | ### 编程基础
105 |
106 | - 数据库
107 | - [MySQL 基础知识(全)](https://juejin.im/post/5a12d62bf265da431d3c4a01)
108 |
109 | ### 面试题
110 |
111 | > 通过面试题梳理知识点细节
112 |
113 | - [Android高级面试_1_Handler相关](笔试面试/Android高级面试_1_Handler相关.md)
114 | - [Android高级面试_2_IPC相关](笔试面试/Android高级面试_2_IPC相关.md)
115 | - [Android高级面试_3_语言相关](笔试面试/Android高级面试_3_语言相关.md)
116 | - [Android高级面试_4_虚拟机相关](笔试面试/Android高级面试_4_虚拟机相关.md)
117 | - [Android高级面试_5_四大组件、系统源码等](笔试面试/Android高级面试_5_四大组件、系统源码等.md)
118 | - [Android高级面试_6_性能优化](笔试面试/Android高级面试_6_性能优化.md)
119 | - [Android高级面试_7_三方库相关](笔试面试/Android高级面试_7_三方库相关.md)
120 | - [Android高级面试_8_热修补插件化等](笔试面试/Android高级面试_8_热修补插件化等.md)
121 | - [Android高级面试_9_网络基础](笔试面试/Android高级面试_9_网络基础.md)
122 | - [Android高级面试_10_跨平台开发](笔试面试/Android高级面试_10_跨平台开发.md)
123 | - [Android高级面试_11_JNINDK](笔试面试/Android高级面试_11_JNINDK.md)
124 | - [Android高级面试_12_项目经验梳理](笔试面试/Android高级面试_12_项目经验梳理.md)
125 | - [Android 中高级工程师面试题总结](笔试面试/Android高级软件工程师2017.md)
126 |
127 | ### 其他
128 |
129 | - [马克笔记—Android 端开源的 Markdown 笔记应用](其他/MarkNote版本1的.md)
130 | - [承上启下:Markdown 笔记应用 MarkNote 的重构之路](其他/MarkNote版本2.md)
131 |
132 | ## 2、资源整理
133 |
134 |
135 |
--------------------------------------------------------------------------------
/其他/Android知识点随记.md:
--------------------------------------------------------------------------------
1 |
2 | ## 异常处理
3 |
4 | 对于未捕获的异常,借助 Thread 的静态方法来进行处理
5 |
6 | ```java
7 | public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
8 | defaultUncaughtExceptionHandler = eh;
9 | }
10 | ```
11 |
12 | ## multidex
13 |
14 | 这是因为安装应用时,有一步是使用 DexOpt 对 Dex 进行优化。这个过程会生成一个 ODex 文件,执行 ODex 的效率会比直接执行 Dex 文件的效率要高很多。在早期的 Android 系统中,DexOpt 把每一个类的方法 id 检索起来,存在一个链表结构里面。但是这个链表的长度是用一个 short 类型来保存的,导致了方法 id 的数目不能够超过65536 个。尽管在新版本的 Android 系统中,修复了 DexOpt 的这个问题,但是我们仍然需要对低版本的 Android 系统做兼容。
15 |
16 | 为了解决方法数超限的问题,需要启用 multidex 将该 dex文 件拆成多个。
17 |
18 | ## 动态布局
19 |
20 | 就是指服务端使用 API 下发数据信息,然后客户端根据下发的信息进行动态布局。
21 |
22 | 另一层含义可能是在代码中进行动态布局而不是使用 XML 的方式。
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/其他/MarkNote版本1的.md:
--------------------------------------------------------------------------------
1 | # 马克笔记—Android 端开源的 Markdown 笔记应用
2 |
3 | 
4 |
5 | > 马克笔记是运行在Android设备上面的一款开源的Markdown笔记,它的功能开发得已经比较完善,已经能够满足大部分用户的需求。现在将其开源到Github上面,用来交流和学习。当然,更希望你能够参与到项目的开发当中,帮助马克笔记变得更加有用。
6 |
7 | ## 1、关于马克笔记
8 |
9 | 马克笔记是一款开源的Markdown笔记应用,它的界面设计采用了Google最新的Material Design风格。该笔记现在的功能已经比较完善,能够满足用户大多数场景的需求。开源该软件的目的是希望与更多的人交流和学习,同时也希望能够有人参与到项目的开发中,一起帮助马克笔记,让它变得更加有用。
10 |
11 | 你可以通过加入[Google+社区](https://plus.google.com/u/1/communities/102252970668657211916)来关注该软件开发的最新动态,并且可以参与Beta测试。
12 |
13 | 马克笔记现在已经发布到了[酷安网](https://www.coolapk.com/apk/178276)上面,也欢迎你下载和使用该软件。另外,笔者还开发了一款清单应用[多功能清单](https://www.coolapk.com/apk/185660),感兴趣的同学也可以了解一下。
14 |
15 | ## 2、应用展示图
16 |
17 | 这里是该应用的一些截图通过Photoshop调整之后得到的展示图,通过展示图,你大概可以了解一下该软件的主要功能和开发状态。在接下来的行文中,我会向你更详细地介绍它使用到的一些技术以及现在开发完成的一些功能和特性。
18 |
19 | ## 3、功能和特性
20 |
21 | 我把该软件当前已经支持的功能列了一个清单:
22 |
23 | |编号|功能|
24 | |:-:|:-|
25 | |1|基本的**添加、修改、归档、放进垃圾箱、彻底删除**操作|
26 | |2|基本的Markdown语法,外加**MathJax**等高级特性|
27 | |3|特色的**时间线**功能,通过类似于AOP的操作记录用户的操作信息|
28 | |4|多种形式的媒体数据,包括**文件、视频、音频、图片、手写和位置信息**等|
29 | |5|**多主题**,支持**夜间主题**,并且有多种可选的**主题色和强调色**|
30 | |6|多彩的**图表**用于统计用户的数据信息|
31 | |7|三种形式的**桌面小控件**,并且可以为每个笔记添加快捷方式|
32 | |8|允许你为笔记指定多个多彩的标签|
33 | |9|使用“树结构”模拟文件夹操作,支持**多层文件夹**,并可以进行层级的搜索|
34 | |10|允许将笔记**导出为PDF、TXT、MD格式的文本、HTML和图片**|
35 | |11|使用**应用独立锁**,加强数据安全|
36 | |12|允许用户**备份数据到外部存储空间和OneDrive**|
37 | |13|图片**自动压缩**,节省本地的数据存储空间|
38 |
39 | 将来希望开发和完善的功能:
40 |
41 | |编号|功能描述|
42 | |:-:|:-|
43 | |1|数据同步,本地的文件管理容易导致多平台的不一致,增加同步服务,能够实现多平台操作|
44 | |2|文件服务器,用于获取图片和文件的链接|
45 | |3|富文本编辑,即时的编辑预览|
46 | |4|允许添加闹钟,并且复选框可以编辑|
47 | |5|添加地图来展示用户的位置信息的变更|
48 |
49 | 你可以从[更新日志](app/src/main/res/raw/changelog.xml)中获取到软件的更新信息。
50 |
51 | ## 4、依赖和用到的一些技术
52 |
53 | 马克笔记用到了MVVM的设计模式,还用到了DataBinding等一系列技术。下面的表格中列出了用到的具体的依赖和简要的描述。在此,还要感谢这些开源项目的作者:
54 |
55 | |编号|依赖|描述|
56 | |:-:|:-|:-|
57 | |1|[arch.lifecycle]()|使用ViewModel+LiveData实现Model和View的解耦|
58 | |2|[Stetho](https://github.com/facebook/stetho)|Facebook开源的安卓调试框架|
59 | |3|[Fabric]()|错误跟踪,用户数据收集|
60 | |4|[RxBinding](https://github.com/JakeWharton/RxBinding)||
61 | |5|[RxJava](https://github.com/ReactiveX/RxJava)||
62 | |6|[RxAndroid](https://github.com/ReactiveX/RxAndroid)||
63 | |7|[OkHttp](https://github.com/square/okhttp)||
64 | |8|[Retrofit](https://github.com/square/retrofit)||
65 | |9|[Glide](https://github.com/bumptech/glide)||
66 | |10|[BRVAH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)|非常好用的Recycler适配器|
67 | |11|[Gson](https://github.com/google/gson)||
68 | |12|[Joda-Time](https://github.com/JodaOrg/joda-time)|Java时间库|
69 | |13|[Apache IO](http://commons.apache.org/io/)|文件操作库|
70 | |14|[Material dialogs](https://github.com/afollestad/material-dialogs)||
71 | |15|[PhotoView](https://github.com/chrisbanes/PhotoView)||
72 | |16|[Hello charts](https://github.com/lecho/hellocharts-android)||
73 | |17|[FloatingActionButton](https://github.com/Clans/FloatingActionButton)||
74 | |18|[HoloColorPicker](https://github.com/LarsWerkman/HoloColorPicker)||
75 | |19|[CircleImageView](https://github.com/hdodenhof/CircleImageView)||
76 | |20|[Changeloglib](https://github.com/gabrielemariotti/changeloglib)|日志信息|
77 | |21|[PinLockView](https://github.com/aritraroy/PinLockView)|锁控件|
78 | |22|[BottomSheet](https://github.com/Kennyc1012/BottomSheet)|底部弹出的对话框|
79 | |23|[Luban](https://github.com/Curzibn/Luban)|图片压缩|
80 | |24|[Flexmark](https://github.com/vsch/flexmark-java)|基于Java的Markdown文本解析|
81 | |25|[PrettyTime](https://github.com/ocpsoft/prettytime)|时间格式美化|
82 |
83 |
84 | 特别需要说明的一点是,马克笔记是在开发了一段时间之后重新引入的ViewModel,因为作者本人水平有限,或者对ViewModel理解不够深入,设计难免有不足的地方,还请批评指正。
85 |
86 | ### 数据库操作
87 |
88 | 对于数据库部分,笔者自己设计了一套数据的访问逻辑,这里使用到了模板和单例等设计模式。它的好处在于,当你想要向程序中添加一个数据库实体的时候,只需要很少的配置即可,可以省去很多的样板代码。而且,由于该项目的一些特殊需求,比如要记录统计信息等,所以就自己设计了一下。当然,可能性能上仍然有许多值得提升的地方,但笔者认为仍不失为一个简单的学习材料。
89 |
90 | ### Markdown解析
91 |
92 | 对于Markdown解析,可以使用js在webview里面解析,也可以像本项目一样在程序种用java进行解析。笔者认为使用Flexmark在java种解析的好处是更方便地对解析的功能进行拓展。如该软件中的MathJax的解析就是在Flexmark的基础上进行的拓展。
93 |
94 | ## 5、参与项目
95 |
96 | 正如一开始提及的那样,马克笔记仍然有许多不足,我希望可以有更多的人帮助马克笔记继续完善它的功能。当然,这并不勉强。如果你希望对该项目贡献代码,你可以fork该项目,并向该项目提交请求。你可以在[waffle.io](https://waffle.io/Shouheng88/NotePal)上面跟踪issue的开发状态。或者,你发现了该软件中存在的一些问题,你可以在issue中向开发者报告。如果有其他的需求,可以直接通过[邮箱](mailto:shouheng2015@gmail.com)邮件开发者。
97 |
98 | ## 6、项目地址
99 |
100 | 因为这篇文章是从Github的Readme文件中拷贝出来的,所以忘记加上Github地址了,抱歉。现在补上:[Github](https://github.com/Shouheng88/MarkNote)
--------------------------------------------------------------------------------
/其他/res/Compressor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/其他/res/Compressor.png
--------------------------------------------------------------------------------
/其他/res/QQ截图20190718000949.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/其他/res/QQ截图20190718000949.png
--------------------------------------------------------------------------------
/其他/res/Screenshot_20190718-003619.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/其他/res/Screenshot_20190718-003619.jpg
--------------------------------------------------------------------------------
/其他/res/easymark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/其他/res/easymark.png
--------------------------------------------------------------------------------
/其他/res/shortcuts.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/其他/res/shortcuts.jpg
--------------------------------------------------------------------------------
/其他/res/themes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shouheng88/Android-notes/416fdc3de0eaebd7f679bfafee29f0148012b2ad/其他/res/themes.jpg
--------------------------------------------------------------------------------
/响应式编程/RxJava系列(2):Flowable和背压.md:
--------------------------------------------------------------------------------
1 | # RxJava2 系列 (2):背压和Flowable
2 |
3 | 背压(Back Pressure)的概念最初并不是在响应式编程中提出的,它最初用在流体力学中,指的是后端的压力,
4 | 通常用于描述系统排出的流体在出口处或二次侧受到的与流动方向相反的压力。
5 |
6 | 在响应式编程中,我们可以将产生信息的部分叫做上游或者叫生产者,处理产生的信息的部分叫做下游或者消费者。
7 | 试想如果在异步的环境中,生产者的生产速度大于消费者的消费速度的时候,明显会出现生产过剩的情景,这时候就需要消费者对多余的数据进行缓存,
8 | 但如果生产的信息数量过多,以至于超出缓存大小,就会出现缓存溢出,甚至可能造成内存耗尽。
9 |
10 | 我们可以制定一个数据丢失的规则,来丢失那些“可以丢失的数据”,以减轻缓存的压力。
11 | 在之前我们介绍了一些方法,比如`throttleXXX`、`debounce`、`sample`等,都是用来解决在生产速度过快的情况下的数据过滤的,它们指定了数据取舍的规则。
12 | 而在`Flowable`,我们可以通过`onBackpressureXXX`一系列的方法来制定当数据生产过快情况下的数据取舍的规则,
13 |
14 | 我们可以把这种处理方式理解成背压,所谓背压,在Rx中就是通过一种下游用来控制上游事件发射频率的机制(就像流体在出口受到了阻力一样)。
15 | 所以,如何理解背压呢?笔者认为,在力学中它是一种现象,在Rx中它是一种机制。
16 |
17 | 在这篇文章中,我们会先介绍背压的相关内容,然后我们再介绍一下`onBackpressureXXX`系列的方法。
18 |
19 | 关于RxJava2的基础使用和方法梳理可以参考:[RxJava2 系列 (1):一篇的比较全面的 RxJava2 方法总结](https://juejin.im/post/5b72f76551882561354462dd)
20 |
21 | 说明:以下文章部分翻译自RxJava官方文档[Backpressure (2.0)](https://github.com/ReactiveX/RxJava/wiki/Backpressure-(2.0))。
22 |
23 | ## 1、背压机制
24 |
25 | 如果将生产和消费整体看作一个管道,生成看作上游,消费看作下游;
26 | 那么当异步的应用场景下,当生产者生产过快而消费者消费很慢的时候,可以通过背压来告知上游减慢生成的速度。
27 |
28 | 通常在进行异步的操作的时候会通过缓存来存储发射出的数据。在早期的RxJava中,这些缓存是无界的。
29 | 这意味着当需要缓存的数据非常多的时候,它们可能会占用非常多的存储空间,并有可能因为虚拟机不断GC而导致程序执行过慢,甚至直接抛出OOM。
30 | 在最新的RxJava中,大多数的异步操作内部都存在一个有界的缓存,当超出这个缓存的时候就会抛出`MissingBackpressureException`异常并结束整个序列。
31 |
32 | 然而,某些情况下的表现会有所不同,它们不会抛出`MissingBackpressureException`异常。比如下面的`range`操作:
33 |
34 | private static void compute(int i) throws InterruptedException {
35 | Thread.sleep(500);
36 | System.out.println("computing : " + i);
37 | }
38 |
39 | private static void testFlowable() throws InterruptedException {
40 | Flowable.range(1, MAX_LENGTH).observeOn(Schedulers.computation()).subscribe(FlowableTest::compute);
41 |
42 | Thread.sleep(500 * MAX_LENGTH);
43 | }
44 |
45 | 在这段代码中我们生成一段整数,然后每隔500毫秒执行依次计算操作。从输出的结果来看,在程序的实际执行过程中,数据的发射是串行的。
46 | 也就是发射完一个数据之后进入`compute`进行计算,等待500毫秒之后才发射下一个。
47 | 因此,在程序的执行过程中没有抛出异常,也没有过多的内存消耗。
48 |
49 | 而下面的这段代码就会在程序运行的时候立刻抛出`MissingBackpressureException`异常:
50 |
51 | PublishProcessor source = PublishProcessor.create();
52 | source.observeOn(Schedulers.computation()).subscribe(v -> compute(v), Throwable::printStackTrace);
53 | for (int i = 0; i < 1_000_000; i++) source.onNext(i);
54 | Thread.sleep(10_000);
55 |
56 | 这是因为`PublishProcessor`底层会调用`PublishSubscription`,而后者实现了`AtomicLong`,它会通过判断引用的long是否为0来抛出异常,这个long型整数会在调用`PublishSubscription.request()`的时候被改写。前面的一个例子的原理就是当每次调用了观察者的`onNext`之后会调用`PublishSubscription.request()`来请求数据,这样相当于消费者会在消费完事件之后向生产者请求,因此整个序列的执行看上去是串行的,从而不会抛出异常。
57 |
58 | ## 2、onBackpressureXXX
59 |
60 | 大多数开发者在遇到`MissingBackpressureException`通常是因为使用`observeOn`方法监听了非背压的`PublishProcessor`, `timer()`, `interval()`或者自定义的`create()`。我们有以下几种方式来解决这个问题:
61 |
62 | ### 2.1 增加缓存大小
63 |
64 | `observeOn`方法的默认缓存大小是16,当生产的速率过快的时候,那么可能很快会超出该缓存大小,从而导致缓存溢出。
65 | 一种简单的解决办法是通过提升该缓存的大小来防止缓存溢出,我们可以使用`observeOn`的重载方法来设置缓存的大小。比如:
66 |
67 | PublishProcessor source = PublishProcessor.create();
68 | source.observeOn(Schedulers.computation(), 1024 * 1024)
69 | .subscribe(e -> { }, Throwable::printStackTrace);
70 |
71 | 但是这种解决方案只能解决暂时的问题,当生产的速率过快的时候还是有可能造成缓存溢出,所以这不是根本的解决办法。
72 |
73 | ### 2.2 通过丢弃和过滤来减轻缓存压力
74 |
75 | 我们可以根据自己的应用的场景和数据的重要性,选择使用一些方法来过滤和丢弃数据。
76 | 比如,丢弃的方式可以选择`throttleFirst`, `throttleLast`, `throttleWithTimeout`等,还可以使用按照时间采样的方式来减少接受的数据。
77 |
78 | PublishProcessor source = PublishProcessor.create();
79 | source.sample(1, TimeUnit.MILLISECONDS)
80 | .observeOn(Schedulers.computation(), 1024)
81 | .subscribe(v -> compute(v), Throwable::printStackTrace);
82 |
83 | 但是,这种方式仅仅用来减少下游接收的数据,当缓存的数据不断增加的时候还是有可能导致缓存溢出,所以,这也不是一种根本的解决办法。
84 |
85 | ### 2.3 onBackpressureBuffer()
86 |
87 | 这种无参的方法会使用一个无界的缓存,只要虚拟机没有抛出OOM异常,它就会把所有的数据缓存起来。
88 |
89 | Flowable.range(1, 1_000_000)
90 | .onBackpressureBuffer()
91 | .observeOn(Schedulers.computation(), 8)
92 | .subscribe(e -> { }, Throwable::printStackTrace);
93 |
94 | 上面的例子即使使用了很小的缓存也不会有异常抛出,因为`onBackpressureBuffer`会将发射的所有数据缓存起来,只会将一小部分的数据传递给`observeOn`。
95 |
96 | 这种处理方式实际上是不存在背压的,因为`onBackpressureBuffer`缓存了所有的数据,我们可以使用该方法的4个重载方法来对背压进行个性化设置。
97 |
98 | ### 2.4 onBackpressureBuffer(int capacity)
99 |
100 | 这个方法使用一个有界的缓存,当达到了缓存大小的时候会抛出一个`BufferOverflowError`错误。
101 | 通过这种方法可以增加默认的缓存大小,但是通过`observeOn`方法一样可以指定缓存的大小,因此,这个方法的应用变得越来越少。
102 |
103 | ### 2.5 onBackpressureBuffer(int capacity, Action onOverflow)
104 |
105 | 这方法除了可以指定一个有界的缓存还提供了一个,当缓存溢出的时候还会回调指定的Action。
106 | 但是这种回调的用途比较有限,因为它除了提供当前回调的栈信息以外提供不了任何有用的信息。
107 |
108 | ### 2.6 onBackpressureBuffer(int capacity, Action onOverflow, BackpressureOverflowStrategy strategy)
109 |
110 | 这个重载方法相对比较实用一些,它除了上面的那些功能之外,还指定了当缓存到达指定的缓存时的行为。
111 | 这里的`BackpressureOverflowStrategy`顾名思义是一个策略,它是一个枚举类型,预定义了三种枚举值,最终会在`FlowableOnBackpressureBufferStrategy`中根据指定的枚举类型选择不同的实现策略,因此,我们可以使用它来指定缓存溢出时候的行为。
112 |
113 | 下面是该枚举类型的三个值及其含义:
114 |
115 | 1. `ERROR`:当缓存溢出的时候会抛出一个异常;
116 | 2. `DROP_OLDEST`:当缓存发生溢出的时候,会丢弃最老的值,并将新的值插入到缓存中;
117 | 3. `DROP_LATEST`:当缓存发生溢出的时候,最新的值会被忽略,只有比较老的值会被传递给下游使用;
118 |
119 | 需要注意的地方是,后面的两种策略会造成下游获取到的值是不连续的,因为有一部分值会因为缓存不够被丢弃,但是它们不会抛出`BufferOverflowException`。
120 |
121 | ### 2.7 onBackpressureDrop()
122 |
123 | 这个方法会在数据达到缓存大小的时候丢弃最新的数据。可以将其看成是`onBackpressureBuffer`+`0 capacity`+`DROP_LATEST`的组合。
124 |
125 | 这个方法特别适用于那种可以忽略从源中发射出值的那种场景,比如GPS定位问题,定位数据会不断发射出来,即使丢失当前数据,等会儿一样能拿到最新的数据。
126 |
127 | component.mouseMoves()
128 | .onBackpressureDrop()
129 | .observeOn(Schedulers.computation(), 1)
130 | .subscribe(event -> compute(event.x, event.y));
131 |
132 | 该方法还存在一个重载方法`onBackpressureDrop(Consumer super T> onDrop)`,它允许我们传入一个接口来指定当某个数据被丢失时的行为。
133 |
134 | ### 2.8 onBackpressureLatest()
135 |
136 | 对应于`onBackpressureDrop()`的,还有`onBackpressureLatest()`方法,该方法只会保留最新的数据并会覆盖较老、没有分发的数据。
137 | 我们可以将其看成是`onBackpressureBuffer`+`1 capacity`+`DROP_OLDEST`的组合。
138 |
139 | 与`onBackpressureDrop()`不同的地方在于,当下游消费过慢的时候,这种方式总会存在一个缓存的值。
140 | 这种特别适用于那种数据的生产非常频繁,但是只有最新的数据会被消费的那种情形。比如,当用户点击了屏幕,那么我们倾向于只处理最新按下的位置的事件。
141 |
142 | component.mouseClicks()
143 | .onBackpressureLatest()
144 | .observeOn(Schedulers.computation())
145 | .subscribe(event -> compute(event.x, event.y), Throwable::printStackTrace);
146 |
147 | 所以,总结一下:
148 |
149 | 1. `onBackpressureDrop()`:不会缓存任何数据,专注于当下,新来的数据来不及处理就丢掉,以后会有更好的;
150 | 2. `onBackpressureLatest()`:会缓存一个数据,当正在执行某个任务的时候有新的数据过来,会把它缓存起来,如果又有新的数据过来,那就把之前的替换掉,缓存里面的总是最新的。
151 |
152 | ## 3、总结
153 |
154 | 以上就是背压机制的一些内容,以及我们介绍了`Flowable`中的几个背压相关的方法。
155 | 实际上,RxJava的官方文档也有说明——`Flowable`适用于数据量比较大的情景,因为它的一些创建方法本身就使用了背压机制。
156 | 这部分方法我们就不再一一进行说明,因为,它们的方法签名和`Observable`基本一致,只是多了一层背压机制。
157 |
158 | 比较匆匆地整理完了背压的内容,但是我想这块还会有更加丰富的内容值得我们去发现和探索。
159 |
160 | 以上。
--------------------------------------------------------------------------------
/响应式编程/RxJava系列(3):用RxJava打造EventBus.md:
--------------------------------------------------------------------------------
1 | # RxJava2 系列 (3):使用 Subject
2 |
3 | 在这篇文章中,我们会先分析一下 RxJava2 中的 Subject ;然后,我们会使用 Subject 制作一个类似于 EventBus 的全局的通信工具。
4 |
5 | 在了解本篇文章的内容之前,你需要先了解 RxJava2 中的一些基本的用法,比如 Observable 以及背压的概念,你可以参考我的其他两篇文章来获取这部分内容:[《RxJava2 系列 (1):一篇的比较全面的 RxJava2 方法总结》](https://juejin.im/post/5b72f76551882561354462dd)和[《RxJava2 系列 (2):背压和Flowable》](https://juejin.im/post/5b759b9cf265da283719d187)。
6 |
7 | ## 1、Subject
8 |
9 | ### 1.1 Subject 的两个特性
10 |
11 | Subject 可以同时代表 Observer 和 Observable,允许从数据源中多次发送结果给多个观察者。除了 onSubscribe(), onNext(), onError() 和 onComplete() 之外,所有的方法都是线程安全的。此外,你还可以使用 toSerialized() 方法,也就是转换成串行的,将这些方法设置成线程安全的。
12 |
13 | 如果你已经了解了 Observable 和 Observer ,那么也许直接看 Subject 的源码定义会更容易理解:
14 |
15 | ```
16 | public abstract class Subject extends Observable implements Observer {
17 |
18 | // ...
19 | }
20 | ```
21 |
22 | 从上面看出,Subject 同时继承了 Observable 和 Observer 两个接口,说明它既是被观察的对象,同时又是观察对象,也就是可以生产、可以消费、也可以自己生产自己消费。所以,我们可以项下面这样来使用它。这里我们用到的是该接口的一个实现 PublishSubject :
23 |
24 | public static void main(String...args) {
25 | PublishSubject subject = PublishSubject.create();
26 | subject.subscribe(System.out::println);
27 |
28 | Executor executor = Executors.newFixedThreadPool(5);
29 | Disposable disposable = Observable.range(1, 5).subscribe(i ->
30 | executor.execute(() -> {
31 | try {
32 | Thread.sleep(i * 200);
33 | subject.onNext(i);
34 | } catch (InterruptedException e) {
35 | e.printStackTrace();
36 | }
37 | }));
38 | }
39 |
40 | 根据程序的执行结果,程序在第200, 400, 600, 800, 1000毫秒依次输出了1到5的数字。
41 |
42 | 在这里,我们用 PublishSubject 创建了一个**主题**并对其监听,然后在线程当中又通知该主题内容变化,整个过程我们都只操作了 PublishSubject 一个对象。显然,使用 Subject 我们可以达到对一个指定类型的值的结果进行监听的目的——我们把值改变之后对应的逻辑写在 subscribe() 方法中,然后每次调用 onNext() 等方法通知结果之后就可以自动调用 subscribe() 方法进行更新操作。
43 |
44 | 同时,因为 Subject 实现了 Observer 接口,并且在 Observable 等的 subscribe() 方法中存在一个以 Observer 作为参数的方法(如下),所以,Subject 也是可以作为消费者来对事件进行消费的。
45 |
46 | public final void subscribe(Observer super T> observer)
47 |
48 | 以上就是 Subject 的两个主要的特性。
49 |
50 | ### 1.2 Subject 的实现类
51 |
52 | 在 RxJava2 ,Subject 有几个默认的实现,下面我们对它们之间的区别做简单的说明:
53 |
54 | 1. `AsyncSubject`:只有当 Subject 调用 onComplete 方法时,才会将 Subject 中的**最后一个事件**传递给所有的 Observer。
55 | 2. `BehaviorSubject`:该类有创建时需要一个默认参数,该默认参数会在 Subject 未发送过其他的事件时,向注册的 Observer 发送;新注册的 Observer 不会收到之前发送的事件,这点和 PublishSubject 一致。
56 | 3. `PublishSubject`:不会改变事件的发送顺序;在已经发送了一部分事件之后注册的 Observer 不会收到之前发送的事件。
57 | 4. `ReplaySubject`:无论什么时候注册 Observer 都可以接收到任何时候通过该 Observable 发射的事件。
58 | 5. `UnicastSubject`:只允许一个 Observer 进行监听,在该 Observer 注册之前会将发射的所有的事件放进一个队列中,并在 Observer 注册的时候一起通知给它。
59 |
60 | 对比 PublishSubject 和 ReplaySubject,它们的区别在于新注册的 Observer 是否能够收到在它注册之前发送的事件。这个类似于 EventBus 中的 StickyEvent 即黏性事件,为了说明这一点,我们准备了下面两段代码:
61 |
62 | private static void testPublishSubject() throws InterruptedException {
63 | PublishSubject subject = PublishSubject.create();
64 | subject.subscribe(i -> System.out.print("(1: " + i + ") "));
65 |
66 | Executor executor = Executors.newFixedThreadPool(5);
67 | Disposable disposable = Observable.range(1, 5).subscribe(i -> executor.execute(() -> {
68 | try {
69 | Thread.sleep(i * 200);
70 | subject.onNext(i);
71 | } catch (InterruptedException e) {
72 | e.printStackTrace();
73 | }
74 | }));
75 |
76 | Thread.sleep(500);
77 | subject.subscribe(i -> System.out.print("(2: " + i + ") "));
78 |
79 | Observable.timer(2, TimeUnit.SECONDS).subscribe(i -> ((ExecutorService) executor).shutdown());
80 | }
81 |
82 | private static void testReplaySubject() throws InterruptedException {
83 | ReplaySubject subject = ReplaySubject.create();
84 | subject.subscribe(i -> System.out.print("(1: " + i + ") "));
85 |
86 | Executor executor = Executors.newFixedThreadPool(5);
87 | Disposable disposable = Observable.range(1, 5).subscribe(i -> executor.execute(() -> {
88 | try {
89 | Thread.sleep(i * 200);
90 | subject.onNext(i);
91 | } catch (InterruptedException e) {
92 | e.printStackTrace();
93 | }
94 | }));
95 |
96 | Thread.sleep(500);
97 | subject.subscribe(i -> System.out.print("(2: " + i + ") "));
98 |
99 | Observable.timer(2, TimeUnit.SECONDS).subscribe(i -> ((ExecutorService) executor).shutdown());
100 | }
101 |
102 | 它们的输出结果依次是
103 |
104 | PublishSubject的结果:(1: 1) (1: 2) (1: 3) (2: 3) (1: 4) (2: 4) (1: 5) (2: 5)
105 | ReplaySubject的结果: (1: 1) (1: 2) (2: 1) (2: 2) (1: 3) (2: 3) (1: 4) (2: 4) (1: 5) (2: 5)
106 |
107 | 从上面的结果对比中,我们可以看出前者与后者的区别在于新注册的 Observer 并没有收到在它注册之前发送的事件。试验的结果与上面的叙述是一致的。
108 |
109 | 其他的测试代码这不一并给出了,详细的代码可以参考[Github - Java Advanced](https://github.com/Shouheng88/Java-advanced)。
110 |
111 | ## 2、用 RxJava 打造 EventBus
112 |
113 | ### 2.1 打造 EventBus
114 |
115 | 清楚了 Subject 的概念之后,让我们来做一个实践——用 RxJava 打造 EventBus。
116 |
117 | 我们先考虑用一个全局的 PublishSubject 来解决这个问题,当然,这意味着我们发送的事件不是黏性事件。不过,没关系,只要这种实现方式搞懂了,用 ReplaySubject 做一个发送黏性事件的 EventBus 也非难事。
118 |
119 | 考虑一下,如果要实现这个功能我们需要做哪些准备:
120 |
121 | 1. **我们需要发送事件并能够正确地接收到事件。**要实现这个目的并不难,因为 Subject 本身就具有发送和接收两个能力,作为全局的之后就具有了全局的注册和通知的能力。因此,不论你在什么位置发送了事件,任何订阅的地方都能收到该事件。
122 | 2. **首先,我们要在合适的位置对事件进行监听,并在合适的位置取消事件的监听。如果我们没有在适当的时机释放事件,会不会造成内存泄漏呢?这还是有可能的。**所以,我们需要对注册监听的观察者进行记录,并提供注册和取消注册的方法,给它们在指定的生命周期中进行调用。
123 |
124 | 好了,首先是全局的 Subject 的问题,我们可以实现一个静态的或者单例的 Subject。这里我们选择使用后者,所以,我们需要一个单例的方式来使用 Subject:
125 |
126 | public class RxBus {
127 |
128 | private static volatile RxBus rxBus;
129 |
130 | private final Subject