├── .gitignore ├── AndroidLeak ├── AndroidLeak.md ├── Android内存泄漏检测工具LeakCanary原理分析.md ├── MAT入门01.png ├── MAT入门02.png ├── MAT入门03.png ├── MAT入门04.png ├── MAT入门05.png ├── MAT入门06.png ├── MAT入门07.png ├── MAT入门08.png ├── MAT入门09.png ├── MAT入门10.png ├── MAT入门11.png ├── MAT入门12.png ├── MAT入门13.png ├── MAT入门14.png ├── MAT入门15.png ├── MAT入门16.png ├── MAT入门17.png ├── MAT入门18.png ├── MAT入门19.png ├── MAT入门20.png ├── MAT入门21.png ├── MAT入门22.png ├── MAT入门23.png ├── MAT入门24.png ├── MAT入门25.png ├── MAT入门26.png ├── MAT入门27.png ├── MAT入门28.gif ├── MAT入门28.png ├── MAT入门29.gif ├── MAT入门29.png ├── MAT入门30.png ├── MAT入门31.png ├── MAT入门32.png ├── MAT入门33.png ├── MAT入门34.png ├── MAT入门35.png ├── MAT入门36.png ├── MAT入门37.png ├── MAT入门38.png ├── MAT入门39.png ├── Outer_Inner │ ├── Outer$1.class │ ├── Outer$Inner.class │ ├── Outer.class │ └── Outer.java ├── procleak.sh └── 手Q中的内存泄漏检测模块.md ├── LICENSE ├── README.md ├── img ├── ANT学习.png ├── ANT学习01.png ├── ANT学习02.png ├── ANT学习03.png ├── ANT学习04.png ├── ANT学习05.png ├── Android-Design-Cheat-Sheet-highres.png ├── Android_UniqueId01.png ├── Android规范-工具01.png ├── Android规范-工具02.png ├── Android规范-目录01.png ├── Android规范-结构01.png ├── Android规范-结构02.png ├── Android规范-结构03.png ├── Annotation01.png ├── Annotation02.png ├── ConcurrentHashMap类图.jpg ├── ConcurrentHashMap结构图.jpg ├── Java一二01.png ├── Java一二02.png ├── Java一二03.png ├── Java一二04.png ├── Java一二05.png ├── MongoDB学习01.png ├── MongoDB学习02.png ├── MongoDB学习03.png ├── MongoDB学习04.png ├── MongoDB学习05.png ├── MongoDB笔记.png ├── Pig学习.png ├── Python_introduction_cn.png ├── Python_introduction_en.png ├── SVN学习.png ├── SVN学习01.png ├── SVN学习02.png ├── SVN学习03.png ├── SVN学习04.png ├── SVN学习05.png ├── SVN学习06.png ├── SVN学习07.png ├── SVN学习08.png ├── SVN学习09.png ├── SVN学习10.png ├── SVN学习11.png ├── SVN学习12.png ├── SVN学习13.png ├── SVN学习14.png ├── SVN学习15.png ├── SVN学习16.png ├── SVN学习17.png ├── SVN学习18.png ├── SVN学习19.png ├── SVN学习20.png ├── SVN学习21.png ├── SVN学习22.png ├── SVN学习23.png ├── SVN学习24.png ├── SVN学习25.png ├── SVN学习26.png ├── SVN学习27.png ├── SVN学习28.png ├── SVN学习29.png ├── SVN学习30.png ├── SVN学习31.png ├── SVN学习32.png ├── SVN学习33.png ├── SVN学习34.png ├── Thinking.jpg ├── UIL-DiscCache架构.png ├── UIL-MemCache架构.png ├── VirtualBox.png ├── complete_android_fragment_lifecycle.png ├── docker version.png ├── git命令大全.png ├── jni引用类型.gif ├── jni流程.PNG ├── map_tuple_bag模式语法.png ├── osx boot2docker vm.png ├── valgrind01.png ├── valgrind02.png ├── valgrind03.png ├── valgrind04.png ├── valgrind05.png ├── 数据结构01.png └── 数据结构02.png ├── post ├── ANT学习.md ├── ANT学习.xmind ├── Android Activity Fragment完整生命周期.md ├── Android ContentProvider入门.md ├── Android Methods Limitation.md ├── Android SurfaceView入门.md ├── Android WebView Java与JavaScript互调.md ├── Android三方库&应用架构.md ├── Android开发-工程结构.md ├── Android开发-应用优化.md ├── Android开发-开发工具.md ├── Android开发规范系列-大纲.xmind ├── Android开发规范系列.md ├── Android桌面悬浮窗.md ├── Android滤镜开发一二.md ├── Android面试问题收集.md ├── Annotation学习笔记.md ├── BUILDER模式.md ├── COMMAND模式.md ├── Git学习.md ├── JNI学习笔记.md ├── Java基本功不牢造成的那些似坑非坑.md ├── Java容器源码分析.md ├── Java自己总结一二.md ├── MongoDB笔记.xmind ├── NoSQL学习笔记.md ├── Pig学习.md ├── Pig学习.xmind ├── Python学习笔记.md ├── Python开发工具收集.md ├── SVN学习.md ├── SVN学习.xmind ├── SublimeText3插件安装笔记.md ├── The Android Design Cheat Sheet.md ├── UIL学习.md ├── docker OS X.md ├── mac android studio快捷键.md ├── ndk开发本地代码优化--Linux本地代码优化.md ├── ndk开发本地代码优化--在Android上跑起Valgrind.md ├── sqlite3_corruption.md ├── 应用部署方式汇总.md ├── 当你想做成一件事的时候可能并没有准备好.md ├── 我在简网上生成的GoodMan001.apk破解笔记.md ├── 数据结构总结一二.md ├── 服务提供者框架.md ├── 简单说说HashMap,HashTable,ConcurrentHashTable.md ├── 简单说说Synchronized,ReentrantLock.md ├── 获取Android设备ID.md └── 设计模式学习.md ├── resume ├── resume.md ├── 深圳互联网公司汇总.md ├── 王汪-公司OA头像.jpg ├── 王汪-北京搜狐-Android开发-武汉大学研究生.doc ├── 王汪-北京搜狐-Android开发-武汉大学研究生.pdf ├── 王汪-武汉大学-计算机-毕业.doc └── 面试自我介绍.md └── uml ├── ServiceProviderFramework.mdj ├── builder.mdj ├── builder模式01.png ├── command.mdj ├── command模式01.png └── 服务提供者框架01.png /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files # 2 | ###################### 3 | .DS_Store 4 | .DS_Store? 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | ehthumbs.db 9 | Thumbs.db 10 | -------------------------------------------------------------------------------- /AndroidLeak/AndroidLeak.md: -------------------------------------------------------------------------------- 1 | ### Android内存泄漏相关汇总 2 | 3 | --- 4 | 5 | #### 一:如何测试内存泄漏场景的存在 6 | 下面描述几种Android应用内存泄漏定位方法,主要是Android中能够为我们所用的检测进程内存占用的方法。 7 | 8 | ##### 1. `procrank`命令 9 | 10 | `procrank`命令可以获取当前系统各进程内存占用快照,但仅限于宏观尺度,无法获取进程空间内存的具体使用情况。 11 | 12 | 源码参见`\extras\procrank`,关键函数介绍如下: 13 | ```c 14 | ... 15 | /* 读取/proc文件夹,获取pids列表 */ 16 | error = pm_kernel_pids(ker, &pids, &num_procs); 17 | 18 | /* 读取/proc/[pid]/pagemap,读取/proc/[pid]/maps文件页信息 */ 19 | error = pm_process_create(ker, pids[i], &proc); 20 | 21 | /* 累加页信息 */ 22 | error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask, 23 | required_flags); 24 | 25 | /* 读取/proc/[pid]/cmdline文件,获取cmdline信息 */ 26 | getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) 27 | 28 | /* 读取/proc/meminfo文件,获取系统内存信息 */ 29 | print_mem_info(); 30 | ... 31 | ``` 32 | 33 | 进入adb shell,执行`procrank`,输出如下: 34 | 35 | ![](./MAT入门01.png) 36 | 37 | 极有可能您的手机`procrank`已被阉割,那就启动Android虚拟机,从`/system/xbin`目录pull一个吧,记得也要pull`/system/lib`目录的`libpagemap.so`~ 38 | 39 | 几个名词介绍: 40 | * VSS – Virtual Set Size 虚拟耗用内存(包含共享库占用的内存) 41 | * RSS – Resident Set Size 实际使用物理内存(包含共享库占用的内存) 42 | * PSS – Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存,共享库由很多进程共享,按每一个进程占有比例乘以共享库占用内存,加上USS,就是PSS) 43 | * USS – Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存) 44 | 45 | 对于`VSS`、`RSS`、`PSS`均包含共享库内存占用部分,实际工作中以`USS`为参照。 46 | 47 | 最底下的一行简单解释:`total`(全部)、`free`(空闲)、`buffers`、`cached`、`shmem`(共享内存)、`slab`。 48 | 49 | 那么如何测试应用在使用过程中存在内存泄漏问题呢? 50 | 51 | 写个脚本,每隔固定采样时间(比如5s),执行一次procrank,把结果输出到指定文件, 52 | 持续多长时间你自己控制,这个过程中疯狂的使用待测app。 53 | 54 | > 注意:procrank命令不支持输出单个进程的内存信息,结果输出到指定文件前,需要用grep做过滤。(grep被阉割,请安装busybox)。 55 | ![](./MAT入门02.png) 56 | 57 | 脚本名称:procleak.sh,当然你也可以把脚本功能完善的更丰富一点。 58 | ```bash 59 | #!/system/bin/sh 60 | 61 | # Uage: 62 | # procleak.sh procname 63 | 64 | if [ $# != 1 ]; then 65 | echo "Uage: $0 procname" 66 | exit 1 67 | fi 68 | 69 | OUTPUT='/sdcard/procleak.log' 70 | 71 | echo 'timestamp\tPID\tVss\tRss\tPss\tUss\tcmdline' >> $OUTPUT 72 | 73 | while true; do 74 | timestamp=`date '+%Y-%m-%d %H:%M:%S'` 75 | key=$1'$' 76 | meminfo=`procrank | busybox grep ${key}` 77 | 78 | echo $timestamp'\t'$meminfo >> $OUTPUT 79 | 80 | sleep 5 81 | done 82 | 83 | ``` 84 | 以手Q运行为例: 85 | 1. `push`到手机sd卡; 86 | 2. 执行`sh procleak.sh com.tencent.mobileqq &`,记下pid; 87 | > 注意:这里有坑,Windows和Linux的换行符不一样,`push`前先转一转。 88 | > 我习惯用Sublime Text,有一个插件不错`LineEndings`,专门用来转行尾换行符。 89 | 3. 放肆的操作手Q,一段时候后,`kill pid`,在sd卡拿出log文件进行分析; 90 | 91 | 拿到`procleak.log`,重点关注`USS`一列数据,可以借助Excel做出内存消耗曲线图。 92 | 93 | ![](./MAT入门03.png) 94 | 95 | 如果内存消耗在一段时间内保持稳定,那么可以认为没有发生leak;反之,内存消耗稳定上升,那就是有leak点了! 96 | 97 | ##### 2. `showmap`命令 98 | `showmap`命令,输出指定进程的详细的内存信息,输出如下: 99 | 100 | ![](./MAT入门18.png) 101 | ![](./MAT入门19.png) 102 | 103 | `VSS`、`RSS`、`PSS`上文已经解释,`object`表示mmap进来的文件名,而`shared clean`、`shared dirty`、`private clean`、`private dirty`含义是? 104 | 105 | 源码参见`\extras\showmap`,关键函数介绍如下: 106 | ```c 107 | ... 108 | /* 读取/proc/[pid]/smaps文件,获取内存使用详细信息 */ 109 | milist = load_maps(pid, addresses, !verbose && !addresses); 110 | ... 111 | ``` 112 | 113 | 我们可以定时执行`showmap`,记录`TOTAL`-`PSS`值,通过分析PSS的波动情况判断是否发生内存泄漏。脚本编写可以参考`procrank`命令部分。 114 | 115 | > 题外话,`procrank`没有`showmap`好使,如果待测试手机起的进程太多,`procrank`执行一次消耗的时间就比较可观了,原因是`procrank`会遍历`/proc/`目录所有进程信息,比如每隔5s执行一次`procrank`,而`procrank`执行耗时超过5s,导致日志信息采样间隔大于5s!! 116 | > 所以我个人还是推荐`showmap`命令。 117 | 118 | ##### 3. `dumpsys`命令 119 | `dumpsys`命令用处很多,基本可以用来dump系统的各种信息,比如内存信息、CPU信息、activities信息、windows信息、wifi信息等(adb shell dumpsys -l,输出支持列表),其源码参见`frameworks\native\cmds\dumpsys\dumpsys.cpp`。 120 | 121 | 这里我们dump内存信息,命令形如`adb shell dumpsys meminfo [packagename]`,输出如下: 122 | 123 | ![](./MAT入门04.png) 124 | 125 | 这里`PrivateDirty`是我们关心的数据,等同于上文`USS`。我们一样可以编写脚本,定时做`dumpsys`,提取`TOTAL`-`PrivateDirty`对应位置数据,汇总输出,最后借助Excel做出内存消耗曲线图。脚本编写可以参考`procrank`命令部分。 126 | 127 | 这份表格各种指标,如不能很好理解,可以参考其实现源码`frameworks\base\core\jni\android_os_Debug.cpp`,关键函数介绍如下: 128 | ```cpp 129 | ... 130 | /* 调用mallinfo(),获取Heap Size */ 131 | static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz) 132 | 133 | /* 调用mallinfo(),获取Heap Alloc */ 134 | static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz) 135 | 136 | /* 调用mallinfo(),获取Heap Free */ 137 | static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz) 138 | 139 | /* 内部调用两个关键函数: 140 | 1. load_maps(pid, stats),读取/proc/[pid]/smaps,分类累加进程空间每一块内存分配信息(参见showmap实现); 141 | 2. read_memtrack_memory(pid, &graphics_mem),SoC厂商实现memtrack模块,读取GPU内存使用,graphics/gl/other按pss/privateDirty对待; 142 | 3. 分类累加pss、privateDirty、privateClean、swappedOut等; 143 | */ 144 | static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, 145 | jint pid, jobject object) 146 | { 147 | ... 148 | load_maps(pid, stats); 149 | 150 | if (read_memtrack_memory(pid, &graphics_mem) == 0) { 151 | stats[HEAP_GRAPHICS].pss = graphics_mem.graphics; 152 | stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics; 153 | stats[HEAP_GL].pss = graphics_mem.gl; 154 | stats[HEAP_GL].privateDirty = graphics_mem.gl; 155 | stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other; 156 | stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other; 157 | } 158 | ... 159 | } 160 | ... 161 | ``` 162 | 163 | > 题外话,对比`dumpsys meminfo`与`showmap`实现源码,`dumpsys`的内存信息更加准确, 164 | > 不仅调用`load_maps(pid, stats)`读取`/proc/[pid]/smaps`文件获取内存分配信息,还调用`read_memtrack_memory(pid, &graphics_mem)`读取GPU的内存分配信息, 165 | > 对于那些使用GPU的应用(包含了texture、shader、vertex buffer等GPU内存占用),显然`dumpsys`是一个更好的选择。 166 | 167 | ##### 4. `cat /proc/meminfo`获取系统内存信息 168 | 169 | ![](./MAT入门05.png) 170 | 171 | 读取`meminfo`文件,可以获取Android系统内存分配、内存使用情况,可以了解当前系统是否处于内存紧张状态,系统层面的宏观认识,对具体到某一应用的内存情况,此方法无能为力。 172 | 173 | ##### 5. `ps`获取进程信息 174 | `ps`加`grep`,展示某一进程信息,其中进程信息包括`RSS`占用情况。可是上文我们说过,`RSS`包括共享库部分,可以参考,但是一般不用。 175 | 176 | ![](./MAT入门06.png) 177 | 178 | ##### 6. Debug.getMemoryInfo()或者ActivityManager.getProcessMemoryInfo() 179 | 调用上述两个函数,都会返回`MemoryInfo`对象,`MemoryInfo`详细描述了应用内存情况,字段如下: 180 | 181 | ![](./MAT入门07.png) 182 | 183 | 各字段的含义: 184 | `dalvik`,是指dalvik所使用的内存; 185 | `native`,是指native使用的内存,比如C\C\++在堆上分配的内存; 186 | `other`,是指除dalvik和native使用的内存,比如共享内存。 187 | `Private`,私有,不包含共享库; 188 | `Share`,包含共享库; 189 | `PSS`,参考上文,比例分配共享库占用的内存。 190 | 191 | 其实你应该会发现,上述两个函数的返回结果,和`dumpsys meminfo`命令返回结果一致,很明显他们内部应该都是走的一套系统调用,参看源码也能佐证。 192 | 193 | * Debug源码参见`frameworks\base\core\java\android\os\Debug.java`,jni层源码参见`frameworks\base\core\jni\android_os_Debug.cpp`,相关函数如下: 194 | ![](./MAT入门20.png) 195 | ![](./MAT入门21.png) 196 | ![](./MAT入门22.png) 197 | * ActivityManager源码参见`frameworks\base\core\java\android\app\ActivityManager.java`,跟进去getProcessMemoryInfo()调用工作由`ActivityManagerService`完成,源码参见`frameworks\base\core\java\android\app\ActivityManagerService.java`,相关函数如下: 198 | ![](./MAT入门23.png) 199 | ![](./MAT入门24.png) 200 | 201 | 我们关心的是Private,调用MemoryInfo`getTotalPrivateDirty()`,可以返回进程私有内存占用,等同于上文`USS`。 202 | 可以在App中起一个线程,每隔5s,读取MemoryInfo,输出`TotalPrivateDirty`到日志文件,测试结束后,借助Excel做出内存消耗曲线图。代码如下: 203 | 204 | ```java 205 | private Thread thread = new Thread() { 206 | 207 | @Override 208 | public void run() { 209 | super.run(); 210 | 211 | String state = Environment.getExternalStorageState(); 212 | // SDCard是否可用 213 | if (Environment.MEDIA_MOUNTED.equals(state)) { 214 | File path = new File(Environment.getExternalStorageDirectory().getPath() + "/dump/"); 215 | if (!path.exists()) { 216 | path.mkdirs(); 217 | } 218 | String logPath = path.getAbsolutePath(); 219 | if (!logPath.endsWith("/")) { 220 | logPath += "/"; 221 | } 222 | logPath = logPath + "procleak.log"; 223 | File file = new File(logPath); 224 | 225 | try { 226 | FileWriter writer = new FileWriter(file); 227 | writer.write("timestamp" + "\t" + "VSS" + "\t" + "PSS" + "\t" + "USS" + "\n"); 228 | writer.flush(); 229 | Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); 230 | 231 | while (!finish) { 232 | Debug.getMemoryInfo(memInfo); 233 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); 234 | String timestamp = sdf.format(new Date(System.currentTimeMillis())); 235 | 236 | writer.write(timestamp + "\t" + memInfo.getTotalSharedDirty() + "\t" + memInfo.getTotalPss() + "\t" + memInfo.getTotalPrivateDirty() + "\n"); 237 | writer.flush(); 238 | 239 | // 延时5s 240 | try { 241 | Thread.sleep(5 * 1000); 242 | } catch (InterruptedException e) { 243 | e.printStackTrace(); 244 | } 245 | } 246 | 247 | writer.close(); 248 | 249 | } catch (IOException e) { 250 | e.printStackTrace(); 251 | } 252 | } 253 | } 254 | 255 | }; 256 | ``` 257 | 258 | > 题外话,借助ActivityManager.getRunningAppProcesses()与ActivityManager.getProcessMemoryInfo(),可以写一个`工具App`, 259 | > 专门用来获取待测试应用的内存使用,输出统计日志(完全可以借助图标库,在工具App中可视化展示), 260 | > 这样在不修改待测试应用源码的(增加循环读内存信息的线程代码)基础上做到检测内存的目的。 261 | 262 | ##### 7. ActivityManager.getMemoryInfo() 263 | 返回`ActivityManager.MemoryInfo`对象,获取系统当前可用内存情况,在这里没什么用。 264 | 265 | 要研究源码,可以参见: 266 | `frameworks\base\core\java\android\app\ActivityManager.java`-getMemoryInfo() 267 | `frameworks\base\core\java\android\app\ActivityManagerService.java`-getMemoryInfo() 268 | `frameworks\base\core\java\android\os\Process.java`-getFreeMemory()/getTotalMemory() 269 | `frameworks\base\core\jni\android_util_Process.cpp`-getFreeMemoryImpl(),其实还是读的`/proc/meminfo` 270 | 271 | 272 | ##### 8. DDMS 273 | 借助DDMS中的Heap页,可以直观的看到应用内存占用情况。 274 | 275 | 具体操作: 276 | * 打开DDMS的Devices视图/Heap视图; 277 | * 选择要监控的进程; 278 | * 选中Devices视图上的`update heap`图标; 279 | * 点击Heap视图中的`Cause GC`按钮,留意`Allocated`值变动; 280 | * 疯狂使用待测应用,期间定时点击`Cause GC`按钮,留意`Allocated`值变动,如果数值持续上涨,那就是内存泄漏了。 281 | 282 | ![](./MAT入门35.png) 283 | ![](./MAT入门08.png) 284 | 285 | 当然该方法比较粗糙,较小的内存泄漏可能不容易用这种方法发现。 286 | 287 | 如果你习惯用Android Studio,`Memory Monitor`可以完成同样的功能,数据可视化形式也更直观、更友好。 288 | 289 | ![](./MAT入门09.png) 290 | 291 | #### 二:如何获取hprof文件 292 | 通过上文介绍的各种方法,我们可以明确待测应用是否发生了内存泄漏,如果发生了内存泄漏,下一步就是dump出泄漏前后heap快照文件,通过分析heap快照文件,明确知道泄漏的对象是哪些,已经如何fix内存泄漏问题。 293 | 294 | ##### 1. Debug.dumpHprofData() 295 | Android API提供了Debug.dumpHprofData()获取hprof文件,通过该方法,需要修改待测试应用的源码,参考代码如下: 296 | 297 | ```java 298 | public static boolean dumpHeapFile() { 299 | boolean ret = false; 300 | 301 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); 302 | String createTime = sdf.format(new Date(System.currentTimeMillis())); 303 | 304 | String state = Environment.getExternalStorageState(); 305 | // SDCard是否可用 306 | if (Environment.MEDIA_MOUNTED.equals(state)) { 307 | File file = new File(Environment.getExternalStorageDirectory().getPath() + "/dump/"); 308 | if (!file.exists()) { 309 | file.mkdirs(); 310 | } 311 | 312 | String hprofPath = file.getAbsolutePath(); 313 | if (!hprofPath.endsWith("/")) { 314 | hprofPath += "/"; 315 | } 316 | hprofPath += createTime + ".hprof"; 317 | 318 | try { 319 | Debug.dumpHprofData(hprofPath); 320 | ret = true; 321 | } catch (IOException e) { 322 | e.printStackTrace(); 323 | } 324 | } else { 325 | ret = false; 326 | } 327 | 328 | return ret; 329 | } 330 | ``` 331 | 332 | 在测试应用前,先dump出`heap快照1`,然后疯狂使用应用,让应用尽情去泄漏,一段时间后再次dump出`heap快照2`。 333 | 334 | ##### 2. DDMS 335 | 直接点击Devices视图上的`Dump HPROF file`图标,导出heap快照文件,同时直接在Eclipse MAT插件中打开heap快照文件。这种方法的好处是,完全不用修改待测应用的源码,也不需要自己转换hprof文件。 336 | 337 | ![](./MAT入门36.png) 338 | 339 | #### 三:再转hprof文件 340 | 获取的hprof文件是Dalvik格式,直接用MAT打开,会报错,需要转成Java虚拟机规范格式。 341 | 342 | hprof-conf old.hprof new.hprof 343 | 344 | #### 四:MAT打开hprof文件 345 | 启动MAT(推荐下载独立的MAT程序,Eclipse中插件装多了,会很卡的,当然Eclipse JVM参数调优可以缓解下,但只是缓解下~),Open Heap Dump,MAT分析结束默认会有两个TAB页:`Overview`、`Leak Suspects`。 346 | 347 | --- 348 | 349 | 先解释使用MAT时会遇到的三个名词: 350 | 351 | 1. `Shallow Heap`:指对象自身所占用的内存大小,不包含其引用的对象所占的内存大小。 352 | * 数组类型:数组元素大小乘以数组长度; 353 | * 非数组类型:对象大小加所有成员大小; 354 | 355 | 2. `Retained Heap`:当前对象大小 + 当前对象可直接或间接引用到的对象的大小总和(只存在从当前对象的引用路径)。 356 | 357 | 3. `GC Root`:Java中GC策略是基于对象`引用是否可达`来判断是否需要GC,引用可达判断起点就是GC Roots。JVM规范定义如下GC Roots: 358 | * Class:class loaded by system class loader; 359 | * Thread:live thread; 360 | * Stack Local:local variable or parameter of Java method; 361 | * JNI Local:local variable or parameter of JNI method; 362 | * JNI Global:global JNI reference; 363 | * Monitor Used:objects used as a monitor for synchronization; 364 | * Held by JVM:objects held from garbage collection by JVM for its purposes; 365 | 366 | JVM在GC时,需要判断哪些内存需要回收,简单理解就是已死的对象占用的内存空间需要回收,如何确定对象已死? 367 | 368 | JVM没用采用引用计数算法,而是采用的`可达性分析(Reachability Analysis)`算法,该算法基本思路是,通过GC Roots对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时(不可达),则证明此对象是不可用的,已死。 369 | 370 | 如图所示,object5、object6、object7,就是引用不可达对象,被GC! 371 | 372 | ![](./MAT入门28.png) 373 | 374 | 下面两张图,自己试着计算Retained Heap(obj1),会对上面的三个名词理解更清晰: 375 | 376 | ![](./MAT入门28.gif) 377 | ![](./MAT入门29.gif) 378 | 379 | 上图,Retained Heap(obj1) = Shallow Heap(obj1 + obj2 + obj4); 380 | 下图,Retained Heap(obj1) = Shallow Heap(obj1 + obj2 + obj3 + obj4); 381 | 382 | 其中MAT在展示对象列表时,会对`GC Root`特别标注,如图: 383 | ![](./MAT入门10.png) 384 | 385 | --- 386 | 387 | 一则最简单的分析示例: 388 | 1. 先从`Leak Suspects`入手,从怀疑点一开始; 389 | ![](./MAT入门11.png) 390 | ![](./MAT入门12.png) 391 | 392 | 2. 可以发现是10个`Byte[1048576]`对象泄漏,泄漏总量41943256Byte,泄漏最短路径是:elementData-data-mContent-main Thread(GC Root); 393 | 3. 剩下的工作就是看代码了。 394 | 395 | 上面的示例显然是最简单的,稍微复杂一点的场景,需要打开`dominator_tree`TAB页,按照`正则`搜索(正则搜索也有不适用的情形,这个时候就需要`OQL`,执行更复杂的查询逻辑),或者`Retained Heap`排序,找出可能的泄漏点。 396 | 然后展开泄漏对象(选择`Path To GC Roots`展开,标识当前对象到GC Root引用链),通过分析引用链,就可以大致知道泄漏的问题所在了。 397 | 398 | ![](./MAT入门13.png) 399 | 400 | 当然MAT有一个辅助窗口很有用,`Inspector`,对于那些匿名内部类对象,可以根据`Inspector`窗口展示的信息,推测出对应源码对应哪一个匿名内部类。内部类会有详细分析,参见后文。 401 | 402 | 比如`MainActivity`中的匿名内部类`FragmentActivity$1`,分析`Inspector`展示的信息,可以得出匿名类`FragmentActivity$1`对应的源码。 403 | 404 | ![](./MAT入门16.png) 405 | ![](./MAT入门15.png) 406 | ![](./MAT入门17.png) 407 | 408 | 如果根据`Inspector`窗口信息还是定位不到匿名内部类对应哪一段代码,可以用`apktool`反编译apk,看对应匿名类的smali,这招基本没问题。 409 | 410 | 比如: 411 | ![](./MAT入门38.png) 412 | ![](./MAT入门39.png) 413 | 414 | 415 | 416 | #### 五:Android常见内存泄漏场景汇总 417 | 其实Android中的内存泄漏,归纳一下,无非也就是几种: 418 | 419 | ##### 1. 引用没释放 420 | 421 | 1.1 有register,没有unregister 422 | 423 | `观察者`模式,不注意在合适的时机移除观察者,就很容易造成内存泄漏。 424 | 425 | **手Q案例** 426 | 手Q `QQAppInterface`中,三个观察者数组,由于QQAppInterface单例,同时生命周期和手Q Application基本一致,一旦相应组件忘记`removeObserver`,三个观察者数组就会持有相关组件引用,产生泄漏。 427 | 428 | ```java 429 | List uiObservers = new Vector(); 430 | List bgObservers = new Vector(); 431 | List qqInitHandlerObservers = new Vector(); 432 | 433 | public void addObserver(BusinessObserver observer, boolean beBackgroudCallback) { 434 | if (beBackgroudCallback) { 435 | if (!bgObservers.contains(observer)) { 436 | bgObservers.add(observer); 437 | } 438 | } else { 439 | if (!uiObservers.contains(observer)) { 440 | uiObservers.add(observer); 441 | } 442 | } 443 | } 444 | 445 | public void removeObserver(BusinessObserver observer) { 446 | uiObservers.remove(observer); 447 | bgObservers.remove(observer); 448 | qqInitHandlerObservers.remove(observer); 449 | } 450 | ``` 451 | 452 | 某一次,`smitt`上报某Activity泄漏问题,根据`forwardxxxxx`提示信息,编写OQL语句,查询泄漏Activity。 453 | 454 | > OQL基本语法:SELECT * FROM [INSTANCEOF] class_name [WHERE filter_expression]; 455 | > 和SQL对比:类相当于表,对象相当于行,字段相当于列; 456 | 457 | 过程如下: 458 | 459 | ![](./MAT入门32.png) 460 | ![](./MAT入门33.png) 461 | ![](./MAT入门34.png) 462 | ![](./MAT入门31.png) 463 | 464 | --- 465 | 466 | 既然在MAT中看到了phantom/weak/soft,那就必须要好好讲讲JDK 1.2对引用这一语言特性的扩充。JDK 1.2之前,如果要实现这样的对象缓存,内存足够的时候,对象占用内存空间一直保留,内存不够的时候,对象占用内存空间自动回收,避免OOM问题,基本是不太可能的。 467 | 468 | 为了解决这种类型的问题,JDK将Reference细化为4种,强度依次减弱: 469 | 470 | 1. 强引用(StrongReference) 471 | ```java 472 | Object obj = new Object(); 473 | ``` 474 | 只要强引用在,GC就永远不会回收被引用对象; 475 | 476 | 2. 软引用(SoftReference) 477 | ```java 478 | Object obj = new Object(); 479 | SoftReference softRef = new SoftReference(obj); 480 | ``` 481 | JVM发现内存吃紧,快要OOM了,JVM将软引用对象标记为待回收对象,执行一次GC,释放软引用对象,进行缓解内存压力的补救工作; 482 | 补救了还不行,没办法了,那就报OOM了; 483 | 484 | 3. 弱引用(WeakReference) 485 | ```java 486 | Object obj = new Object(); 487 | WeakReference weakRef = new WeakReference(obj); 488 | ``` 489 | JVM要执行GC了,不管内存够不够用,先释放弱引用对象再说; 490 | 491 | 4. 虚引用(PhantomReference) 492 | 虚引用主要用来跟踪对象被垃圾回收器回收的活动,用户代码基本不会用到; 493 | 494 | 对于`软引用`、`弱引用`,都可以用来构造缓存系统,`软引用`适合构造数据敏感缓存,而`弱引用`适合构造数据不敏感缓存。 495 | 496 | 当然,`弱引用`还有一大作用,将引用路径调整为弱引用路劲,堵住泄漏(只要JVM执行GC,路径就断开了)。 497 | 498 | --- 499 | 500 | 501 | 1.2 Context泄漏 502 | 503 | Activity泄漏最为典型,Context-Activity生命周期和Activity生命周期不一致。 504 | 505 | **官方案例**(Android早期源码的bug,现已fix) 506 | 507 | 场景:下面代码的初衷,在设备屏幕方向变化时,不用每次都构造Drawable对象,提高界面展示效率。 508 | ```java 509 | private static Drawable sBackground; 510 | 511 | @Override 512 | protected void onCreate(Bundle state) { 513 | super.onCreate(state); 514 | 515 | TextView label = new TextView(this); 516 | label.setText("Leaks are bad"); 517 | 518 | if (sBackground == null) { 519 | sBackground = getDrawable(R.drawable.large_bitmap); 520 | } 521 | label.setBackgroundDrawable(sBackground); 522 | 523 | setContentView(label); 524 | } 525 | ``` 526 | 527 | 原因:label.setBackgroundDrawable(sBackground)调用,导致sBackground持有label引用,进而持有Context-Activity! 528 | ![](./MAT入门25.png) 529 | ![](./MAT入门26.png) 530 | 一个static对象持有Context-Activity引用,该Activity onDestory()后,显然不会回收了!! 531 | 532 | 怎么改:sBackground持有label的WeakReference。 533 | ![](./MAT入门27.png) 534 | 535 | 回到这个示例,static的Drawable持有TextView的WeakReference,进而到Context-Activity的引用路径为弱引用路劲,进而也就能堵住Activity泄漏了! 536 | 537 | **Message的坑** 538 | 539 | 场景:下面代码最常见不过,借助Handler/Looper执行某一任务,或者实现线程间通信。 540 | ```java 541 | public class SampleActivity extends Activity { 542 | 543 | // 内部类,持有SampleActivity引用 544 | private Handler mLeakyHandler = new Handler() { 545 | @Override 546 | public void handleMessage(Message msg) { 547 | // ... 548 | } 549 | }; 550 | 551 | @Override 552 | protected void onCreate(Bundle savedInstanceState) { 553 | super.onCreate(savedInstanceState); 554 | 555 | // Runnable在内部封装成Message,Meassge持有mLeakyHandler引用 556 | mLeakyHandler.postDelayed(new Runnable() { 557 | @Override 558 | public void run() { /* ... */ } 559 | }, 1000 * 60 * 10); // 延时10min执行 560 | 561 | // Meassge持有mLeakyHandler引用 562 | mLeakyHandler.sendMessageDelayed(msg, 1000 * 60 * 60); 563 | 564 | // 直接导致当前Activity onDestory(),但Activity会被立刻GC嘛? 565 | finish(); 566 | } 567 | } 568 | ``` 569 | 570 | 其中`postDelayed()`、`sendMessageDelayed()`最后均会调用内部`enqueueMessage()`,将Message加入Looper的MessageQueue,此时会看到Messge持有Handler强引用。 571 | ```java 572 | private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 573 | msg.target = this; 574 | if (mAsynchronous) { 575 | msg.setAsynchronous(true); 576 | } 577 | return queue.enqueueMessage(msg, uptimeMillis); 578 | } 579 | ``` 580 | 581 | 至此,就有一条很清晰的引用链`Messge-Handler-Activity`。 582 | 583 | 想象一下,当前消息队列含有大量的Message等待处理,你发的Messge需要等待很久才会获得执行机会,你现在关闭Activity,Message不执行完,你的Activity就一直泄漏;发送DelayMessage也同理。 584 | 585 | 比如下面的一个泄漏示例: 586 | 587 | ![](./MAT入门37.png) 588 | 589 | 怎么改,两种方案: 590 | * Handler.removeCallbacksAndMessages(null) 591 | 选择合适的时机,调用该函数,清空MessageQueue,阻断引用链`Messge-Handler-Activity`; 592 | 593 | * 使用静态内部Handler + WeakReference 594 | 静态内部handler不会持有外部Activity引用,采用WeakReference来持有外部Activity引用,堵住泄漏引用链; 595 | 596 | 手Q中就是这样干的: 597 | 598 | ![](./MAT入门30.png) 599 | 600 | * github开源方案,如[github.com/badoo/android-weak-handler](https://github.com/badoo/android-weak-handler)等,解决内存泄漏的同时,还支持对MessqgeQueue任务更有效的调度。 601 | 602 | --- 603 | 604 | `内部类`,简单理解,就是一个类的定义放在另外一个类的定义内部,同时定义为非static。 605 | 606 | 注意有匿名和非匿名之分。 607 | 608 | ```java 609 | public class Outer { 610 | 611 | private int i1 = 0; 612 | protected int i2 = 0; 613 | public int i3 = 0; 614 | int i4 = 0; 615 | 616 | // 内部类 617 | public class Inner { 618 | 619 | public int func() { 620 | return i1 + i2 + i3 + i4; 621 | } 622 | 623 | } 624 | 625 | // 匿名内部类 626 | public Inner mInner = new Inner() { 627 | public int func() { 628 | return super.func() + 10; 629 | } 630 | }; 631 | 632 | } 633 | ``` 634 | 635 | `内部类`可以访问外部类所有成员(private、protected、public、包访问控制权限),但是该特性把握不好,会是`内存泄漏`、Android`方法数超65535`的罪魁祸首! 636 | 637 | 为什么如此,我们借助`javap`反编译Outer$Inner.class,Outer.class,查看相关字节码,一目了然! 638 | 639 | > javac Outer.java 640 | > javap -v Outer$Inner.class 641 | > javap -v Outer.class 642 | 643 | 截取`Outer$Inner.class`部分字节码 644 | 645 | ```java 646 | ... 647 | // 编译器给内部类添加成员变量,final,指向外部类对象 648 | final Outer this$0; 649 | flags: ACC_FINAL, ACC_SYNTHETIC 650 | 651 | 652 | // 编译器给内部类构造函数添加形参,外部类对象 653 | public Outer$Inner(Outer); 654 | flags: ACC_PUBLIC 655 | 656 | Code: 657 | stack=2, locals=2, args_size=2 658 | 0: aload_0 659 | 1: aload_1 660 | // 字段设置字节码,this$0指向外部类对象 661 | 2: putfield #1 // Field this$0:LOuter; 662 | ... 663 | public int func(); 664 | flags: ACC_PUBLIC 665 | 666 | Code: 667 | stack=2, locals=1, args_size=1 668 | 0: aload_0 669 | 1: getfield #1 // Field this$0:LOuter; 670 | // 访问外部类private成员,通过access方法 671 | 4: invokestatic #3 // Method Outer.access$000:(LOuter;)I 672 | 7: aload_0 673 | 8: getfield #1 // Field this$0:LOuter; 674 | // 访问外部类protected成员,直接走.操作符 675 | 11: getfield #4 // Field Outer.i2:I 676 | 14: iadd 677 | 15: aload_0 678 | 16: getfield #1 // Field this$0:LOuter; 679 | // 访问外部类public成员,直接走.操作符 680 | 19: getfield #5 // Field Outer.i3:I 681 | 22: iadd 682 | 23: aload_0 683 | 24: getfield #1 // Field this$0:LOuter; 684 | // 访问外部类包访问权限成员,直接走.操作符 685 | 27: getfield #6 // Field Outer.i4:I 686 | ... 687 | ``` 688 | ```java 689 | ... 690 | static int access$000(Outer); 691 | flags: ACC_STATIC, ACC_SYNTHETIC 692 | 693 | Code: 694 | stack=1, locals=1, args_size=1 695 | 0: aload_0 696 | 1: getfield #1 // Field i1:I 697 | 4: ireturn 698 | LineNumberTable: 699 | line 1: 0 700 | ... 701 | ``` 702 | 703 | 总结,内部类能够访问外部类所有成员,原因有二: 704 | * 内部类持有`外部类引用` 705 | 如果内部类泄漏,因为存在对外部类的引用,外部类也就泄漏了; 706 | 707 | * 内部类访问外部类private成员,是因为外部类对那些内部类会访问的private成员,生成了`access()`访问方法 708 | 那些内部类不妨问的private成员,就不管了; 709 | 这里对这些private成员多生成了一个`access()`,显然会增加Android主dex方法数; 710 | 711 | --- 712 | 713 | ##### 2. 资源没close 714 | 一般这些资源对象,为了加快资源读写速度都是带缓冲区的,缓冲不仅存在于Dalvik堆,也会存在于系统层面,如果简单的把资源对象的引用置为null,系统层面缓冲就得不到回收,也就发生了内存泄漏。 715 | 716 | 2.1 File close() 717 | 718 | 2.2 Cursor close() 719 | 720 | 2.3 Bitmap recycle() 721 | 722 | ##### 3. JNI内存泄露 723 | 724 | JVM进程内存模型如下图,Dalvik可以等效看待: 725 | 726 | ![](./MAT入门29.png) 727 | 728 | JVM GC负责对永久代、新生代、老年代的内存的回收,而JNI层通过malloc系统调用分配的堆内存,不在JVM GC负责范围之内,这一块的内存泄漏直接会导致应用所属的虚拟机进程挂掉。 729 | 730 | 如何解决:C++/C代码编写规范,避免内存泄漏;或者使用`Valgrind`等工具检测。 731 | 732 | 个人经历案例:... 733 | 734 | #### 未完待续,[手Q中的内存泄漏检测模块](./手Q中的内存泄漏检测模块.md)~ 735 | 736 |
737 | #### 六:参考链接 738 | 1. [How do I discover memory usage of my application in Android?](http://stackoverflow.com/questions/2298208/how-do-i-discover-memory-usage-of-my-application-in-android) 739 | 2. [也谈Android内存那点事——深入内存数据和MAT应用](http://km.oa.com/group/2714/articles/show/109931?kmref=search) 740 | 3. [Android内存泄露工具MAT使用介绍](http://km.oa.com/group/23213/articles/show/197041?kmref=search) 741 | 4. [Android内存 系列文章](http://hubingforever.blog.163.com/blog/#m=0&t=1&c=fks_084065082084086064081084083095085081080067080086083068093) 742 | 5. [Memory Analyzer tool(MAT)分析内存泄漏---理解Retained Heap、Shallow Heap、GC Root](http://blog.csdn.net/hhww0101/article/details/8133219) 743 | 6. [内存管理(3)-Android 内存泄露分析](http://www.jianshu.com/p/c59c199ca9fa) 744 | 7. [linux 内存查看方法:meminfo\maps\smaps\status 文件解析](http://www.cnblogs.com/jiayy/p/3458076.html) 745 | 8. [Android内存分析和调优](http://my.oschina.net/jerikc/blog/391907) 746 | 9. [Android 4.4 meminfo 实现分析](http://tech.uc.cn/?p=2714) 747 | 10. [Android源码学习之六——ActivityManager框架解析](http://blog.csdn.net/caowenbin/article/details/6036726) 748 | 11. [Avoiding memory leaks](http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html) 749 | 12. [android-weak-handler](https://github.com/badoo/android-weak-handler) 750 | 13. [深入理解java内部类](http://km.oa.com/group/21869/articles/show/202156?kmref=related_post) 751 | -------------------------------------------------------------------------------- /AndroidLeak/Android内存泄漏检测工具LeakCanary原理分析.md: -------------------------------------------------------------------------------- 1 | ## Android内存泄漏检测工具LeakCanary原理分析 2 | 3 | 用于Android应用内存泄漏监测,Square开源的又一大作,设计非常精巧。 4 | 5 | https://github.com/square/leakcanary。 6 | 7 | 可以监测任何感兴趣的对象,包括`Activity`、`Fragment`,默认只监测`Activity`。 8 | 9 | 库的用法很简单,说说原理吧~ 10 | 11 | ### 原理 12 | ###### 1. `ActivityRefWatcher`中,注册Activity生命周期监听接口,当Activity onDestroy()被调用时,将当前Activity加入内存泄漏监听队列; 13 | 14 | ```java 15 | private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = 16 | new Application.ActivityLifecycleCallbacks() { 17 | @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 18 | } 19 | 20 | @Override public void onActivityStarted(Activity activity) { 21 | } 22 | 23 | @Override public void onActivityResumed(Activity activity) { 24 | } 25 | 26 | @Override public void onActivityPaused(Activity activity) { 27 | } 28 | 29 | @Override public void onActivityStopped(Activity activity) { 30 | } 31 | 32 | @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 33 | } 34 | 35 | @Override public void onActivityDestroyed(Activity activity) { 36 | ActivityRefWatcher.this.onActivityDestroyed(activity); 37 | } 38 | }; 39 | 40 | public void watchActivities() { 41 | // Make sure you don't get installed twice. 42 | stopWatchingActivities(); 43 | application.registerActivityLifecycleCallbacks(lifecycleCallbacks); 44 | } 45 | 46 | public void stopWatchingActivities() { 47 | application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); 48 | } 49 | ``` 50 | 51 | 注意注册Activity生命周期监听接口只在Android 4.0以上支持,如果要支持Android 4.0以下版本,可以参考`手机QQ`中采用的方法,见下一篇文章。 52 | 53 | ###### 问题来了,如果要监听Fragment对象有无泄漏,怎么办? 54 | 55 | ```java 56 | public class ExampleApplication extends Application { 57 | 58 | public static RefWatcher getRefWatcher(Context context) { 59 | ExampleApplication application = (ExampleApplication) context.getApplicationContext(); 60 | return application.refWatcher; 61 | } 62 | 63 | private RefWatcher refWatcher; 64 | 65 | @Override public void onCreate() { 66 | super.onCreate(); 67 | // 默认开启对Activity泄漏的监听 68 | refWatcher = LeakCanary.install(this); 69 | } 70 | } 71 | 72 | // 定义自己的Fragment基类,onDestroy方法中添加对当前Fragment对象的watch 73 | public abstract class BaseFragment extends Fragment { 74 | 75 | @Override public void onDestroy() { 76 | super.onDestroy(); 77 | RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity()); 78 | refWatcher.watch(this); 79 | } 80 | } 81 | 82 | ``` 83 | 84 | ###### 2. `RefWatcher`中,`watch`方法将对象用`WeakReference`引起来,监听对象是否内存泄漏 85 | ###### 这里的原理是:`WeakReference`和`ReferenceQueue`配合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列 86 | ###### 检测方法就很简单了,主动GC,触发`WeakReference`被GC,同时检测GC前后,ReferenceQueue是否包含被监听对象;如果不包含,则说明该对象没有被GC,一定存在到GC Roots的强引用链,也就是发生了泄漏。 87 | 88 | ```java 89 | public void watch(Object watchedReference, String referenceName) { 90 | checkNotNull(watchedReference, "watchedReference"); 91 | checkNotNull(referenceName, "referenceName"); 92 | if (debuggerControl.isDebuggerAttached()) { 93 | return; 94 | } 95 | final long watchStartNanoTime = System.nanoTime(); 96 | String key = UUID.randomUUID().toString(); 97 | retainedKeys.add(key); 98 | final KeyedWeakReference reference = 99 | new KeyedWeakReference(watchedReference, key, referenceName, queue); 100 | 101 | watchExecutor.execute(new Runnable() { 102 | @Override public void run() { 103 | ensureGone(reference, watchStartNanoTime); 104 | } 105 | }); 106 | } 107 | 108 | void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) { 109 | long gcStartNanoTime = System.nanoTime(); 110 | 111 | long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); 112 | removeWeaklyReachableReferences(); 113 | if (gone(reference) || debuggerControl.isDebuggerAttached()) { 114 | return; 115 | } 116 | gcTrigger.runGc(); 117 | removeWeaklyReachableReferences(); 118 | if (!gone(reference)) { 119 | long startDumpHeap = System.nanoTime(); 120 | long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); 121 | 122 | File heapDumpFile = heapDumper.dumpHeap(); 123 | 124 | if (heapDumpFile == null) { 125 | // Could not dump the heap, abort. 126 | return; 127 | } 128 | long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); 129 | heapdumpListener.analyze( 130 | new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs, 131 | heapDumpDurationMs)); 132 | } 133 | } 134 | 135 | private boolean gone(KeyedWeakReference reference) { 136 | return !retainedKeys.contains(reference.key); 137 | } 138 | 139 | private void removeWeaklyReachableReferences() { 140 | // WeakReferences are enqueued as soon as the object to which they point to becomes weakly 141 | // reachable. This is before finalization or garbage collection has actually happened. 142 | KeyedWeakReference ref; 143 | while ((ref = (KeyedWeakReference) queue.poll()) != null) { 144 | retainedKeys.remove(ref.key); 145 | } 146 | } 147 | ``` 148 | 149 | ###### 3. dump文件分析,`HeapAnalyzer`类 150 | 基于Square开源的`haha`库,专用于分析Android heap dump文件,https://github.com/square/haha。 151 | 152 | 查看`haha`库的历史,可以发现是由`Eclipse Memory Analyzer`改版而来,支持Android Dalvik格式 heap dump文件的分析。 153 | 154 | 分析的结果即泄漏对象到GC Roots强引用的最短路径,形式如下: 155 | ``` 156 | In com.example.leakcanary:1.0:1 com.example.leakcanary.MainActivity has leaked: 157 | * GC ROOT thread java.lang.Thread. (named 'AsyncTask #1') 158 | * references com.example.leakcanary.MainActivity$3.this$0 (anonymous class extends android.os.AsyncTask) 159 | * leaks com.example.leakcanary.MainActivity instance 160 | ``` 161 | 162 | 其实看结果,就是我们平时用MAT工具分析的结果,系统的Android内存泄漏分析技巧,可以参见下一篇上传的文章。 163 | 164 | --- 165 | #### 对第2步基本原理的进一步补充: 166 | 1. 主动GC,采用`Runtime.getRuntime().gc()`(对比System.gc()的优点是能保证及时触发GC),同时GC后等待100ms,等待Java虚拟机把这个弱引用加入`ReferenceQueue`。 167 | 参考`GcTrigger`类。 168 | ```java 169 | GcTrigger DEFAULT = new GcTrigger() { 170 | @Override public void runGc() { 171 | // Code taken from AOSP FinalizationTest: 172 | // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/ 173 | // java/lang/ref/FinalizationTester.java 174 | // System.gc() does not garbage collect every time. Runtime.gc() is 175 | // more likely to perfom a gc. 176 | Runtime.getRuntime().gc(); 177 | enqueueReferences(); 178 | System.runFinalization(); 179 | } 180 | 181 | private void enqueueReferences() { 182 | // Hack. We don't have a programmatic way to wait for the reference queue daemon to move 183 | // references to the appropriate queues. 184 | try { 185 | Thread.sleep(100); 186 | } catch (InterruptedException e) { 187 | throw new AssertionError(); 188 | } 189 | } 190 | }; 191 | ``` 192 | 2. 步骤2执行时机的选择,参考`AndroidWatchExecutor`类。 193 | 在主线程空闲阶段,由主线程提交步骤2,即将步骤2放入子线程执行,这样可以尽可能小的降低子线程执行对主线程时间片抢占带来的影响。 194 | ```java 195 | @Override public void execute(final Runnable command) { 196 | if (isOnMainThread()) { 197 | executeDelayedAfterIdleUnsafe(command); 198 | } else { 199 | mainHandler.post(new Runnable() { 200 | @Override public void run() { 201 | executeDelayedAfterIdleUnsafe(command); 202 | } 203 | }); 204 | } 205 | } 206 | 207 | private boolean isOnMainThread() { 208 | return Looper.getMainLooper().getThread() == Thread.currentThread(); 209 | } 210 | 211 | private void executeDelayedAfterIdleUnsafe(final Runnable runnable) { 212 | // This needs to be called from the main thread. 213 | Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { 214 | @Override public boolean queueIdle() { 215 | backgroundHandler.postDelayed(runnable, DELAY_MILLIS); 216 | return false; 217 | } 218 | }); 219 | } 220 | ``` 221 | 222 | 全文完~ 223 | 224 | -------------------------------------------------------------------------------- /AndroidLeak/MAT入门01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门01.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门02.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门03.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门04.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门05.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门06.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门07.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门08.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门09.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门10.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门11.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门12.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门13.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门14.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门15.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门16.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门17.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门18.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门19.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门20.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门21.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门22.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门23.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门24.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门25.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门26.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门27.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门28.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门28.gif -------------------------------------------------------------------------------- /AndroidLeak/MAT入门28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门28.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门29.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门29.gif -------------------------------------------------------------------------------- /AndroidLeak/MAT入门29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门29.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门30.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门31.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门32.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门33.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门34.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门35.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门36.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门37.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门38.png -------------------------------------------------------------------------------- /AndroidLeak/MAT入门39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/MAT入门39.png -------------------------------------------------------------------------------- /AndroidLeak/Outer_Inner/Outer$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/Outer_Inner/Outer$1.class -------------------------------------------------------------------------------- /AndroidLeak/Outer_Inner/Outer$Inner.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/Outer_Inner/Outer$Inner.class -------------------------------------------------------------------------------- /AndroidLeak/Outer_Inner/Outer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/AndroidLeak/Outer_Inner/Outer.class -------------------------------------------------------------------------------- /AndroidLeak/Outer_Inner/Outer.java: -------------------------------------------------------------------------------- 1 | public class Outer { 2 | 3 | private int i1 = 0; 4 | protected int i2 = 0; 5 | public int i3 = 0; 6 | int i4 = 0; 7 | 8 | // 内部类 9 | public class Inner { 10 | 11 | public int func() { 12 | return i1 + i2 + i3 + i4; 13 | } 14 | 15 | } 16 | 17 | // 匿名内部类 18 | public Inner mInner = new Inner() { 19 | public int func() { 20 | return super.func() + 10; 21 | } 22 | }; 23 | 24 | } -------------------------------------------------------------------------------- /AndroidLeak/procleak.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | # Uage: 4 | # procleak.sh procname 5 | 6 | if [ $# != 1 ]; then 7 | echo "Uage: $0 procname" 8 | exit 1 9 | fi 10 | 11 | OUTPUT='/sdcard/procleak.log' 12 | 13 | echo 'timestamp\tPID\tVss\tRss\tPss\tUss\tcmdline' >> $OUTPUT 14 | 15 | while true; do 16 | timestamp=`date '+%Y-%m-%d %H:%M:%S'` 17 | key=$1'$' 18 | meminfo=`procrank | busybox grep ${key}` 19 | 20 | echo $timestamp'\t'$meminfo >> $OUTPUT 21 | 22 | sleep 5 23 | done 24 | -------------------------------------------------------------------------------- /AndroidLeak/手Q中的内存泄漏检测模块.md: -------------------------------------------------------------------------------- 1 | #### 手Q中的内存泄漏检测模块 2 | --- 3 | 4 | 出自`acolorzhang`之手,学习前辈知识,简单代码确很值得一学! 5 | 6 | ###### 1. 位置 7 | 包com.tencent.mobileqq.memoryleak 8 | * LeakInspector 9 | - MonitorInstrumentation 10 | * ActivityLeakSolution 11 | * DumpMemInfoHandler 12 | 13 | 包com.tencent.mobileqq.startup.step 14 | * InitLeakInspector 15 | - LeakListener 16 | 17 | 其中`InitLeakInspector`步骤,自动机调度,依附于`LoadDex`步骤。 18 | 19 | `InitLeakInspector`步骤的工作很简单: 20 | 1. 创建`LeakInspector`单例(整个手Q,有且只能有一个!!),并把`LeakListener`对象传给`LeakInspector`实例用于回调。 21 | 比如dump堆快照前来次回调,让你有机会给UI提示;再比如dump完了回调,让你给UI提示;还有一个比较重要`filter()`,每次在进行指定`activity`内存泄漏检测前,回调一下,根据过滤规则判断还要不要继续检测! 22 | 23 | 2. 开启`activity`监控,也很简单,第一次反射拿到`android.app.ActivityThread`的单例`sCurrentActivityThread`,第二次反射拿到`sCurrentActivityThread`的成员`mInstrumentation`,保存旧的`mInstrumentation`,用自己的`MonitorInstrumentation`替换系统的。 24 | 代码很短,却给我很大启示,活用java反射hack系统,完成自己的目的! 25 | 26 | ```java 27 | public static boolean startActivityInspect() { 28 | Object currentActivityThread = BaseApplicationImpl.sCurrentActivityThread; 29 | Field field = currentActivityThread.getClass().getDeclaredField("mInstrumentation"); 30 | field.setAccessible(true); 31 | field.set(currentActivityThread, new MonitorInstrumentation()); 32 | } 33 | 34 | private static class MonitorInstrumentation extends Instrumentation { 35 | @Override 36 | public void callActivityOnDestroy(Activity activity) { 37 | sOldInstr.callActivityOnDestroy(activity); 38 | afterOnDestroy(activity); 39 | } 40 | 41 | public static void afterOnDestroy(Activity activity) { 42 | startInspect(activity); 43 | } 44 | } 45 | ``` 46 | 47 | 3. `MonitorInstrumentation`实现只重写了`callActivityOnDestroy()`方法,一旦手Q中有activity被destory,该方法就会被回调,给子线程传Runnable`InspectorRunner`,同时调用清理资源函数,清理与该activity相关的资源,参见`ActivityLeakSolution`实现。 48 | 49 | 4. `InspectorRunner`先用弱引用持有当前被destory的activity,然后每间隔1s,检测弱引用`get()`操作是否为空, 50 | 不为空显然还没被gc,继续循环,循环100次。 51 | 52 | ```java 53 | private class InspectorRunner implements Runnable { 54 | private WeakReference ref; 55 | private String className; 56 | 57 | InspectorRunner(WeakReference ref, String name, int retryCount) { 58 | this.ref = ref; 59 | try { 60 | className = ref.get().getClass().getSimpleName(); 61 | } catch(Exception e) { 62 | className = "Default"; 63 | } 64 | } 65 | 66 | @Override 67 | public void run() { 68 | if (ref.get() != null) {//还没有释放 69 | if (++retryCount < LOOP_MAX_COUNT) { 70 | System.gc(); 71 | mHandler.postDelayed(this, 1000); 72 | return ; 73 | } else { 74 | //到这里是检查完毕了,回调一下通知结果 75 | if (QLog.isColorLevel()) { 76 | QLog.d(TAG, QLog.CLR, "inspect " + digest + " leaked"); 77 | } 78 | mListener.onLeaked(digest, ref); 79 | } 80 | } else { 81 | if (QLog.isColorLevel()) { 82 | QLog.d(TAG, QLog.CLR, "inspect " + digest + " finished no leak"); 83 | } 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | 5. 如果检测100次(很显然时间是至少100s),还没被回收,就判断该activity发生了泄漏,dump dalvik堆快照,over~ 90 | 91 |
92 | 关键知识点: 93 | 1. Instrumentation; 94 | 2. 弱引用; 95 | 3. java反射hack系统framwork层; 96 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 wangwang4git 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | just-do 2 | ======= 3 | 4 | for fun... 5 | 6 | ---- 7 | 8 | ## list 9 | 1. [Android WebView Java与JavaScript互调](./post/Android WebView Java与JavaScript互调.md) 10 | 2. [Android SurfaceView入门](./post/Android SurfaceView入门.md) 11 | 3. [Android ContentProvider入门](./post/Android ContentProvider入门.md) 12 | 4. [简单说说HashMap,HashTable,ConcurrentHashTable](./post/简单说说HashMap,HashTable,ConcurrentHashTable.md) 13 | 5. [简单说说Synchronized,ReentrantLock](./post/简单说说Synchronized,ReentrantLock.md) 14 | 6. [Annotation学习笔记](./post/Annotation学习笔记.md) 15 | 7. [Java基本功不牢造成的那些似坑非坑](./post/Java基本功不牢造成的那些似坑非坑.md) 16 | 8. [Android滤镜开发一二](./post/Android滤镜开发一二.md) 17 | 9. [Android开发规范系列](./post/Android开发规范系列.md) 18 | 10. [Java自己总结一二](./post/Java自己总结一二.md) 19 | 11. [数据结构总结一二](./post/数据结构总结一二.md) 20 | 12. [Android三方库&应用架构](./post/Android三方库&应用架构.md) 21 | 13. [Android桌面悬浮窗](./post/Android桌面悬浮窗.md) 22 | 14. [Android Activity Fragment完整生命周期](./post/Android Activity Fragment完整生命周期.md) 23 | 15. [SVN学习](./post/SVN学习.md) 24 | 16. [获取Android设备ID](./post/获取Android设备ID.md) 25 | 17. [Android Methods Limitation](./post/Android Methods Limitation.md) 26 | 18. [ANT学习](./post/ANT学习.md) 27 | 19. [Golang学习](https://github.com/wangwang4git/golearning) 28 | 20. [百度PushSDK反编译调研](https://github.com/wangwang4git/baidu-push-android) 29 | 21. [NoSQL学习笔记](./post/NoSQL学习笔记.md) 30 | 22. [The Android Design Cheat Sheet](./post/The Android Design Cheat Sheet.md) 31 | 23. [我在简网上生成的GoodMan001.apk破解笔记](./post/我在简网上生成的GoodMan001.apk破解笔记.md) 32 | 24. [Git学习](./post/Git学习.md) 33 | 25. [SublimeText3插件安装笔记](./post/SublimeText3插件安装笔记.md) 34 | 26. [Python开发工具收集](./post/Python开发工具收集.md) 35 | 27. [Python学习笔记](./post/Python学习笔记.md) 36 | 28. [Pig学习笔记](./post/Pig学习.md) 37 | 29. [sqlite3_corruption](./post/sqlite3_corruption.md) 38 | 30. [设计模式学习](./post/设计模式学习.md) 39 | 31. [Android内存泄漏相关汇总](./AndroidLeak/AndroidLeak.md) 40 | 32. [Android内存泄漏检测工具LeakCanary原理分析](./AndroidLeak/Android内存泄漏检测工具LeakCanary原理分析.md) 41 | 33. [docker OS X](./post/docker OS X.md) 42 | 34. [JNI总结](./post/JNI学习笔记.md) 43 | 35. [mac android studio快捷键](./post/mac android studio快捷键.md) 44 | 36. [ndk开发本地代码优化--Linux本地代码优化](./post/ndk开发本地代码优化--Linux本地代码优化.md) 45 | 46 | 47 | ## TO DO LIST 48 | - [ ] Android滤镜开发一二 49 | - [ ] Android开发规范系列 50 | - [X] Java自己总结一二 51 | - [X] 数据结构总结一二 52 | - [ ] Android三方库&应用架构 53 | - [X] SVN学习 54 | - [ ] Android Methods Limitation 55 | - [ ] ANT学习 56 | - [ ] NoSQL学习笔记 57 | - [X] Git学习 58 | - [ ] SublimeText3插件安装笔记 59 | - [ ] Python开发工具收集 60 | - [ ] Python学习笔记 61 | - [ ] Pig学习笔记 62 | - [ ] 设计模式学习 63 | - [ ] Android内存泄漏相关汇总 64 | - [ ] JNI学习笔记 65 | - [ ] mac android studio快捷键 -------------------------------------------------------------------------------- /img/ANT学习.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ANT学习.png -------------------------------------------------------------------------------- /img/ANT学习01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ANT学习01.png -------------------------------------------------------------------------------- /img/ANT学习02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ANT学习02.png -------------------------------------------------------------------------------- /img/ANT学习03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ANT学习03.png -------------------------------------------------------------------------------- /img/ANT学习04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ANT学习04.png -------------------------------------------------------------------------------- /img/ANT学习05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ANT学习05.png -------------------------------------------------------------------------------- /img/Android-Design-Cheat-Sheet-highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android-Design-Cheat-Sheet-highres.png -------------------------------------------------------------------------------- /img/Android_UniqueId01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android_UniqueId01.png -------------------------------------------------------------------------------- /img/Android规范-工具01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android规范-工具01.png -------------------------------------------------------------------------------- /img/Android规范-工具02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android规范-工具02.png -------------------------------------------------------------------------------- /img/Android规范-目录01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android规范-目录01.png -------------------------------------------------------------------------------- /img/Android规范-结构01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android规范-结构01.png -------------------------------------------------------------------------------- /img/Android规范-结构02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android规范-结构02.png -------------------------------------------------------------------------------- /img/Android规范-结构03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Android规范-结构03.png -------------------------------------------------------------------------------- /img/Annotation01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Annotation01.png -------------------------------------------------------------------------------- /img/Annotation02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Annotation02.png -------------------------------------------------------------------------------- /img/ConcurrentHashMap类图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ConcurrentHashMap类图.jpg -------------------------------------------------------------------------------- /img/ConcurrentHashMap结构图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/ConcurrentHashMap结构图.jpg -------------------------------------------------------------------------------- /img/Java一二01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Java一二01.png -------------------------------------------------------------------------------- /img/Java一二02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Java一二02.png -------------------------------------------------------------------------------- /img/Java一二03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Java一二03.png -------------------------------------------------------------------------------- /img/Java一二04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Java一二04.png -------------------------------------------------------------------------------- /img/Java一二05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Java一二05.png -------------------------------------------------------------------------------- /img/MongoDB学习01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/MongoDB学习01.png -------------------------------------------------------------------------------- /img/MongoDB学习02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/MongoDB学习02.png -------------------------------------------------------------------------------- /img/MongoDB学习03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/MongoDB学习03.png -------------------------------------------------------------------------------- /img/MongoDB学习04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/MongoDB学习04.png -------------------------------------------------------------------------------- /img/MongoDB学习05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/MongoDB学习05.png -------------------------------------------------------------------------------- /img/MongoDB笔记.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/MongoDB笔记.png -------------------------------------------------------------------------------- /img/Pig学习.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Pig学习.png -------------------------------------------------------------------------------- /img/Python_introduction_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Python_introduction_cn.png -------------------------------------------------------------------------------- /img/Python_introduction_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Python_introduction_en.png -------------------------------------------------------------------------------- /img/SVN学习.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习.png -------------------------------------------------------------------------------- /img/SVN学习01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习01.png -------------------------------------------------------------------------------- /img/SVN学习02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习02.png -------------------------------------------------------------------------------- /img/SVN学习03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习03.png -------------------------------------------------------------------------------- /img/SVN学习04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习04.png -------------------------------------------------------------------------------- /img/SVN学习05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习05.png -------------------------------------------------------------------------------- /img/SVN学习06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习06.png -------------------------------------------------------------------------------- /img/SVN学习07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习07.png -------------------------------------------------------------------------------- /img/SVN学习08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习08.png -------------------------------------------------------------------------------- /img/SVN学习09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习09.png -------------------------------------------------------------------------------- /img/SVN学习10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习10.png -------------------------------------------------------------------------------- /img/SVN学习11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习11.png -------------------------------------------------------------------------------- /img/SVN学习12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习12.png -------------------------------------------------------------------------------- /img/SVN学习13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习13.png -------------------------------------------------------------------------------- /img/SVN学习14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习14.png -------------------------------------------------------------------------------- /img/SVN学习15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习15.png -------------------------------------------------------------------------------- /img/SVN学习16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习16.png -------------------------------------------------------------------------------- /img/SVN学习17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习17.png -------------------------------------------------------------------------------- /img/SVN学习18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习18.png -------------------------------------------------------------------------------- /img/SVN学习19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习19.png -------------------------------------------------------------------------------- /img/SVN学习20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习20.png -------------------------------------------------------------------------------- /img/SVN学习21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习21.png -------------------------------------------------------------------------------- /img/SVN学习22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习22.png -------------------------------------------------------------------------------- /img/SVN学习23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习23.png -------------------------------------------------------------------------------- /img/SVN学习24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习24.png -------------------------------------------------------------------------------- /img/SVN学习25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习25.png -------------------------------------------------------------------------------- /img/SVN学习26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习26.png -------------------------------------------------------------------------------- /img/SVN学习27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习27.png -------------------------------------------------------------------------------- /img/SVN学习28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习28.png -------------------------------------------------------------------------------- /img/SVN学习29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习29.png -------------------------------------------------------------------------------- /img/SVN学习30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习30.png -------------------------------------------------------------------------------- /img/SVN学习31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习31.png -------------------------------------------------------------------------------- /img/SVN学习32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习32.png -------------------------------------------------------------------------------- /img/SVN学习33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习33.png -------------------------------------------------------------------------------- /img/SVN学习34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/SVN学习34.png -------------------------------------------------------------------------------- /img/Thinking.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/Thinking.jpg -------------------------------------------------------------------------------- /img/UIL-DiscCache架构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/UIL-DiscCache架构.png -------------------------------------------------------------------------------- /img/UIL-MemCache架构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/UIL-MemCache架构.png -------------------------------------------------------------------------------- /img/VirtualBox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/VirtualBox.png -------------------------------------------------------------------------------- /img/complete_android_fragment_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/complete_android_fragment_lifecycle.png -------------------------------------------------------------------------------- /img/docker version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/docker version.png -------------------------------------------------------------------------------- /img/git命令大全.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/git命令大全.png -------------------------------------------------------------------------------- /img/jni引用类型.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/jni引用类型.gif -------------------------------------------------------------------------------- /img/jni流程.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/jni流程.PNG -------------------------------------------------------------------------------- /img/map_tuple_bag模式语法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/map_tuple_bag模式语法.png -------------------------------------------------------------------------------- /img/osx boot2docker vm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/osx boot2docker vm.png -------------------------------------------------------------------------------- /img/valgrind01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/valgrind01.png -------------------------------------------------------------------------------- /img/valgrind02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/valgrind02.png -------------------------------------------------------------------------------- /img/valgrind03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/valgrind03.png -------------------------------------------------------------------------------- /img/valgrind04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/valgrind04.png -------------------------------------------------------------------------------- /img/valgrind05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/valgrind05.png -------------------------------------------------------------------------------- /img/数据结构01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/数据结构01.png -------------------------------------------------------------------------------- /img/数据结构02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/img/数据结构02.png -------------------------------------------------------------------------------- /post/ANT学习.md: -------------------------------------------------------------------------------- 1 | #### ANT学习 2 | 3 | ###### 笔记思维导图,[下载][2] 4 | 5 |
![alt text](../img/ANT学习.png "ANT")
6 | 7 | 8 | #### 参考文献 9 | 1. [Ant in Action 第2版 中文版][1] 10 | 11 | 12 | [1]: http://book.douban.com/subject/3112887/ 13 | [2]: ./ANT学习.xmind 14 | -------------------------------------------------------------------------------- /post/ANT学习.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/post/ANT学习.xmind -------------------------------------------------------------------------------- /post/Android Activity Fragment完整生命周期.md: -------------------------------------------------------------------------------- 1 | ## Android Activity Fragment[完整生命周期][1] 2 | 3 |
![Alt text](../img/complete_android_fragment_lifecycle.png "Lifecycle")
4 | 5 | [1]: https://github.com/xxv/android-lifecycle "Android Lifecycle" 6 | -------------------------------------------------------------------------------- /post/Android ContentProvider入门.md: -------------------------------------------------------------------------------- 1 | ## Android ContentProvider入门 2 | 3 | #### 基础 4 | * `ContentProvider`是Android提供的一种数据访问抽象机制,封装实际的数据访问机制,比如`Sqlite3` 5 | * 用于应用间数据共享 6 | * Android提供了哪些`ContentProvider`,[参见][1] 7 | * `ContentProvider`类似于以下业内抽象机制 8 | > 网站,具有唯一URI,在`Manifest`文件中授权,`ContentProvider`注册表 9 | > REST,数据获取和操作,类似于REST的URL风格 10 | > Web服务,通过URI将内部数据公开为服务,输出数据类型由`Android MIME`描述,`Android MIME`类型,包含两部分,类型和子类型 11 | > 存储过程,对`ContentProvider`的URI调用将返回一个`Cursor` 12 | * 每一个`ContentProvider`对应一个`Sqlite3`数据库,其中对应到Android Java层面就是一个Class,每一个`Sqlite3`数据库表存储具体的数据,对应到Android Java层面就是上述Class的内部类(Nested Class),其中每一个内部类对应一个内部接口(Nested Interface),用于描述该表的列结构 13 | > 例如:`android.provider.Contacts`--`android.provider.Contacts.People`--`android.provider.Contacts.PeopleColumns` 14 | 15 | ### 注意 16 | 17 | * `where`子句注意点:两种方式,一种通过`URI`传递where子句,另一种使用`显示`where子句 18 | * 将文件插入到`ContentProvider`中,插入操作返回`uri`,根据`uri`获取输出流,文件写入输出流,系统自动将文件引用存入`_data`列 19 | 20 | ### 实现 21 | 22 | * 第一步,设计`数据库`,`表结构`,编写`元数据`类,其中`元数据`类包括`ContentProvider`的`Authority`信息,数据库表对应内部类`column`描述信息 23 | 24 | ```java 25 | public class BookProviderMetaData { 26 | 27 | public static final String AUTHORITY = "com.androidbook.provider.BookProvider"; 28 | 29 | public static final String DATABASE_NAME = "book.db"; 30 | public static final int DATABASE_VERSION = 1; 31 | 32 | public static final String BOOKS_TABLE_NAME = "books"; 33 | 34 | private BookProviderMetaData() { 35 | } 36 | 37 | public static final class BookTableMetaData implements BaseColumns { 38 | 39 | public static final String TABLE_NAME = "books"; 40 | 41 | public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/books"); 42 | 43 | public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.androidbook.book"; 44 | public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.androidbook.book"; 45 | 46 | public static final String DEFAULT_SORT_ORDER = "modified DESC"; 47 | 48 | // columns 49 | // String 50 | public static final String BOOK_NAME = "name"; 51 | 52 | // String 53 | public static final String BOOK_ISBN = "isbn"; 54 | 55 | // String 56 | public static final String BOOK_AUTHOR = "author"; 57 | 58 | // Integer 59 | public static final String CREATED_DATE = "created"; 60 | 61 | // Integer 62 | public static final String MODIFIED_DATE = "modified"; 63 | 64 | } 65 | 66 | } 67 | ``` 68 | 69 | * 第二步,实现数据库`helper`类 70 | 71 | ```java 72 | private class DatabaseHelper extends SQLiteOpenHelper { 73 | 74 | public DatabaseHelper(Context context) { 75 | super(context, BookProviderMetaData.DATABASE_NAME, null, BookProviderMetaData.DATABASE_VERSION); 76 | } 77 | 78 | @Override 79 | public void onCreate(SQLiteDatabase sqLiteDatabase) { 80 | Log.i(TAG, "inner onCreate() called"); 81 | sqLiteDatabase.execSQL("CREATE TABLE " + BookProviderMetaData.BookTableMetaData.BOOK_NAME + " (" 82 | + BookProviderMetaData.BookTableMetaData._ID + " INTEGER PRIMARY KEY," 83 | + BookProviderMetaData.BookTableMetaData.BOOK_NAME + " TEXT," 84 | + BookProviderMetaData.BookTableMetaData.BOOK_ISBN + " TEXT," 85 | + BookProviderMetaData.BookTableMetaData.BOOK_AUTHOR + " TEXT," 86 | + BookProviderMetaData.BookTableMetaData.CREATED_DATE + " INTEGER," 87 | + BookProviderMetaData.BookTableMetaData.MODIFIED_DATE + " INTEGER" 88 | + ");"); 89 | } 90 | 91 | @Override 92 | public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i2) { 93 | Log.i(TAG, "inner onUpgrade() called"); 94 | Log.w(TAG, "upgrading database from version " + i + " to " + i2 + ", which will destroy all old data"); 95 | sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + BookProviderMetaData.BookTableMetaData.BOOK_NAME); 96 | onCreate(sqLiteDatabase); 97 | } 98 | } 99 | ``` 100 | 101 | * 第三步,实现自己的`contentprovider`类 102 | 103 | ```java 104 | do to list... 105 | 106 | ``` 107 | 108 | ### 参考 109 | 1. [ContentProvider和Uri详解][2] 110 | 111 | [1]: http://developer.android.com/intl/zh-cn/reference/android/provider/package-summary.html "content provider" 112 | [2]: http://www.cnblogs.com/linjiqin/archive/2011/05/28/2061396.html "ContentProvider和Uri详解" -------------------------------------------------------------------------------- /post/Android Methods Limitation.md: -------------------------------------------------------------------------------- 1 | ## Android Methods Limitation 2 | 3 | 1. [How to solve the issue with Dalvik compiler limitation on 64K methods?](http://stackoverflow.com/questions/15436956/how-to-solve-the-issue-with-dalvik-compiler-limitation-on-64k-methods) 4 | 2. [Does the Android ART runtime have the same method limit limitations as Dalvik?](http://stackoverflow.com/questions/21490382/does-the-android-art-runtime-have-the-same-method-limit-limitations-as-dalvik) 5 | 3. [[DEX] Sky’s the limit? No, 65K methods is](https://medium.com/@rotxed/dex-skys-the-limit-no-65k-methods-is-28e6cb40cf71) 6 | 7 | 8 | 文章3小节: 9 | * 原因描述 10 | > You can reference a very large number of methods in a DEX file, but you can only invoke the first 65536, because that’s all the room you have in the method invocation instruction. 11 | > 12 | > […] the limitation is on the number of methods referenced, not the number of methods defined. If your DEX file has only a few methods, but together they call 70,000 different externally-defined methods, you’re going to exceed the limit. 13 | 14 | * 计算 15 | > 计算`APK`每一个`Package`包含的`Method`数量。 16 | > 工具`dex-method-counts`[主页](https://github.com/mihaip/dex-method-counts)。 17 | 18 | * 解决方案 19 | > 对于`google-play-services.jar`函数过多问题,采用`剔除`不需要组件策略。工具`strip_play_services.sh`[主页](https://gist.github.com/dextorer/a32cad7819b7f272239b)。 20 | > 借助`ProGuard`工具,剔除不需要的函数。 21 | > Android的`插件`机制,封装相关逻辑代码到`DEX`文件,APP运行时通过`ClassLoader`动态加载。 22 | > Android的插件机制,又是一个好大的知识点,可以参考[《Android 插件化 动态升级》][4]。 23 | 24 | 25 | [4]: http://www.trinea.cn/android/android-plugin/ 26 | -------------------------------------------------------------------------------- /post/Android SurfaceView入门.md: -------------------------------------------------------------------------------- 1 | ## Android SurfaceView入门 2 | 3 | #### 对比View优势 4 | * 大概最大的优势是,`view`绘制在UI线程,即`onDraw`函数在主线程执行,而`SurfaceView`的绘制操作可以放在自定义的任意子线程 5 | 6 | #### MVC 7 | * 要完成`SurfaceView`的绘制工作,依赖于`SurfaceHolder`相关回调函数,其中`SurfaceView`包含`MyWindow`类型的成员`mWindow` 8 | * 在这里,我们认为`SurfaceView`是`View`,`SurfaceHolder`是`Controller`,`Surface`是`Model` 9 | 10 | #### 实现 11 | * 既然`SurfaceView`的最大优势是允许非UI线程绘制,那么实现过程中肯定要编写相关的子线程绘制操作 12 | * 子线程部分 13 | 14 | ```java 15 | public class MyThread extends Thread { 16 | 17 | private SurfaceHolder mSurfaceHolder; 18 | private boolean mRun; 19 | 20 | public boolean isRun() { 21 | return mRun; 22 | } 23 | 24 | public void setRun(boolean mRun) { 25 | this.mRun = mRun; 26 | } 27 | 28 | public MyThread(SurfaceHolder mSurfaceHolder) { 29 | this.mSurfaceHolder = mSurfaceHolder; 30 | } 31 | 32 | @Override 33 | public void run() { 34 | super.run(); 35 | 36 | Canvas canvas; 37 | 38 | while (mRun) { 39 | 40 | canvas = mSurfaceHolder.lockCanvas(); 41 | 42 | canvas.drawColor(Color.YELLOW); 43 | 44 | Paint paint = new Paint(); 45 | paint.setColor(Color.RED); 46 | paint.setTextSize(20.0f); 47 | Rect rect = new Rect(10, 10, 200, 200); 48 | String text = "hello world!"; 49 | 50 | canvas.drawRect(rect, paint); 51 | canvas.drawText(text, 100, 300, paint); 52 | 53 | 54 | mSurfaceHolder.unlockCanvasAndPost(canvas); 55 | } 56 | } 57 | } 58 | ``` 59 | * SurfaceView部分 60 | 61 | ```java 62 | public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback { 63 | 64 | private MyThread mMyThread; 65 | private SurfaceHolder mSurfaceHolder; 66 | 67 | public MySurfaceView(Context context) { 68 | super(context); 69 | init(); 70 | } 71 | 72 | public MySurfaceView(Context context, AttributeSet attrs) { 73 | super(context, attrs); 74 | init(); 75 | } 76 | 77 | public MySurfaceView(Context context, AttributeSet attrs, int defStyle) { 78 | super(context, attrs, defStyle); 79 | init(); 80 | } 81 | 82 | private void init() { 83 | mSurfaceHolder = getHolder(); 84 | mSurfaceHolder.addCallback(this); 85 | mMyThread = new MyThread(mSurfaceHolder); 86 | } 87 | 88 | @Override 89 | public void surfaceCreated(SurfaceHolder surfaceHolder) { 90 | mMyThread.setRun(true); 91 | mMyThread.start(); 92 | } 93 | 94 | @Override 95 | public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) { 96 | 97 | } 98 | 99 | @Override 100 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 101 | mMyThread.setRun(false); 102 | } 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /post/Android WebView Java与JavaScript互调.md: -------------------------------------------------------------------------------- 1 | ## Android WebView Java与JavaScript互调 2 | 3 | #### Java调用JavaScript方式 4 | * HTML中定义`JavaScript`函数 5 | ```javascript 6 | 12 | ``` 13 | * `Java`层直接调用 14 | ```java 15 | WebSettings webSettings = mWebView.getSettings(); 16 | webSettings.setJavaScriptEnabled(true); 17 | 18 | public void myOnClick(View view) { 19 | mWebView.loadUrl("javascript:add(1, 2)"); 20 | } 21 | ``` 22 | 23 | #### JavaScript返回值 24 | * 一种方法是定义`JavaScriptInterface`类,其中存放结果`HashMap`,然后`JavaScript`层`set`结果,`Java`层`get`结果 25 | ```java 26 | final class MyJavaScriptInterface { 27 | private HashMap mValueMap = new HashMap(); 28 | 29 | @JavascriptInterface 30 | public void set(String key, String value) { 31 | mValueMap.put(key, value); 32 | } 33 | 34 | @JavascriptInterface 35 | public String get(String key) { 36 | return mValueMap.get(key); 37 | } 38 | } 39 | ``` 40 | ```javascript 41 | // Javascript 42 | window.mywebview.set('ret', v); 43 | ``` 44 | ```java 45 | // Java 46 | myJavaScriptInterface.get("ret"); 47 | ``` 48 | * 另一种方法是截获JavaScript`alert`函数 49 | ```java 50 | private class MyWebChromeClient extends WebChromeClient { 51 | @Override 52 | public boolean onJsAlert(WebView view, String url, String message, JsResult result) { 53 | Log.i(TAG, "ret = " + message); 54 | result.confirm(); 55 | return true; 56 | } 57 | } 58 | mWebView.setWebChromeClient(new MyWebChromeClient()); 59 | ``` 60 | ```javascript 61 | // Javascript 62 | 69 | ``` 70 | 71 | #### JavaScript调用Java方式 72 | * `Java`层定义`JavaScriptInterface`类 73 | ```java 74 | WebSettings webSettings = mWebView.getSettings(); 75 | webSettings.setJavaScriptEnabled(true); 76 | mWebView.addJavascriptInterface(new MyJavaScriptInterface(), "mywebview"); 77 | mWebView.loadUrl("file:///android_asset/www/index.html"); 78 | 79 | final class MyJavaScriptInterface { 80 | @JavascriptInterface 81 | public String printHello(String world) { 82 | return "hello " + world + "!"; 83 | } 84 | } 85 | ``` 86 | > 注:Android SDK 4.2+需增加注解@JavascriptInterface 87 | 88 | * `HTML`中编写调用代码 89 | ```html 90 | 91 |

调用java函数

92 | 93 | 94 | ``` 95 | 96 | ####Java返回值 97 | * `java`函数返回值,直接返回给`JavaScript`变量,如上 98 | -------------------------------------------------------------------------------- /post/Android三方库&应用架构.md: -------------------------------------------------------------------------------- 1 | #### Android三方库 2 | 3 | 1. `SnappyDB`,key-value存储,用于构建缓存系统,[主页][1]。 4 | 5 | 2. `RoboSpice`,用于异步网络请求,解决`AsyncTask`、`Loader`设计问题,[主页][2]。 6 | 7 | 3. `Dagger`,Android平台依赖注入,解耦与模块化,[主页][4]。 8 | 9 | 4. `RoboGuice`,依赖注入库,[主页][5]。 10 | 11 | 5. `SmartImageView`,异步加载、本地/内存二级缓存,简单,适合用于三方库学习,[主页][6]。 12 | 13 | 6. `UniversalImageLoader`,异步加载、本地/内存二级缓存,[主页][7],设计的缓存架构推荐好好读读,相关内容参见[文章][8]。 14 | 15 | 7. `Picasso`,`Square`出品,必属精品,图片异步加载缓存,[主页][9]。 16 | 17 | 8. `UrlImageViewHelper`,也是个图片异步加载缓存库,可以学习学习源码,[主页][10]。 18 | 19 | 9. `ACRA`,Android崩溃日志报告,[主页][12]。 20 | 21 | 10. `PhotoView`,图片显示,支持手势缩放,[主页][13]。 22 | 23 | 11. `GestureImageView`,图片显示,支持手势缩放,[主页][14]。 24 | 25 | 12. `Android-Gif-Drawable`,显示gif图,[主页][15]。 26 | 27 | 13. `Android-PullToRefresh`,下拉刷新,个人不推荐该下拉刷新的交互方式,[主页][16]。 28 | 29 | 14. `ActionBar-PullToRefresh`,下拉刷新,个人推荐的交互方式,[主页][17],同时Google支持包也提供官方实现`SwipeRefreshLayout`,[参考][18]。 30 | 31 | 15. `SuperToasts`,Android Toast的增强版本,[主页][19]。 32 | 33 | 16. `android-async-http`,Android异步网络请求库,[主页][20]。 34 | 35 | 17. `greenDAO`,Sqlite3 ORM库,[主页][21]。 36 | 37 | 18. `OrmLite`,ORM库,[主页][22]。 38 | 39 | 19. `EventBus`,组件间事件总线,用于组件解耦,[主页][23]。 40 | 41 | 20. `Otto`,事件总线,`Square`出品,[主页][24]。 42 | 43 | 21. `NineOldAndroids`,Android 3.0增加`属性动画`,该库实现`属性动画`向前版本兼容,[主页][25]。 44 | 45 | 22. `fastjson`,阿里出品Json序列化/反序列化库,[主页][26]。 46 | 47 | 23. `Jackson`,Json序列化/反序列化库,[主页][27]。 48 | 49 | 24. `Gson`,Google出品Json序列化/反序列化库,[主页][28]。 50 | 51 | 25. `DebugLog`,日志信息增强可读性,[主页][29]。 52 | 53 | 26. `ProgressWheel`,圆形进度条,[主页][30]。 54 | 55 | 27. `FloatLabeledEditView`,`EditView`浮动提示,[主页][31]。 56 | 57 | 28. `PagerSlidingTabStrip`,An interactive indicator to navigate between the different pages of a ViewPager,[主页][32]。 58 | 59 | 29. `Android-ViewPagerIndicator`,交互同上,[主页][33]。 60 | 61 | 30. `line-breaking-widget-layout-for-android`,`PredicateLayout`用于实现自动换行的容器控件,[参考][34]。 62 | 63 | 31. `StickyGridHeaders`,分组`GridView`,[主页][35]。 64 | 65 | 32. `SlidingMenu`,侧导航,[主页][36]。 66 | 67 | 33. `ActionBarSherlock`,`ActionBar`兼容包,[主页][37],但是Google出了官方兼容包`ActionBarCompact`(现在已经不推荐使用`ActionBarSherlock`),[参考][38],两者[对比][39]。 68 | 69 | 34. `DrawerLayout`,抽屉导航,Google官方兼容包`DrawerLayout`,[参考][40]。 70 | 71 | 35. `HoloAccent`,holo风格组件颜色快速替换,[主页][41]。 72 | 73 | 36. `commons-codec`,common encoders and decoders,[主页][42]。 74 | 75 | 37. `sanselan`,java实现的图片库,支持图片信息读取,项目中用来获取`EXIF`信息,[主页][43]。 76 | 77 | 38. `Glide`,图像加载缓存,[主页][44]。 78 | 79 | 39. `ButterKnife`,控件依赖注入,避免重复编写`findViewById`等,[主页][45]。 80 | 81 | 40. `Android-Priority-JobQueue`,Android任务调度,支持任务持久化,[主页][46]。 82 | 83 | 41. `MPAndroidChart`,显示质量挺不错的Android图表库,[主页][49]。 84 | 85 | ---- 86 | #### Android应用架构经验 87 | 88 | 1. [健壮且可读的安卓架构设计][3] 89 | 2. [图片异步加载/缓存开源库选型][11] 90 | 3. [做一个懒惰高效的Android程序员][47],[原文链接][48] 91 | 92 | ---- 93 | #### To Do List 94 | - [ ] Dragger学习与应用,对比`RoboGuice`、`ButterKnife` 95 | - [ ] 研究ORM实现原理 96 | 97 | [1]: https://github.com/nhachicha/SnappyDB "SnappyDB" 98 | [2]: https://github.com/stephanenicolas/robospice "RoboSpice" 99 | [3]: http://blog.jobbole.com/66606/ 100 | [4]: http://square.github.io/dagger/ "Dagger" 101 | [5]: https://github.com/roboguice/roboguice "RoboGuice" 102 | [6]: http://loopj.com/android-smart-image-view/ "SmartImageView" 103 | [7]: https://github.com/nostra13/Android-Universal-Image-Loader "UniversalImageLoader" 104 | [8]: ./UIL学习.md "UIL学习笔记" 105 | [9]: http://square.github.io/picasso/ "Picasso" 106 | [10]: https://github.com/koush/UrlImageViewHelper "UrlImageViewHelper" 107 | [11]: http://blog.jobbole.com/66115/ 108 | [12]: https://github.com/ACRA/acra "ACRA" 109 | [13]: https://github.com/chrisbanes/PhotoView "PhotoView" 110 | [14]: https://github.com/jasonpolites/gesture-imageview "GestureImageView" 111 | [15]: https://github.com/koral--/android-gif-drawable "Android-Gif-Drawable" 112 | [16]: https://github.com/chrisbanes/Android-PullToRefresh "Android-PullToRefresh" 113 | [17]: https://github.com/chrisbanes/ActionBar-PullToRefresh "ActionBar-PullToRefresh" 114 | [18]: http://developer.android.com/intl/zh-cn/reference/android/support/v4/widget/SwipeRefreshLayout.html "SwipeRefreshLayout" 115 | [19]: https://github.com/JohnPersano/SuperToasts "SuperToasts" 116 | [20]: http://loopj.com/android-async-http/ "android-async-http" 117 | [21]: http://greendao-orm.com/ "greenDAO" 118 | [22]: http://ormlite.com/ "OrmLite" 119 | [23]: https://github.com/greenrobot/EventBus "EventBus" 120 | [24]: http://square.github.io/otto/ "Otto" 121 | [25]: http://nineoldandroids.com/ "NineOldAndroids" 122 | [26]: https://github.com/alibaba/fastjson "fastjson" 123 | [27]: https://github.com/FasterXML/jackson "Jackson" 124 | [28]: http://code.google.com/p/google-gson/ "Gson" 125 | [29]: https://github.com/MustafaFerhan/DebugLog "DebugLog" 126 | [30]: https://github.com/Todd-Davies/ProgressWheel "ProgressWheel" 127 | [31]: https://github.com/wrapp/floatlabelededittext "Float Labeled EditText" 128 | [32]: https://github.com/astuetz/PagerSlidingTabStrip "PagerSlidingTabStrip" 129 | [33]: https://github.com/JakeWharton/Android-ViewPagerIndicator "Android-ViewPagerIndicator" 130 | [34]: http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android "PredicateLayout" 131 | [35]: http://tonicartos.github.io/StickyGridHeaders/ "StickyGridHeaders" 132 | [36]: https://github.com/jfeinstein10/SlidingMenu "SlidingMenu" 133 | [37]: http://actionbarsherlock.com/ "ActionBarSherlock" 134 | [38]: http://android-developers.blogspot.com/2013/08/actionbarcompat-and-io-2013-app-source.html "ActionBarCompat" 135 | [39]: http://stackoverflow.com/questions/7844517/difference-between-actionbarsherlock-and-actionbar-compatibility "ActionBarSherlock vs ActionBarCompat" 136 | [40]: https://developer.android.com/intl/zh-cn/reference/android/support/v4/widget/DrawerLayout.html "DrawerLayout" 137 | [41]: https://github.com/negusoft/holoaccent "HoloAccent" 138 | [42]: http://commons.apache.org/proper/commons-codec/ "Commons Codec" 139 | [43]: http://commons.apache.org/proper/commons-imaging/ "Commons Imaging" 140 | [44]: https://github.com/bumptech/glide "Glide" 141 | [45]: http://jakewharton.github.io/butterknife/ "ButterKnife" 142 | [46]: https://github.com/path/android-priority-jobqueue "Android-Priority-JobQueue" 143 | [47]: http://blog.jobbole.com/76361/ 144 | [48]: http://www.technotalkative.com/lazy-part-8-wireframemockup-tools/ 145 | [49]: https://github.com/PhilJay/MPAndroidChart "MPAndroidChart" 146 | -------------------------------------------------------------------------------- /post/Android开发-工程结构.md: -------------------------------------------------------------------------------- 1 | ## Android开发-工程结构 2 | 3 | #### Android工程典型结构 4 | 使用`Eclipse`与`Android Studio`开发,Android Project的结构组织有较大出入,本文将分别描述 5 | 6 | ###### 1. Eclipse,默认`Ant`构建工具[工程结构,参考1][1] 7 |
8 | 9 | | 名称 | 描述 | 10 | | :---: | :--- | 11 | | AndroidManifest.xml | 描述应用基本特征,定义应用组件(四大组件`Activity`、`Service`、`BroadcastReceiver`、`ContentProvider`) | 12 | | project.properties | 工程设置,包括编译目标、ProGuard配置等;低版本`ADT`该文件名为`default.properties` | 13 | | local.properties | 设置本地Java SDK路径,用于`Ant`构建 | 14 | | ant.properties | 配置Release发布的签名信息,用于`Ant`构建 | 15 | | build.xml | `Ant`构建文件 | 16 | | /src/ | 包括`Java`源码、`AIDL`源码(.aidl)、`RenderScript`源码(.rs),当然少不了相应的包结构目录 | 17 | | /bin/ | 包含各种编译输出结果(具体编辑过程,后面文章在说),包括最终的`APK`文件 | 18 | | /jni/ | 包含`NDK`工程源文件、编译输出 | 19 | | /gen/ | 包含资源文件编译输出的`R`文件,aidl文件编译输出的`Java`文件,rs文件编译输出的`Java`文件 | 20 | | /libs/ | 第三方jar包、`so`文件,对于so文件需要将指定版本放置于对应子目录(`armeabi`、`armeabi-v7`、`x86`、`mips`,安装App时筛选正确版本的so) | 21 | | /assets/ | 可以放置任何文件,简单打包进apk文件,所以在R.java中不存在id,通过`AssetManager`访问 | 22 | | /res/ | 包含各种子路径,各个子路径存放对应资源,各个子路径(稍后详述,子目录名称规则) | 23 | 24 |
25 | 26 | ###### 2. Android Studio,默认`Gradle`构建工具[Gradle Android Plugin,参考2][2] 27 | > Android Studio新建Android工程,存在`Project`与`Module`的概念,一个Android应用对应一个`Module`(和`Visual Studio`的`WorkSpace`、`Project`一个意思); 28 | > Gradle和Maven一样,强调约定优于配置,所以下面给出的均是约定的结构 29 | 30 | 对于一个`Module`结构如下: 31 | 32 | | 名称 | 描述 | 33 | | :---: | :--- | 34 | | /src/ | 包含两种`source sets`,分别是`src/main/`、`src/androidTest/` | 35 | | /src/main/AndroidManifest.xml | 参见上表`AndroidManifest.xml`描述 | 36 | | /src/main/ic_launcher-web.png | 512*512应用图标,用于`Google Play`展示 | 37 | | /src/main/java/ | 包含工程`Java`源码 | 38 | | /src/main/aidl/ | 包含工程`AIDL`源码 | 39 | | /src/main/rs/ | 包含工程`RenderScript`源码 | 40 | | /src/main/jni/ | 包含`NDK`工程源码 | 41 | | /src/main/assets/ | 包含工程资源,参见上表`/assets/`描述 | 42 | | /src/main/res/ | 包含工程资源,参见上表`/res/`描述 | 43 | | /libs/ | 第三方jar包 | 44 | | /build/ | 包含唯一子目录`/build/generated/`,存放编译中间结果(R文件,rs编译输出,aidl编译输出等) | 45 | | /build.gradle | 该`Module`构建脚本 | 46 | 47 | 注意:在Android Studio中引入`.so`本地库,由于一开始`gradle`的`android`插件在这一块支持的不好,导致有三种方式[方式一二][13]/[方式三][14],我们推荐第三种方式:新建目录`/src/main/jniLibs`,并把相关`.so`本地库拷贝至该目录,不需要修改`build.gradle`。 48 | 49 |
![Alt text](../img/Android规范-结构03.png "引入so文件")
50 | 51 | 52 | 对于`Project`结构如下: 53 | 54 | | 名称 | 描述 | 55 | | :---: | :--- | 56 | | /libraries/ | 可以放置依赖的本地工程,可与`Eclipse`依赖本地Library方式对比(个人觉得Eclipse的方式要方便) | 57 | | /XXXX/ | 上文所描述的`Module`文件夹 | 58 | | /local.properties | 参见上表`local.properties`描述,用于`Gradle`构建 | 59 | | /build.gradle | 该`Project`构建脚本 | 60 | | /settings.gradle | 配置需要构建的模块列表,与`/build.gradle`协作完成多模块构建(和`Maven`多模块构建方式基本一致) | 61 | 62 |
63 | 64 | ###### 3. 到底是选择Eclipse开发还是选择Android Studio开发? 65 | * Android Studio是Google基于`IntelliJ IDEA`专门针对Android推出,智能程度甩Eclipse几条街 66 | * 依赖管理承接Maven,如果你用Eclipse,一个一个下载jar包,jar包版本冲突解决,都太麻烦了 67 | * Gradle构建脚本灵活容易,多平台发布 68 | * Android Studio多分辨率渲染 69 | 70 |
![Alt text](../img/Android规范-结构02.png "渲染对比")
71 | 72 | * 所以开发Android强烈推荐Android Studio(Beta版已出) 73 | 74 | ###### 4. /res/目录详解[Resource Types,参考3][3] 75 | > 如下目录都不能再新建子目录,否则编译失败(参见`aapt`) 76 | 77 | | 名称 | 描述 | 78 | | :---: | :--- | 79 | | res/anim/ | 放置补间动画、插值器、布局动画媒介等XML文件,动画部分后文再说[参考4][4] | 80 | | res/animator/ | 放置属性动画XML文件,动画部分后文再说[参考4][4] | 81 | | res/color/ | 放置颜色状态XML文件[参考5][5] | 82 | | res/drawable/ | 放置PNG、JPEG、GIF文件,9-Patch、Shape、[逐帧动画][10]等XML文件[**重点!**参考6][6]| 83 | | res/layout/ | 放置布局XML文件[参考7][7] | 84 | | res/menu/ | 放置菜单定义XML文件[参考8][8] | 85 | | res/raw/ | 可放置任意文件,但是会被`aapt`编译,所以可以使用R.raw.XXXX方式引用资源,推荐只放置一些多媒体数据 | 86 | | res/xml/ | 放置用于应用配置的XML文件 | 87 | | res/values/ | 放置涉及颜色值、尺寸值、属性定义值、字符串、样式等XML文件 | 88 | 89 | 既然上表说到`res/drawable/`是重点,那么现在具体说一说。 90 | * 一是`drawable`的种类很多,显示的图片文件(.png、.jpg、.gif、.9.png),还有一堆用XML描述的文件 91 | 92 | > 用小Bitmap文件构建的XML Bitmap,在XML中指定小Bitmap文件的平铺方式,完成有规律图片的制作; 93 | 94 | > 用.9文件构建的XML .9,用于控制.9图片拉伸抖动; 95 | 96 | > 用一组drawable组合成的层叠drawable(Layer List),典型应用自定义ProgressBar(进度显示,其实就是通过曾叠drawable); 97 | 98 | > 用一组drawable组合成的状态drawable(State List),典型应用自定义Button的点击状态和非点击状态; 99 | 100 | > 用一组drawable组合成的等级drawable(Level List),对于ImageView组件,在不同场景需要显示不同图片,可以使用这种drawable(不过i哦我用的不多); 101 | 102 | > 用两个drawable组合成的过度drawable(Transition Drawable),用于设置淡出淡入过渡动画; 103 | 104 | > 用XML描述的Shape,可以指定Shape的形状、圆角、颜色、边框样式颜色、渐变,**对于能够用Shape写出来的drawable就坚决不用切图!** 105 | 106 | > 用现有drawable制作拉伸drawable(Scale Drawable); 107 | 108 | > 用现有drawable制作水平、竖直方向裁剪drawable(Clip Drawable),对于ImageView组件,当需要显示图片的部分区域时比较有用; 109 | 110 | > Inset Drawable,自己去搜索吧... 111 | 112 | * 二是`res/drawable/`文件夹可能包含各种稀奇古怪的后缀,比如`res/drawable-hdpi/`,这就要说到Android适配的一个点了 113 | 114 | > 即对于`res/drawable-mdpi/`与`res/drawable-hdpi/`中同名的一张.png资源,**Android有一套查找最适合资源的策略**[参考9][9]; 115 | 116 |
![Alt text](../img/Android规范-结构01.png "how Android finds the best-matching resource")
117 | 118 | > 大致意思就是说,Android系统先根据系统配置直接剔除一些冲突的`后缀文件夹`; 119 | 120 | > 然后根据[限定匹配表][11],从上往下匹配,找到一项匹配项时,立刻剔除一些冲突的`后缀文件夹`;再重复这个过程; 121 | 122 | > 最后找到适合的`后缀文件夹`,取出指定的.png资源; 123 | 124 | > 但是需要注意的是,在根据限定匹配表匹配的时候,如果匹配了`屏幕密度`,则直接跳出,不再进行后续匹配工作; 125 | 126 | * 三是`drawable`文件夹的后缀怎么取?感觉可以按`限定匹配表`任意组合,可实际情况是,实际情况是**只按`屏幕密度`来取** 127 | 128 | > 对于XML文件,直接放置于`res/drawable/`; 129 | 130 | > 对于图片文件(.png、.jpg、.gif、.9.png),放置于`res/drawable-ldpi/`、`res/drawable-mdpi/`、`res/drawable-hdpi/`、`res/drawable-xhdpi/`、`res/drawable-xxhdpi/`、`res/drawable-xxxhdpi/`,[参考12][12]; 131 | 132 | 133 | --- 134 | #### 参考文献 135 | 1. [Managing Projects][1] 136 | 2. [Android Tools Project Site][2] 137 | 3. [Resource Types][3] 138 | 4. [Animation Resources][4] 139 | 5. [Color State List Resource][5] 140 | 6. [Drawable Resources][6] 141 | 7. [Layout Resource][7] 142 | 8. [Menu Resource][8] 143 | 9. [How Android Finds the Best-matching Resource][9] 144 | 10. [AnimationDrawable][10] 145 | 11. [providing-resources.html#table2][11] 146 | 12. [create-bitmaps][12] 147 | 13. [在Android Studio 中加入jar 和.so 文件][13] 148 | 14. [有关Android studio工具添加高德地图API的so库文件使用说明][14] 149 | 150 | 151 | [1]: http://developer.android.com/intl/zh-cn/tools/projects/index.html 152 | [2]: http://tools.android.com/tech-docs/new-build-system/user-guide 153 | [3]: http://developer.android.com/intl/zh-cn/guide/topics/resources/available-resources.html 154 | [4]: http://developer.android.com/intl/zh-cn/guide/topics/resources/animation-resource.html 155 | [5]: http://developer.android.com/intl/zh-cn/guide/topics/resources/color-list-resource.html 156 | [6]: http://developer.android.com/intl/zh-cn/guide/topics/resources/drawable-resource.html 157 | [7]: http://developer.android.com/intl/zh-cn/guide/topics/resources/layout-resource.html 158 | [8]: http://developer.android.com/intl/zh-cn/guide/topics/resources/menu-resource.html 159 | [9]: http://developer.android.com/intl/zh-cn/guide/topics/resources/providing-resources.html#BestMatch 160 | [10]: http://developer.android.com/intl/zh-cn/reference/android/graphics/drawable/AnimationDrawable.html 161 | [11]: http://developer.android.com/intl/zh-cn/guide/topics/resources/providing-resources.html#table2 162 | [12]: http://developer.android.com/intl/zh-cn/training/basics/supporting-devices/screens.html#create-bitmaps 163 | [13]: http://my.oschina.net/zhibuji/blog/147441 164 | [14]: http://bbs.amap.com/thread-16638-1-1.html 165 | -------------------------------------------------------------------------------- /post/Android开发-应用优化.md: -------------------------------------------------------------------------------- 1 | ## Android开发-应用优化 2 | 3 | Android应用优化,涉及的范围挺广的,这里推荐一本挺薄的书[《Android应用性能优化》][1],网上有电子版。 4 | 5 | 我想就自己的开发经历,讲讲优化,对自己也算是个复习梳理。 6 | 7 | 8 | Android程序优化,应该是涉及三个层次的:其一,Java语言层面通用的优化;其二,Android开发层面通用的优化;其三,应用内热点的优化。 9 | 10 | #### Java语言通用优化 11 | ###### 1. 容器按需选择 12 | 13 | Java和Android定义了许多容器,需要你有一定的经验去选择最适合当前场景的容器。 14 | 15 | 比如并发场景下,你需要一个`HashMap`,你有两种选择`Collections.synchronizedMap`,`ConcurrentHashMap`,但显然后一种效率要高。 16 | 17 | 再比如,你要自己构建一个LRU缓存,显然用Android提供的`LruCache`更好。 18 | 19 | 再比如,你需要实现`HashMap hashMap = new HashMap();`,应该考虑使用Android提供的`SparseArray sparseArray = new SparseArray();`。 20 | 21 | 这里罗列一下Android提供的几个容器: 22 | 23 | `Pair` 24 | `LruCache` 25 | `SparseArray` 26 | `SparseIntArray` 27 | `SparseLongArray` 28 | `SparseBooleanArray` 29 | `LongSparseArray` 30 | `ArrayMap` 31 | 32 | ###### 2. 33 | 34 | 35 | 36 | --- 37 | ## 参考文献 38 | 1. [《Android应用性能优化》][1] 39 | 2. [Android性能调优][2] 40 | 41 | [1]: http://book.douban.com/subject/19976838/ 42 | [2]: http://www.trinea.cn/android/android-performance-demo/ -------------------------------------------------------------------------------- /post/Android开发-开发工具.md: -------------------------------------------------------------------------------- 1 | ## Android开发-开发工具 2 | 3 | #### Android开发的那些工具 4 | 本文将分三块描述,其一是Android `SDK`中提供的一些小工具;其二是用于开发Android应用的`Android Studio`+`Gradle`集成开发工具与构建工具;其三是一些很棒的`第三方`工具。 5 |
6 | 7 | #### 1. Android SDK中的那些[小工具][1] 8 | 伴随Android SDK提供的工具分为两大块,一是`SDK Tools`,与平台无关的工具;二是`Platform Tools`,平台相关的工具。 9 | 10 | 这些工具在SDK中具体的存放路径很`飘逸`,一方面不同版本的SDK同一个工具存放的位置可能不一致(现在新版本比老版本好一点了);另一方面`SDK Tools`与`Platform Tools`工具分散在多个文件夹下,文件名命名也容易让人误解,比如明明是`SDK Tools`工具却放置在`Platform Tools`文件夹下。 11 | 12 | 具体`飘逸`的路径如下表: 13 | 14 | | 路径 | 描述 | 15 | | :----: | :---- | 16 | | /SDK/tools/ | 一些平台无关的工具,如`ddms`、`emulator`等 | 17 | | /SDK/platform-tools/ | 一些平台无关与一些平台相关的工具,如`adb`、`sqlite3`等 | 18 | | /SDK/build-tools/19.0.3/ | 平台相关(API 19)的工具,如`aapt`、`aidl`等 | 19 | 20 | 下面分别介绍下。 21 | 22 | ##### Platform Tools 23 | 平台相关工具,顾名思义这些工具是为了支持平台新特性,同时向下兼容。 24 | 25 | * 最常用的平台相关工具:`ADB`,[Android Dedug Bridge][2] 26 | 27 | 常见用法如下: 28 | 29 | | 命令 | 描述 | 30 | | :----: | :---- | 31 | | adb devices | 列出当前可链接设备 | 32 | | adb shell | 启动当前设备/模拟器`shell`环境 | 33 | | adb -s 名称 shell | 启动指定设备`shell`环境 | 34 | | adb install apk文件全路径名 | 向当前设备/模拟器`安装`应用 | 35 | | adb uninstall 应用包名 | 从当前设备/模拟器`卸载`应用 | 36 | | adb pull 源文件 目标文件 | 从当前设备/模拟器拷贝文件到本地 | 37 | | adb push 源文件 目标文件 | 从本地向当前设备/模拟器拷贝文件 | 38 | 39 | 注意:使用`adb shell`进入设备`shell`环境,但是该shell环境很多工具都没有,比如`grep`,因为Android删除了部分Linux标准工具。如果想使用这些工具,可以在越狱的手机上安装[`Busybox`][3]。 40 | 41 | * `dumpsys` 42 | 43 | 同时`shell`环境下,有一个有用的命令`dumpsys`,用来显示当前系统/应用信息。 44 | 45 | | 命令 | 描述 | 46 | | :----: | :---- | 47 | | meminfo | 显示内存信息 | 48 | | cpuinfo | 显示CPU信息 | 49 | | account | 显示accounts信息 | 50 | | activity | 显示所有的activities的信息 | 51 | | window | 显示键盘,窗口和它们的关系 | 52 | | wifi | 显示wifi信息 | 53 | 54 | 比如想显示`快站管理App`的当前`Activity`信息,可以使用`adb shell dumpsys activity com.sohu.zhan.zhanmanager`命令。 55 | 56 | * `dx` 57 | 58 | 我们知道Android系统的java虚拟机是`Dalvik`(Android L引入的ART以后再说),该虚拟机字节码为`DEX`格式。我们在PC上编译java源码产生的class文件,最后就要经过`dx`工具转换为`Dalvik`字节码,这一步还有一些优化,比如合并常量池、消除冗余信息等。 59 | 60 | * `dexdump` 61 | 62 | 和`dx`相对,反编译用。 63 | 64 | * `aapt` 65 | 66 | Android工程中有相当多的资源文件,包括`AndroidMaifest.xml`、`/res/`目录、`/asserts/`目录,全是由`aapt`工具进行验证、编译、压缩,并生成`R.java`文件。 67 | 68 | * `aidl` 69 | 70 | 编译`*.aidl`文件为`*.java`文件。 71 | 72 | * `llvm-rs-cc` 73 | 74 | 编译`*.rs`文件为`*.java`文件与`*.bc`文件。[参考4][4] 75 | 76 | ##### SDK Tools 77 | 非平台相关工具,主要包括一些sdk管理、虚拟机管理、调试、性能优化等工具,下面主要介绍会用到的一些。 78 | 79 | * [`zipalign`][5] 80 | 81 | 对签名后的apk文件做对齐处理,将apk包中的资源文件距离文件起始偏移为4字节的整数倍。和C语言结构体对其一个意思,加快访问速度。 82 | 83 | * `android` 84 | 85 | 管理SDK。 86 | 87 | 比较重要的几个命令: 88 | android list target 列出已经安装的SDK; 89 | android list avd 列出已经创建的虚拟机; 90 | 91 | * `emulator` 92 | 93 | 管理虚拟机。 94 | 95 | 比较重要的几个命令: 96 | emulator -avd 启动模拟器; 97 | 98 | * `ddms`(Dalvik Debug Monitor Server) 99 | 100 | 用于应用的Debug,包括日志输出,整合全部的分析工具等。 101 | 102 | * `draw9patch` 103 | 104 | 用于`.9图片`编辑。**重点**,可以看看[这里][6]。 105 | 106 | 107 | * `hierarchyviewer` 108 | 109 | 用于布局分析与优化。**重点**,网上一大堆文章,这里是[官方文档][7]。 110 | 111 | * `hprof-conv` 112 | 113 | 在分析应用内存使用情况,比如内存泄露,OOM等,可以获取`heap dump`内存文件用`MAT(Memory Analyzer Tool)`进行离线分析。由于Android虚拟机`dalvik`的特殊性,产生的内存文件hprof与标准的java hprof文件格式不一致,那么就需要`hprof-conv`工具转换,才可以用`MAT`工具进行分析。**重点**,可以看看[这里][8],这篇文章直接在`DDMS`中进行操作,相关转换工作已经被隐藏了。 114 | 115 | * `traceview` 116 | 117 | Android应用运行数据分析工具,用于定位程序执行的热点,指导后续程序优化用。**重点**,可以看看[这里][9]。 118 | 119 | * `dmtracedump` 120 | 121 | 用于生成Android应用函数调用关系图。我在项目中未使用过,感觉`traceview`足够了。 122 | 123 |
![Alt text](../img/Android规范-工具01.png "调用关系")
124 | 125 | * `systrace` 126 | 127 | Android 4.1加入的新的性能分析工具,存放位置`/sdk/platform-tools/systrace`。我在项目中还没有具体使用过,这里是[官方文档][10]。 128 | 129 |
![Alt text](../img/Android规范-工具02.png "应用性能分析")
130 | 131 | **注:上文几个标记为`重点`的优化工具需要重点留意。** 132 |
133 | 134 | #### 2. 简单说说`Gradle`构建工具 135 | Gradle有多好,打算写一篇相关的文章,现在给一篇[参考文章][11]吧! 136 |
137 | 138 | #### 3. 三方工具 139 | 写在前面,市面上的手机阉割了很多有用的命令行工具,比如`sqlite3`、`tcpdump`,或者想安装[`busybox`][13]。 140 | 141 | 简单的安装步骤如下: 142 | 1. 获取命令行工具,要么从网上下载,要么从模拟器中`pull`; 143 | 2. `push`到手机SD卡; 144 | 3. 重新挂载`/system`目录为可读写(root模式); 145 | > mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system 146 | 4. 手机SD卡到`/system/bin`或`/system/xbin`目录; 147 | > cat /mnt/sdcard/sqlite3 > /system/xbin/sqlite3 148 | 5. 命令行工具权限修改; 149 | > chmod 755 sqlite3 150 | 6. OK~ 151 | 152 |
153 | 154 | 155 | --- 156 | #### 参考文献 157 | 1. [Tools Help][1] 158 | 2. [Android Debug Bridge][2] 159 | 3. [为Android安装BusyBox][3] 160 | 4. [Android RenderScript on LLVM][4] 161 | 5. [zipalign][5] 162 | 6. [App自适应draw9patch不失真背景][6] 163 | 7. [Using Hierarchy Viewer][7] 164 | 8. [Android应用程序的内存分析][8] 165 | 9. [Android系统性能调优工具介绍][9] 166 | 10. [Analyzing Display and Performance][10] 167 | 11. [Announcing .. Gradle Tutorial Series][11] 168 | 12. [Android的一些常用命令提示符(cmd)指令][12] 169 | 13. [为Android安装BusyBox —— 完整的bash shell][13] 170 | 171 | [1]: http://developer.android.com/intl/zh-cn/tools/help/index.html#tools-sdk 172 | [2]: http://developer.android.com/intl/zh-cn/tools/help/adb.html 173 | [3]: http://www.cnblogs.com/xiaowenji/archive/2011/03/12/1982309.html 174 | [4]: https://events.linuxfoundation.org/slides/2011/lfcs/lfcs2011_llvm_liao.pdf 175 | [5]: http://developer.android.com/intl/zh-cn/tools/help/zipalign.html 176 | [6]: http://www.cnblogs.com/qianxudetianxia/archive/2011/04/17/2017591.html 177 | [7]: http://developer.android.com/intl/zh-cn/tools/debugging/debugging-ui.html#HierarchyViewer 178 | [8]: http://www.cnblogs.com/wisekingokok/archive/2011/11/30/2245790.html 179 | [9]: http://my.oschina.net/innost/blog/135174#OSC_h3_11 180 | [10]: https://developer.android.com/intl/zh-cn/tools/debugging/systrace.html 181 | [11]: http://rominirani.com/2014/07/28/gradle-tutorial-series-an-overview/ 182 | [12]: http://www.cnblogs.com/allenzheng/archive/2012/11/11/2765197.html 183 | [13]: http://www.cnblogs.com/xiaowenji/archive/2011/03/12/1982309.html 184 | -------------------------------------------------------------------------------- /post/Android开发规范系列-大纲.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/post/Android开发规范系列-大纲.xmind -------------------------------------------------------------------------------- /post/Android开发规范系列.md: -------------------------------------------------------------------------------- 1 | ## Android开发规范系列 2 | 3 | 编写这个系列,一方面,`浩哥`让我总结些许Android开发规范,目的逐步规范组内Android产品开发;另一方面,于己更是一番复习总结,两全其美。 4 | 5 | 初步决定,这个系列将包含如下部分: 6 | 7 |
![Alt text](../img/Android规范-目录01.png "大纲")
8 | 9 | 系列内容主要参考自网络,我会尽可能详尽保留参考文献。 10 | 11 | 第一篇:[《Android开发-工程结构》](./Android开发-工程结构.md "工程结构") 12 | 第二篇:[《Android开发-开发工具》](./Android开发-开发工具.md "开发工具") 13 | 14 | 15 | --- 16 | #### TODO LIST 17 | - [x] Android开发-工程结构 18 | - [ ] Android开发-开发工具 19 | - [ ] Android开发-代码规范 20 | - [ ] Android开发-资源规范 21 | - [ ] Android开发-开发日志 22 | - [ ] Android开发-应用发布 23 | - [ ] Android开发-经验总结 24 | - [ ] Android开发-应用优化 25 | -------------------------------------------------------------------------------- /post/Android桌面悬浮窗.md: -------------------------------------------------------------------------------- 1 | ## Android桌面悬浮窗 2 | 3 | #### 基本原理 4 | 1. `WindowManager`三个方法(实现接口`ViewManager`):`addView`、`removeView`、`updateViewLayout` 5 | 2. 涉及`addView`、`updateViewLayout`的布局信息,由`WindowManager.LayoutParams`提供,关键参数 6 | 7 | ``` 8 | type:TYPE_PHONE // 权限:android.permission.SYSTEM_ALERT_WINDOW 9 | flags:FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL 10 | gravity:Gravity.LEFT | Gravity.TOP 11 | width 12 | heigth 13 | x 14 | y 15 | ... 16 | ``` 17 | 18 | 3. 悬浮窗显示逻辑 19 | 20 | ``` 21 | 当前界面是桌面,且没有悬浮窗显示,则创建悬浮窗 22 | 当前界面是桌面,且有悬浮窗显示,则更新悬浮窗信息 23 | 当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗 24 | ``` 25 | 26 | 其中判断当前界面是否是桌面,代码如下 27 | 28 | ```java 29 | private boolean isHome() { 30 | ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 31 | // Android3.0+需要权限:android.permission.GET_TASKS 32 | List rti = mActivityManager.getRunningTasks(1); 33 | return getHomes().contains(rti.get(0).topActivity.getPackageName()); 34 | } 35 | 36 | private List getHomes() { 37 | List names = new ArrayList(); 38 | PackageManager packageManager = getPackageManager(); 39 | Intent intent = new Intent(Intent.ACTION_MAIN); 40 | intent.addCategory(Intent.CATEGORY_HOME); 41 | List resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); 42 | for (ResolveInfo ri : resolveInfo) { 43 | names.add(ri.activityInfo.packageName); 44 | } 45 | return names; 46 | } 47 | ``` 48 | 49 | 判断有无悬浮窗,逻辑如下 50 | 51 | ```java 52 | public static boolean isWindowShowing() { 53 | return smallWindow != null || bigWindow != null; 54 | } 55 | ``` 56 | 57 | 58 | #### 参考文章 59 | 1. [Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果][1] 60 | 2. [Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现][2] 61 | 62 | [1]: http://blog.csdn.net/guolin_blog/article/details/8689140 63 | [2]: http://blog.csdn.net/guolin_blog/article/details/16919859 -------------------------------------------------------------------------------- /post/Android滤镜开发一二.md: -------------------------------------------------------------------------------- 1 | ## Android滤镜开发一二 2 | 3 | #### 滤镜开发步骤 4 | 1. 颜色映射 5 | 2. 叠加材质 6 | 3. 应用相框 7 | 4. 反复 8 | 9 | #### 给我的滤镜英文名称取一个好听的名字 10 | 1. Instafix`色彩校正` 11 | 2. Ansel`单色` 12 | 3. Testino`强烈` 13 | 4. XPro`LOMO` 14 | 5. Retro`复古` 15 | 6. Black & White`黑白` 16 | 7. Sepia`回忆` 17 | 8. Cyano`深海` 18 | 9. Georgia`甜美` 19 | 10. Sahara`暖日` 20 | 10. HDR`反转` 21 | 22 | #### 滤镜算法搜集 23 | 1. Instafix`色彩校正` 24 | 25 | * [Gamma校正][4] 26 | > 步骤:`归一化`,`预补偿`,`反归一化` 27 | 28 | ```c 29 | // 脚本变量,java层设置 30 | rs_allocation gammaSource; 31 | uint32_t gammaHeight; 32 | uint32_t gammaWidth; 33 | // 脚本内部变量,gamma校正待查表 34 | static int32_t redLut[256]; 35 | static int32_t greenLut[256]; 36 | static int32_t blueLut[256]; 37 | 38 | void init() { 39 | for (uint32_t i = 0; i < 256; ++i) { 40 | redLut[i] = -1; 41 | greenLut[i] = -1; 42 | blueLut[i] = -1; 43 | } 44 | } 45 | 46 | void createGammaLut() { 47 | float redAverage = 0.0f, greenAverage = 0.0f, blueAverage = 0.0f; 48 | uint32_t n = 1; 49 | for (uint32_t i = 0; i < gammaHeight; ++i) { 50 | for (uint32_t j = 0; j < gammaWidth; ++j) { 51 | uchar4 in = rsGetElementAt_uchar4(gammaSource, j, i); 52 | redAverage = ((n - 1) * redAverage + in.r) / n; 53 | greenAverage = ((n - 1) * greenAverage + in.g) / n; 54 | blueAverage = ((n - 1) * blueAverage + in.b) / n; 55 | ++n; 56 | } 57 | } 58 | 59 | float gammaRed = log(128.0f / 255) / log(redAverage / 255); 60 | float gammaGreen = log(128.0f / 255) / log(greenAverage / 255); 61 | float gammaBlue = log(128.0f / 255) / log(blueAverage / 255); 62 | for (uint32_t i = 0; i < gammaHeight; ++i) { 63 | for (uint32_t j = 0; j < gammaWidth; ++j) { 64 | uchar4 in = rsGetElementAt_uchar4(gammaSource, j, i); 65 | if (redLut[in.r] == -1) { 66 | redLut[in.r] = rsClamp(255.0f * pow((in.r / 255.0f), gammaRed), 0, 255); 67 | } 68 | if (greenLut[in.g] == -1) { 69 | greenLut[in.g] = rsClamp(255.0f * pow((in.g / 255.0f), gammaGreen), 0, 255); 70 | } 71 | if (blueLut[in.b] == -1) { 72 | blueLut[in.b] = rsClamp(255.0f * pow((in.b / 255.0f), gammaBlue), 0, 255); 73 | } 74 | } 75 | } 76 | } 77 | 78 | void gammaFilterKernel(const uchar4 *in, uchar4 *out) { 79 | out->r = redLut[in->r]; 80 | out->g = greenLut[in->g]; 81 | out->b = blueLut[in->b]; 82 | } 83 | ``` 84 | 85 | * [颜色均衡][5] 86 | > 步骤:`统计直方图(归一化)`,`累计直方图`,`计算新值` 87 | 88 | ```c 89 | rs_allocation histogramSource; 90 | uint32_t histogramHeight; 91 | uint32_t histogramWidth; 92 | static uint32_t histogram[3][256]; 93 | 94 | void createHistogramLut() { 95 | for (uint32_t i = 0; i < histogramHeight; ++i) { 96 | for (uint32_t j = 0; j < histogramWidth; ++j) { 97 | uchar4 in = rsGetElementAt_uchar4(histogramSource, j, i); 98 | ++histogram[0][in.r]; 99 | ++histogram[1][in.g]; 100 | ++histogram[2][in.b]; 101 | } 102 | } 103 | 104 | uint32_t count = histogramWidth * histogramHeight; 105 | for (uint32_t channel = 0; channel < 3; ++channel) { 106 | uint32_t low = 0; 107 | uint32_t high = 255; 108 | float percentage, nextPercentage; 109 | 110 | nextPercentage = (float) histogram[channel][0] / count; 111 | for (uint32_t i = 0; i < 255; ++i) { 112 | percentage = nextPercentage; 113 | nextPercentage += (float) histogram[channel][i + 1] / count; 114 | if (fdim(percentage, nextPercentage) > 0.006) { 115 | low = i; 116 | break; 117 | } 118 | } 119 | 120 | nextPercentage = (float) histogram[channel][255] / count; 121 | for (uint32_t i = 255; i > 0; --i) { 122 | percentage = nextPercentage; 123 | nextPercentage += (float) histogram[channel][i - 1] / count; 124 | if (fdim(percentage, nextPercentage) > 0.006) { 125 | high = i; 126 | break; 127 | } 128 | } 129 | 130 | for (uint32_t i = 0; i < low; ++i) { 131 | histogram[channel][i] = 0; 132 | } 133 | for (uint32_t i = 255; i > high; --i) { 134 | histogram[channel][i] = 255; 135 | } 136 | 137 | float base = 0; 138 | float mult = 255.0f / (high - low); 139 | for (uint32_t i = low; i <= high; i++) { 140 | histogram[channel][i] = (int) base; 141 | base += mult; 142 | } 143 | } 144 | } 145 | 146 | void histogramFilterKernel(const uchar4 *in, uchar4 *out) { 147 | out->r = histogram[0][in->r]; 148 | out->g = histogram[1][in->g]; 149 | out->b = histogram[2][in->b]; 150 | } 151 | ``` 152 | 153 | 2. Sepia`回忆` 154 | 155 | * [灰度变换][6] 156 | > 魔法数字:`0.21R + 0.72G + 0.07B` 157 | > 查表 158 | 159 | ````c 160 | const float3 luminosity = {0.21f, 0.72f, 0.07f}; 161 | const uint8_t sepiaRedLut[256] = {24, 24, 25, 26, 27, 28, 29, 30, 30, 30, 31, 32, 33, 34, 35, 162 | 36, 37, 37, 38, 38, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 47, 48, 49, 50, 50, 51, 52, 53, 163 | 54, 55, 56, 57, 57, 58, 58, 59, 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 71, 72, 164 | 72, 73, 74, 75, 76, 77, 78, 78, 79, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 89, 89, 90, 91, 165 | 92, 93, 93, 94, 95, 96, 97, 97, 98, 99, 100, 101, 102, 102, 103, 104, 105, 106, 107, 108, 166 | 109, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 118, 119, 120, 121, 122, 123, 124, 167 | 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 168 | 142, 143, 144, 145, 146, 146, 147, 148, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 169 | 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 170 | 176, 177, 178, 178, 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 193, 171 | 194, 195, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 172 | 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 173 | 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 174 | 247, 248, 249, 250, 251, 252, 253, 255}; 175 | const uint8_t sepiaGreenLut[256] = {16, 16, 16, 17, 18, 18, 19, 20, 20, 20, 21, 22, 22, 23, 176 | 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 31, 32, 33, 33, 34, 35, 36, 36, 36, 37, 177 | 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 47, 48, 48, 49, 50, 51, 51, 52, 53, 54, 54, 178 | 55, 55, 56, 57, 58, 59, 60, 61, 61, 61, 62, 63, 64, 65, 66, 67, 67, 68, 68, 69, 70, 72, 73, 179 | 74, 75, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 86, 87, 88, 90, 90, 91, 92, 93, 180 | 94, 95, 96, 97, 97, 98, 99, 100, 101, 103, 104, 105, 106, 106, 107, 108, 109, 110, 111, 112, 181 | 113, 114, 115, 116, 117, 118, 119, 120, 122, 123, 123, 124, 125, 127, 128, 129, 130, 131, 182 | 132, 132, 134, 135, 136, 137, 138, 139, 141, 141, 142, 144, 145, 146, 147, 148, 149, 150, 183 | 151, 152, 154, 155, 156, 157, 158, 160, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 184 | 171, 173, 174, 175, 176, 177, 178, 179, 180, 182, 183, 184, 185, 187, 188, 189, 189, 191, 185 | 192, 193, 194, 196, 197, 198, 198, 200, 201, 202, 203, 205, 206, 207, 208, 209, 210, 211, 186 | 212, 213, 215, 216, 217, 218, 219, 220, 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 187 | 232, 233, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 188 | 251, 252, 253, 255}; 189 | const uint8_t sepiaBlueLut[256] = {5, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 190 | 11, 11, 12, 12, 13, 13, 14, 14, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 20, 20, 21, 191 | 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 28, 28, 29, 29, 30, 31, 31, 31, 32, 33, 33, 34, 192 | 35, 36, 37, 38, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 47, 48, 49, 50, 51, 52, 53, 193 | 53, 54, 55, 56, 57, 58, 59, 60, 60, 61, 62, 63, 65, 66, 67, 67, 68, 69, 70, 72, 73, 74, 75, 194 | 75, 76, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 93, 95, 97, 98, 99, 100, 195 | 101, 102, 104, 104, 106, 107, 108, 109, 111, 112, 114, 115, 115, 117, 118, 120, 121, 122, 196 | 123, 124, 125, 127, 128, 129, 131, 132, 133, 135, 136, 137, 138, 139, 141, 142, 144, 145, 197 | 147, 147, 149, 150, 151, 153, 154, 156, 157, 159, 159, 161, 162, 164, 165, 167, 168, 169, 198 | 170, 172, 173, 174, 176, 177, 178, 180, 181, 182, 184, 185, 186, 188, 189, 191, 192, 193, 199 | 194, 196, 197, 198, 200, 201, 203, 204, 205, 206, 207, 209, 210, 211, 213, 214, 215, 216, 200 | 218, 219, 220, 221, 223, 224, 225, 226, 227, 229, 230, 231, 232, 234, 235, 236, 237, 238, 201 | 239, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 255}; 202 | 203 | void sepiaFilterKernel(const uchar4 *in, uchar4 *out) { 204 | float3 f3 = {in->x, in->y, in->z}; 205 | float f = dot(f3, luminosity); 206 | out->x = sepiaRedLut[(uint8_t)f]; 207 | out->y = sepiaGreenLut[(uint8_t)f]; 208 | out->z = sepiaBlueLut[(uint8_t)f]; 209 | } 210 | ```` 211 | 212 | 213 | #### TIDOLIST 214 | 1. 215 | 216 | 217 | #### 参考文献 218 | 1. [只需 4 步,手把手教你如何实现滤镜功能][1] 219 | 2. [Instagram 是用什么语言编写的?为什么它的图片滤镜效果那么出众?][2] 220 | 3. [PhotoProcessing][3] 221 | 4. [gamma原理及快速实现算法(C/C++)][4] 222 | 5. [颜色直方图均衡化][5] 223 | 6. [Three algorithms for converting color to grayscale][6] 224 | 225 | [1]: http://zihua.li/2014/06/implement-instagram-like-filters 226 | [2]: http://www.zhihu.com/question/20242095 227 | [3]: https://github.com/lightbox/PhotoProcessing 228 | [4]: http://blog.csdn.net/lxy201700/article/details/24929013 229 | [5]: http://blog.csdn.net/gxiaob/article/details/9824487 230 | [6]: http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/ 231 | -------------------------------------------------------------------------------- /post/Android面试问题收集.md: -------------------------------------------------------------------------------- 1 | #### Android面试 2 | 3 | ###### 线程/进程部分 4 | 1. **Android App启动后,默认有几个`线程`,各个`线程`的作用?** 5 | 2. Android中提供的`线程间通信`机制,原理? 6 | 3. Android中提供的`进程间通信`机制,原理? 7 | 4. Android App中实现`异步任务`的方式有哪些? 8 | 5. **`AsyncTask`的缺陷?** 9 | 6. **Android App`进程优先级`有几类?** 10 | 11 | ###### 四大组件部分 12 | 1. 说说Android中的`四大组件`都有哪些? 13 | 2. `Activity`组件,`生命周期`,回调时机?`状态保存/恢复`? 14 | 3. **`Activity`启动模式有哪些,简单介绍?** 15 | 4. `Service`组件,哪两种启动方式?`生命周期`? 16 | 5. **`onStartCommand`返回值有哪些,各有什么含义?** 17 | 6. `Broadcast Receiver`组件,`生命周期`? 18 | 7. **`Broadcast Receiver`组件使用方式,`冷插拔`、`热插拔`?** 19 | 8. **广播事件发送,`普通广播`、`有序广播`?** 20 | 9. `Content Provider`组件,`生命周期`? 21 | 10. **多个App请求同一个`Content Provider`组件,会起几个`Content Provider`实例?** 22 | 11. `Application`生命周期? 23 | 12. `AndroidManifest`文件组成? 24 | 13. **简单介绍Android的`权限系统`?** 25 | 26 | ###### 控件/窗口部分 27 | 1. Android中控件`绘制`流程? 28 | 2. Android文本控件`字体设置`? 29 | 3. 讲讲项目中`自定义控件`,自定义方法,实现效果? 30 | 4. **Android中`事件传递机制`?** 31 | 5. **图像控件`OOM`问题如何解决?** 32 | 6. 适配器控件继承`BaseAdapter`需要重写哪些函数?`列表项缓存`,编码中重用的方式? 33 | 7. `Fragment`控件生命周期? 34 | 8. **操作Fragment时`replace`和`add`函数的区别?** 35 | 36 | ###### 其他 37 | 1. **Android `APK`文件生成步骤?** 38 | 2. **Android App`反编译`?** 39 | 3. **Android App性能优化,包括哪些优化方式?** 40 | 4. `dip`、`dpi`、`px`、`sp`各个含义,使用场合? 41 | 5. Android中数据持久化方式? 42 | 6. **你在项目中尝试的`适配`方法有哪些?** 43 | 6. 项目中用过哪些`开源库`?具体源码有浏览过吗? 44 | 45 | ###### 注意 46 | 1. 加粗部分为**拔高**项。 47 | 48 | ###### 修正历史 49 | | 版本 | 作者 | 时间 | 50 | |--------|--------|--------| 51 | | v0.1 | 王汪 | 2014.09.09 | -------------------------------------------------------------------------------- /post/Annotation学习笔记.md: -------------------------------------------------------------------------------- 1 | ## Annotation学习笔记 2 | 3 | #### 默认的三种注解(`Java SE5`加入) 4 | * `@Override` 5 | * `@Deprecated` 6 | * `@SuppressWarnings` 7 | 8 | #### 定义注解(不支持继承) 9 | * 元注解`meta-annotation`,负责注解其他的注解 10 | 11 | > `@Target` 12 | > `@Retention` 13 | > `@Documented` 14 | > `@Inherited` 15 | 16 | > ![alt text](../img/Annotation01.png "元注解") 17 | 18 | * 注解元素 19 | 20 | > 注解元素类型限制 21 | > * 所有基本类型(int,float,boolean等) 22 | > * String 23 | > * Class 24 | > * enum 25 | > * Annotation,嵌套注解 26 | > * 以上类型的数组 27 | 28 | > 默认值限制 29 | > * 元素要么有默认值,要么在使用时提供初始值,不允许空值 30 | 31 | * 定义注解示例 32 | 33 | > 示例选取:定义基本的对象/关系映射功能,能够自动生成数据库表 34 | 35 | ```java 36 | // 用于表名称注解 37 | @Target(ElementType.TYPE) 38 | @Retention(RetentionPolicy.RUNTIME) 39 | public @interface DBTable { 40 | public String name() default ""; 41 | } 42 | 43 | // 用于表列约束注解 44 | @Target(ElementType.FIELD) 45 | @Retention(RetentionPolicy.RUNTIME) 46 | public @interface Constraints { 47 | public boolean primaryKey() default false; 48 | 49 | public boolean allowNull() default true; 50 | 51 | public boolean unique() default false; 52 | } 53 | 54 | // 用于表列类型注解 55 | @Target(ElementType.FIELD) 56 | @Retention(RetentionPolicy.RUNTIME) 57 | public @interface SQLString { 58 | public int value() default 0; 59 | 60 | public String name() default ""; 61 | 62 | public Constraints constraints() default @Constraints; 63 | } 64 | 65 | // 用于表列类型注解 66 | @Target(ElementType.FIELD) 67 | @Retention(RetentionPolicy.RUNTIME) 68 | public @interface SQLInteger { 69 | public String name() default ""; 70 | 71 | public Constraints constraints() default @Constraints; 72 | } 73 | ``` 74 | 75 | * 使用注解 76 | 77 | > 基于上述示例 78 | > 元素赋值快捷方式,如果元素名为`value`,在应用该注解时,该元素为唯一需要赋值的元素 79 | 80 | ```java 81 | @DBTable(name = "MEMBER") 82 | public class Member { 83 | 84 | @SQLString(30) 85 | public String firstName; 86 | @SQLString(30) 87 | public String lastName; 88 | @SQLInteger 89 | public Integer age; 90 | @SQLString(value = 30, constraints = @Constraints(primaryKey = true)) 91 | public String handle; 92 | 93 | public static int memberCount; 94 | 95 | public String getFirstName() { 96 | return firstName; 97 | } 98 | 99 | public String getLastName() { 100 | return lastName; 101 | } 102 | 103 | public Integer getAge() { 104 | return age; 105 | } 106 | 107 | public String getHandle() { 108 | return handle; 109 | } 110 | 111 | @Override 112 | public String toString() { 113 | return handle; 114 | } 115 | 116 | } 117 | ``` 118 | 119 | * 实现注解处理器 120 | 121 | > 基于上述示例,实现的注解处理器示例 122 | 123 | ```java 124 | public class TableCreator { 125 | 126 | /** 127 | * @param args 128 | * @throws ClassNotFoundException 129 | */ 130 | public static void main(String[] args) throws ClassNotFoundException { 131 | // TODO Auto-generated method stub 132 | 133 | if (args.length < 1) { 134 | System.out.println("arguments:annotated classes"); 135 | System.exit(0); 136 | } 137 | 138 | for (String className : args) { 139 | Class cl = Class.forName(className); 140 | 141 | DBTable dbTable = cl.getAnnotation(DBTable.class); 142 | if (dbTable == null) { 143 | System.out.println("No DBTable annotation in class " 144 | + className); 145 | continue; 146 | } 147 | String tableName = dbTable.name(); 148 | if (tableName.length() < 1) { 149 | tableName = cl.getName().toUpperCase(); 150 | } 151 | 152 | List columnDefs = new ArrayList(); 153 | 154 | for (Field field : cl.getDeclaredFields()) { 155 | String columnName = null; 156 | Annotation[] anns = field.getAnnotations(); 157 | if (anns.length < 1) { 158 | continue; 159 | } 160 | if (anns[0] instanceof SQLInteger) { 161 | SQLInteger sInt = (SQLInteger) anns[0]; 162 | if (sInt.name().length() < 1) { 163 | columnName = field.getName().toUpperCase(); 164 | } else { 165 | columnName = sInt.name(); 166 | } 167 | 168 | columnDefs.add(columnName + " INT" 169 | + getConstraints(sInt.constraints())); 170 | } 171 | 172 | if (anns[0] instanceof SQLString) { 173 | SQLString sString = (SQLString) anns[0]; 174 | if (sString.name().length() < 1) { 175 | columnName = field.getName().toUpperCase(); 176 | } else { 177 | columnName = sString.name(); 178 | } 179 | 180 | columnDefs.add(columnName + " VARCHAR(" + sString.value() 181 | + ")" + getConstraints(sString.constraints())); 182 | } 183 | } 184 | 185 | StringBuilder createCommand = new StringBuilder("CREATE TABLE " 186 | + tableName + "("); 187 | for (String columnsDef : columnDefs) { 188 | createCommand.append("\n\t" + columnsDef + ","); 189 | } 190 | String tableCreate = createCommand.substring(0, 191 | createCommand.length() - 1) 192 | + ");"; 193 | System.out.println("Table Creation SQL for " + className + " is:\n" 194 | + tableCreate); 195 | 196 | } 197 | } 198 | 199 | private static String getConstraints(Constraints con) { 200 | String constraints = ""; 201 | 202 | if (!con.allowNull()) { 203 | constraints += " NOT NULL"; 204 | } 205 | if (con.primaryKey()) { 206 | constraints += " PRIMARY KEY"; 207 | } 208 | if (con.unique()) { 209 | constraints += " UNIQUE"; 210 | } 211 | 212 | return constraints; 213 | } 214 | 215 | } 216 | ``` 217 | 218 | * `JUnit4`与`注解` 219 | 220 | > 之前版本`JUnit`,需要创建测试类,并且测试方法采用`test函数名`形式,这里`函数名`指待测试的函数,使用`assert`特殊族函数 221 | > `JUnit4`增加`@Test`注解,不用新建测试类(`嵌入式`与`非嵌入式`),并且测试方法命名随意,可以使用java assert语句与java异常机制 222 | > `JUnit4`的非嵌入式测试可以基于`继承`与`组合`方式 223 | > 简单示例如下: 224 | 225 | ```java 226 | public class HashSetTest { 227 | 228 | HashSet testObject = new HashSet(); 229 | 230 | @Test 231 | void initialization() { 232 | assert testObject.isEmpty(); 233 | } 234 | 235 | @Test 236 | void _contains() { 237 | testObject.add("one"); 238 | 239 | assert testObject.contains("one"); 240 | } 241 | 242 | @Test 243 | void _remove() { 244 | testObject.add("one"); 245 | testObject.remove("one"); 246 | 247 | assert testObject.isEmpty(); 248 | } 249 | 250 | public static void main(String[] args) { 251 | OSExecuteCommand("java net.mindview.atunit.AtUnit HashSetTest"); 252 | } 253 | 254 | } 255 | ``` 256 | 257 | > 具体java单元测试后续学习,留待日后 258 | 259 | #### Android中的注解[2] 260 | ![alt text](../img/Annotation02.png "Android中的注解") 261 | 262 | 263 | #### TIDOLIST 264 | 1. 使用`apt`处理注解 265 | 266 | #### 参考文献 267 | 1. 《Java编程思想》 268 | 2. [Java Annotation 及几个常用开源项目注解原理简析][2] 269 | 270 | [2]: http://www.trinea.cn/android/java-annotation-android-open-source-analysis/ 271 | -------------------------------------------------------------------------------- /post/BUILDER模式.md: -------------------------------------------------------------------------------- 1 | #### BUILDER模式 2 | 3 | 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 4 | 5 | ###### 1. 结构 6 | ![](../uml/builder模式01.png) 7 | 8 | ###### 2. 适用性 9 | * 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时; 10 | * 当构造过程必须允许被构造的对象有不同的表示时; 11 | 12 | ###### 3. 实现 13 | * Builder.java 14 | 15 | ```java 16 | public interface Builder { 17 | 18 | public void buildPart(); 19 | 20 | } 21 | ``` 22 | 23 | * ConcreteBuilder.java 24 | 25 | ```java 26 | public class ConcreteBuilder implements Builder { 27 | 28 | private Product mProduct; 29 | 30 | @Override 31 | public void buildPart() { 32 | 33 | } 34 | 35 | public Product getResult() { 36 | return mProduct; 37 | } 38 | 39 | } 40 | ``` 41 | 42 | * Product.java 43 | 44 | ```java 45 | public interface Product { 46 | } 47 | 48 | ``` 49 | 50 | * Director.java 51 | 52 | ```java 53 | public class Director { 54 | 55 | private Builder mBuilder; 56 | 57 | public Director(Builder mBuilder) { 58 | this.mBuilder = mBuilder; 59 | } 60 | 61 | public void construct() { 62 | mBuilder.buildPart(); 63 | } 64 | 65 | } 66 | ``` 67 | 68 | * 测试用例 69 | 70 | ```java 71 | public class Main { 72 | 73 | public static void main(String[] args) { 74 | ConcreteBuilder builder = new ConcreteBuilder(); 75 | Director director = new Director(builder); 76 | director.construct(); 77 | // 获取产品 78 | Product product = builder.getResult(); 79 | } 80 | 81 | } 82 | ``` 83 | 84 | 85 | ###### 4. BUILDER模式变种(构建复杂的对象) 86 | -------------------------------------------------------------------------------- /post/COMMAND模式.md: -------------------------------------------------------------------------------- 1 | #### COMMAND模式 2 | 3 | 命令模式是对命令的封装,命令模式把发出命令的责任和执行命令的责任分隔开,委派给不同的对象。 4 | 5 | 命令模式又称为行动模式(action)或交易模式(transaction)。 6 | 7 | ###### 1. 结构 8 | ![](../uml/command模式01.png) 9 | 10 | ###### 2. 描述 11 | 命令模式涉及5个角色,分别为: 12 | 13 | * Client:创建具体命令,确定该命令接收者,创建请求者发送命令; 14 | * Command:具体命令抽象接口; 15 | * ConcreteCommand:具体命令,其中包含命令接收者成员,实现命令发送者、命令接收者弱耦合; 16 | * Receiver:命令接收者,是否需要与`ConcreteCommand`一一对应? 17 | * invoker:命令请求者,调用命令对象,执行请求; 18 | 19 | ###### 3. 实现 20 | Spring、Struts框架中,有采用命令模式。 21 | 22 | * Command.java 23 | 24 | ```java 25 | public interface Command { 26 | 27 | // 执行方法 28 | void execute(); 29 | 30 | } 31 | ``` 32 | 33 | * ConcreteCommand.java 34 | 35 | ```java 36 | public class ConcreteCommand implements Command { 37 | 38 | private Receiver mReceiver; 39 | 40 | public ConcreteCommand(Receiver mReceiver) { 41 | this.mReceiver = mReceiver; 42 | } 43 | 44 | @Override 45 | public void execute() { 46 | mReceiver.action(); 47 | } 48 | 49 | } 50 | ``` 51 | 52 | * Receiver.java 53 | 54 | ```java 55 | public class Receiver { 56 | 57 | // 行动方法 58 | public void action() { 59 | System.out.println("Receiver action~"); 60 | } 61 | 62 | } 63 | ``` 64 | 65 | * Invoker.java 66 | 67 | ```java 68 | public class Invoker { 69 | 70 | private Command mConmand; 71 | 72 | public Invoker(Command mConmand) { 73 | this.mConmand = mConmand; 74 | } 75 | 76 | // 行动方法 77 | public void action() { 78 | mConmand.execute(); 79 | } 80 | 81 | } 82 | ``` 83 | 84 | * 测试 85 | 86 | ```java 87 | public class Main { 88 | 89 | public static void main(String[] args) { 90 | Receiver receiver = new Receiver(); 91 | Command command = new ConcreteCommand(receiver); 92 | 93 | Invoker invoker = new Invoker(command); 94 | invoker.action(); 95 | } 96 | 97 | } 98 | ``` 99 | 100 | ###### 4. 扩展——宏命令 101 | 宏命令是包含命令的命令,即命令的组合。 102 | 103 | ###### 5. 参考文档 104 | * [《JAVA与模式》之命令模式](http://www.cnblogs.com/java-my-life/archive/2012/06/01/2526972.html) 105 | * [命令模式在MVC框架中的应用](http://blog.csdn.net/wsh622827/article/details/4759368) 106 | -------------------------------------------------------------------------------- /post/Git学习.md: -------------------------------------------------------------------------------- 1 | #### Git学习 2 | 3 | ###### 笔记思维导图 4 | 5 |
![alt text](../img/git命令大全.png "Git")
6 | 7 | -------------------------------------------------------------------------------- /post/JNI学习笔记.md: -------------------------------------------------------------------------------- 1 | ### JNI笔记 2 | 3 | #### JNI是什么 4 | 5 | * JNI(Java Native Interface)是JVM规范中的一部份,目的java、native互相调用 6 | * JNA(Java Native Access)基于JNI封装,java调用native框架,native不能调用java 7 | 8 | #### JNI开发流程 9 | 10 | ![image](../img/jni流程.PNG) 11 | 12 | > 注意: 13 | > 不同平台动态库后缀:windows:\*.dll,linux/unix:\*.so,mac os x:\*.jnilib 14 | 15 | #### JVM查找native方法 16 | 17 | * JNI规范命名规则,java按照指定命名查找本地函数,查找效率低,不灵活 18 | javah生成.h文件名:包名(.用\_替换)\_类名.h 19 | 函数名:JNIEXPORT 返回类型 JNICALL java_包名(.用\_替换)\_类名\_函数名(JNIEnv *, jclass或者jobject, ...) 20 | JNIEXPORT宏:函数用于外部调用,定义jni_md.h 21 | JNICALL宏:函数参数入栈顺序、栈清理规则,定义jni_md.h 22 | 23 | * JNI函数RegisterNatives(),本地函数注册,java按照注册信息查找本地函数,灵活 24 | JNI_OnLoad(),System.loadLibrary()时调用,进行资源初始化,调用RegisterNatives()注册 25 | JNI_OnUnload(),JVM释放so时调用,进行资源回收 26 | 27 | RegisterNatives()的一则demo: 28 | 29 | ``` 30 | 31 | /* 32 | * used in RegisterNatives to describe native method name, signature, 33 | * and function pointer. 34 | */ 35 | typedef struct { 36 | char *name; 37 | char *signature; 38 | void *fnPtr; 39 | } JNINativeMethod; 40 | 41 | static JNINativeMethod s_methods[] = { 42 | {"callCustomClass", "(LMyJavaClass;)V", (void*)callCustomClass}, 43 | }; 44 | 45 | int JNI_OnLoad(JavaVM* vm, void* reserved) 46 | { 47 | JNIEnv* env = NULL; 48 | if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 49 | { 50 | return JNI_ERR; 51 | } 52 | jclass cls = env->FindClass("LRegisterNativesTest;"); // 需要获取java类 53 | if (cls == NULL) 54 | { 55 | return JNI_ERR; 56 | } 57 | int len = sizeof(s_methods) / sizeof(s_methods[0]); 58 | if (env->RegisterNatives(cls, s_methods, len) < 0) 59 | { 60 | return JNI_ERR; 61 | } 62 | return JNI_VERSION_1_4; 63 | } 64 | 65 | 66 | ``` 67 | 68 | #### JNI类型系统 69 | 70 | * 基本类型 java/JNI映射表 71 | 72 | | Java Type | Native Type | Description | 73 | |:---:|:---:|:---:| 74 | | boolean | jboolean |unsigned 8 bits | 75 | | byte | jbyte | signed 8 bits | 76 | | char | jchar | unsigned 16 bits | 77 | | short | jshort | signed 16 bits | 78 | | int | jint | signed 32 bits | 79 | | long | jlong | signed 64 bits | 80 | | float | jfloat | 32 bits | 81 | | double | jdouble | 64 bits | 82 | | void | void | N/A | 83 | 84 | \#define JNI_FALSE 0 85 | \#define JNI_TRUE 1 86 | 87 | typedef jint jsize; 88 | 89 | > 注意: 90 | > JNI jchar 2字节,不同于C/C++ char 1字节; 91 | > JNI jlong 8字节,不同于C/C++ long 4字节; 92 | 93 | * 引用类型 java/JNI映射表 94 | 95 | ![image](../img/jni引用类型.gif) 96 | 97 | 其中: 98 | C jclass类型 99 | typedef jobject jclass; 100 | 101 | C++ jclass类型 102 | class _jobject {}; 103 | class _jclass : public _jobject {}; 104 | ... 105 | typedef _jobject *jobject; 106 | typedef _jclass *jclass; 107 | 108 | * Field Method IDs 109 | 110 | Field Method IDs是常规的指针类型,不同于jclass 111 | 112 | ``` 113 | struct _jfieldID; /* opaque structure */ 114 | typedef struct _jfieldID *jfieldID; /* field IDs */ 115 | 116 | struct _jmethodID; /* opaque structure */ 117 | typedef struct _jmethodID *jmethodID; /* method IDs */ 118 | ``` 119 | 120 | * JNI枚举类型jvalue 121 | 122 | 实现变长参数列表函数,jvalue就特别有用 123 | 124 | ``` 125 | typedef union jvalue { 126 | jboolean z; 127 | jbyte b; 128 | jchar c; 129 | jshort s; 130 | jint i; 131 | jlong j; 132 | jfloat f; 133 | jdouble d; 134 | jobject l; 135 | } jvalue; 136 | ``` 137 | 138 | * JNI类型签名 139 | 140 | JNI类型签名和JVM类型签名保持一致 141 | 142 | | Type Signature | Java Type | 143 | |:---:|:---:| 144 | | Z | boolean | 145 | | B | byte | 146 | | C | char | 147 | | S | short | 148 | | I | int | 149 | | J | long | 150 | | F | float | 151 | | D | double | 152 | | L fully-qualified-class ; | fully-qualified-class | 153 | | [ type | type[] | 154 | | ( arg-types ) ret-type | method type | 155 | 156 | #### JNI字符串处理 157 | 158 | JNI字符串类型jstring,包含一堆字符串处理函数,原因是JNI把Java中的所有对象当作一个C指针传递到本地方法中,这个指针指向JVM中的内部数据结构,而内部的数据结构在内存中的存储方式是不可见的,只能从JNIEnv指针指向的函数表中选择合适的JNI函数来操作JVM中的数据结构。 159 | 160 | * 访问字符串 161 | 162 | ``` 163 | const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy); 164 | ``` 165 | 返回unicode字符串,底层字符存储不连续,会创建缓冲区,连续存放字符,返回的就是缓冲区指针,isCopy等于JNI_TRUE表示有缓存区创建 166 | 167 | 168 | ``` 169 | const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy); 170 | ``` 171 | 返回utf-8字符串,isCopy含义见上 172 | 173 | 174 | ``` 175 | const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy); 176 | ``` 177 | 参考GetStringChars,目的提高JVM返回源字符串直接指针的可能性,效率会高一点 178 | 179 | 180 | * 释放字符串 181 | 182 | 因为底层可能创建了缓冲区,所以使用完后需要做释放 183 | 184 | ``` 185 | void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars); 186 | ``` 187 | ``` 188 | void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf); 189 | ``` 190 | ``` 191 | void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray); 192 | ``` 193 | 194 | * 字符串长度 195 | 196 | ``` 197 | jsize GetStringLength(JNIEnv *env, jstring string); 198 | ``` 199 | 返回unicode字符串长度 200 | 201 | 202 | ``` 203 | jsize GetStringUTFLength(JNIEnv *env, jstring string); 204 | ``` 205 | 返回utf-8字符串长度 206 | 207 | * 创建字符串 208 | 209 | ``` 210 | jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len); 211 | ``` 212 | 用unicode字符串创建java String 213 | 214 | 215 | ``` 216 | jstring NewStringUTF(JNIEnv *env, const char *bytes); 217 | ``` 218 | 用utf-8字符串创建java String 219 | 220 | * 获取字符串指定范围字符 221 | 222 | 因为不涉及底层创建缓冲区,所以不需要release函数 223 | 224 | ``` 225 | void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); 226 | ``` 227 | 获取指定区域unicode字符 228 | 229 | 230 | ``` 231 | void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf); 232 | ``` 233 | 获取指定区域utf-8字符 234 | 235 | #### JNI数组处理 236 | 237 | JNI数组,基本类型数组,引用类型数组,处理方式不一样 238 | 239 | * 数组长度 240 | 241 | ``` 242 | jsize GetArrayLength(JNIEnv *env, jarray array); 243 | ``` 244 | 返回数组长度 245 | 246 | * 基本类型 访问数组 247 | 248 | ``` 249 | void GetArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf); 250 | ``` 251 | 获取数组元素至缓冲区 252 | 253 | ``` 254 | NativeType *GetArrayElements(JNIEnv *env, ArrayType array, jboolean *isCopy); 255 | ``` 256 | 获取数组指针,底层可能会创建缓冲区 257 | 258 | ``` 259 | void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy); 260 | ``` 261 | 参考GetArrayElements,目的提高JVM返回源字符串直接指针的可能性,效率会高一点 262 | 263 | * 基本类型 设置数组 264 | 265 | ``` 266 | void SetArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf); 267 | ``` 268 | 缓冲区数据copy到jni数组 269 | 270 | * 基本类型 释放数组 271 | 272 | 因为底层可能创建了缓冲区,所以使用完后需要做释放 273 | 274 | ``` 275 | void ReleaseArrayElements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode); 276 | ``` 277 | ``` 278 | void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode); 279 | ``` 280 | 281 | * 基本类型 创建数组 282 | 283 | ``` 284 | ArrayType NewArray(JNIEnv *env, jsize length); 285 | ``` 286 | 287 | * 引用类型 访问数组 288 | 只支持访问单个元素 289 | 290 | ``` 291 | jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index); 292 | ``` 293 | 获取index处对象 294 | 295 | * 引用类型 设置数组 296 | 297 | ``` 298 | void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value); 299 | ``` 300 | 设置index处对象为value 301 | 302 | * 引用类型 创建数组 303 | 304 | ``` 305 | jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement); 306 | ``` 307 | 创建数组,元素类型elementClass 308 | 309 | #### 参考文献 310 | 1. [JNI官方文档](https://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html) 311 | -------------------------------------------------------------------------------- /post/Java基本功不牢造成的那些似坑非坑.md: -------------------------------------------------------------------------------- 1 | ## Java基本功不牢造成的那些似坑非坑 2 | 3 | ##### `Java`中的变量要么是对象的引用,要么是基础类型 4 | 5 | > 那么对于不可变`String`类 6 | 7 | ```java 8 | String s = "Hello"; 9 | s += "World!"; 10 | ``` 11 | 12 | > 重新构造新`String`对象,同时修改引用`s` 13 | 14 | ##### `==`比较变量存储的值,对于基本类型比较基本类型值,对于引用比较引用值 15 | 16 | > 可是`JVM`中的对象池技术会让引用比较有点混乱 17 | 18 | ```java 19 | String s1 = "Hi", s2 = "Hi"; 20 | Integer a = 12, b = 12; 21 | String s3 = new String(s1); 22 | Integer c = -222, d = -222; 23 | 24 | s1 == s2; // true,对象池优化导致 25 | s1 == s3; // false 26 | s1.equals(s3); // true 27 | a == b; // true,对象池优化导致 28 | c == d; // false,Integer对象池缓存范围-128~127 29 | c.equals(d); // true 30 | ``` 31 | 32 | ##### `Object.hashCode`生成的值,看起来像地址,但是与地址没啥关系 33 | 34 | ##### `Object.toString`默认行为是输出`类名`+`hashCode` 35 | > 对于数组 36 | 37 | ```java 38 | String[] words = {"Hello", "World"}; 39 | System.out.println(words); 40 | ``` 41 | 42 | > 输出`[Ljava.lang.String;@1ce08c7` 43 | > 如果要输出数组内容,就需要借助类`Arrays` 44 | 45 | ```java 46 | System.out.println(Arrays.toString(words)); 47 | ``` 48 | 49 | > 但是这样显然破坏了`OOP`的原则 50 | 51 | ##### TODO... 52 | 53 | -------------------------------------------------------------------------------- /post/MongoDB笔记.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/post/MongoDB笔记.xmind -------------------------------------------------------------------------------- /post/NoSQL学习笔记.md: -------------------------------------------------------------------------------- 1 | #### NoSQL学习笔记 2 | 3 | ###### 1.NoSQL 4 | 本[站点][1]包含完整的NoSQL现状描述,NoSQL分类,各个NoSQL实现简短介绍。 5 | 对于`文档类`NoSQL,这里有一篇介绍[文章][2]。 6 | 7 | ###### 2.MongoDB 8 | 参考自[《MongoDB权威指南》][3]。 9 |
![](../img/MongoDB笔记.png)
10 | 11 | 12 | [1]: http://nosql-database.org/ 13 | [2]: http://blog.jobbole.com/9426/ 14 | [3]: http://book.douban.com/subject/6068947/ -------------------------------------------------------------------------------- /post/Pig学习.md: -------------------------------------------------------------------------------- 1 | #### Pig学习 2 | 3 | ###### 笔记思维导图,[下载][2] 4 | 5 |
![alt text](../img/Pig学习.png "Pig学习")
6 | 7 | 8 | #### 参考文献 9 | 1. [Pig编程指南][1] 10 | 11 | 12 | [1]: http://book.douban.com/subject/21357721/ 13 | [2]: ./Pig学习.xmind 14 | -------------------------------------------------------------------------------- /post/Pig学习.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/post/Pig学习.xmind -------------------------------------------------------------------------------- /post/Python学习笔记.md: -------------------------------------------------------------------------------- 1 | #### Python笔记 2 | 3 | ###### 1. 一张图片入门Python 4 | 中文版 5 |
![](../img/Python_introduction_cn.png)
6 | 7 | 英文版 8 |
![](../img/Python_introduction_en.png)
9 | 10 | ###### 2. Python输出日志 11 | 日志输出代码结构。 12 | ```python 13 | #coding:utf-8 14 | 15 | import logging 16 | 17 | # 创建logger 18 | logger = logging.getLogger("wwang") 19 | logger.setLevel(logging.INFO) 20 | 21 | # 创建handler 22 | handler = logging.StreamHandler() 23 | 24 | # 定义输出格式 25 | formatter = logging.Formatter("%(asctime)s-%(name)s-%(levelname)s:%(message)s") 26 | 27 | handler.setFormatter(formatter) 28 | logger.addHandler(handler) 29 | 30 | def log(msg): 31 | logger.info(msg) 32 | 33 | if __name__ == '__main__': 34 | log("test") 35 | ``` 36 | 37 | 38 | #### 参考文章 39 | 1. [一张图片入门Python][1] 40 | 41 | 42 | [1]: http://www.cnblogs.com/youxin/archive/2013/05/05/3061114.html 43 | -------------------------------------------------------------------------------- /post/Python开发工具收集.md: -------------------------------------------------------------------------------- 1 | #### Python开发利器 2 | 3 | ###### 1. `pip` 4 | 安装Python Package便捷工具,[参考][1]。 5 | 6 | Windows执行`pip`指令如下: 7 | ``` 8 | python -m pip [options] 9 | ``` 10 | 11 | * install 12 | 从`PyPi`安装,[PyPi][3]就是Python的中央仓库。 13 | ``` 14 | pip install SomePackage # latest version 15 | pip install SomePackage==1.0.4 # specific version 16 | pip install 'SomePackage>=1.0.4' # minimum version 17 | ``` 18 | pip默认安装`Wheels`格式package,只有在找不到`Wheels`时才进行`source`安装。 19 | 对于没有`Wheels`格式package,可以使用`pip wheel`工具打包,[参考][5]。 20 | 同时可以从`依赖列表`文件安装依赖项。 21 | ``` 22 | pip freeze > requirements.txt 23 | pip install -r requirements.txt 24 | ``` 25 | `依赖列表`文件适用场景,四种,参考[requirements-files][4]。 26 | 27 | * uninstall 28 | 顾名思义,卸载package。 29 | ``` 30 | pip uninstall SomePackage 31 | ``` 32 | 33 | * list 34 | 输出当前已安装package。 35 | ``` 36 | pip list 37 | pip list --outdated 38 | ``` 39 | 40 | * show 41 | 显示package详细信息。 42 | ``` 43 | pip show pip 44 | ``` 45 | 46 | * search 47 | 搜索package。 48 | ``` 49 | pip search "query" 50 | ``` 51 | 52 | * 配置 53 | pip配置,可以采用`命令行参数`、`环境变量`、`配置文件`,其中`配置文件`参考[Config file][6]。 54 | 配置优先级: 55 | ``` 56 | Command line options have precedence over environment variables, which have precedence over the config file. 57 | Within the config file, command specific sections have precedence over the global section. 58 | ``` 59 | 60 | ###### 2. `virtualenv` 61 | Python沙盒,[参考][2]。 62 | 注意:Python安装目录不能包含空格,不然`virtualenv`使用会有问题! 63 | 64 | Windows执行`virtualenv`指令如下: 65 | ``` 66 | python -m virtualenv [OPTIONS] DEST_DIR 67 | ``` 68 | 69 | Windows指令如下: 70 | ``` 71 | .\Scripts\activate 72 | .\Scripts\deactivate 73 | ``` 74 | 75 | 测试沙盒安装`django`,`pip install django`。 76 | 77 | 78 | #### 参考文献 79 | 1. [pip][1] 80 | 2. [virtualenv][2] 81 | 3. [pip wheel][5] 82 | 83 | 84 | [1]: https://pip.pypa.io/en/latest/quickstart.html 85 | [2]: https://virtualenv.pypa.io/en/latest/virtualenv.html 86 | [3]: https://pypi.python.org/pypi/ 87 | [4]: https://pip.pypa.io/en/latest/user_guide.html#requirements-files 88 | [5]: https://pip.pypa.io/en/latest/reference/pip_wheel.html 89 | [6]: https://pip.pypa.io/en/latest/user_guide.html#config-file 90 | -------------------------------------------------------------------------------- /post/SVN学习.md: -------------------------------------------------------------------------------- 1 | #### SVN学习 2 | 3 | ###### 笔记思维导图,[下载][3] 4 | 5 |
![alt text](../img/SVN学习.png "SVN")
6 | 7 | 8 | ###### 编程访问SVN 9 | 1. java方式,推荐[SVNKit][4]。 10 | 11 | #### 参考文献 12 | 1. [版本控制之道-使用Subversion][1] 13 | 2. [读懂diff][2] 14 | 15 | 16 | [1]: http://book.douban.com/subject/2038779/ 17 | [2]: http://www.ruanyifeng.com/blog/2012/08/how_to_read_diff.html 18 | [3]: ./SVN学习.xmind 19 | [4]: http://svnkit.com/index.html 20 | -------------------------------------------------------------------------------- /post/SVN学习.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/post/SVN学习.xmind -------------------------------------------------------------------------------- /post/SublimeText3插件安装笔记.md: -------------------------------------------------------------------------------- 1 | #### Sublime Text 3插件安装笔记 2 | 3 | 推荐一份[Sublime Text 3 文档](http://feliving.github.io/Sublime-Text-3-Documentation/)。 4 | 5 | ###### 1. 安装`Package Control` 6 | * 自动安装 7 | 菜单`View`-`Show Console`,输入如下代码后运行(代码[参考](https://sublime.wbond.net/installation))。 8 | ``` 9 | import urllib.request,os,hashlib; h = '7183a2d3e96f11eeadd761d777e62404' + 'e330c659d4bb41d3bdf022e94cab3cd0'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by) 10 | ``` 11 | 12 | * 手动安装 13 | 对于公司内网采用代理访问外网的情况,默认安装方式无效,只能手动安装。 14 | 1. 菜单项`Preferences`-`Browse Packages…`; 15 | 2. 返回上级目录,进入`Installed Packages`目录; 16 | 3. 下载[`Package Control.sublime-package`](https://sublime.wbond.net/Package%20Control.sublime-package),保存到`Installed Packages`目录(可能需要覆盖原`Package Control.sublime-package`文件); 17 | 4. 重启Sublime Text 3。 18 | 19 | **注意:**`Package Control`公司内网同样需要配置代理才可以下载插件,配置示例如下: 20 | ``` 21 | { 22 | "http_proxy": "http://web-proxyhk.oa.com:8080" 23 | } 24 | 25 | ``` 26 | 27 | ###### 2. `SideBarEnhancements` 28 | 增强`Side Bar`。 29 | 30 | ###### 3. `All Autocomplete` 31 | 自动完成-扩展至所有打开的文件,而不是局限于当前编辑的文件。 32 | 33 | ###### 4. `SublimeCodeIntel` 34 | 通用型代码智能提示。 35 | 36 | 用法:For Windows 37 | * Jump to definition = ``Alt+Click`` 38 | * Jump to definition = ``Control+Windows+Alt+Up`` 39 | * Go back = ``Control+Windows+Alt+Left`` 40 | * Manual Code Intelligence = ``Control+Shift+space`` 41 | 42 | 智能提示配置文件需安装在`用户目录`,如Windows存储路径`C:\Users\wwang\.codeintel`(先删除现有目录)。 43 | Python配置文件如下: 44 | ``` 45 | { 46 | "Python": { 47 | "python":"D:/Python27/python.exe", 48 | "pythonExtraPaths": 49 | [ 50 | "D:/Python27", 51 | "D:/Python27/DLLs", 52 | "D:/Python27/Lib", 53 | "D:/Python27/Lib/lib-tk", 54 | "D:/Python27/Lib/site-packages" 55 | ] 56 | } 57 | } 58 | ``` 59 | 60 | ###### 5. `BracketHighlighter` 61 | 括号、引号、标签等匹配。 62 | 63 | ###### 6. `TrailingSpaces` 64 | 高亮每一行末尾空格、tab。 65 | 66 | 执行删除操作, 一是菜单项`Edit`-`Trailing Spaces`,二是自己配置快捷键`Preferences`-`Key Bindings - User`,如下: 67 | ``` 68 | { "keys": ["ctrl+alt+d"], "command": "delete_trailing_spaces" }, 69 | { "keys": ["ctrl+alt+o"], "command": "toggle_trailing_spaces" } 70 | 71 | ``` 72 | 73 | ###### 7. `Markdown Preview` 74 | 包含两种`Markdown`解析器:`python-markdown`、`github markdown API`,但整体来说还是没有`Haroopad`编辑器方便。 75 | 76 | 自己配置快捷键,预览时由用户自己选择解析器,如下: 77 | ``` 78 | { "keys": ["alt+m"], "command": "markdown_preview_select", "args": {"target": "browser"} } 79 | ``` 80 | 81 | ###### 8. `JsFormat` 82 | 格式化`js`代码或者`json`数据! 83 | 84 | ###### 9. `LineEndings` 85 | 转换`行尾`换行符。 86 | 87 | ###### 10. `ConvertToUTF8` 88 | 由于Sublime Text对中文编码支持不好,国人开发`ConvertToUTF8`专用于解决该问题。 89 | 打开文件将`中文编码`转换为`utf-8`;保存文件将`utf-8`转换为打开文件时检测到的`中文编码`。 90 | 同时支持文件编码转换,菜单项`File`-`Set File Encoding to`。 91 | 92 | ###### 11. `sublimeTmpl` 93 | html/js/css/php/python/ruby代码创建模板。 94 | 菜单项`File`-`New File (SublimeTmpl)`,或者命令`cmd+shift+p`-`tmpl`。 95 | 96 | ###### 12. `AAAPackageDev` 97 | 简化开发Sublime Text的`syntax definitions`,实现自定义语法高亮。 98 | 本人用于实现日志的高亮,方便浏览查找。 99 | 具体如何开发,请参看[参考一](http://docs.sublimetext.info/en/latest/extensibility/syntaxdefs.html)、[参考二](http://manual.macromates.com/en/language_grammars#naming_conventions)。 100 | 101 | ###### 13. `MarkdownEditing` 102 | 编辑`Markdown`。 103 | 104 | ###### 14. `IMESupport` 105 | 解决Sublime Text输入法不跟随bug。 106 | 107 | ###### 参考 108 | 1. [Packsge Control](https://sublime.wbond.net/) 109 | 2. [SideBarEnhancements](https://github.com/titoBouzout/SideBarEnhancements) 110 | 3. [All Autocomplete](https://github.com/alienhard/SublimeAllAutocomplete) 111 | 4. [SublimeCodeIntel](http://sublimecodeintel.github.io/SublimeCodeIntel/) 112 | 5. [BracketHighlighter](https://github.com/facelessuser/BracketHighlighter) 113 | 6. [TrailingSpaces](https://github.com/SublimeText/TrailingSpaces) 114 | 7. [Markdown Preview](https://github.com/revolunet/sublimetext-markdown-preview) 115 | 8. [JsFormat](https://github.com/jdc0589/JsFormat) 116 | 9. [LineEndings](https://github.com/SublimeText/LineEndings) 117 | 10. [ConvertToUTF8](https://github.com/seanliang/ConvertToUTF8) 118 | 11. [sublimeTmpl](https://github.com/kairyou/sublimeTmpl) 119 | 12. [AAAPackageDev](https://github.com/SublimeText/AAAPackageDev) 120 | 13. [MarkdownEditing](http://ttscoff.github.io/MarkdownEditing) 121 | 14. [IMESupport](https://github.com/chikatoike/IMESupport) 122 | -------------------------------------------------------------------------------- /post/The Android Design Cheat Sheet.md: -------------------------------------------------------------------------------- 1 | #### The Android Design Cheat Sheet 2 | 3 |
![](../img/Android-Design-Cheat-Sheet-highres.png)
4 | 5 | 参考链接:[http://www.doubleencore.com/2014/01/android-design-cheat-sheet/][1] 6 | 7 | ###### 附iOS版本 8 | 9 | 参考链接:[http://www.doubleencore.com/2014/08/ios-design-cheat-sheet/][2] 10 | 11 | [1]: http://www.doubleencore.com/2014/01/android-design-cheat-sheet/ 12 | [2]: http://www.doubleencore.com/2014/08/ios-design-cheat-sheet/ 13 | -------------------------------------------------------------------------------- /post/UIL学习.md: -------------------------------------------------------------------------------- 1 | #### UIL学习 2 | 3 | ###### 1. DiscCache框架 4 | 策略模式+工厂方法模式 5 | 6 |
![alt text](../img/UIL-DiscCache架构.png "DiscCache")
7 | 8 | ###### 2. MemCache框架 9 | 策略模式+工厂方法模式+装饰者模式 10 | 11 |
![alt text](../img/UIL-MemCache架构.png "MemCache")
12 | 13 | 14 | #### 几篇参考文献 15 | 1. [Android-Universal-Image-Loader-part1][1] 16 | 2. [Android-Universal-Image-Loader异步加载图片框架学习][2] 17 | 18 | 19 | [1]: http://blog.csdn.net/asce1885/article/details/10306623 20 | [2]: http://www.cnblogs.com/PDW-Android/p/3640054.html -------------------------------------------------------------------------------- /post/docker OS X.md: -------------------------------------------------------------------------------- 1 | # Docker in OS X 2 | 3 | ## boot2docker 4 | 5 | docker依赖Linux内核特性,所以在Windows、OS X上不能直接跑docker服务,需要先运行Linux虚拟机,然后在Linux虚拟机中运行docker服务,也就有了`boot2docker`工具,进而导致在Windows、OS X上搭建docker运行环境学习成本变高,本文目的就是解决OS X上搭建docker运行环境问题。 6 | 7 | `boot2docker`工具包含了`VirtualBox`虚拟机,`boot2docker.iso` Linux镜像,`boot2docker`命令,`docker`命令。 8 | 9 | ![image](../img/osx boot2docker vm.png) 10 | 11 | 在MAC OS X上玩docker的过程就变成了这样: 12 | 13 | 1. 使用boot2docker命令,创建一个VirtualBox Linux虚拟机; 14 | 2. 使用boot2docker命令,启动这个虚拟机,虚拟机中会同步运行一个docker daemon; 15 | 3. 使用docker命令,与虚拟机中docker daemon通信,执行一些docker操作,如下载镜像,启动容器,停止容器等,这里docker命令就是上图的`Docker Client`; 16 | 17 | 18 | > 注意:boot2docker只是开发人员简单玩玩而已,生产环境不推荐,生产环境还是上Linux相关发行版。 19 | 20 | 21 | ### boot2docker命令使用 22 | 23 | #### 创建虚拟机 24 | 25 | boot2docker init 26 | 27 | 先决条件:cp /usr/local/share/boot2docker/boot2docker.iso ~/.boot2docker/ 28 | 29 | 第一次运行,根据boot2docker.iso创建VirtualBox Linux虚拟机,后续运行不再重复创建。 30 | 31 | #### 启动虚拟机 32 | 33 | boot2docker up 34 | 35 | 启动的虚拟机,可以打开VirtualBox查看。 36 | 37 | ![image](../img/VirtualBox.png) 38 | 39 | #### 关闭虚拟机 40 | 41 | boot2docker down 42 | 43 | #### 删除虚拟机 44 | 45 | boot2docker delete 46 | 47 | #### 环境变量设置 48 | 49 | eval "$(boot2docker shellinit)" 50 | 51 | 主要是VirtualBox Linux虚拟机网络设置。 52 | 53 | > boot2docker命令更多使用,boot2docker help 54 | 55 | 基于上面boot2docker命令使用介绍,完整的起一个docker运行环境的步骤如下: 56 | 57 | 1. boot2docker init 58 | 2. boot2docker up 59 | 3. eval "$(boot2docker shellinit)" 60 | 61 | docker运行环境起来后,执行 docker version ,如果可以看到如下版本信息,证明docker环境成功起来了,下面就可以进行docker操作实战了。 62 | 63 | ![image](../img/docker version.png) 64 | 65 | > 如果遇到`certificate`问题,参考下文【问题fix】部分。 66 | 67 | #### 运行hello world容器 68 | 69 | 所有的编程语言入门都有一个hello world程序,docker入门一样也有一个hello world容器。 70 | 71 | 下载容器,默认从官方docker hub下载,公司内部网络环境大家是懂的,不配置代理显然是不行的。当然官方docker hub被G|F|W墙这个问题,由于公司代理可以帮你翻墙,也就不再是问题了。 72 | 73 | docker容器运行在VirtualBox Linux虚拟机,下载镜像也是由VirtualBox Linux虚拟机中的docker daemon完成,代理显然是配置VirtualBox Linux虚拟机的网络代理。 74 | 75 | 配置步骤如下: 76 | 77 | 1. boot2docker ssh,ssh到VirtualBox Linux虚拟机; 78 | 2. sudo vi /var/lib/boot2docker/profile,编辑虚拟机配置文件; 79 | export HTTP_PROXY=http://your.proxy.name:8080 80 | export HTTPS_PROXY=http://your.proxy.name:8080 81 | 3. exit,退出ssh; 82 | 4. boot2docker ssh sudo /etc/init.d/docker restart,重启docker daemon; 83 | 84 | 代理配置好后,就可以从官方docker hub下载镜像了,运行hello world容器, 85 | 86 | docker run hello-world 87 | 88 | 如果没有问题,会有如下输出。 89 | 90 | ...... 91 | 92 | Hello from Docker. 93 | 94 | ...... 95 | 96 | hello world容器运行成功,代表OS X上构建的docker环境正常跑起来了,至于docker的使用,不是本篇文章的主题。 97 | 98 | #### docker hub TEG盖亚私服接入 99 | 100 | ... 101 | 102 | ### 问题fix 103 | 104 | #### certificate 105 | 106 | 执行docker version等命令,报certificate非法,如下: 107 | 108 | An error occurred trying to connect: Get https://192.168.59.1 03:2376/v1.19/version: x509: certificate is valid for 127.0.0.1, 10.0.2.15, not 192.168.59.103 109 | 110 | 这是boot2docker 1.7.0的一个bug,执行如下命令fix 111 | 112 | boot2docker ssh sudo /etc/init.d/docker restart 113 | 114 | 115 | -------------------------------------------------------------------------------- /post/mac android studio快捷键.md: -------------------------------------------------------------------------------- 1 | ## mac android studio快捷键 2 | 3 | --- 4 | 5 | #### 返回 6 | 7 | `cmd + [` 8 | 9 | #### 前进 10 | 11 | `cmd + ]` 12 | 13 | --- 14 | 15 | #### 快速全局搜索 16 | 17 | `shift + shift` 18 | 19 | --- 20 | 21 | #### 编码区域全屏 22 | `shift + cmd + f12` 23 | 24 | #### 类/接口继承关系 25 | 26 | `ctrl + h` 27 | 28 | #### 文件结构,快速跳转指定方法 29 | 30 | `cmd + f12` 31 | 32 | #### 函数调用路径 33 | 34 | `ctrl + alt + h` 35 | 36 | #### 当前窗口快速预览函数实现 37 | 38 | `cmd + y` 39 | 40 | --- 41 | 42 | #### 工程全局查找 43 | 44 | `shift + cmd + f` 45 | 46 | #### 查看常量值,函数文档,类文档等 47 | 48 | `ctrl + j` 49 | 50 | -------------------------------------------------------------------------------- /post/ndk开发本地代码优化--Linux本地代码优化.md: -------------------------------------------------------------------------------- 1 | ## ndk开发本地代码优化--Linux本地代码优化 2 | 3 | ndk开发本身就是对Android App热点逻辑的优化,即用C/C++执行流替代Java执行流,那么ndk开发的native代码是不是就不用优化了呢? 4 | 5 | 好像也不是吧,如果native代码写的不优美,native层一样会暴露一堆问题,比如内存泄露,函数热点等。 6 | 7 | 于是花一些业余时间,梳理梳理,ndk开发的本地代码如何优化? 8 | 9 | Android系统内核是Linux,Android上native代码优化策略应该和Linux一致,秉承这个想法,先从Linux上native程序优化方法入手,然后举一反三扩展到Android。 10 | 11 | ### Linux本地代码优化 12 | 13 | Linux本地代码优化,可选择的优化工具,问题定位的方法比较成熟,一堆调优工具中熟悉一些`Valgrind`,故先用`Valgrind`练手。 14 | 15 | `Valgrind`包含两大工具: 16 | 17 | * `Memcheck`,可谓是本地代码运行阶段内存泄露检测利器,所有的内存读写都被检查,hook malloc/new/free/delete调用; 18 | * `Callgrind`,该工具分析程序运行过程中,各个函数的调用情况以及所占整个程序运行期间的百分比; 19 | 20 | #### Valgrind - Memcheck工具的使用 21 | 22 | 安装`Valgrind`,可以在[官网](http://valgrind.org/)下载源码包make,由于我用的mac,故直接用homebrew安装。 23 | 24 | ``` 25 | brew install valgrind 26 | ``` 27 | 28 | 这里为了讲解`Memcheck`工具的使用,我们编写了一个小demo,构造有内存泄露的场景: 29 | 30 | ``` 31 | void f(void) 32 | { 33 | int* x = malloc(10 * sizeof(int)); 34 | x[10] = 0; 35 | } 36 | 37 | int main(void) 38 | { 39 | f(); 40 | return 0; 41 | } 42 | ``` 43 | 44 | 有内存泄露程序`test1`,可以看到f()函数开始位置malloc了一块堆内存,但f()函数返回前没有及时free这块堆内存,这显然是典型的内存泄露,同时`x[10] = 0;`语句涉及内存访问越界的问题,我们看看如何用`Memcheck`工具检测这个泄露点。 45 | 46 | 编译test1,注意添加`-g`参数保留行号信息,便于根据`Memcheck`工具输出日志反推源码位置, 47 | 48 | ``` 49 | gcc -g test1.c -o test1 50 | ``` 51 | 52 | 运行test1,使用`Memcheck`工具检测可能内存泄露点,`Valgrind`参数如下所示,这里还有一篇官网 [入门指南](http://valgrind.org/docs/manual/quick-start.html), 53 | 54 | ``` 55 | valgrind --tool=memcheck --leak-check=yes ./test1 56 | ``` 57 | 58 | 注意:`Memcheck`工具为每一个问题点输出日志信息,越界访问日志如下: 59 | 60 | ``` 61 | ==10331== Invalid write of size 4 62 | ==10331== at 0x100000F4C: f (test.c:29) 63 | ==10331== by 0x100000F73: main (test.c:34) 64 | ==10331== Address 0x100a73188 is 0 bytes after a block of size 40 alloc'd 65 | ==10331== at 0x100007EA1: malloc (vg_replace_malloc.c:303) 66 | ==10331== by 0x100000F43: f (test.c:28) 67 | ==10331== by 0x100000F73: main (test.c:34) 68 | ``` 69 | 70 | 10331是进程id,Invalid write表征问题类型,即写非法地址,后两行是函数调用栈信息,还有对应源码行号,这些信息足够让你定位到问题所在然后fix了。 71 | 72 | 内存泄露日志如下: 73 | 74 | ``` 75 | ==10331== 40 bytes in 1 blocks are definitely lost in loss record 19 of 59 76 | ==10331== at 0x100007EA1: malloc (vg_replace_malloc.c:303) 77 | ==10331== by 0x100000F43: f (test.c:28) 78 | ==10331== by 0x100000F73: main (test.c:34) 79 | ``` 80 | 81 | definitely lost表征问题类型,即内存泄露,后两行同样是函数调用栈信息,还有对应源码行号,这些信息也足够让你定位到问题所在然后fix了。 82 | 83 | 内存泄露日志有两种类型: 84 | * `definitely lost`,明显的内存泄露,遇到这类问题,必须要fix泄露点; 85 | * `probably lost`,可能的泄露点,这是由于C/C++语言指针处理的特点造成的,这部分可能不太准确,可以不用管; 86 | 87 | `Memcheck`工具输出的日志我们需要关注的就这些,最后还会有一个总结性的日志,如下: 88 | 89 | ``` 90 | ==10331== LEAK SUMMARY: 91 | ==10331== definitely lost: 40 bytes in 1 blocks 92 | ==10331== indirectly lost: 0 bytes in 0 blocks 93 | ==10331== possibly lost: 0 bytes in 0 blocks 94 | ==10331== still reachable: 0 bytes in 0 blocks 95 | ==10331== suppressed: 22,082 bytes in 182 blocks 96 | ``` 97 | 98 | 其实这一部分日志基本可以不用关注,这里还是简单说下各个分类的含义: 99 | 100 | * definitely lost: 明确地已经发生泄漏了,你需要修复这里; 101 | * indirectly lost: 说明内存泄漏发生在基于指针(pointer-based)的数据结构里,如果修复了definitely lost,那么indirectly lost也应该会消失; 102 | * possibly lost:可能内存泄漏,这是由于C/C++语言指针处理的特点造成的,这部分可能不太准确; 103 | * still reachable: 表示该内存在程序运行完的时候,仍旧有指针指向它; 104 | * suppressed:表明这个leak error已经被取消了,你可以忽略掉它; 105 | 106 | 上述内容就是`Valgrind`中`Memcheck`工具检测内存泄露的分析方法,是不是特别简单? 107 | 108 | 我们按照`Memcheck`工具输出的日志修改相应源码,重新编译运行,日志输出如下: 109 | 110 | ``` 111 | ==11603== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 16 from 16) 112 | ``` 113 | 114 | 日志输出,问题解决了,over~ 115 | 116 | #### Valgrind - Callgrind工具的使用 117 | 118 | native程序除了可能有内存泄露问题,可能还有函数热点问题,如果native程序执行特别耗时,就需要有一种方法定位到到底耗时在哪里,为什么耗时,是不是可以优化。 119 | 120 | Linux上做这件事情的工具很多,比如`perf`,`gprof`等,这里不对它们进行介绍,因为`Valgrind`中的`Callgrind`工具也可以完成同样的工作,我们就讲讲`Callgrind`工具挖掘函数热点的方法。 121 | 122 | 同样这里为了讲解`Callgrind`工具的使用,我们编写了一个小demo,构造函数热点的场景: 123 | 124 | ``` 125 | void test() 126 | { 127 | for (int i = 0; i < 10000; ++i) 128 | { 129 | int n = 1 + i; 130 | } 131 | 132 | return; 133 | } 134 | 135 | int main(int argc, char const *argv[]) 136 | { 137 | for (int i = 0; i < 100; ++i) 138 | { 139 | test(); 140 | } 141 | 142 | printf("hello world\n"); 143 | 144 | return 0; 145 | } 146 | ``` 147 | 148 | 上述存在函数热点的程序`test2`,可以看到test()函数做了10000次无意义的循环,同时main()函数也做了100次无意义的循环,我们看看如何用`Callgrind`工具检测这些热点。 149 | 150 | 编译、运行test2,使用`Callgrind`工具检测可能的函数热点, 151 | 152 | ``` 153 | gcc -g test2.c -o test2 154 | valgrind --tool=callgrind ./test2 155 | ``` 156 | 157 | 当前目录输出`callgrind.out.17023`,17023为进程号。 158 | 159 | 注意: 如果你调试的程序是多线程,你也可以在命令行中加一个参数`--separate-threads=yes`,此时当前目录除了输出`callgrind.out.进程号`,还会输出子线程结果文件`callgrind.out.进程号-线程编号`。 160 | 161 | 162 | ``` 163 | valgrind --tool=callgrind --separate-threads=yes ./test2 164 | ``` 165 | 166 | 拿到`callgrind.out.17023`文件如何分析?`callgrind.out.17023`是文本描述文件,好办法是图形化,图形化分析方式两种。 167 | 168 | ##### 方式一:借助dot工具转png 169 | 170 | 借助`dot`图形工具,将`callgrind.out.17023`文本描述文件转化为png。 171 | 172 | 首先安装`dot`图形工具,`dot`工具包含在`Graphviz`工具包中,先下载`Graphviz`,可以在[官网](http://www.graphviz.org/)下载源码包make,当然mac直接用homebrew安装。 173 | 174 | ``` 175 | brew install graphviz 176 | ```` 177 | 178 | 还需要把`Callgrind`工具输出的性能数据`callgrind.out.17023`转换成`dot`格式数据,还需要一个工具`gprof2dot`完成转换工作,github托管[链接](https://github.com/jrfonseca/gprof2dot),当然mac用homebrew安装。 179 | 180 | 181 | ``` 182 | brew install gprof2dot 183 | ```` 184 | 185 | `dot`、`gprof2dot`工具都安装ok,执行如下命令便在当前目录输出函数调用关系图,通过png图形我们可以很直观的知道哪段程序执行慢,并且了解相关调用关系。 186 | 187 | ``` 188 | gprof2dot -f callgrind callgrind.out.17023 | dot -Tpng -o output.png 189 | ```` 190 | 191 | 生成的图片示例: 192 | 193 | ![image](../img/valgrind01.png) 194 | 195 | 函数调用关系图很大,因为包含相当多的系统函数调用,我们不看这些,只看自己的函数调用关系,如下所示: 196 | 197 | ![image](../img/valgrind02.png) 198 | 199 | main()函数时间占比`52.95%`,main()函数又调用test()函数,test()函数时间占比`52.86%`,基本可以看出test()就是热点函数。 200 | 201 | 上述内容就是`Valgrind`中`Callgrind`工具检测函数热点的分析方法,是不是也特别简单? 202 | 203 | 当然如果你觉得输出png太呆板,可以试一试`xdot`工具,`xdot`工具支持交互方式查看函数调用关系图,当前底层还是调用`dot`工具,github托管[链接](https://github.com/jrfonseca/xdot.py),mac用homebrew安装。 204 | 205 | 206 | ``` 207 | brew install xdot 208 | ```` 209 | 210 | 安装`xdot`工具后,执行如下指令,就可以在GUI界面中以交互的方式查看函数调用关系图啦~ 211 | 212 | ``` 213 | gprof2dot -f callgrind callgrind.out.17023 | xdot 214 | ```` 215 | 216 | ![image](../img/valgrind03.png) 217 | 218 | 219 | 我们按照`Callgrind`工具输出的信息修改相应源码,重新编译运行,再次输出png,发现函数热点问题解决了,over~ 220 | 221 | ##### 方式二:借助KCachegrind可视化工具直接打开Callgrind输出文件 222 | 223 | 上述方式使用`dot`工具将`Callgrind`工具输出转换为png,总觉得还是太麻烦了,如果你喜欢简单,方式二绝对是一个好主意,同时我也推荐能用方式二就不要用方式一。 224 | 225 | `KCachegrind`可视化工具以非常友好的方式输出函数调用关系、函数耗时排名等信息,官网[链接](https://kcachegrind.github.io/html/Home.html)。 226 | 227 | `KCachegrind`GUI部分采用KDE构建或者QT构建,KDE构建的工具命名为`KCachegrind`,QT构建的工具命名为`QCachegrind`,可以按自己喜爱选择,我更偏向QT,所以选择QT构建的`KCachegrind`工具。 228 | 229 | 安装`QCachegrind`可以下载源码make,注意依赖QT环境,mac直接用homebrew: 230 | 231 | ``` 232 | // 依赖graphviz支持 233 | brew install qcachegrind --with-graphviz 234 | ``` 235 | 236 | 安装`QCachegrind`后,直接在终端启动,打开`Callgrind`工具输出的结果文件`callgrind.out.17023`,界面如下: 237 | 238 | ``` 239 | qcachegrind 240 | ``` 241 | 242 | ![image](../img/valgrind04.png) 243 | 244 | UI左侧按函数执行耗时排序,可以很清楚的知道哪一个函数耗时多,如图main()、test()函数执行耗时可观,是潜在的热点函数。其中,`Incl.`表示inclusive,包含其调用的函数的时间消耗;`Self`表示exclusive,具有排他性,只表示自身的时间消耗。 245 | 246 | UI右上部分显示当前函数详细信息,包括源码。 247 | 248 | UI右下角显示当前函数的局部调用图。 249 | 250 | 基本上使用`QCachegrind`工具可以很方便、快速的定位到函数热点,剩下的工作就是看源码解决问题了,是不是很简单,哈哈~ 251 | 252 | ##### 方式一,方式二简单总结 253 | 254 | 拿到`Callgrind`输出文件两种图形化分析方法,强烈推荐`KCachegrind`方式! 255 | 256 | #### Linux本地代码优化总结 257 | 258 | 可以看到Linux上本地代码优化还是有很多成熟工具辅助的。当然了工具只是辅助,优化的前提是自己对系统各个模块有一定的了解,同时系统在运行阶段暴露了内存、性能问题,只有在存在优化必要的时候才进行系统优化,避免为了优化而优化这种费力不讨好的工作。 259 | 260 | 正所谓工欲善其事,必先利其器,Linux上本地代码优化涉及的方法与工具我们都有了一个基本的认识,下一步就是将这些工具移植到Android平台,检测ndk开发的本地代码,下一篇文章详述。 261 | -------------------------------------------------------------------------------- /post/ndk开发本地代码优化--在Android上跑起Valgrind.md: -------------------------------------------------------------------------------- 1 | ## ndk开发本地代码优化--在Android上跑起Valgrind 2 | 3 | 承接上一篇[《ndk开发本地代码优化--Linux本地代码优化》](./ndk开发本地代码优化--在Android上跑起Valgrind.md),重点讲解了如何通过`Valgrind`工具分析Linux上的程序的`内存泄露`问题、`函数热点`问题。Android基于Linux内核而来,如果将`Valgrind`工具移植到Android平台,是不是可以像在Linux上一样,通过几条简单的命令,就可以检测出Android上运行的native程序的问题呢?答案是肯定的。 4 | 5 | Android上的程序分两种: 6 | 7 | * 一种是native程序,即在终端可调用执行的程序,如/system/bin目录下的一堆命令行工具,这一类native程序可以用移植到Android上的`Valgrind`工具分析,移植到Android上的`Valgrind`工具也就是一个native程序; 8 | * 另一种就是我们平常开发的App应用,App应用包括基于Framework用java写的一套上层逻辑,这一部分的优化可以考虑使用`Traceview`等工具,App应用还可能包括用C/C++编写的一些native库,这一部分native库的分析可以用移植到Android上的`Valgrind`工具; 9 | 10 | 综上所述,Android上的`Valgrind`工具可以用来分析`纯native的程序`和`App应用中的native库`。本文先介绍如何交叉编译`Valgrind`工具到Android,然后介绍`纯native的程序`的分析优化过程,`App应用中的native库`分析优化留待下一篇。 11 | 12 | ### Valgrind交叉编译 13 | 14 | ndk本身就提供了交叉编译工具链,详见目录`$NDK_HOME/toolchains`: 15 | 16 | ![image](../img/Valgrind05.png) 17 | 18 | 包括`arm`、`aarch64`(64位ARM架构)、`mipsel`(32位mips架构)、`mips64el`(64位mips架构)、`x86`、`x86_64`(64位X86架构)六种架构的编译工具链,其中每一种架构都有多种编译器可供选择,如`arm`架构可供选择的编译器有`gcc-4.8`、`gcc-4.9`、`clang-3.5`、`clang-3.6`、`clang-3.4-obfuscator`,其中`clang-3.4-obfuscator`是我自己定制的支持native代码混淆的clang编译器。 -------------------------------------------------------------------------------- /post/sqlite3_corruption.md: -------------------------------------------------------------------------------- 1 | #### Android Sqlite3 Corruption学习 2 | 3 | 1. 接口[`DatabaseErrorHandler `][1] 4 | 5 | 2. final 类[`DefaultDatabaseErrorHandler`][2] 6 | 调用`SQLiteDatabase`如下函数,配置`DatabaseErrorHandler`的实现,如果为null,默认采用`DefaultDatabaseErrorHandler` 7 | ```java 8 | SQLiteDatabase openOrCreateDatabase(String, android.database.sqlite.SQLiteDatabase.CursorFactory, DatabaseErrorHandler) 9 | SQLiteDatabase openDatabase(String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, DatabaseErrorHandler) 10 | ``` 11 | 12 | 3. 异常[`SQLiteDatabaseCorruptException`][3] 13 | 标识sqlite3数据库文件污染 14 | 15 | 4. 应用内需要捕获`SQLiteDatabaseCorruptException`实现自定义行为 16 | 定义`DatabaseErrorHandler `子类; 17 | 定义`SQLiteOpenHelper`子类,调用父类构造函数传递自定义`DatabaseErrorHandler `; 18 | 自定义`DatabaseErrorHandler `子类,参考`DefaultDatabaseErrorHandler`[源码][4]。 19 | 20 | 5. 导致数据库文件`corrupt`原因,[参考][5] 21 | * File overwrite by a rogue thread or process 22 | > a) Continuing to use a file descriptor after it has been closed; 23 | > b) Backup or restore while a transaction is active; 24 | > c) Deleting a hot journal; 25 | * File locking problems 26 | > a) Filesystems with broken or missing lock implementations; 27 | > b) Posix advisory locks canceled by a separate thread doing close(); 28 | > c) Two processes using different locking protocols; 29 | > d) Unlinking or renaming a database file while in use; 30 | > e) Multiple links to the same file; 31 | * Failure to sync 32 | > a) Disk drives that do not honor sync requests; 33 | > b) Disabling sync using PRAGMAs; 34 | * Disk Drive and Flash Memory Failures 35 | > Non-powersafe flash memory controllers; 36 | > Fake capacity USB sticks; 37 | * Memory corruption 38 | * Other operating system problems 39 | * Bugs in SQLite 40 | 41 | 6. 对于API大于等于11、API小于11的差别 42 | API小于11,压根没有`DatabaseErrorHandler`一说!! 43 | API大于等于11,SQLiteOpenHelper[源码](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/database/sqlite/SQLiteOpenHelper.java); 44 | API大于等于11,SQLiteDatabase[源码](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/database/sqlite/SQLiteDatabase.java); 45 | API小于11,SQLiteDatabase[源码](https://github.com/wangwang4git/platform_frameworks_base/blob/froyo-release/core%2Fjava%2Fandroid%2Fdatabase%2Fsqlite%2FSQLiteDatabase.java); 46 | 47 | [1]: http://developer.android.com/reference/android/database/DatabaseErrorHandler.html 48 | [2]: http://developer.android.com/reference/android/database/DefaultDatabaseErrorHandler.html 49 | [3]: http://developer.android.com/reference/android/database/sqlite/SQLiteDatabaseCorruptException.html 50 | [4]: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/database/DefaultDatabaseErrorHandler.java 51 | [5]: https://www.sqlite.org/howtocorrupt.html 52 | -------------------------------------------------------------------------------- /post/应用部署方式汇总.md: -------------------------------------------------------------------------------- 1 | #### 应用部署方式汇总 2 | 3 | 1. Supervisord 4 | > [主页][1] 5 | 6 | 2. 7 | 8 | [1]: http://supervisord.org/ -------------------------------------------------------------------------------- /post/当你想做成一件事的时候可能并没有准备好.md: -------------------------------------------------------------------------------- 1 | #### 当你想做成一件事的时候可能并没有准备好——微店准备篇 2 | 3 | ![](../img/Thinking.jpg) 4 | 5 | 这是写给妹子,也是给大家~ 6 | 7 | 妹子工作一年有余,一直不满足自己当前的工作,一方面不能发挥自己能力优势,另一方面缺乏成就感。 8 | 9 | 今年10月,我从北京跳槽来深圳,结束和妹子毕业后一年多的异地恋。 10 | 11 | 我在深圳新工作的这段时间,了解到香港代购的火爆,大到Apple类电子产品、名牌服装、高端化妆品,小到家庭日用,如奶粉、尿不湿、药膏牙刷、甚至姨妈巾!再者,我了解到妹子相对她身边的朋友同事,对市面上国内外名目繁多的化妆品略懂一点,她们同事一行多次去香港购置化妆品都是她帮忙参考建议,这是她的能力优势,这样的能力优势能够带来持续不断的成就感! 12 | 13 | 发现自己的能力优势,然后在自己的工作中加以运用,让工作的成就感持续不断,这样的工作就是你我喜欢的工作(我庆幸自己很喜欢Coding这份工作)。如果不能运用于自己的工作中,你需要做的就是用业余时间,选择一份副业开始(如何不影响现有工作?其实开始之后会慢慢平衡),运用自己的能力优势,坚持! 14 | 15 | 然后,妹子想开个香港化妆品微店,我认同,我负责开店技术。 16 | 17 | 同时我接触互联网一年有余,开店前也带着妹子有模有样的需求/市场/运营分析。妹子也是听着一愣一愣,佩服崇拜,事后结果却是,妹子觉得我其实是傻逼,当然也让我觉得现在的很多产品是傻逼。 18 | 19 | --- 20 | 下面是我的傻逼过程: 21 | 22 | 1.香港化妆品非常便宜!一是香港免税;二是香港化妆品名目繁多,打折促销力度大;三是香港化妆品专柜/超市假货很少很少。我们只赚一点点,冲着质优价低,买回来稍微一宣传不愁没人买。 23 | 24 | 2.第一期买哪些货?女生用的化妆品品牌个体性太强,化妆品类别也太多,第一次囤货也不敢囤高端一点的化妆品,那就囤代购圈量卖的好的、便宜一点的、适合冬天用的、具有普适性的。 25 | 26 | 3.购买市场在哪?朋友同学天天化妆,朋友的朋友同学的同学天天化妆,平均十个里面有一个买,购买量也是非常可观。这是什么,这是社交电商,这是让马云曾经都感到颤栗的模式。同时我们的朋友同学,购买力相对还不错,对于国外化妆品品牌也有一定的认知度,绝对的优质目标人群。 27 | 28 | 4.货屯回来了怎么卖?我和妹子一致反感当下朋友圈无节制的软广告,妹子不希望她也是别人反感的对象,尤其是好朋友。我推荐微店,理由包括(这些也是电商平台必备的产品属性):商品上线下线管理,库存管理,支付,订单管理,客户管理。 29 | 30 | 店名怎么取,如何亲切又不世俗?几乎斟酌一个上午,勉强憋出一个:小小美妆铺,缘由是妹子小名小小。 31 | 32 | 申请了微店,我亲力亲为将妹子囤的每一件化妆品,正反面拍照,照片裁剪压缩(纯粹为了节省大家浏览微店的手机流量),滤镜,加上商品名称,上架。 33 | 34 | 就这样?远远不够!让妹子换位思考,你进微店购买化妆品,你的关注点?商品介绍,包括适用什么肤质,主要功效有哪些,甚至使用方法。Google每一种商品介绍信息,由于图片的呈现效果更好,每一种商品的介绍信息我们只用图片呈现,绝不用文字! 35 | 36 | 就这样?我还找了设计师,让她帮忙设计了微店Logo。 37 | 38 | 为了朋友方便添加妹子微信询问商品信息,为了减少朋友搜索妹子微信号的输入成本,确定妹子的微信号:xiaoxiaogouhk,即小小购HongKong的拼音。微信号直接来自微店主题的拼音,一律小写字母,不需要切换不同符号,切换成本最低。 39 | 40 | 支付,我们推荐微店支付,也支持支付宝支付。 41 | 42 | 快递,调查顺丰、EMS、申通、中通、圆通、韵达、天天等价格,我确定了四个档次:(顺丰),(申通、中通、EMS),(圆通、韵达),(天天),为了保证快递质量,只发韵达以上档次的快递。快递的化妆品,不管多少,都会有可爱的礼品袋帮大家包裹好。 43 | 44 | 5.怎么推广微店?朋友圈,推广文案怎么写才显亲切?怎么在文案中体现折扣信息?同样让妹子换位思考,什么样的推广文案,她的好朋友们适合帮她转发? 45 | 46 | 我说一下结果,上线推广,只有我、妹子、妹子的几个好朋友帮忙发朋友圈,微店当天访问量破百! 47 | 48 | 6.微店如何持续推广,持续运营?既然妹子和我不认同朋友圈无节制广告,那就用微信公众号,定期输出公众号软文来推广。 49 | 50 | 考虑很多朋友想购买一些国外化妆品,但碍于不了解而不敢尝试,由此确定公众号主旨:分享国内外化妆品品牌知识/选购知识。当然公众号会在分享知识之余推送微店新品通知,诱导朋友浏览微店。 51 | 52 | 我们也追求公众号文章的内容、排版。想想现在有些公众号,内容东拼西凑,排版五颜六色,什么用户体验。 53 | 54 | 公众号文章什么时间发,阅读量会最大化?查询数据表明,大约晚上10点发~ 55 | 56 | --- 57 | 58 | 经过上述步骤,微店上线、推广一气呵成,就是大家在朋友圈看到的我的那条状态,那一天公众号文章阅读量同样破百。 59 | 60 | 过程其实挺艰辛,我和妹子每天工作都很忙,准备这些事只能晚上,1点之后入睡已然很是平常,但妹子很是坚持,妹子说她喜欢,觉得充实。大家很多时候会想做一些事情,却碍于一些因素放弃,其实挺可惜,为什么不快速试错一把? 61 | 62 | 那微店上线第一周多少笔订单?4单,其中1单还是我体验购物流程的订单!谢谢阿江、汉林、曾敏! 63 | 64 | 第一周为什么会如此失败? 65 | 66 | 《创新者的窘境》封面的一句话再好不过:“就算我们把每件事情都做对了,仍有可能错失城池”。 67 | 68 | 更何况我们做的每一件事似乎都不对了,但没事,接下来妹子快速调整。 69 | 70 | PS:按产品思维,公众号文章内容太长是大忌,碎片化阅读、快节奏生活、浮躁内心,已经让很多人越来越少的阅读有长度的文章了,人性包括了懒。 71 | -------------------------------------------------------------------------------- /post/我在简网上生成的GoodMan001.apk破解笔记.md: -------------------------------------------------------------------------------- 1 | #### 我在简网上生成的GoodMan001.apk破解笔记 2 | 3 | 1. App界面组件研究 4 | > `SplashActivity`后进入主界面,主界面样式由`MainActivityFactory`工厂类提供,生成具体导航样式主界面 5 | > `buildCubeNavIntent`三种导航样式主界面:`GridMenuActivity`,`VerticalMenuActivity`,`SplitMenuActivity` 6 | > `buildFixNavIntent`一种导航样式主界面:`FixNavActivity` 7 | > 两种其他样式主界面:`MommyActivity`,`AppSquareActivity` 8 | 9 | 2. 相关导航实现研究 10 | > `ActionBar`的实现基于开源框架`ActionBarSherlock`,但是当前Google已经出了一套Support v7包向下兼容Android低版本,当前`ActionBarSherlock`已经用的不多了 11 | > `Drawer导航`的实现基于开源框架`SlidingMenu`,同样当前Google已经出了一套Support v4包向下兼容Android低版本,当前`SlidingMenu`已经用的不多了 12 | 13 | 3. 相关交互实现研究 14 | > 类似于`Flipboard`的翻页效果基于`openaphid/android-flip` 15 | > 下拉刷新基于``实现`chrisbanes/Android-PullToRefresh` 16 | > 类似于`Pinterest`的瀑布流基于`GDG-Korea/PinterestLikeAdapterView` 17 | > 似乎简网在App里面自己做了一个浏览器组件,基于`lobobrowser`,`steadystate.css`,需要参看App里哪里有相关应用 18 | 19 | 4. 相关功能实现研究 20 | > `推送`基于`个推` 21 | > `统计`基于`有盟` 22 | > `定位`基于`百度地图sdk` 23 | > `xml`基于`FasterXML/jackson-core` 24 | > `一维码,二维码扫描`基于`ZBar` 25 | > `社会化分享`发现腾讯相关jar包 26 | 27 | #### 总结 28 | > 以上是反编译apk文件的结果,可以看出为什么一个功能非常简单的站点最后的apk文件也是近7M的原因,因为相关功能基本都是`原生框架实现` -------------------------------------------------------------------------------- /post/数据结构总结一二.md: -------------------------------------------------------------------------------- 1 | ## 数据结构一二 2 | 3 | #### 哈希表 4 | 哈希函数,尽可能寻找`均匀(uniform)`的哈希函数。 5 | 6 | 常用构造哈希函数方法: 7 | 1. 直接定址法 8 | > 线性函数 H(key) = a * key + b 9 | 2. 数字分析法 10 | 3. 平方取中法 11 | 4. 折叠法 12 | 5. 除留余数法 13 | > 一般情况取质数或者不包含小于20的质因数的合数 14 | 6. 随机数法 15 | 16 | 哈希`冲突(collision)`,只能尽可能少,不能完全避免。 17 | 18 | 常见处理冲突方法: 19 | 1. 开放定址法 20 | > Hi = (H(key) + di) MOD m,根据di序列取法不同有`线性探测再散列`、`二次探测再散列`、`伪随机探测再散列` 21 | 2. 再哈希法 22 | > Hi = RHi(key) 23 | 3. 链地址法 24 | 4. 建立一个公共溢出区 25 | 26 | 哈希表效率分析: 27 | 1. 哈希表裝填因子 28 | 29 | #### 插入排序 30 | * 时间复杂度:O(n^2) 31 | * 空间复杂度:O(1) 32 | * 稳定 33 | 34 | 代码: 35 | 36 | ```c 37 | void insertSort(int arr[], int size) { 38 | int i = 1; 39 | for (; i < size; ++i) { 40 | // 判断是否需要插入 41 | if (arr[i] < arr[i - 1]) { 42 | int temp = arr[i]; 43 | arr[i] = arr[i - 1]; 44 | // 后移 45 | int j = i - 2; 46 | for (; j >= 0; --j) { 47 | if (temp < arr[j]) { 48 | arr[j + 1] = arr[j]; 49 | } else { 50 | break; 51 | } 52 | } 53 | arr[j + 1] = temp; 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | 60 | 改进算法: 61 | * 折半插入排序 62 | * 2-路插入排序 63 | * 希尔排序 64 | > 分割若干子序列插入排序;待整个序列`基本有序`,全体插入排序 65 | > 算法时间复杂度与所选`增量序列`有关 66 | 67 | Shell排序代码: 68 | 69 | ```c 70 | // 一趟shell排序 71 | void shellInsert(int arr[], int size, int dk) { 72 | int i = dk; 73 | for (; i < size; ++i) { 74 | // 判断是否需要插入 75 | if (arr[i] < arr[i - dk]) { 76 | int temp = arr[i]; 77 | arr[i] = arr[i - dk]; 78 | // 后移 79 | int j = i - dk - dk; 80 | for (; j >= 0; j -= dk) { 81 | if (temp < arr[j]) { 82 | arr[j + dk] = arr[j]; 83 | } else { 84 | break; 85 | } 86 | } 87 | arr[j + dk] = temp; 88 | } 89 | } 90 | } 91 | 92 | void shellSort(int arr[], int size) { 93 | int dk[] = { 5, 3, 1 }; 94 | int i = 0; 95 | for (; i < sizeof(dk) / sizeof(dk[0]); ++i) { 96 | shellInsert(arr, size, dk[i]); 97 | } 98 | } 99 | ``` 100 | 101 | #### 冒泡排序 102 | * 时间复杂度:O(n^2) 103 | * 空间复杂度:O(1) 104 | * 稳定 105 | 106 | 代码: 107 | 108 | ```c 109 | void bubbleSort(int arr[], int size) { 110 | // 有交换则置1 111 | int flag = 1; 112 | // n-1趟冒泡 113 | int i = size - 1; 114 | for (; i >= 1 && flag; --i) { 115 | flag = 0; 116 | int j = 0; 117 | for (; j < i; ++j) { 118 | if (arr[j] > arr[j + 1]) { 119 | // 交换 120 | int temp = arr[j]; 121 | arr[j] = arr[j + 1]; 122 | arr[j + 1] = temp; 123 | flag = 1; 124 | } 125 | } 126 | } 127 | } 128 | ``` 129 | 130 | 改进部分:设置标志位,当第i趟冒泡没有发生交换操作,则提前跳出。 131 | 132 | #### 快速排序 133 | * 时间复杂度:O(nlog(n)) 134 | * 空间复杂度:O(log(n)) 135 | * 不稳定 136 | 137 | 具体步骤:先对整体序列一趟划分`partition`,分割为独立两子序列,递归调用划分。 138 | 139 | 划分操作:选取`pivot`,比它小移到左侧,比它大移到右侧。 140 | 141 | 代码: 142 | 143 | ```c 144 | // 划分操作 145 | int partition(int arr[], int low, int high) { 146 | int pivot = arr[low]; 147 | while (low < high) { 148 | while (low < high && arr[high] >= pivot) { 149 | --high; 150 | } 151 | // 放置左侧 152 | arr[low] = arr[high]; 153 | while (low < high && arr[low] <= pivot) { 154 | ++low; 155 | } 156 | // 放置右侧 157 | arr[high] = arr[low]; 158 | } 159 | arr[low] = pivot; 160 | return low; 161 | } 162 | 163 | void quickSort(int arr[], int low, int high) { 164 | // 长度大于1 165 | if (low < high) { 166 | int pivotIndex = partition(arr, low, high); 167 | // 对低子表递归 168 | quickSort(arr, low, pivotIndex - 1); 169 | // 对高子表递归 170 | quickSort(arr, pivotIndex + 1, high); 171 | } 172 | } 173 | ``` 174 | 175 | 改进部分:划分操作,对pivot选取,比如`三者取中法`。 176 | 177 | #### 选择排序 178 | * 时间复杂度:O(n^2) 179 | * 空间复杂度:O(1) 180 | * 稳定 181 | 182 | 具体步骤:每一趟在后续记录中选取最小的记录作为有序序列中第i个记录。 183 | 184 | 代码: 185 | 186 | ```c 187 | void selectSort(int arr[], int size) { 188 | // 选择趟数size - 1 189 | int i = 0; 190 | for (; i < size - 1; ++i) { 191 | // 选择最小 192 | int index = i; 193 | int j = i + 1; 194 | for (; j < size; ++j) { 195 | if (arr[index] > arr[j]) { 196 | index = j; 197 | } 198 | } 199 | if (index != i) { 200 | // 交换 201 | int temp = arr[i]; 202 | arr[i] = arr[index]; 203 | arr[index] = temp; 204 | } 205 | } 206 | } 207 | ``` 208 | 209 | #### 树形选择排序(败者树、胜者树) 210 | * 时间复杂度:O(nlog(n)) 211 | * 空间复杂度:O(n) 212 | * 稳定 213 | 214 | 如图所示: 215 |
![alt text](../img/数据结构02.png "败者树")
216 | 217 | 代码: 218 | 219 | ```java 220 | public static int[] completeBinaryTree(int[] arr) { 221 | int newSize = arr.length * 2 - 1; 222 | int[] resultArr = new int[newSize]; 223 | if (arr.length == 1) { 224 | resultArr[0] = arr[0]; 225 | return resultArr; 226 | } 227 | for (int i = 0; i < arr.length; ++i) { 228 | resultArr[newSize - 1 - i] = arr[arr.length - 1 - i]; 229 | } 230 | if (arr.length == 2) { 231 | resultArr[0] = arr[0] > arr[1] ? arr[1] : arr[0]; 232 | } 233 | // 构建完全二叉树(败者树) 234 | int minIndex = (newSize - 1 - 1) / 2; 235 | while (minIndex > 0) { 236 | for (int i = newSize - 1; i > 0; i -= 2) { 237 | minIndex = (i - 1) / 2; 238 | resultArr[minIndex] = resultArr[i] > resultArr[i - 1] ? resultArr[i - 1] : resultArr[i]; 239 | } 240 | } 241 | 242 | return resultArr; 243 | } 244 | 245 | public static void treeSort(int[] arr) { 246 | if (arr == null || arr.length == 0) { 247 | return; 248 | } 249 | 250 | int[] tree = completeBinaryTree(arr); 251 | int arrLen = arr.length; 252 | int treeLen = tree.length; 253 | 254 | for (int i = 0; i < arrLen; ++i) { 255 | arr[i] = tree[0]; 256 | 257 | // 查找 258 | int minIndex = 0; 259 | for (int j = treeLen - 1; j > 0; --j) { 260 | if (tree[0] == tree[j]) { 261 | minIndex = j; 262 | tree[j] = Integer.MAX_VALUE; 263 | break; 264 | } 265 | } 266 | 267 | // 败者树恢复 268 | while (minIndex > 0) { 269 | if (minIndex % 2 == 0) { 270 | tree[(minIndex - 1) / 2] = tree[minIndex] > tree[minIndex - 1] ? tree[minIndex - 1] : tree[minIndex]; 271 | minIndex = (minIndex - 1) / 2; 272 | } else { 273 | tree[minIndex / 2] = tree[minIndex] > tree[minIndex + 1] ? tree[minIndex + 1] : tree[minIndex]; 274 | minIndex = minIndex / 2; 275 | } 276 | } 277 | } 278 | 279 | } 280 | ``` 281 | 282 | 283 | #### 堆排序 284 | * 时间复杂度:O(nlog(n)) 285 | * 空间复杂度:O(1) 286 | * 不稳定 287 | 288 | 具体步骤:**建堆**,输出堆顶元素,调整堆。 289 | > 建堆过程,即是反复调整堆的过程。 290 | 291 | 代码: 292 | 293 | ```c 294 | // 堆调整(大顶堆),堆采用数组存储,下标1, 2, ..., n 295 | void heapAdjust(int arr[], int s, int len) { 296 | int rc = arr[s]; 297 | // 调整 298 | int j = 2 * s; 299 | for (; j <= len; j *= 2) { 300 | // 选取左右子树最大值下标 301 | if (j < len && arr[j] < arr[j + 1]) { 302 | ++j; 303 | } 304 | // 判断是否需要终止调整过程 305 | if (rc >= arr[j]) { 306 | break; 307 | } else { 308 | arr[s] = arr[j]; 309 | s = j; 310 | } 311 | } 312 | arr[s] = rc; 313 | } 314 | 315 | void heapSort(int arr[], int size) { 316 | // 建堆 317 | int i = size / 2; 318 | for (; i > 0; --i) { 319 | heapAdjust(arr, i, size); 320 | } 321 | // 排序 322 | for (i = size; i > 1; --i) { 323 | // 交换 324 | int temp = arr[i]; 325 | arr[i] = arr[1]; 326 | arr[1] = temp; 327 | // 调整 328 | heapAdjust(arr, 1, i - 1); 329 | } 330 | } 331 | ``` 332 | 333 | #### 归并排序 334 | 2-路归并排序 335 | * 时间复杂度:O(nlog(n)) 336 | * 空间复杂度:O(n) 337 | * 稳定 338 | 339 | 代码: 340 | 341 | ```java 342 | // 归并操作,arr[i..m]与arr[m+1..n]归并到arr2[i..n] 343 | void merge(int arr[], int arr2[], int i, int m, int n) { 344 | int k = i; 345 | int j = m + 1; 346 | for (; i <= m && j <= n; ++k) { 347 | if (arr[i] < arr[j]) { 348 | arr2[k] = arr[i++]; 349 | } else { 350 | arr2[k] = arr[j++]; 351 | } 352 | } 353 | // 剩余项处理 354 | for (; i <= m; ++i) { 355 | arr2[k++] = arr[i]; 356 | } 357 | for (; j <= n; ++j) { 358 | arr2[k++] = arr[j]; 359 | } 360 | } 361 | 362 | // 归并递归项 363 | void mergeSort(int arr[], int arr2[], int low, int high) { 364 | if (low == high) { 365 | arr2[low] = arr[low]; 366 | } else { 367 | int m = (low + high) / 2; 368 | int arr3[8]; 369 | mergeSort(arr, arr3, low, m); 370 | mergeSort(arr, arr3, m + 1, high); 371 | // 归并 372 | merge(arr3, arr2, low, m, high); 373 | } 374 | } 375 | ``` 376 | 377 | #### 外部排序 378 | 步骤: 379 | 1. 将外存n个记录的文件分割成m个长度为l的子文件(可以采用hash); 380 | 2. 依次读入子文件,内部排序,写回外存,称这些有序子文件为`归并段`; 381 | 3. 对这些`归并段`,采用k-路平衡归并,直到整个文件有序。 382 | > 对m个初始归并段进行k-路平衡归并,归并趟数 s = [logk(m)]下取整。 383 | 384 | 比如`2-路平衡归并`: 385 |
![alt text](../img/数据结构01.png "2-路平衡归并")
386 | 387 | 简单的递归实现,输入`5个归并段`,2-路平衡归并: 388 | 389 | ```java 390 | public static List merge(List data1, List data2) { 391 | if (data1 == null && data2 != null) { 392 | return data2; 393 | } 394 | if (data1 != null && data2 == null) { 395 | return data1; 396 | } 397 | if (data1 == null && data2 == null) { 398 | return new ArrayList(); 399 | } 400 | 401 | List result = new ArrayList(); 402 | 403 | int i = 0, j = 0; 404 | for (; i < data1.size() && j < data2.size(); ) { 405 | if (data1.get(i) < data2.get(j)) { 406 | result.add(data1.get(i)); 407 | ++i; 408 | } else { 409 | result.add(data2.get(j)); 410 | ++j; 411 | } 412 | } 413 | 414 | for (; i < data1.size(); ++i) { 415 | result.add(data1.get(i)); 416 | } 417 | for (; j < data2.size(); ++j) { 418 | result.add(data2.get(j)); 419 | } 420 | 421 | return result; 422 | } 423 | 424 | public static List mergeSort(List> data) { 425 | if (data == null || data.isEmpty()) { 426 | return new ArrayList(); 427 | } 428 | 429 | if (data.size() == 1) { 430 | return new ArrayList(data.get(0)); 431 | } 432 | 433 | if (data.size() == 2) { 434 | return merge(data.get(0), data.get(1)); 435 | } 436 | 437 | int size = data.size(); 438 | List temp1 = mergeSort(data.subList(0, size / 2)); 439 | List temp2 = mergeSort(data.subList(size / 2, size)); 440 | return merge(temp1, temp2); 441 | } 442 | 443 | // 测试 444 | List> data = new ArrayList>(); 445 | List data1 = new ArrayList(Arrays.asList(1, 3)); 446 | List data2 = new ArrayList(Arrays.asList(5, 7)); 447 | List data3 = new ArrayList(Arrays.asList(0, 2)); 448 | List data4 = new ArrayList(Arrays.asList(4, 6)); 449 | List data5 = new ArrayList(Arrays.asList(8, 9)); 450 | data.add(data1); 451 | data.add(data2); 452 | data.add(data3); 453 | data.add(data4); 454 | data.add(data5); 455 | 456 | System.out.println(mergeSort(data).toString()); 457 | ``` 458 | 459 | 比较操作可以借助`胜者树`、`败者树`加以改进。 460 | 461 | -------------------------------------------------------------------------------- /post/服务提供者框架.md: -------------------------------------------------------------------------------- 1 | #### 服务提供者框架 2 | 3 | ###### 1. 定义 4 | 多个服务提供者实现一个服务,系统为客户端提供多个实现,并把它们从多个实现中解耦出来。 5 | 6 | 相关组件: 7 | * 服务接口 8 | * 服务提供者接口 9 | * 提供者注册API 10 | * 服务访问API 11 | 12 | ###### 2. 实现,参考JDBC、JMS 13 | ![](../uml/服务提供者框架01.png) 14 | 15 | ###### 3. 代码 16 | * Service.java 17 | 18 | ```java 19 | public interface Service { 20 | 21 | public void service(); 22 | 23 | } 24 | ``` 25 | 26 | * ServiceImpl.java 27 | 28 | ```java 29 | public class ServiceImpl implements Service { 30 | 31 | @Override 32 | public void service() { 33 | System.out.println("service by " + ServiceImpl.class.getSimpleName()); 34 | } 35 | 36 | } 37 | ``` 38 | 39 | * Provider.java 40 | 41 | ```java 42 | public interface Provider { 43 | 44 | public Service newService(); 45 | 46 | } 47 | ``` 48 | 49 | * ProviderImpl.java 50 | 51 | ```java 52 | public class ProviderImpl implements Provider { 53 | 54 | static { 55 | Services.registerProvider(ProviderImpl.class.getName(), new ProviderImpl()); 56 | } 57 | 58 | @Override 59 | public Service newService() { 60 | return new ServiceImpl(); 61 | } 62 | 63 | } 64 | ``` 65 | 66 | * 测试 67 | 68 | ```java 69 | public class Main { 70 | 71 | public static void main(String[] args) { 72 | try { 73 | Class.forName("com.tencent.victor.ProviderImpl"); 74 | Service service = Services.newService("com.tencent.victor.ProviderImpl"); 75 | service.service(); 76 | } catch (ClassNotFoundException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | 81 | } 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /post/简单说说HashMap,HashTable,ConcurrentHashTable.md: -------------------------------------------------------------------------------- 1 | ## 简单说说HashMap,HashTable,ConcurrentHashTable 2 | 3 | #### 背景 4 | * 线程不安全的`HashMap` 5 | 多线程环境下,使用`HashMap`进行`put`操作会引起死循环,导致`CPU`利用率接近100% 6 | > 参见:[不正当使用HashMap导致cpu 100%的问题追究][4],原因为多线程下`HashMap`扩容,可能会创建出两个新数组容器,造成链表闭环,导致死循环 7 | 8 | * 效率低下的`HashTable` 9 | 使用`synchronized`保证线程安全 10 | 线程竞争激烈情况下效率非常低下 11 | 12 | * `ConcurrentHashMap`的锁分段技术 13 | 锁分拆技术(lock spliting) 14 | 锁分离技术(lock striping) 15 | > 分拆锁(lock spliting)就是若原先的程序中多处逻辑都采用同一个锁,但各个逻辑之间又相互独立,就可以拆(Spliting)为使用多个锁,每个锁守护不同的逻辑。分拆锁有时候可以被扩展,分成可大可小加锁块的集合,并且它们归属于相互独立的对象,这样的情况就是分离锁(lock striping)。 16 | > (摘自《Java并发编程实践》) 17 | 18 | `ConcurrentHashMap`类图 19 | ![alt text](../img/ConcurrentHashMap类图.jpg "类图") 20 | > 其中要注意,`putIfAbsent`方法返回值的处理,参见[ConcurrentMap.putIfAbsent(key,value) 用法讨论][6] 21 | 22 | `ConcurrentHashMap`结构图 23 | ![alt text](../img/ConcurrentHashMap结构图.jpg "类图") 24 | 25 | #### 示例 26 | 27 | ```java 28 | private static final ConcurrentHashMap mConcurrentHashMap = new ConcurrentHashMap<>(); 29 | 30 | Thread thread = new Thread(new Runnable() { 31 | 32 | @Override 33 | public void run() { 34 | // TODO Auto-generated method stub 35 | 36 | final Random random = new Random(100); 37 | 38 | for (int i = 0; i < 1000; ++i) { 39 | new Thread(new Runnable() { 40 | 41 | @Override 42 | public void run() { 43 | // TODO Auto-generated method stub 44 | 45 | Integer integer = random.nextInt(10); 46 | 47 | mConcurrentHashMap.putIfAbsent(integer, "integer = " + integer); 48 | 49 | } 50 | }, "child-thread" + (i + 1)).start(); 51 | } 52 | } 53 | }, "main-thread"); 54 | thread.start(); 55 | 56 | System.out.println("mConcurrentHashMap.size() = " + mConcurrentHashMap.size()); 57 | Iterator> iterator = mConcurrentHashMap.entrySet().iterator(); 58 | while (iterator.hasNext()) { 59 | Entry entry = iterator.next(); 60 | System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue()); 61 | } 62 | ``` 63 | 64 | #### TO DO LIST 65 | 1. [ConcurrentHashMap能完全替代HashTable吗?][5] 66 | 2. 源码分析... 67 | 68 | 69 | #### 参考文献: 70 | 1. [聊聊并发(四)深入分析ConcurrentHashMap][1] 71 | 2. [深入剖析ConcurrentHashMap(1)][2] 72 | 3. [深入剖析ConcurrentHashMap(2)][3] 73 | 74 | [1]: http://ifeve.com/concurrenthashmap/ 75 | [2]: http://ifeve.com/java-concurrent-hashmap-1/ 76 | [3]: http://ifeve.com/java-concurrent-hashmap-2/ 77 | [4]: http://ifeve.com/hashmap-infinite-loop/ 78 | [5]: http://ifeve.com/concurrenthashmap-vs-hashtable/ 79 | [6]: http://wxl24life.iteye.com/blog/1746794 -------------------------------------------------------------------------------- /post/简单说说Synchronized,ReentrantLock.md: -------------------------------------------------------------------------------- 1 | ## 简单说说Synchronized,ReentrantLock 2 | 3 | #### 背景,应该就是`Synchronized`的缺点 4 | * `Synchronized`产生原因,`原子性(Atomicity)`与`可见性(visibility)`,其中可见性涉及到`JMM`的`happens-before`原语,这又涉及到`Memory Barrier`,推荐这篇文章[《并发导论》][1] 5 | 6 | * `Synchronized`使用示例 7 | 8 | ```java 9 | synchronized (lockObject) { 10 | // update object state 11 | } 12 | ``` 13 | 14 | * 但是`Synchronized`存在的缺点是:它无法中断一个正在等候获得锁的线程,也无法通过投票得到锁...同时多个线程争用同一个锁,jvm的总体开销有点大 15 | 16 | #### `ReentrantLock` 17 | * 默认是不公平(`unfair`)锁 18 | * 有一个与锁相关的`获取计数器`,获取一次加一,获取两次加二,注意释放两次才代表真正释放锁 19 | 20 | * `ReentrantLock`使用示例 21 | 22 | ```java 23 | ReentrantLock lock = new ReentrantLock(); 24 | lock.lock(); 25 | try { 26 | // update object state 27 | } 28 | finally { 29 | lock.unlock(); 30 | } 31 | ``` 32 | 33 | * 相比较`Synchronized`,`ReentrantLock`在调度的开支上花的时间相对少,从而为更高的吞吐率留下空间,实现了更有效的CPU利用 34 | 35 | #### `Condition` 36 | * 对比`JDK 1.4`以前版本中的`Object.wait()`,`Object.notify()`,`Object.notifyAll()`,上述三种线程间同步方式有问题,可问题是什么,需要继续学习,留在`todolist`... 37 | 38 | * `Condition`提供`await()`,`signal()`,`signalAll()`用于实现上述功能 39 | 40 | * `Condition`使用示例,需要和`ReentrantLock`配合,简单使用参见[《关于JAVA Condition 条件变量》][3] 41 | ```java 42 | ReentrantLock lock = new ReentrantLock(); 43 | Condition condition = lock.newCondition(); 44 | lock.lock(); 45 | try { 46 | condition.await(); 47 | // update object state 48 | } 49 | finally { 50 | lock.unlock(); 51 | } 52 | 53 | condition.signal(); 54 | condition.signalAll(); 55 | ``` 56 | #### 那么在什么时候使用`ReentrantLock` 57 | * 需要实现`时间锁等候`,`可中断锁等候`,`无块结构锁`,`多个条件变量`,`锁投票`,`高度争用`的情形 58 | 59 | #### TODOLIST 60 | 1. [《ReentrantLock代码剖析之ReentrantLock.lock》][4] 61 | 2. `Object.wait()`,`Object.notify()`,`Object.notifyAll()`会存在的问题到底是什么? 62 | 63 | #### 参考文献 64 | 1. [Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制][2] 65 | 66 | 67 | [1]: http://ifeve.com/concurrency-paper/ 68 | [2]: http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 69 | [3]: http://my.oschina.net/leoson/blog/106452 70 | [4]: http://www.cnblogs.com/MichaelPeng/archive/2010/02/12/1667947.html 71 | -------------------------------------------------------------------------------- /post/获取Android设备ID.md: -------------------------------------------------------------------------------- 1 | #### 获取Android设备ID 2 | 3 | 获取Android设备Unique ID,麻烦困难,原因是Google官方就不认可该行为。 4 | > This worries us, because we think that tracking such identifiers isn’t a good idea, and that there are better ways to achieve developers’ goals. 5 | 6 | 但是如果确实需要获取,可以结合如下方式。 7 | 8 | ###### 1. Identifying Devices 9 | ```java 10 | TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE); 11 | String imei = tm.getDeviceId(); 12 | ``` 13 | 对于不同制式手机,返回不同格式ID,官方描述如下: 14 | 15 |
![alt text](../img/Android_UniqueId01.png "getDeviceId()")
16 | 17 | 该方法存在问题: 18 | * 非手机设备:对于`wifi-only`、`music players`等设备,无`device id`; 19 | * `刷机`、`恢复出厂值`会重置`device id`; 20 | * 需要`权限`,`READ_PHONE_STATE`; 21 | * `Bug`,少数手机返回垃圾信息,如`zeros`、`asterisks`。 22 | 23 | ###### 2. Mac Address 24 | ```java 25 | WifiManager wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE); 26 | WifiInfo wifiInfo = wifiManager.getConnectionInfo(); 27 | String mac = wifiInfo.getMacAddress(); 28 | ``` 29 | 返回`wifi`或者`bluetooth`硬件地址。 30 | 31 | 该方法存在问题: 32 | * `无Wifi`设备; 33 | * `Wifi`关闭,可能不返回; 34 | * 需要`权限`,`ACCESS_WIFI_STATE`。 35 | 36 | ###### 3. Serial Number 37 | ```java 38 | String serial = android.os.Build.SERIAL; 39 | ``` 40 | 该方法存在问题: 41 | * Android 2.3+才可用。 42 | 43 | ###### 4. ANDROID_ID 44 | ```java 45 | String androidId = Secure.getString(this.getBaseContext().getContentResolver(), Secure.ANDROID_ID); 46 | ``` 47 | Android设备第一次启动产生、存储的`64-bit`。 48 | 49 | 该方法存在问题: 50 | * Android <= 2.1 / Android >= 2.3可靠、稳定,但在`2.2`并非100%可靠; 51 | * 主流厂商的设备,`2.2`有一个bug,就是每个设备都会产生`相同ANDROID_ID`:9774d56d682e549c; 52 | * `刷机`会重置。 53 | 54 | ###### 5. Tracking Installations 55 | Google官方给出的框架,获取`installation ID`: 56 | ```java 57 | public class Installation { 58 | private static String sID = null; 59 | private static final String INSTALLATION = "INSTALLATION"; 60 | 61 | public synchronized static String id(Context context) { 62 | if (sID == null) { 63 | File installation = new File(context.getFilesDir(), INSTALLATION); 64 | try { 65 | if (!installation.exists()) 66 | writeInstallationFile(installation); 67 | sID = readInstallationFile(installation); 68 | } catch (Exception e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | return sID; 73 | } 74 | 75 | private static String readInstallationFile(File installation) throws IOException { 76 | RandomAccessFile f = new RandomAccessFile(installation, "r"); 77 | byte[] bytes = new byte[(int) f.length()]; 78 | f.readFully(bytes); 79 | f.close(); 80 | return new String(bytes); 81 | } 82 | 83 | private static void writeInstallationFile(File installation) throws IOException { 84 | FileOutputStream out = new FileOutputStream(installation); 85 | String id = UUID.randomUUID().toString(); 86 | out.write(id.getBytes()); 87 | out.close(); 88 | } 89 | } 90 | ``` 91 | 92 | ###### 6. 网上给出的获取设备ID框架 93 | ```java 94 | import android.content.Context; 95 | import android.content.SharedPreferences; 96 | import android.provider.Settings.Secure; 97 | import android.telephony.TelephonyManager; 98 | 99 | import java.io.UnsupportedEncodingException; 100 | import java.util.UUID; 101 | 102 | public class DeviceUuidFactory { 103 | protected static final String PREFS_FILE = "device_id.xml"; 104 | protected static final String PREFS_DEVICE_ID = "device_id"; 105 | 106 | protected static UUID uuid; 107 | 108 | public DeviceUuidFactory(Context context) { 109 | if( uuid ==null ) { 110 | synchronized (DeviceUuidFactory.class) { 111 | if( uuid == null) { 112 | final SharedPreferences prefs = context.getSharedPreferences( PREFS_FILE, 0); 113 | final String id = prefs.getString(PREFS_DEVICE_ID, null ); 114 | 115 | if (id != null) { 116 | // Use the ids previously computed and stored in the prefs file 117 | uuid = UUID.fromString(id); 118 | } else { 119 | final String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); 120 | 121 | // Use the Android ID unless it's broken, in which case fallback on deviceId, 122 | // unless it's not available, then fallback on a random number which we store 123 | // to a prefs file 124 | try { 125 | if (!"9774d56d682e549c".equals(androidId)) { 126 | uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8")); 127 | } else { 128 | final String deviceId = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getDeviceId(); 129 | uuid = deviceId!=null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID(); 130 | } 131 | } catch (UnsupportedEncodingException e) { 132 | throw new RuntimeException(e); 133 | } 134 | 135 | // Write the value out to the prefs file 136 | prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString() ).commit(); 137 | } 138 | } 139 | } 140 | } 141 | } 142 | 143 | 144 | /** 145 | * Returns a unique UUID for the current android device. As with all UUIDs, this unique ID is "very highly likely" 146 | * to be unique across all Android devices. Much more so than ANDROID_ID is. 147 | * 148 | * The UUID is generated by using ANDROID_ID as the base key if appropriate, falling back on 149 | * TelephonyManager.getDeviceID() if ANDROID_ID is known to be incorrect, and finally falling back 150 | * on a random UUID that's persisted to SharedPreferences if getDeviceID() does not return a 151 | * usable value. 152 | * 153 | * In some rare circumstances, this ID may change. In particular, if the device is factory reset a new device ID 154 | * may be generated. In addition, if a user upgrades their phone from certain buggy implementations of Android 2.2 155 | * to a newer, non-buggy version of Android, the device ID may change. Or, if a user uninstalls your app on 156 | * a device that has neither a proper Android ID nor a Device ID, this ID may change on reinstallation. 157 | * 158 | * Note that if the code falls back on using TelephonyManager.getDeviceId(), the resulting ID will NOT 159 | * change after a factory reset. Something to be aware of. 160 | * 161 | * Works around a bug in Android 2.2 for many devices when using ANDROID_ID directly. 162 | * 163 | * @see http://code.google.com/p/android/issues/detail?id=10603 164 | * 165 | * @return a UUID that may be used to uniquely identify your device for most purposes. 166 | */ 167 | public UUID getDeviceUuid() { 168 | return uuid; 169 | } 170 | } 171 | ``` 172 | 173 | 174 | #### 参考文献 175 | 1. [Identifying App Installations][1] 176 | 2. [How can I get the UUID of my Android phone in an application?][2] 177 | 178 | 179 | [1]: http://android-developers.blogspot.com/2011/03/identifying-app-installations.html 180 | [2]: http://stackoverflow.com/questions/5088474/how-can-i-get-the-uuid-of-my-android-phone-in-an-application -------------------------------------------------------------------------------- /post/设计模式学习.md: -------------------------------------------------------------------------------- 1 | #### 设计模式学习 2 | 3 | 断断续续写点设计模式的学习笔记吧~ 4 | 5 | 1. [BUILDER模式](./BUILDER模式.md) 6 | 2. [服务提供者框架](./服务提供者框架.md) 7 | 3. [COMMAND模式](./COMMAND模式.md) 8 | 9 | 10 | #### 资源推荐 11 | 1. [Design pattern samples in Java.](https://github.com/iluwatar/java-design-patterns) -------------------------------------------------------------------------------- /resume/resume.md: -------------------------------------------------------------------------------- 1 |

王 汪

2 |
联系方式:189-1134-3690    电子邮箱:wangwang4whu@126.com
3 |
醉心技术,博观而约取;天道酬勤,厚积而薄发。
4 |
[https://github.com/wangwang4git][1]
5 | 6 | #### 教育背景 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
2011.09 - 2013.06武汉大学计算机学院硕士(保研)GPA3.84(全院TOP20%)
2007.09 - 2011.06武汉大学电子信息学院学士GPA3.47(专业TOP5%)
21 | 22 | #### 工作经历 23 | **1. 2013.07 - 至今,北京搜狐技术中心,软件研发工程师** 24 | * 部门Android开发主程 25 | 26 | #### 专业技能 27 | * 熟悉常用数据结构、算法 28 | * 熟悉常用设计模式 29 | * 熟练掌握Java,了解JVM 30 | * 熟练掌握Android应用开发、适配、优化、逆向分析(Android平台经验2年+) 31 | * 熟练掌握C/C++ 32 | * 熟悉嵌入式开发、Linux开发 33 | * 了解iOS开发 34 | 35 | #### 项目经历 36 | **1. 搜狐快站 - 站点管理APP** 37 | * 移动站内容管理工具,涉及素材、文章、订单、商品、论坛管理,统计数据展示。 38 | * 主导APP开发,Android Design,Gradle,RenderScript,自定义RichEdit组件。 39 | 40 | **2. 推送SDK** 41 | * 快站用户向自己的站点生成的APP推送信息,当前支持文本、链接两种信息格式。 42 | * 主导SDK开发,逆向Baidu Push SDK,Go,MQTT。 43 | 44 | **3. 搜狐快站 - 站点生成APP** 45 | * 帮用户建立的站点输出WebView套壳APP。 46 | * 主导模板APP开发,自动化Build Bash脚本。 47 | 48 | **4. 搜狐相册APP** 49 | * Android手机本地图片云备份、管理工具,支持时间、地点、相册三维度图片浏览。 50 | * 负责APP开发,NDK,MediaStore,UIL库学习。 51 | 52 | **5. 武汉大学BBS APP** 53 | * BBS Android客户端第一版,登陆用户看贴、回帖、发帖,账户管理,匿名用户看贴、回帖。 54 | * 主导团队开发,Git。 55 | 56 | **6. 武汉大学孔子学院 - 视频点播APP** 57 | * 教学视频点播iPad客户端,基于苹果公司HTTP Live Streaming流媒体协议。 58 | * 负责软件开发,FFmpeg,SOAP。 59 | 60 | **7. 河南省电网电压稳定监控系统** 61 | * 国家电网项目《基于WAMS的电压稳定在线监控系统》,武大电气学院与河南省电网合作申请。 62 | * 负责软件开发,Qt,部署平台Lenovo RH680/RHEL 5.7。 63 | 64 | #### 主要奖项 65 | * 2008.09,国家励志奖学金 66 | * 2009.12,数学建模竞赛(电机工程学会杯)全国三等奖 67 | * 2010.08,嵌入式系统专题邀请赛(Intel杯)全国二等奖 68 | * 2010.09,国家奖学金、武汉大学科研优秀奖 69 | * 2012.07,有道难题-网易创新大赛全国三等奖 70 | * 2013.07,武汉大学优秀毕业生 71 | 72 | [1]: https://github.com/wangwang4git "fork me" 73 | -------------------------------------------------------------------------------- /resume/深圳互联网公司汇总.md: -------------------------------------------------------------------------------- 1 | #### 深圳互联网公司汇总 2 | 3 | * 腾讯 4 | 5 | > 职位挑选:搜索`Android`、`深圳`、`技术类` 6 | * SNG01-手机QQ iOS/android终端软件开发工程师(深圳) 第一页 7 | * SNG03-QQ Android开发工程师(深圳) 第二页 8 | * SNG06-Andriod开发工程师(深圳) 第四页 9 | 10 | * 百度 11 | * 京东 12 | > 拉勾投递简历 Android开发工程师 http://www.lagou.com/jobs/159579.html# 13 | 14 | * 网信金融 15 | > 拉勾投递简历 Android开发工程师 http://www.lagou.com/jobs/179997.html 16 | 17 | * 知名移动互联网公司 18 | > 猎聘投递简历 Android 高级研发工程师 http://a.liepin.com/5681301/job_5329998.shtml 19 | -------------------------------------------------------------------------------- /resume/王汪-公司OA头像.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/resume/王汪-公司OA头像.jpg -------------------------------------------------------------------------------- /resume/王汪-北京搜狐-Android开发-武汉大学研究生.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/resume/王汪-北京搜狐-Android开发-武汉大学研究生.doc -------------------------------------------------------------------------------- /resume/王汪-北京搜狐-Android开发-武汉大学研究生.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/resume/王汪-北京搜狐-Android开发-武汉大学研究生.pdf -------------------------------------------------------------------------------- /resume/王汪-武汉大学-计算机-毕业.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/resume/王汪-武汉大学-计算机-毕业.doc -------------------------------------------------------------------------------- /resume/面试自我介绍.md: -------------------------------------------------------------------------------- 1 | 面试官,您好,我叫王汪。 2 | 本科在武汉大学电信学院,主要学习电信硬件相关知识。本科成绩不错,多次获得奖学金,同时对数学和嵌入式兴趣挺大,积极参加相应学科竞赛,比如全部数模竞赛,全国嵌入式竞赛,并获取不错成绩。研究生在武汉大学计算机学院,研究移动音频编解码。研一开始接触Android、iOS,期间几个实验室项目主要是流媒体相关APP,比如简历上列举的项目经历6iPad点播App。期间还和小伙伴去杭州参加网易有道应用创新大赛,带领小伙伴开发武汉大学BBSAndroid客户端,整个过程坚定了工作从事Android的想法。 3 | 去年7月初入职北京搜狐,刚开始在相册组,和另一名同事负责搜狐云相册App开发,相册App主要是一款用户手机照片云备份App。春节前开始独立负责部门内部创新项目搜狐快站Android端开发。搜狐快站是一个面向商家、公司、学校这些用户搭建移动端站点的平台,支持图文、电商、论坛。我负责开发用户站点Android端内容管理App,用户站点生成App、推送系统Android SDK,对应简历上项目经历123。 4 | 一年多工作,积极学习Android,试着看一些开源库、Android源码、Android新特性,也进一步学习Java方面知识,包括JDK容器源码,Java虚拟机等。同时也积极学习新技术,积极看一些互联网行业新闻,包括有学习Go语言,对Go那一套高并发机制的了解。 5 | 总的来说,自己是一个喜爱技术喜爱移动互联网的人。 6 | -------------------------------------------------------------------------------- /uml/builder模式01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/uml/builder模式01.png -------------------------------------------------------------------------------- /uml/command模式01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/uml/command模式01.png -------------------------------------------------------------------------------- /uml/服务提供者框架01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangwang4git/just-do/197bc5ad559343643f04fb86708925fdbe1d3300/uml/服务提供者框架01.png --------------------------------------------------------------------------------