├── .gitignore ├── Android-Java ├── AndroidOfficialDistributeGuild.md ├── JSR133.md ├── EffectiveAndroidUI.md ├── android-best-practices.md ├── 3077508.md ├── DependencyInjection.md ├── ProGuard.md ├── LaunchModeIntentFlags.md ├── AOP.md ├── AndroidOfficialDevelopGuild-Tools.md ├── 50AndroidHacks.md ├── GoogleIO2015.md ├── JavaSynchorinization.md ├── AndroidBasic.md ├── AndroidOfficialDevelopGuild-BestPractice4Performance.md ├── StylingViews.md ├── MVVM.md ├── MultiDex.md ├── AndroidDevSkillTree.md ├── Kotlin.md ├── AndroidOfficialDevelopGuild-BestPractice4BackgroundJob.md ├── App-Dev-Notes.md ├── RxAndroidBestPractice.md ├── WebRTC.md ├── MaterialDesign.md ├── NewInAndroid.md ├── AndroidOfficialDevelopGuild-BuildAppWithContactsAndSignIn.md ├── MemoryLeak.md ├── HandlingRuntimeChanges.md ├── TDD.md ├── Canvas-Drawables.md ├── JavaObjectMemoryUsage.md ├── AndroidAnimation.md ├── CustomViewViewGroup.md ├── AndroidOfficialDevelopGuild-BestPractice4UserInput.md ├── MVP.md ├── Fragments.md ├── GoogleDev100Days.md ├── AndroidOfficialDevelopGuild-BestPractice4UserInterface.md ├── AndroidOfficialMaterialDesignGuild.md ├── AndroidOfficialDevelopGuild-BuildAppWithConnectivity.md ├── Gradle.md ├── AndroidOfficialDevelopGuild-GettingStarted.md ├── Rx.md ├── AndroidTouchSystem.md ├── AndroidProjectArch.md ├── DesignPatternsInsideAndroid.md ├── InsideJVM.md ├── UnderstandAndroidSourceCode.md ├── AndroidTDD.md ├── AndroidOfficialDevelopGuild-BuildingAppsWithContentSharing.md └── AndroidOfficialDevelopGuild-BestPractice4InteractionAndEngagement.md ├── assets ├── MVP.png ├── MVP1.png ├── rx_lift.png ├── rx_lift_2.png ├── flux_detail.png ├── rx_subscribe.png ├── rxflux_arch.png ├── mvp-controller.png ├── volley-request.png ├── AdvancedTextView.png ├── androidstack-02.png ├── flux-graph-simple.png ├── intercept_example.png ├── jvm_memory_area.png ├── menu-refactored.jpg ├── perf_tune_process.png ├── clean_architecture1.png ├── java_generic_terms.png ├── layout_three_steps.png ├── transitions_diagram.png ├── PercentRelativeLayout.png ├── ignorant_view_example.png ├── memory_mode_generation.png ├── interested_view_example.png ├── AndroidPlatformComponents.png ├── clean_architecture_android.png ├── java_class_loader_relation.jpeg ├── mobile_radio_state_machine.png ├── react_component_lifecycle.png ├── xflavour_folder_structure.png ├── clean_architecture_evolution.png ├── groovy_closure_invoke_syntax.png ├── FullFragmentAndActivityLifeCycle.png ├── java_primitive_types_mem_usage.png ├── android_activity_life_cycle_change.png ├── IterateCollectionPerformanceCompare.png ├── android_activity_life_cycle_change2.png └── patterns-scrolling-techniques_flex_space_image_xhdpi_003.webm ├── cover ├── logo.png └── background.jpg ├── Backend ├── REST.md ├── Feed.md └── DBNF.md ├── config.json ├── README.md ├── misc ├── Parcer.md ├── OOP6Principles.md ├── BetterDesignWithImmutableParams.md ├── copy.md └── DarkTime.md ├── Frontend ├── Flux.md ├── JavascriptGoodParts.md ├── React-basic.md └── ES6.md ├── MOOC └── UTAustinX_UT.9.01x.md ├── TODO.md ├── SUMMARY.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /.settings/ 3 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDistributeGuild.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Android-Java/JSR133.md: -------------------------------------------------------------------------------- 1 | ##JSR133(Java memory model) 2 | + 指令重排是很常见的 3 | + -------------------------------------------------------------------------------- /assets/MVP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/MVP.png -------------------------------------------------------------------------------- /assets/MVP1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/MVP1.png -------------------------------------------------------------------------------- /cover/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/cover/logo.png -------------------------------------------------------------------------------- /assets/rx_lift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/rx_lift.png -------------------------------------------------------------------------------- /assets/rx_lift_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/rx_lift_2.png -------------------------------------------------------------------------------- /cover/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/cover/background.jpg -------------------------------------------------------------------------------- /assets/flux_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/flux_detail.png -------------------------------------------------------------------------------- /assets/rx_subscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/rx_subscribe.png -------------------------------------------------------------------------------- /assets/rxflux_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/rxflux_arch.png -------------------------------------------------------------------------------- /assets/mvp-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/mvp-controller.png -------------------------------------------------------------------------------- /assets/volley-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/volley-request.png -------------------------------------------------------------------------------- /assets/AdvancedTextView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/AdvancedTextView.png -------------------------------------------------------------------------------- /assets/androidstack-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/androidstack-02.png -------------------------------------------------------------------------------- /assets/flux-graph-simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/flux-graph-simple.png -------------------------------------------------------------------------------- /assets/intercept_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/intercept_example.png -------------------------------------------------------------------------------- /assets/jvm_memory_area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/jvm_memory_area.png -------------------------------------------------------------------------------- /assets/menu-refactored.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/menu-refactored.jpg -------------------------------------------------------------------------------- /assets/perf_tune_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/perf_tune_process.png -------------------------------------------------------------------------------- /assets/clean_architecture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/clean_architecture1.png -------------------------------------------------------------------------------- /assets/java_generic_terms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/java_generic_terms.png -------------------------------------------------------------------------------- /assets/layout_three_steps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/layout_three_steps.png -------------------------------------------------------------------------------- /assets/transitions_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/transitions_diagram.png -------------------------------------------------------------------------------- /Android-Java/EffectiveAndroidUI.md: -------------------------------------------------------------------------------- 1 | ## UI上的一些高效方式/最佳实践 2 | 3 | 地址: -------------------------------------------------------------------------------- /assets/PercentRelativeLayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/PercentRelativeLayout.png -------------------------------------------------------------------------------- /assets/ignorant_view_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/ignorant_view_example.png -------------------------------------------------------------------------------- /assets/memory_mode_generation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/memory_mode_generation.png -------------------------------------------------------------------------------- /assets/interested_view_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/interested_view_example.png -------------------------------------------------------------------------------- /assets/AndroidPlatformComponents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/AndroidPlatformComponents.png -------------------------------------------------------------------------------- /assets/clean_architecture_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/clean_architecture_android.png -------------------------------------------------------------------------------- /assets/java_class_loader_relation.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/java_class_loader_relation.jpeg -------------------------------------------------------------------------------- /assets/mobile_radio_state_machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/mobile_radio_state_machine.png -------------------------------------------------------------------------------- /assets/react_component_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/react_component_lifecycle.png -------------------------------------------------------------------------------- /assets/xflavour_folder_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/xflavour_folder_structure.png -------------------------------------------------------------------------------- /Android-Java/android-best-practices.md: -------------------------------------------------------------------------------- 1 | ## Futurice公司安卓团队的建议 2 | 3 | 地址: 4 | -------------------------------------------------------------------------------- /assets/clean_architecture_evolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/clean_architecture_evolution.png -------------------------------------------------------------------------------- /assets/groovy_closure_invoke_syntax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/groovy_closure_invoke_syntax.png -------------------------------------------------------------------------------- /assets/FullFragmentAndActivityLifeCycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/FullFragmentAndActivityLifeCycle.png -------------------------------------------------------------------------------- /assets/java_primitive_types_mem_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/java_primitive_types_mem_usage.png -------------------------------------------------------------------------------- /assets/android_activity_life_cycle_change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/android_activity_life_cycle_change.png -------------------------------------------------------------------------------- /assets/IterateCollectionPerformanceCompare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/IterateCollectionPerformanceCompare.png -------------------------------------------------------------------------------- /assets/android_activity_life_cycle_change2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/android_activity_life_cycle_change2.png -------------------------------------------------------------------------------- /Android-Java/3077508.md: -------------------------------------------------------------------------------- 1 | # List.toArray()再强转是一定会失败的 2 | 3 | 地址: -------------------------------------------------------------------------------- /Backend/REST.md: -------------------------------------------------------------------------------- 1 | # REST API设计 2 | ## http status code 3 | + http://racksburg.com/choosing-an-http-status-code/ 4 | + https://developer.github.com/v3/issues/ 5 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Android&Java 技术笔记", 3 | "introduction": "个人笔记汇总。", 4 | "path": { 5 | "images":"assets", 6 | "toc" : "SUMMARY.md" 7 | } 8 | } -------------------------------------------------------------------------------- /assets/patterns-scrolling-techniques_flex_space_image_xhdpi_003.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Piasy/notes/HEAD/assets/patterns-scrolling-techniques_flex_space_image_xhdpi_003.webm -------------------------------------------------------------------------------- /Android-Java/DependencyInjection.md: -------------------------------------------------------------------------------- 1 | #依赖注入(以Dagger 2为例) 2 | 3 | ##Custom scope 4 | Scope提供了作用域范围内的单例特性,对象的引用在scope内会一直存在,例如:ApplicationScope,ActivityScope。 5 | Scope其实就是一个Component的对象,Scope的注解就是在Component上的。 -------------------------------------------------------------------------------- /Android-Java/ProGuard.md: -------------------------------------------------------------------------------- 1 | # ProGuard 2 | 3 | + 参考:[\[译\] ProGuard 选项](http://chaosleong.github.io/blog/2015/12/03/%E8%AF%91-ProGuard-%E9%80%89%E9%A1%B9) 4 | + [流行开源库ProGuard选项生成器](https://proguard.herokuapp.com/) -------------------------------------------------------------------------------- /Android-Java/LaunchModeIntentFlags.md: -------------------------------------------------------------------------------- 1 | # Launch mode 和 Intent flags专题 2 | 3 | ## 参考阅读 4 | + http://blog.csdn.net/liuhe688/article/details/6754323/ 5 | + http://talentprince.github.io/blog/2014/12/18/activity-chang-yong-shu-xing-yu-launch-mode-zheng-jie/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 技术笔记 2 | 3 | > 个人日常学习之余记下的笔记,仅供参考。 4 | 5 | ## 版权信息 6 | 7 | 作者 Piasy 授权极客学院转载,如需转载,请标明出处。 8 | 9 | >联系方式:[xz4215@gmail.com](mailto:xz4215@gmail.com) 10 | 11 | >项目地址: 12 | 13 | >GitBook版本: -------------------------------------------------------------------------------- /Android-Java/AOP.md: -------------------------------------------------------------------------------- 1 | # Aspect Oriented Programming(AOP) 2 | 3 | ## refs 4 | + http://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/ 5 | + [AOP实现analytic](http://blog.egorand.me/going-aspect-oriented-with-aspectj-and-google-analytics/),另可参考[Tracklytics](https://github.com/orhanobut/tracklytics) 6 | -------------------------------------------------------------------------------- /Backend/Feed.md: -------------------------------------------------------------------------------- 1 | # Feed系统的设计 2 | 3 | ## 推模式 4 | + 一个用户产生内容后,为其每个粉丝都创建一个feed,即推到每个粉丝那里; 5 | + 每个内容保存多份,空间消耗比较大,但是读的时候比较简单,直接用feed的所有者去选择即可; 6 | 7 | ## 拉模式 8 | + 每个内容只保存一份,读的时候从各个来源读; 9 | + 读的时候DB操作压力比较大; 10 | 11 | ## 推拉结合 12 | + 粉丝较多的用户,他们产生的内容采取拉模式,粉丝少的采取推模式; 13 | + 或者其他结合方式; 14 | 15 | ## 参考资料 16 | + [百万用户时尚分享网站feed系统扩展实践](http://www.csdn.net/article/2013-11-07/2817430-design-decisions-for-scaling-your-high-traffic-feeds) -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-Tools.md: -------------------------------------------------------------------------------- 1 | #Developer tools 2 | 3 | ##Allocation tracker使用 4 | + 用于记录app在profile周期中所有的内存分配,包括对象的调用栈、大小、分配代码 5 | + 查找短周期内相同对象在相似代码位置的创建与销毁 6 | + 查找代码中可能导致内存使用效率低的位置 7 | + 使用过程 8 | + 打开工程,编译安装运行 9 | + Android Studio的Android Monitor选项卡中,Memory tab,点击Start Allocation Tracking 10 | + 在需要测试的场景中与应用交互 11 | + 交互完成后,再次点击Start Allocation Tracking 12 | + .alloc文件将在一会儿之后传输到工程根目录下的captures文件夹中,有可能传输会需要较长时间,需要耐心等待 13 | -------------------------------------------------------------------------------- /misc/Parcer.md: -------------------------------------------------------------------------------- 1 | #[使用词法分析、语法分析工具进行带语法文本处理](https://mp.weixin.qq.com/s?__biz=MzA3NDM0ODQwMw==&mid=210542047&idx=1&sn=9c813595c727c0fa028651b9dcdbab12&scene=1&srcid=TnBi1Ixk8247K63uF86D&key=dffc561732c2265107a83ae80cf78dd17d8685e8fd87d050901f4d4e4b420b70e9d4c8ae1af928b4364f83c331fea171&ascene=0&uin=MzMwMTQwNjU1&devicetype=iMac+MacBookPro11%2C2+OSX+OSX+10.10.5+build(14F27)&version=11020201&pass_ticket=cdWJp0BSgHizAPjaS94Os5Ape6%2FGJ%2BFY%2FOCyf%2FeongPxPoK3T4URIkSStJ6hekuO) 2 | 3 | + flex/bison 4 | + clojure下的神器instaparse 5 | + jison: javascript bison 6 | + [antlr4](https://github.com/antlr) 7 | + parsec Haskell下的神器 -------------------------------------------------------------------------------- /Android-Java/50AndroidHacks.md: -------------------------------------------------------------------------------- 1 | # 《50 Android Hacks》一书 2 | 3 | ## `include`, `merge`, `ViewStub` 的使用 4 | + `include` 可以用 `merge` 作为根节点,这样合并时可以省去一层 layout 5 | + `tools:showIn` 可以在预览中显示完整的 layout 6 | + `ViewStub` 可以懒惰加载 layout 7 | 8 | ## 获取 view 的高度和宽度 9 | + 可以在 `Activity.onCreate` 中 post 一个 runnable 到 view 上,runnable 执行时,view 的正确尺寸就可以获取到了 10 | 11 | ## 利用 ProGuard 移除 log 代码 12 | 13 | ``` 14 | # remove log call 15 | -assumenosideeffects class android.util.Log { 16 | public static *** d(...); 17 | } 18 | -assumenosideeffects class timber.log.Timber { 19 | public static *** d(...); 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /Android-Java/GoogleIO2015.md: -------------------------------------------------------------------------------- 1 | #Google IO 2015摘要 2 | 3 | ##Android development tools 4 | + [Android Design Support Library](http://android-developers.blogspot.com/2015/05/android-design-support-library.html):方便的Material Design开发; 5 | + Improving the Grade Plugin & Build System:build大提速; 6 | + [Cloud Test lab](https://developers.google.com/cloud-test-lab/):超多机型,模拟器&&真机; 7 | + Emulator:更稳定; 8 | + Android Studio C/C++ Support:IDE支持(代码分析,reformat)、native代码调试支持、自动代码生成支持; 9 | + New Support Annotations:助于代码分析,Bug分析; 10 | + Data Binding:新的设计模式,MVVM; 11 | + Profiling Tools:功能更强大,更便捷; 12 | + Developer Services:广告、统计等服务的IED集成; 13 | + New Features in Upcoming Releases:theme设置预览、布局design界面blue print、预览界面拖拽支持等; -------------------------------------------------------------------------------- /Android-Java/JavaSynchorinization.md: -------------------------------------------------------------------------------- 1 | #Java同步机制 2 | 3 | ##synchronized关键字 4 | + 有两种用法(或三种) 5 | + synchronized方法 6 | + synchronized代码块 7 | + synchronized(this) 8 | + synchronized(object) 9 | + synchronized方法有两种效果 10 | + 对于同一个对象,多线程调用synchronized方法将只有一个线程能够进入执行,其他线程等待(不仅仅是对同一个方法来说,如果一个类的多个方法使用了synchronized修饰,记为func1, func2...,那多线程访问时,如果线程A正在访问func1,其他线程不仅访问func1会被阻塞,访问func2也会被阻塞) 11 | + 当正在执行的线程退出该方法时,其对对象状态(成员变量)造成的修改,将立即同步到其他线程中 12 | + synchronized(this) 13 | 和synchronized方法有同样的效果,即synchronized方法,一个非synchronized但是整个方法体都是用synchronized(this)包括起来的方法,两者效果是等价的 14 | 即可以理解为synchronized方法其实就是方法体都用synchronized(this)所括起来的方法 15 | + synchronized(object) 16 | 相当于把加锁对象由this改为object,对同一个object synchronized括起来的代码块,同时只能有一个线程执行 17 | -------------------------------------------------------------------------------- /Backend/DBNF.md: -------------------------------------------------------------------------------- 1 | # 关系型数据库设计范式 2 | 3 | ## NF1 4 | 每一个属性(每一列)都是不可再拆分的。类似通过json字符串保存一个object/array到一列中,就是很不合理的设计。 5 | 6 | ## NF2 7 | + 完全函数依赖:属性组X确定的情况下,属性y就可以确定,则称y函数依赖于X,如果对于X的任意真子集X1,依赖不成立,则称y完全函数依赖于X,否则称部分函数依赖。 8 | + 码(key):码是属性组(可以多于一列),当一个属性组确定之后,这一行的所有其他属性都可以确定,那这个属性组就是码。 9 | + 主属性:码中的属性就是主属性,其余的就是非主属性。 10 | + NF2:NF1的基础上,没有非主属性对码的部分函数依赖。 11 | + 第一步:找出数据表中所有的码。 12 | + 第二步:根据第一步所得到的码,找出所有的主属性。 13 | + 第三步:数据表中,除去所有的主属性,剩下的就都是非主属性了。 14 | + 第四步:查看是否存在非主属性对码的部分函数依赖。 15 | + 如果不满足NF2,通过拆分表,使之满足NF2的过程,成为模式分解。 16 | 17 | ## NF3 18 | + 在NF2的前提下,消除非主属性对码的传递函数依赖。 19 | 20 | ## BCNF 21 | + 消除主属性对码的部分函数依赖、传递函数依赖。 22 | 23 | ## 参考资料 24 | + [解释一下关系数据库的第一第二第三范式? - 回答作者:知乎用户](http://www.zhihu.com/question/24696366/answer/29189700?utm_campaign=webshare&utm_source=weibo&utm_medium=zhihu) -------------------------------------------------------------------------------- /Frontend/Flux.md: -------------------------------------------------------------------------------- 1 | # Flux基础 2 | 3 | ![flux_detail.png](../assets/flux_detail.png) 4 | 5 | ## 组件 6 | + Dispatcher 7 | + Store 8 | + Cache data 9 | + Expose public getters to access data (never have public setters) 10 | + Respond to specific actions from the dispatcher 11 | + Always emit a change when their data changes 12 | + Only emit changes during a dispatch 13 | + View 14 | + Are react components that are controlled by a container 15 | + Have all of the UI and rendering logic 16 | + Receive all information and callbacks as props 17 | + Container 18 | + Are react components that control a view 19 | + Primary job is to gather information from stores and save it in their state 20 | + Have no props and no UI logic 21 | + Action 22 | + Describe a user's action, are not setters. (e.g. `select-page` not `set-page-id`) -------------------------------------------------------------------------------- /Android-Java/AndroidBasic.md: -------------------------------------------------------------------------------- 1 | # [Android develop basics, based on Android 5.0](http://www.vogella.com/tutorials/Android/article.html) 2 | 3 | ## 什么是Android 4 | + 安卓系统架构图: 5 | 6 | ![AndroidPlatformComponents.png](../assets/AndroidPlatformComponents.png) 7 | 8 | ## 安卓开发工具 9 | + Android RunTime(ART) 10 | + 安卓5.0使用ART技术,在安装应用的时候,应用程序代码就被翻译为机器码,加快应用程序运行; 11 | + 将导致程序代码增大30%左右,但能加速运行、节约电量; 12 | + `dex2oat`工具负责将.dex文件转换为ELF格式的文件,其中包含dex代码、native代码、meta-data等; 13 | + ART中的垃圾回收也进行了优化,以加速应用程序的休眠与恢复; 14 | 15 | + 从源码到安卓应用程序的过程 16 | + Java编译器将java源码编译为`.class`文件; 17 | + SDK中的`dx`工具将`.class`文件打包为`.dex`文件(Dalvik Executable),这一过程会对重复信息进行优化; 18 | + `aapt`工具将`.dex`文件和资源文件(XML、图片等)打包为`.apk`文件(Android Package); 19 | + `adb`工具将`.apk`文件安装到设备上,并启动应用程序; 20 | 21 | + 安全性:由Linux内核保证 22 | + 每个应用程序在底层系统中都有唯一的user和group id; 23 | + 应用程序的文件是私有的,只有对应的user才能访问; 24 | + 每个应用程序运行在独立的进程; -------------------------------------------------------------------------------- /MOOC/UTAustinX_UT.9.01x.md: -------------------------------------------------------------------------------- 1 | # UTAustinX_UT.9.01x: Effective Thinking Through Mathematics 2 | 3 | ## 1.2 4 | 我的解答过程 5 | 6 | + 从小规模开始思考 7 | + 4个中挑最重的需要几次对比 8 | + 并未将其当做个例,而是一个可扩展的问题 9 | + 更多或者更少个,需要几次对比 10 | + 如果两次不够,需要多少次 11 | + 但是忽略了一个问题:一次并非只能对比两个 12 | + 在此前提下,得出了一个通项公式 13 | + 意识到前提错误之后,和视频中的学生一样,4 V 4 + 1 14 | + 但在发现不可行之后依然在寻求通项,并未寻求其他方式,从而得出了错误结论 15 | 16 | 17 | + 敢于尝试,即便是最基本,甚至错误的尝试,从试错中逐步改良 18 | + 系统性思考,思维缜密逻辑清晰 19 | + 扩展性思维很好,但是首要任务是着眼于当前的问题,但是当前问题解决之后保持思考是个好习惯 20 | 21 | ## 1.3 22 | 我的解答过程 23 | 24 | + 首先尝试 25 | + 首先尝试是好事,但是简单尝试之后应该总结规律,得出程序化的过程 26 | + 尝试某种意义上说是搜索,尝试几次之后,应该知道整个搜索的执行过程 27 | + 搜索过程中,所有的情况都需要考虑,而不是仅仅根据第一印象而作出判断 28 | + Go ahead and try things! 29 | + Be **methodical** and **systematic** 30 | + Explore all of the possibilities 31 | + Draw a picture 32 | 33 | ## 2.2 34 | 证明有无穷多个素数 35 | 36 | + 从小规模,具体的问题开始入手,寻找规律;而不是直接从通用、普遍的情况开始入手; 37 | 38 | ## 3.2 39 | 有理数,小数,分数 40 | 41 | + 从具体的例子开始增强理解,而不是从普遍情况入手; 42 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BestPractice4Performance.md: -------------------------------------------------------------------------------- 1 | # Best Practices for Performance 2 | 3 | ## Managing Your App's Memory 4 | + 安卓系统的内存没有swap机制,但是有分页机制 5 | + 安卓系统不会整理堆上的内存,即:小块的可用内存可能无法再被使用 6 | + `ActivityManager::getMemoryClass`返回系统对堆内存大小的限制 7 | + 谨慎使用Service 8 | + 用户界面销毁时,释放内存资源 9 | + 内存吃紧时(`onTrimMemory()`回调),释放非必须的内存资源 10 | + 检查允许使用的堆大小:`getMemoryClass()`, `getLargeMemoryClass()` 11 | + bitmap要小心使用 12 | + 使用优化的容器类型:`SparseArray`, `SparseBooleanArray`, `LongSparseArray` 13 | + 注意数据结构的内存overhead 14 | + 不要过度使用继承 15 | + 合理选择序列化/反序列化方式 16 | + 合理选择依赖注入框架,dagger比较不错,但是guice和roboguice开销很大 17 | + 合理选择第三方库 18 | + ProGuard, zipalign 19 | + Service可以考虑使用跨进程 20 | 21 | ## 优化layout 22 | + 不要简单地认为RelativeLayout慢,需要测量!也许两层LinearLayout比一层RelativeLayout更慢,但也有可能更快,都需要用测量来确认; 23 | + include和merge标签的使用,merge作为layout的根节点,在include的时候将移除,直接将子节点展开到它的父节点中; 24 | + 使用ViewStub按需加载layout,ViewStub的加载很轻量,在需要的时候再加载它所stub的layout;ViewStub与merge无法同时使用; 25 | -------------------------------------------------------------------------------- /Android-Java/StylingViews.md: -------------------------------------------------------------------------------- 1 | #[使用Style修饰View](http://blog.danlew.net/2014/11/19/styles-on-android/?utm_source=Android+Weekly&utm_campaign=a94f126150-Android_Weekly_129&utm_medium=email&utm_term=0_4eb677ad19-a94f126150-337892465) 2 | + 当多个View语义上相同时:不仅样式相同,而且View的功能也相同; 3 | 4 | + 当某项数值在多处使用时,style中的数值使用引用方式定义(抽离为dimen,color等); 5 | + 将数值抽离出来,使用引用方式,不仅可以减小重复,方便修改;还能根据不同屏幕尺寸、横竖屏,为同一引用定义不同的值; 6 | 7 | + 一个View不能使用多个style,但可以通过继承style,并且改变parent style的值,达到和CSS类似效果; 8 | 9 | + 尽可能使用`android:textAppearance`属性,来定义`TextView`及其子类(`EditText`,`Button`等)的文字样式; 10 | 11 | + 只会使用一次的时候,不要使用style;当需要重复使用的时候,添加style很方便,不必过度提前考虑; 12 | 13 | + 不要只因为多个View使用同样的attributes就创建style;因为如果功能不同,有可能以后会变得样式不一样; 14 | 15 | + style指定parent有两种方式:隐式,`name="Parent.Child"`;显式,`name="Child" parent="Parent"`; 16 | + 不要两者同时使用,否则只有显式会生效; 17 | + 建议`name`属性不要带点,使用`parent`属性指定父style,而`parent`属性中使用点,隐式指定父style; 18 | 19 | + 不要混淆style和theme; 20 | + style修饰单个View,theme修饰View集合,或整个Activity; -------------------------------------------------------------------------------- /Android-Java/MVVM.md: -------------------------------------------------------------------------------- 1 | #Data binding(MVVM,Model-View-ViewModel) 2 | 3 | ##组成部分 4 | + Model:数据,业务逻辑 5 | + View:显示,UI 6 | + ViewModel:绑定前两者 7 | 8 | ##工作流程 9 | 传统MVC模式中,controller把model推到view中,而在MVVM中,ViewModel改变Model的内容后,framework将负责把变化更新到View中;Model和View通过ViewModel的接口若耦合; 10 | 得益于此,MVVM的测试不依赖于View的存在,只需注入相应依赖即可;测试model时,检查ViewModel的对应方法是否被调用;测试View时,使用mock Model,检查View内容的显示正确性; 11 | 12 | ##To reads(When stable released/decide to use) 13 | + https://developer.android.com/tools/data-binding/guide.html 14 | + http://stablekernel.com/blog/mvvm-on-android-using-the-data-binding-library 15 | + https://medium.com/@fabioCollini/android-data-binding-f9f9d3afc761 16 | + https://blog.stylingandroid.com/data-binding-part-1/ 17 | + https://realm.io/news/data-binding-android-boyar-mount/ 18 | + https://medium.com/ribot-labs/approaching-android-with-mvvm-8ceec02d5442 19 | + https://github.com/LyndonChin/MasteringAndroidDataBinding/blob/master/README.md 20 | + Android Data Binding 1.0-rc4 is now available and fixes the issues with gradle plugin 1.4-beta6 -------------------------------------------------------------------------------- /Android-Java/MultiDex.md: -------------------------------------------------------------------------------- 1 | # MultiDex专题 2 | 3 | ## 首先,了解自己当前的状况 4 | + dex方法数统计工具 5 | + [Dexcount Gradle Plugin](https://github.com/KeepSafe/dexcount-gradle-plugin),gradle插件,每次打包过程中把每个包的方法数写到build文件夹下的一个文件中 6 | + [dex-method-counts](https://github.com/mihaip/dex-method-counts),命令行工具,统计dex文件内的方法数 7 | + [ClassyShark](https://github.com/google/android-classyshark),GUI工具,查看apk内的dex分包,每个dex文件的方法数统计,每个dex文件里面有哪些class 8 | + [ProGuard](ProGuard.md),移除未引用的类,避免触及65535方法上限 9 | + 这些工具都可以用来了解当前工程的方法数(使用ProGuard前后),知悉各个包、各个库的方法数量,对于方法数很多、使用量少的库,应该移除依赖,或者通过适当配置ProGuard来移除,应该尽量避免MultiDex 10 | 11 | ## 尽量减少方法数,避免MultiDex,无法避免时优化MultiDex的性能 12 | + multidex时,放到主dex文件的类是由proguard来检测的,有时它并不是完整的,需要手动配置,还可以通过检测冷启动期间被加载了哪些类,然后通过配置把它们加入主dex中,从而达到multidex时冷启动加速的效果。[详情](https://medium.com/groupon-eng/android-s-multidex-slows-down-app-startup-d9f10b46770f) 13 | + 全新的Android编译系统:[Jack & Jill](http://tools.android.com/tech-docs/jackandjill)(尚不成熟) 14 | 15 | ## 副dex异步加载技术 16 | + 使用pre-dex jar来减小主dex文件的大小,但需要保证在使用某个库的类之前,pre-dex jar已经被加载,[详情](https://medium.com/@Macarse/lazy-loading-dex-files-d41f6f37df0e) 17 | + [美团、Facebook、微信团队对MultiDex加载的优化简述](http://zongwu233.github.io/the-touble-of-multidex/?) 18 | -------------------------------------------------------------------------------- /Android-Java/AndroidDevSkillTree.md: -------------------------------------------------------------------------------- 1 | # 安卓开发技能树 2 | 3 | ## 界面实现UI 4 | + 快速布局实现 5 | + 从设计图、效果图中,快速拆分区块,确定每一区块用什么View实现 6 | + 快速编写xml文件,实现静态效果图 7 | + xml文件编写符合规范:缩进、dimen、color、string均抽取到values目录下的相应文件中 8 | + 第三方库 9 | + 系统控件无法满足需求时,能快速确定是否有合适的第三方库 10 | + 屏幕适配 11 | + 采用边距方式相对定位,而不是绝对坐标,大小的确定也要考虑屏幕大小 12 | + 主流尺寸开发,其他尺寸发现bug后修复 13 | + 处理手机屏幕旋转 14 | + 高级/炫酷/复杂 视觉/交互效果的实现 15 | 16 | ## 界面实现功能 17 | + 逻辑控制、数据处理、显示,三者分离(MVC/MVP) 18 | + 异步处理,线程(RxAndroid) 19 | 20 | ## 动效 21 | + 基本动效搭配、时间轴 22 | + 引导动画 23 | + 过场动画 24 | 25 | ## Material design 26 | 27 | ## 测试 28 | + 单元测试 29 | + 集成测试(UI测试) 30 | + TDD 31 | 32 | ## 性能优化 33 | + 时间 34 | + 数据处理、操作时间复杂度,不阻塞UI更新,不掉帧; 35 | + 空间 36 | + 内存使用优化,引用、内存管理,减小运行内存,避免内存泄漏; 37 | 38 | ## 架构 39 | 40 | ## 视野 41 | 42 | ## 安卓系统 43 | 44 | ## Java 45 | 46 | ## 设计模式 47 | 48 | ## Debug/解决问题 49 | + 定位代码位置的能力:stacktrace分析,打log分析,traces分析,回滚测试,二分查找,去掉代码定位bug代码位置 50 | + 定位代码位置后分析bug原因,并设计解决方案,尽量减小对其他部分的修改、影响 51 | + 经验,遇到过的bug,同时能整理总结,遇到同样的bug能有印象 52 | 53 | ## 安卓开发者之间的区分点是什么? 54 | + 代码质量 55 | + 可读性,风格符合规范 56 | + 可维护性,代码结构好,改bug不会因为改代码而头疼 57 | + 可扩展性,开闭原则,后续加功能,通过增加模块/类来实现,而不是修改已有类,这样能降低修改引入的风险 58 | + 可测试性,高测试覆盖,用测试用例保证代码逻辑质量 59 | + TDD,真正的TDD,先有测试,再有实现 60 | + 视野 61 | + 经验 62 | + 紧跟、引领技术潮流(视野+能力) -------------------------------------------------------------------------------- /Android-Java/Kotlin.md: -------------------------------------------------------------------------------- 1 | # Kotlin学习笔记 2 | 3 | ## Basic Syntax 4 | + 可见性 5 | Classes, objects, interfaces, constructors, functions, properties and their setters can have visibility modifiers. 6 | + private:包及子包私有;`foo.bar`为`foo`的子包 7 | + protected:仅用于Class/Interface;可见性与private几乎一样,但是子类也是可见的 8 | + internal:默认值;visible everywhere within the same module(?) 9 | + public:visible everywhere 10 | + 包管理 11 | + 声明的包路径不需要与文件系统路径一致 12 | + private的可见性是包及子包私有 13 | + 父包的内容需要import才能访问 14 | + 定义函数 15 | + 类似于go的语法 16 | ```kotlin 17 | fun sum(a: Int, b: Int): Int { 18 | return a + b 19 | } 20 | ``` 21 | + 当函数体只是返回一个表达式的值得时候,可以简化为:`public fun sum(a: Int, b: Int): Int = a + b` 22 | + 非public/protected时,返回类型可以省略 23 | + 不返回值时,返回类型可定义为Unit,Unit可以省略(public也可省略) 24 | + Infix notation:当方法为成员方法,且只有一个参数时,可以简写为`1 shl 2`,等同于`1.shl(2)` 25 | + 可以定义默认参数,默认参数不必须在参数列表后面,但是调用的时候需要结合命名参数功能 26 | + 命名参数:调用函数的时候,可以通过参数名为其赋值,可以不按函数声明的参数顺序传参;结合默认参数,可以使得函数调用非常简洁 27 | + 函数体有block(分支、跳转)时,不支持返回类型推导 28 | + vararg支持:函数的最后一个参数可以通过vararg修饰,达到var args的目的`fun asList(vararg ts: T): List {...}` 29 | 30 | ## To read 31 | + http://antonioleiva.com/kotlin/ 32 | + http://antonioleiva.com/collection-operations-kotlin/ 33 | + http://antonioleiva.com/plaid-kotlin-1/ -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BestPractice4BackgroundJob.md: -------------------------------------------------------------------------------- 1 | # Best Practices for Background Jobs 2 | 3 | ## Creating a Background Service 4 | + `IntentService`被设计于用来在一个单独的后台线程执行耗时操作,它是对Service组件的一个封装, 5 | 通过`HandlerThread`和`ServiceHandler`将其任务代码放到单独的线程执行 6 | + 处理的结果需要通过EventBus或者LocalBroadcast发送到Activity 7 | + 由于Service同时只会存在一个实例,所以如果同时发送多个Intent,它们会串行执行 8 | 9 | ## Loading Data in the Background 10 | + CursorLoader可用于从ContentProvider异步加载数据 11 | 12 | ## Managing Device Awake State 13 | + 保持屏幕不锁屏:在Activity的onCreate函数中执行:`getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);` 14 | + 或者在layout中加入:`android:keepScreenOn="true"` 15 | + 保持CPU运行:wake lock 16 | + 例如:后台Service在锁屏后仍希望进行数据处理 17 | + 声明权限:`` 18 | + 获得wake lock 19 | 20 | ```java 21 | PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); 22 | WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 23 | "MyWakelockTag"); 24 | wakeLock.acquire(); 25 | // do sth 26 | wakelock.release(); 27 | ``` 28 | 29 | + IntentService结合WakefulBroadcastReceiver,可以有效提高后台任务的电池效率 30 | + Scheduling Repeating Alarms,使用`AlarmManager` 31 | -------------------------------------------------------------------------------- /misc/OOP6Principles.md: -------------------------------------------------------------------------------- 1 | # 面向对象六大原则(SOLID+) 2 | 3 | ## 基本概念 4 | 功能:比如一个ImageLoader,负责实现图片加载,但是要支持缓存;那么这个ImageLoader就是我们讨论的功能; 5 | 6 | 依赖:上述ImageLoader功能,需要支持缓存,但是缓存其实也是一个很大的功能,而且会有多种策略,那么ImageLoader就不应该把缓存功能紧耦合,而是作为一个外部的依赖; 7 | 8 | 使用者:上述ImageLoader作为一个功能,可以打包成一个库,那么用这个库的APP就是使用者; 9 | 10 | 使用者的需求是经常变化的,因为他们的业务会随着迭代而不断调整,那么ImageLoader这个功能好的实现就是无论使用者的需求怎么变化,都无需改变ImageLoader这一功能的实现。但是对于缓存这一核心特性,也应该做到按需定制,而无需改变ImageLoader的实现代码; 11 | 12 | ## 单一职责原则(S) 13 | 非常直观的,一个类越简单,就越难有bug,50行代码基本不可能有bug,即便出了bug,也很容易分析出来,甚至都不用调试。 14 | 15 | 所以,尽可能的拆分,尽可能地简化一个类的职责,并且聚焦于这个单一的职责。一个类只聚焦于自己的职责时,需要修改一个功能,那么被修改的类也就会尽可能的少了。 16 | 17 | ## 开闭原则(O) 18 | 抽象来说,就是“对扩展开放,对修改封闭”。具体点,就是功能的修改、增加,尽可能通过增加新的类(扩展)来实现,而不是修改已有的类。 19 | 20 | 但是如何达到这个效果?就是尽量把可能会变动的地方抽象出来,使用接口进行依赖,具体的实现通过依赖注入来耦合。这样,修改功能就是增加新的实现,在使用者的代码里面注入新的实现。使用者的需求变了,代码的修改是不可避免的,但是功能的框架,如果能不修改,那就降低了引入bug的可能性。 21 | 22 | ## 里氏替换原则(L) 23 | 所有引用基类的地方必须能透明地使用其子类的对象。满足此条件时,对依赖的声明(成员、参数)都通过抽象基类来完成,则使用新的实现类时,只需替换注入/传递的参数即可,功能实现无需改变。更进一步,使用接口声明依赖,能一定程度上避免继承引入的问题。 24 | 25 | ## 接口隔离原则(I) 26 | 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可,可以达到屏蔽其他接口可见性的效果。 27 | 28 | ## 依赖倒置原则(D) 29 | 模块间的依赖通过抽象发生,抽象即抽象类或者接口;使用接口更佳,而且接口内不要定义任何数据; 30 | 31 | ## 迪米特法则(最小知识原则) 32 | 一个类应该对自己依赖(使用)的其他类知道得最少,所谓知道,就是使用。可以类比接口隔离原则,依赖尽可能少的接口,每个接口又仅仅包含确实和自己的功能相关的方法。 33 | -------------------------------------------------------------------------------- /Android-Java/App-Dev-Notes.md: -------------------------------------------------------------------------------- 1 | # 《App研发录》一书 2 | 3 | ## 第1章:重构 4 | + BaseActivity要有两个,一个是业务无关的,位于base module中,另一个是业务相关的,位于app module中,后者封装了业务相关的公用逻辑和代码;同理,当fragment, dialog fragment有业务相关的共性时,也就是时候为app module准备一个base类了; 5 | + package by feature,被外部引用的类,就不要作为内部类了;未被包外引用的类,就要声明为package private; 6 | + (如果)activity只用于管理fragment,fragment inflate view,绑定view的生命周期需要拆分开来,避免onViewCreated过于复杂; 7 | + 绑定view和初始化变量、调用获取数据的方法要拆分开来,单一职责! 8 | + 具体而言,应该有`getLayoutRes`, `initFields`, `bindView`, `startBusiness`, `unbindView`这5个方法 9 | + `getLayoutRes`:返回layout的id,用于onCreateView; 10 | + `initFields`, `bindView`, `startBusiness`:初始化成员变量、绑定view(包括设置listener)、开始执行业务逻辑,被onViewCreated**依次**调用; 11 | + `unbindView`:解绑view(包括重置listener); 12 | + 为view设置listener,不要设置为this,而是创建新的;listener对象的创建开销并不是瓶颈,而代码整洁度的提升效果是很明显的; 13 | + fragment间数据传递:[FragmentArgs](https://github.com/sockeqwe/fragmentargs);Effective Java item 74:尽量不要使用serialisable接口; 14 | + Adapter模板:建议使用[AdapterDelegates](https://github.com/sockeqwe/AdapterDelegates),Favor composition over inheritance;而对于具体的一个AdapterDelegate,主要规范点在于onBindViewHolder,子view的listener如何设置,事件如何传递到fragment中? 15 | + 我的做法是,ViewHolder在构造函数中设置listener,同时它构造时接受一个接口,这个接口从fragment => adapter => ViewHolder => listener,如此达到事件传回fragment的目的; 16 | + 实体化编程/immutable/parcelable,推荐使用[Auto-parcel](https://github.com/frankiesardo/auto-parcel) + [Gson](https://github.com/google/gson); 17 | -------------------------------------------------------------------------------- /Android-Java/RxAndroidBestPractice.md: -------------------------------------------------------------------------------- 1 | #Rx在Android中的最佳实践 2 | 3 | + By default, RxJava is synchronous 4 | 测试起来更方便;使用`Observable.from`创建observable,所有的subscription将立即触发;不要假设接收的subscription是异步的; 5 | + Hot and cold subscriptions 6 | 通常,只有observable被订阅(subscribe)时,所有的操作才会被执行;根据不同的实现,有可能每次有新的subscriber都会新创建一个operation,也有可能并不会;`.cache`操作可以缓存吐出的数据,后面的subscriber都会收到同一个数据; 7 | + Use subjects when in trouble 8 | subject既能接收数据,也能吐出数据; 9 | + Pay attention to the thread 10 | [rx线程模型](http://www.grahamlea.com/2014/07/rxjava-threading-examples/);但是把切换回主线程放到最后并不一定是最好的; 11 | + Read the RxJava wiki and look at the diagrams 12 | [官方wiki](https://github.com/ReactiveX/RxJava/wiki) 13 | + Subscribing with Observer vs. Action 14 | 使用Action1时如果发生错误,将抛出异常;最好是使用一个公用的ErrorHandler; 15 | + Subscriptions leak memory 16 | 匿名的observable会持有外部类的强引用,有可能导致内存泄漏;好的办法是每次使用observable,都将最后的subscription都保存起来,然后在合适的时机集中unSubscribe,以避免内存泄漏; 17 | + [Loading data from multiple sources with RxJava](http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/) 18 | + 多个source,由快到慢 19 | + concat/concatWith,根据参数的顺序,依次把每个observable发射的item拼接起来,形成一个新的序列;参数之间发射的item不会重叠,即第一个observable的所有item发射完之后,第二个observable才会开始发射; 20 | + first/takeFirst,从序列中取出第一个item,可以加过滤条件;first在过滤时如果没有满足条件的item,将会抛出异常,而takeFirst不会抛异常,只会调用onCompleted; 21 | + 两者结合,当第一个满足条件的item取到之后,concat参数中后面的observable将不会被subscribe;如果参数observable都是cold的,那么后面observable的产生操作将不会被执行; -------------------------------------------------------------------------------- /Android-Java/WebRTC.md: -------------------------------------------------------------------------------- 1 | #WebRTC 2 | 3 | ##目标 4 | + 便捷的为web应用添加视频聊天、p2p数据传输功能; 5 | + API开源、免费、标准化,嵌入到浏览器中,并且比现有插件技术更高效; 6 | 7 | ##API 8 | + [MediaStream (aka getUserMedia)](http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-mediastream) 9 | + [RTCPeerConnection](http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcpeerconnection) 10 | + [RTCDataChannel](http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcdatachannel) 11 | 12 | + MediaStream 13 | 具有input和output,input获取视频数据、音频数据;output将数据展示到video/audio标签,或发送到peer; 14 | ```javascript 15 | var stream = navigator.getUserMedia() 16 | stream.getAudioTracks() 17 | stream.getVideoTracks() 18 | 19 | URL.createObjectURL() 20 | ``` 21 | + Constraints 22 | `applyConstraints()`函数为getUserMedia设置限制条件,支持分辨率、长宽比、前后摄像头、帧率、宽高等; 23 | 在一个标签页中设置了Constraints之后,后面打开的标签页都受影响; 24 | + Screen and tab capture 25 | chrome目前支持屏幕录制、浏览器标签页录制API; 26 | + Signaling: session control, network and media information 27 | WebRTC使用`RTCPeerConnection`API进行peer间通讯,`signaling`进程负责维护通信、发送控制信息; 28 | + Session control messages: to initialize or close communication and report errors. 29 | + Network configuration: to the outside world, what's my computer's IP address and port? 30 | + Media capabilities: what codecs and resolutions can be handled by my browser and the browser it wants to communicate with? 31 | 32 | ##[WebRTC on Android](https://tech.appear.in/2015/05/25/Introduction-to-WebRTC-on-Android/) -------------------------------------------------------------------------------- /Android-Java/MaterialDesign.md: -------------------------------------------------------------------------------- 1 | ##Material Design 2 | 3 | #[Ripples](https://blog.stylingandroid.com/ripples-part-1/) 4 | ```xml 5 | 6 | 9 | ``` 10 | + 实现点击态、正常态的视觉切换(反馈) 11 | + 使用多个drwable,通过selector实现; 12 | + 通过新的ripple api实现点击反馈; 13 | + 动画可能超出parent layout的范围; 14 | + 可以通过为ripple加一个item来限制动画的范围; 15 | + 在ripple中使用theme的元素定义color,可以保证整个app中view颜色的一致性; 16 | + `android:colorPrimary`(AppCompat中使用`colorPrimary`)定义ActionBar的颜色; 17 | + `android:colorPrimaryDark`(AppCompat中使用`colorPrimaryDark`)定义状态栏的颜色(5.0之前无效); 18 | + 直接Activity会导致ActionBar不显示,应继承自AppCompatActivity(或ActionBarActivity,已弃用); 19 | 20 | ##AppCompat 21 | + 在5.0之前使用Material Design的兼容库; 22 | + AppCompat不包括ripple,因为ripple使用了5.0引入的render thread去进行渲染; 23 | 24 | ##TextAppearance 25 | + `android.R.style.TextAppearance.Material.*`(AppCompat使用`R.style.TextAppearance.AppCompat.*`); 26 | 27 | ##CardView 28 | + 支持圆角、阴影的卡片式View,继承自FrameLayout 29 | + 在5.0以前,通过为CardView设置额外的padding来绘制阴影;因此,如果想要去掉阴影区域额外的padding,目前只能通过将`contentPaddingXXXX`属性设为负值来实现; 30 | 31 | ##RecyclerView 32 | + ListView默认有selector overlay,点击有视觉效果;RecyclerView需要使用`RecyclerView.ItemDecoration`手动实现(ItemDecoration还可以做更多事情,例如divider等); 33 | 34 | ##RecyclerView的item支持拖拽 35 | 36 | ##Activity切换动画 37 | + `ActivityOptionsCompat.makeSceneTransitionAnimation`,`ActivityCompat.finishAfterTransition` -------------------------------------------------------------------------------- /misc/BetterDesignWithImmutableParams.md: -------------------------------------------------------------------------------- 1 | #函数调用时,传递参数应该是不可变的(Immutable) 2 | 考虑以下代码: 3 | ```java 4 | Hashmap map = new HashMap(); 5 | map.put("key", "value") 6 | service.doSomething(map); 7 | map.clear() 8 | ``` 9 | 测试时想要验证doSomething调用时的参数内容(状态),使用Mockito的ArgumentCaptor,capture到的都将是空的map,因为capture到的对象,在调用doSomething后,又被修改了(clear)。 10 | 一方面目前Mockito的实现,并非capture时立即创建参数的副本,而是直接持有其引用,所以后面的修改在将会生效,即修改操作发生后,再进行验证,参与验证的将是修改后的值。 11 | 但是另一方面来看,这也是设计上的缺陷(code/design smell),更优雅的方式应该是在函数调用时传入不可变的对象,这样也会避免隐藏的bug,例如:被调用函数并未立即使用参数,而是在回调中/异步线程中使用参数,因此即便函数调用是同步进行的,后续的修改也会导致被调用函数使用参数时值发生了变化。 12 | 更好的方式是这样的: 13 | ```java 14 | Map map = new HashMap(); 15 | map.put("key", "value") 16 | service.doSomething(map); 17 | Map mapTwo = new HashMap(); 18 | mapTwo.put("key2", "value2"); 19 | serviceTwo.doSomethingElse(mapTwo); 20 | ``` 21 | 另外,使用AutoValue/AutoParcel可以很方便的创建不可变的对象,但是在使用过程中还是容易“入坑”,例如使用了Collection类,即便元素对象是不可变的,但是collection并不是,如果按照上面的方式去实现,依然会导致问题,一方面,新new一个Map是一种解决方式,~~另一方面,如果是使用List,可以通过变长参数的方式来传递,这样就能避免这一问题~~,变长参数使用时仍然可能会有问题,例如:调用时传递的是一个数组对象,而非手动传递多个参数,那么如果多次调用之间传递的是同一个数组对象,那还是存在上面的问题,所以,无需变成传递变长参数,而是在调用时保证之后不再修改参数对象(TODO:go语言中有把数组打散之后传递的语法,是否能避免此问题?)。 22 | 23 | 参考: [Google网上论坛](https://groups.google.com/d/msg/mockito/KBRocVedYT0/T-vgvqwjh0QJ) 24 | 25 | update at 2015. 09. 15 26 | 经过更多的实践与思考,我对上述问题有了新的认识,上述问题的解决,只能放到函数调用方来做,即便在被调用方的第一行代码对传入参数进行一次深拷贝,还是无法保证深拷贝这一操作会早于调用方后续的修改。只能通过限制/强制保证调用方传进来的数据就是不会且不可改变的数据,才能避免此问题。 27 | 而如何保证这一点,可以通过调用方传参时进行深拷贝,或者unmodifiable+程序员保证调用后不再读写该数据,来实现。 28 | [更多阅读](copy.md) -------------------------------------------------------------------------------- /Android-Java/NewInAndroid.md: -------------------------------------------------------------------------------- 1 | #各个安卓版本引入的主要新特性 2 | 3 | ##Android 6.0 Marshamallo 4 | + [Runtime Permission System](http://android-developers.blogspot.jp/2015/08/building-better-apps-with-runtime.html) 5 | + Step 1: check the platform, `Build.VERSION.SDK_INT >= Build.VERSION_CODES.M` 6 | + Step 2: check the permission status, [`checkSelfPermission()`](http://goo.gl/T7vE7b),只可能返回两种状态:已授权,未授权;不存在“未请求”状态; 7 | + Step 3: explain the permission, [`shouldShowRequestPermissionRationale()`](http://goo.gl/bFyfVj),官方文档说“显示Rationale是为了解释不那么明显的权限请求,该方法就是检查是否需要显示Rationale”,那么到底是检查是不是“不那么明显”呢?还是检查是否已授权呢?显然应该是后者,系统怎么可能检查什么“不那么明显”?那么问题是这个方法和`checkSelfPermission`有什么区别呢?因为后者是异步的?还是说因为这是两种不同的使用场景? 8 | + Step 4: request the permission, [`requestPermissions()`](http://goo.gl/yNuizg),不能保证一定被授权;而且此过程Activity可能会被pause,有些权限的授予甚至会要求重启APP进程;如果设置了manifest的Activity标签中设置了`android:noHistory="true"`,将不能请求权限,因为此Activity收不到任何回调; 9 | + Step 5: handle the response, `onRequestPermissionResult()` 10 | + 测试,shouldShowRequestPermissionRationale和checkSelfPermission在效果上有没有什么区别?未请求过权限和授权被拒绝,这两个函数的效果有何区别?每次都选择拒绝授权,每次都调用requestPermissions,会是什么效果?是否第二次拒绝后就不会弹对话框了?那么如何判断是否请求过权限?SharedPreference?[解答](http://blog.piasy.com/Android-Runtime-Permission-Test/)。 11 | + [App Linking](https://developer.android.com/preview/features/app-linking.html):通过注册,系统对链接的处理将直接打开官方(注册)APP,而不是显示对话框,[解读blog](https://chris.orr.me.uk/android-app-linking-how-it-works/); 12 | + [Auto Backup for Apps](http://developer.android.com/preview/backup/index.html) 13 | + [基于百分比的Layout](https://developer.android.com/reference/android/support/percent/package-summary.html) 14 | 15 | ##5.0 Lollipop 16 | + Material design:包括视觉、移动、交互、主题、widgets、阴影、动效等; 17 | + Notifications:包括锁屏界面、优先级通知、云同步通知; 18 | 19 | ##4.4 KitKat -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BuildAppWithContactsAndSignIn.md: -------------------------------------------------------------------------------- 1 | # Building Apps with Contacts & Sign-In 2 | 3 | ## Accessing Contacts Data 4 | + 官方文档上使用[`CursorLoader`](http://developer.android.com/reference/android/support/v4/content/CursorLoader.html)加载本地通讯录列表,然后用`SimpleCursorAdapter`显示在`ListView`中,示例比较繁琐 5 | + 项目实践中的代码: 6 | 7 | ```java 8 | Cursor cur = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, 9 | null, null, null, null); 10 | if (cur == null || cur.getCount() == 0) { 11 | if (cur != null) { 12 | cur.close(); 13 | } 14 | throw new RuntimeException("Permission denied"); // 不同系统行为不一样,有的是cursor为null,有的是cursor数据为空 15 | } 16 | while (cur.moveToNext()) { 17 | String name = cur.getString( 18 | cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); 19 | String phone = cur.getString( 20 | cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); 21 | //... 22 | } 23 | cur.close(); 24 | ``` 25 | 26 | + 可以通过发送`Intent`实现对通讯录的修改,会启动系统通讯录应用,在其中让用户进行确认修改,这种方式无需修改通讯录权限: 27 | 28 | ```java 29 | Intent intent = new Intent(Intents.Insert.ACTION); 30 | intent.setType(ContactsContract.RawContacts.CONTENT_TYPE); 31 | intent.putExtra(Intents.Insert.EMAIL, mEmailAddress) 32 | .putExtra(Intents.Insert.EMAIL_TYPE, CommonDataKinds.Email.TYPE_WORK) 33 | .putExtra(Intents.Insert.PHONE, mPhoneNumber) 34 | .putExtra(Intents.Insert.PHONE_TYPE, Phone.TYPE_WORK); 35 | startActivity(intent); 36 | ``` 37 | 38 | + API 14以上的系统,启动通讯录应用修改完通讯录后,按下返回键不会回到原来的APP,API 15以上可以用以下办法解决: 39 | 40 | ```java 41 | // Sets the special extended data for navigation 42 | editIntent.putExtra("finishActivityOnSaveCompleted", true); 43 | ``` 44 | 45 | ## [Adding Sign-In](http://developer.android.com/intl/zh-cn/training/sign-in/index.html) 46 | Google提供的授权API,让用户可以使用Google账户授权登录app,享用Google账户关联的优势。 47 | -------------------------------------------------------------------------------- /misc/copy.md: -------------------------------------------------------------------------------- 1 | #深入Java深浅拷贝、immutable、unmodifiable 2 | 3 | + 建议:函数调用的时候,调用方传给被调用方的参数,如果在调用之后还会被修改,那么调用方应该给被调用方传一个当时的拷贝,深拷贝,否则: 4 | + 可能被调用方是异步执行的,如果调用函数之后,参数发生了修改,那么被调用方执行的时候,看到的就是被修改之后的数据,这将导致严重、隐蔽、非必现的BUG,而这种BUG是最让人头疼的 5 | + 可能被调用方会修改传入的参数,这就导致函数执行完毕之后,调用方看到的数据发生了非预期的变化,这同样会导致严重、隐蔽的BUG 6 | + 深拷贝:这里需要弄清楚深浅拷贝的区别,用“=”号给非基本类型赋值,均是浅拷贝,例如List,以下代码就是浅拷贝: 7 | ```java 8 | List list1 = new ArrayList<>(); 9 | list1.add(1); 10 | List list2 = list1; 11 | list1.add(2); 12 | ``` 13 | 代码执行完毕之后,list2将包含整数1和2。 14 | 而以下代码则是深拷贝: 15 | ```java 16 | List list1 = new ArrayList<>(); 17 | list1.add(1); 18 | List list2 = new ArrayList<>(list1); 19 | list1.add(2); 20 | ``` 21 | 代码执行完毕之后,list2将只包含整数1,不包含整数2。 22 | + Immutable对象:上述情形如果遇到immutable对象,即不可变对象,其实是不需要深拷贝的(不仅不需要,还应该杜绝拷贝,因为纯属浪费)。但是前提是对象是真正的immutable。反面例子为: 23 | ```java 24 | public class NonStrictlyImmutable { 25 | private final List mList = new ArrayList<>(); 26 | 27 | public List getList() { 28 | return mList; 29 | } 30 | } 31 | ``` 32 | mList成员设置为了`private final`,NonStrictlyImmutable对象实例化完成后mList所引用的实际对象也不可再被改变,然而mList这个List的元素确是可以改变的,`nonStrictlyImmutable.getList().add(1)`并不会报编译错误,而这一行代码却实实在在改变了nonStrictlyImmutable对象的值! 33 | 而如果`getList()`函数不直接返回`mList`引用,创建一个副本,或者使其不可被改变,则可以达到”严格意义上的“immutable。例如: 34 | ```java 35 | public class NonStrictlyImmutable { 36 | private final List mList = new ArrayList<>(); 37 | 38 | public List getList() { 39 | // 以下两种方式都可以,各有优劣 40 | // return new ArrayList<>(mList); 41 | // return Collections.unmodifiableList(mList); 42 | 43 | return Collections.unmodifiableList(mList); 44 | } 45 | } 46 | ``` 47 | 上面两种方式各有优劣:前者允许对新获取到的副本进行修改操作而不会抛出异常,但会把底层数组数据创建多份;后者不会创建多份底层数组数据,但是如果对`getList()`返回的引用进行修改操作,将会抛出异常;见仁见智。 48 | + unmodifiable:unmodifiable不等于immutable,所以对于前面提到的建议做法,直接传入unmodifiable是不对的,因为这样只能阻止被调用方修改传入数据后导致调用方出错,并不能阻止调用方修改后导致被调用方出错。unmodifiable并未拷贝底层数组数据,而是实现了另外一个List的实现类,该类的修改操作均抛出异常。 -------------------------------------------------------------------------------- /Android-Java/MemoryLeak.md: -------------------------------------------------------------------------------- 1 | #Memory leak专题 2 | + 神器:[LeakCanary](https://github.com/square/leakcanary),memory leak检测工具; 3 | 4 | ##Hanlder、Runnable、Thread的非静态内部类、匿名类,都会持有外部类的强引用,都可能造成内存泄漏; 5 | + Java的非静态内部类、匿名类,会持有外部类的强引用,静态的不会持有;对于Activity/Fragment内定义的Handler/Runnable,是最容易因此导致内存泄漏的,因为它们可能会postDelayed,从而导致Activity/Fragment及其内部的资源无法GC;推荐做法是定义静态内部类/静态匿名成员,访问外部类的成员和方法通过WeakRefrence实现; 6 | + WeakRefrence需要在内部类内创建才符合其语义? 7 | 8 | ##[Prior to Android Lollipop, alert dialogs may cause memory leaks in your Android apps.](https://corner.squareup.com/2015/08/a-small-leak.html) 9 | + 考虑以下代码 10 | ```java 11 | while (true) { 12 | MyMessage msg = queue.take(); // might block 13 | System.out.println("Received: " + msg); 14 | } 15 | ``` 16 | msg对象是栈上的局部变量,每次循环都将会重写,一旦被重写,上一次循环的msg引用指向的对象将不再被其引用;但是在Dalvik虚拟机的实现中,如果queue.take()阻塞了,那么本次循环的msg未被赋值,则上次的msg的引用将不会被清除, 17 | + HandlerThread 18 | ```java 19 | for (;;) { 20 | Message msg = queue.next(); // might block 21 | if (msg == null) { 22 | return; 23 | } 24 | msg.target.dispatchMessage(msg); 25 | msg.recycleUnchecked(); 26 | } 27 | ``` 28 | msg每次循环的后面都被recycle了(清空了内容),所以泄漏的仅仅是一个空的msg对象,影响不大(LeakCanary将默认忽略Message对象的泄漏)。 29 | + 遇上AlertDialog 30 | ```java 31 | new AlertDialog.Builder(this) 32 | .setPositiveButton("Baguette", new DialogInterface.OnClickListener() { 33 | @Override public void onClick(DialogInterface dialog, int which) { 34 | MyActivity.this.makeBread(); 35 | } 36 | }) 37 | .show(); 38 | ``` 39 | DialogInterface.OnClickListener的匿名实现类持有了MainActivity的强引用;而在AlertDialog的实现中,OnClickListener类将被包装在一个Message对象中,而且这个Message会在其内部被复制一份,两份Message中只有一个被recycle,另一个(OnClickListener的成员变量引用的Message对象)将会leak! 40 | + 解决办法 41 | + ART VM(>=5.0),JVM不存在此问题 42 | + Message对象的泄漏无法避免,但是如果仅仅是一个空的Message对象,而且将被放入对象池作为后用,是没有问题的 43 | + `DialogInterface.OnClickListener`对象不持有外部类的强引用:static类实现;DetachableClickListener(监听窗口解除事件,手动释放引用); 44 | + 当worker thread空闲后,向HandlerThread发送一个空的消息,解除上一个Message的泄漏 -------------------------------------------------------------------------------- /Android-Java/HandlingRuntimeChanges.md: -------------------------------------------------------------------------------- 1 | # Handling Runtime Changes 2 | Runtime Changes包括orientation,键盘可见性,语言设置等,这些内容发生变化后, 3 | 系统将重启Activity(先执行`onDestroy`,再执行`onCreate`),以便APP可以响应 4 | 这些变化。 5 | 6 | `onSaveInstanceState()`和`onRestoreInstanceState()`回调在这种情形下可以 7 | 使得APP能保存已有状态,在Activity重新创建时能够恢复已有状态,为用户提供一致的体验。 8 | 9 | 如果需要把已加载的数据应用到新的状态中,有两种方式: 10 | 11 | + 在配置发生变化时,保留数据 12 | + 当配置发生变化时,不要重新创建Activity(系统默认行为),而是响应相应事件, 13 | 手动改变Activity状态 14 | 15 | ## Retaining an Object During a Configuration Change 16 | `onSaveInstanceState()`和`onRestoreInstanceState()`并不是设计用来传递大量数据的, 17 | 其传递的Bundle对象有大小限制。而且Bundle中的数据都会先反序列化再序列化,耗时较多。 18 | 19 | 可以使用Fragment,调用`Fragment::setRetainInstance(true)`,在Activity重新创建之后, 20 | 通过FragmentManager获取到重用的Fragment对象,进而获取到已有的数据。这里需要注意的是, 21 | Fragment不能直接或者间接持有Activity的引用,否则可能会导致老的Activity对象的内存泄漏。 22 | 23 | 利用这一方式,有一种`HeadlessFragment`的用法,这个Fragment没有UI,只负责后台加载数据, 24 | 它不会因为Activity的配置变化销毁而销毁,可以保证数据获取过程的连续性。 25 | 26 | ## Handling the Configuration Change Yourself 27 | Activity可以在manifest中声明自行处理配置变化,同时`onConfigurationChanged()`回调会 28 | 在配置变化时被执行。这种方式是最为复杂的,应该是最后的考虑选项。 29 | 30 | ```xml 31 | android:configChanges="orientation|screenSize|keyboardHidden" 32 | ``` 33 | 34 | 配置发生变化之后,`getResources()`函数返回的对象是新配置下的资源对象,可以直接使用, 35 | 36 | ## Loaders 37 | 相比于上述通过Fragment保留数据的方式,更建议使用Loaders进行替换。Loaders自安卓3.0引入, 38 | 用于异步加载数据,Activity和Fragment均可以使用。 39 | 40 | Loader框架包含了一个`CursorLoader`实现,包括了异步请求数据,Activity/Fragment销毁重新 41 | 创建时直接返回已有数据等功能。 42 | 43 | + Activity/Fragment重新创建时数据保留:Loader对象并不会被销毁,其生命周期由framework维护 44 | (包括不会销毁与需要销毁的情形); 45 | + 异步加载:基于AsyncTask,但异步逻辑无需实现; 46 | + 数据更新:自行检测数据变更,但是提供了更新数据的相应API; 47 | + framework/support内的实现,Google背书; 48 | + framework提供了[`LoaderTestCase`](http://developer.android.com/reference/ 49 | android/test/LoaderTestCase.html)类,用于测试; 50 | + 需要什么测试? 51 | + Loader有其数据更新/发送/重置的逻辑,需要单元测试; 52 | + Loader对于framework的依赖无法隔离(Handler, AsyncTask等),而且LoaderTestCase 53 | 也是需要`AndroidJUnitTestRunner`执行的; 54 | + Loader和数据源有对接,需要集成测试; 55 | + 对比MVP? 56 | + 相当于presenter; 57 | + 能否结合?如果仅仅是对于设计模式来说,当然是可以的;但对[Mosby](https://github.com/ 58 | sockeqwe/mosby)来说,两者是有重复工作的,例如retain state,life cycle绑定,而且这部分 59 | 工作都能满足需求,也有背书; 60 | -------------------------------------------------------------------------------- /Android-Java/TDD.md: -------------------------------------------------------------------------------- 1 | # 《Test Driven Development: By Example》一书 2 | 3 | ## 核心思想 4 | + Rules 5 | + Write new code only if you first have a failing automated test. 6 | + Eliminate duplication. 7 | + Red/green/refactor. The TDDs mantra. 8 | + Red: write a little test that doesn’t work, perhaps doesn’t even compile at first 9 | + Green: make the test work quickly, committing whatever sins necessary in the process 10 | + Refactor: eliminate all the duplication created in just getting the test to work 11 | 12 | ## By Example 13 | + 首先,来了一个需求,不是思考需要一个什么对象,需要一个什么方法,而是需要一个什么测试,来验证我们的实现是正确的 14 | + 更进一步的,是需求可测试化:需求不是一句话,而是一个todo list,其实就是所有的(尽量)测例,有什么需求都通过测例来描述,在什么情况下软件应该是什么行为,要细化到可以直接翻译为代码 15 | + 思考需要解决的问题的时候,都是以:我需要什么样的测试以验证实现正确开始,而不是如何实现 16 | + 测试化的需求可能繁简程度不一,那么就需要先从简单的开始,同时学会把复杂的测例拆分为简单的测例 17 | + 为尚不存在的代码编写测例时,首先假设它有完美的接口满足其功能,同时先不要管封装、数据类型等复杂问题,让测试代码尽量简单,例如: 18 | ```java 19 | public void testMultiplication() { 20 | Dollar five= new Dollar(5); 21 | five.times(2); 22 | assertEquals(10, five.amount); 23 | } 24 | ``` 25 | + 一步一步来,先让让测例写出来,但同时要记下需要改善的地方,先写出一个可以跑,但是失败的测例,然后再逐步实现、逐步改善测例与实现 26 | + 编写的测例首先应该是不能编译的,借助IDE,我们可以很方便的添加相应的类、成员、方法,使得测例能够编译,注意!这一步不要加入任何实现,仅仅让测例可编译,一旦加入了实现,就容易沉浸在实现中了 27 | + 测例运行起来当然是失败的,但这是巨大的进步!从“实现XX功能”,到“让这个测例通过”,简单了无数倍! 28 | + TDD五部曲,有点naive,但值得借鉴 29 | + Add a little test 30 | + Run all tests and fail 31 | + Make a little change,最小改动,例如直接把amount初始值赋值为10 32 | + Run the tests and succeed 33 | + Refactor to remove duplication 34 | 35 | 36 | + 好代码的“天敌”:依赖与重复 37 | + 依赖:上面的测例显然依赖于实现,改实现就必须要改测例,而这并不是我们想要的,我们的目标是编写有意义(能验证实现正确性)的测例,但是变更实现细节不需要修改测例,目前看来有点难;实际开发也是如此,如果代码依赖于某一组件的实现细节,那以后想要替换另一组件,就很麻烦; 38 | + 重复:If dependency is the problem, duplication is the symptom. 代码的重复往往起源于重复的逻辑,例如逻辑一样的表达式出现在多个地方;而对象(面向对象)天生就是用来把重复的逻辑抽象出来的,别辜负了对象; 39 | + 还好,在程序中,消除重复,就能消除依赖 40 | + 找到重复,消除重复 41 | + 和实际代码的重复不太一样,不是基本相同的逻辑在重复;测例与实现的重复在于数据的重复,例如直接`int amount= 10;`和测例的测试数据是重复的 42 | + 书中的例子非常简单,但道理很深刻:TDD不是关于一些小的、简单的步骤,而是关于如何能够把难题拆分为小的、简单的步骤! 43 | + 其实当能够把每一步拆分得不能再小时,就表明对问题的理解达到了一定的程度,此时当然知道应该把问题拆分为何种粒度的子问题,保持思考! 44 | + 让测例能够通过的合适的Dollar类代码,谁都会写,但是这个思考的过程却未必,而这正是践行TDD应该注意的地方! 45 | + 完成上步后,就是时候考虑实现的优雅性了,将其也加入到todo list中吧! 46 | 47 | 48 | + 一步一步来! -------------------------------------------------------------------------------- /Frontend/JavascriptGoodParts.md: -------------------------------------------------------------------------------- 1 | # 《JavaScript 语言精粹》 2 | 3 | ## 对象 4 | + JavaScript 中的对象是可变的键控集合(keyed collections) 5 | + JavaScript 中的对象是无类型(type-free)的 6 | + JavaScript 包含一种原型链的特性,允许对象继承另一个对象的属性。正确使用它可以减少对象初始化的时间和内存。 7 | + 取属性值,可以用 `||` 填充默认值,可以用 `&&` 避免对 undefined 取值产生异常 8 | + 对象通过引用来传递,直接赋值不会被复制 9 | + !原型,原型链 10 | + 反射:`typeof`, `hasOwnProperty` 11 | + 枚举属性:`for in` (会包含原型链中的所有属性) 12 | + `delete` 可以删除对象的属性(是否可以删除来自原型链的属性?) 13 | + 减少全局变量污染:可以把所有全局变量用一个对象包起来(极大程度减少冲突) 14 | 15 | ## 函数 16 | + 函数也是对象。对象是可变的键控集合,并拥有一个连接到原型对象的隐藏连接。对象字面量产生的对象连接到 `Object.prototype`,函数对象连接到 `Function.prototype`(而它又连接到 `Object.prototype`) 17 | + 每个函数在创建时会附加两个隐藏属性:函数的上下文和实现函数行为的代码 18 | + !函数的 `prototype` 与 `Function.prototype`,闭包 19 | + 调用 20 | + 除了形参,每个函数还接收两个附加参数,`this` 和 `arguments` 21 | + 函数有 4 种调用模式 22 | + 传参过少,缺失的值为 `undefined`,传参过多,多余的将被忽略 23 | + 4 函数调用模式,差异主要在如何初始化 `this` 参数 24 | + 方法调用模式:被保存为一个对象的属性时,这个函数被称为该对象的一个方法;调用表达式中包含提取该属性的动作(`obj.inc(1)` 或 `obj['inc'](1)`)都是方法调用;在方法中可以使用 `this` 引用包含它的对象,而 `this` 的绑定发生在调用的时候(和 Java 似乎没有区别) 25 | + 函数调用模式:如果函数没有被保存为一个对象的属性,就称之为函数调用;此时 `this` 被绑定到全局变量上(而不是外部函数的 `this`),这一点很不好,例如方法的内部函数无法共享方法对 对象的访问权,但可以通过以下方式实现访问: 26 | 27 | ~~~ javascript 28 | obj.double = function () { 29 | var that = this 30 | 31 | var helper = function () { 32 | that.val *= 2 33 | } 34 | 35 | helper() 36 | } 37 | ~~~ 38 | 39 | + 构造器调用模式:如果在函数前面带上 `new` 来调用,那这个调用会创建一个连接到该函数的 `prototype` 成员的新对象,同时,`this` 会被绑定到新对象上;如果一个函数的目的就是为了结合 `new` 前缀来调用,就称之为 _构造器函数_,通常将这种函数保存在大写开头命名的变量中;不建议使用; 40 | + `apply` 调用模式:`apply` 是函数对象的方法,调用它将会执行该函数,而且 `apply` 可以传两个参数,第一个绑定到函数的 `this` 上,第二个绑定到函数的 `arguments` 上 41 | + `arguments` 可以访问传递给函数的所有实际参数,包括未在形式参数中声明的参数;可以用它实现变长参数,但不建议;`arguments` 不是数组,它只是有一个 `length` 属性; 42 | + 一个函数总会返回一个值,没有指定时,返回 `undefined` 43 | + 如果函数调用时在前面加上了 `new` 前缀,且返回值不是一个对象,则返回 `this`(该新对象) 44 | + 异常,一个 `try` 只会有一个 `catch`,多种可能的异常需要根据异常对象的属性来进行区分处理 45 | + 为基本类型扩充功能 46 | + JavaScript 没有块级作用域,函数内定义的变量在函数内任何位置都可见,所以最好在函数开头声明所有变量 47 | + 通过函数字面量创建的函数对象包含一个连接到外部上下文的连接,这被称作闭包 48 | + 内部函数可以访问在外部函数 49 | + 模块模式:一个定义了私有变量和函数的函数;利用闭包,创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可以访问到的地方 50 | 51 | ## 继承 52 | + 函数化 53 | + 以任何方式创建一个新对象 54 | + 有选择地定义私有实例变量和方法 55 | + 给新创建的对象扩充方法,这些方法有特权访问参数、第 2 步中定义的私有变量和方法 56 | + 返回新创建的对象 57 | -------------------------------------------------------------------------------- /Frontend/React-basic.md: -------------------------------------------------------------------------------- 1 | # React基础 2 | 3 | ## [React设计哲学](http://www.infoq.com/cn/articles/react-art-of-simplity/) 4 | + 编写可预测,符合习惯的代码:代码简单易懂,易于维护 5 | + 使用JSX直观的定义用户界面:传统方式,即便V与M文件分离,其逻辑依然是紧密关联的,所以干脆将它们文件上放到一起 6 | + 简化的组件模型:所谓组件,其实就是状态机器;除了状态,组件还有属性;UI与状态绝对一致,修改UI仅仅修改状态即可; 7 | + 每一次界面变化都是整体刷新:简化UI更新逻辑,由framework负责实际高效局部刷新; 8 | + 单向数据流动:Flux,永远只有从模型到视图的数据流动 9 | + 让数据模型也变简单:Immutability 10 | + React思想的衍生:React Native, React Canvas等等 11 | 12 | ## [React开发神器Webpack](http://www.infoq.com/cn/articles/react-and-webpack) 13 | + 同时支持CommonJS和AMD模块 14 | + 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性 15 | + 可以基于配置或者智能分析打包成多个文件,实现公共模块或者按需加载 16 | + 支持对CSS,图片等资源进行打包,从而无需借助Grunt或Gulp 17 | + 开发时在内存中完成打包,性能更快,完全可以支持开发过程的实时打包需求 18 | + 对sourcemap有很好的支持,易于调试 19 | 20 | ## [理解JSX和组件](http://www.infoq.com/cn/articles/react-jsx-and-component) 21 | + JSX本身并不是什么高深的技术,可以说只是一个比较高级但很直观的语法糖 22 | + 这种使用代码构建界面的方式,完全消除了业务逻辑和界面元素之间的隔阂,让代码更加直观和易于维护 23 | + JSX本身就和XML语法类似,可以定义属性以及子元素,唯一特殊的是可以用大括号来加入JavaScript表达式 24 | + React使用组件来封装界面模块,整个界面就是一个大组件,开发过程就是不断优化和拆分界面组件、构造整个组件树的过程 25 | + 组件的`props`与`state`,前者类似于函数调用的参数,后者类似于函数内部的局部变量 26 | + 组件内部不应该修改`props`,组件外部也无法修改`state`,`props`表示那些一旦定义,就不再改变的特性,而`state`是会随着用户互动而产生变化的特性 27 | + 组件生命周期函数 28 | 29 | ![react_component_lifecycle.png](../assets/react_component_lifecycle.png) 30 | 31 | + `this.props.children`与`React.Children`,前者包括所有的属性,后者是react的一个API,处理了不同的情形,使得需要使用前者时不用考虑各种情况 32 | + 使用`propTypes`来限定属性的类型,使用`getDefaultProps`来设置属性的默认值,例如: 33 | 34 | ```javascript 35 | var MyTitle = React.createClass({ 36 | propTypes: { 37 | title: React.PropTypes.string.isRequired, 38 | }, 39 | 40 | getDefaultProps : function () { 41 | return { 42 | title : 'Hello World' 43 | }; 44 | }, 45 | 46 | render: function() { 47 | return

{this.props.title}

; 48 | } 49 | }); 50 | ``` 51 | 52 | + 使用`ref`属性,获取真实DOM节点,必须等到虚拟 DOM 插入文档以后,才能使用这个属性: 53 | 54 | ```javascript 55 | var MyComponent = React.createClass({ 56 | handleClick: function() { 57 | this.refs.myTextInput.focus(); 58 | }, 59 | render: function() { 60 | return ( 61 |
62 | 63 | 64 |
65 | ); 66 | } 67 | }); 68 | ``` 69 | 70 | + 用户在表单填入的内容,属于用户跟组件的互动,所以不能用`this.props`读取,可以通过设置事件回调响应输入变化 71 | + 组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount 方法设置 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI 72 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO list 2 | 3 | ## Java & Android 4 | + 常识 5 | + Official Android Train 6 | + Official API guide 7 | + Official tools 8 | + Material design 9 | + 深入 10 | + Zygote 11 | + VSync 12 | + [Android Project Butter: VSync, Triple buffer, **Choreographer**](http://blog.csdn.net/innost/article/details/8272867) 13 | + [~~自定义View,ViewGroup以高性能实现UI~~](https://medium.com/android-news/prefmatters-using-custom-views-in-android-to-improve-performance-part-1-4dc9bdd75396)([笔记](Android-Java/CustomViewViewGroup.md)) 14 | + 【源码】Choreographer 15 | + 【源码】Message/Looper: Asynchronous Message, Synchronization Barrier 16 | + 【源码】Activity/Fragment的显示、切换原理 17 | + Activity, Fragment生命周期 18 | + Activity启动模式,launchMode和IntentFlag,区别 19 | + Service生命周期,两种启动模式,区别 20 | + BroadcastReceiver,两种方式,区别 21 | + ContentProvider 22 | + Thread, AsyncTask, IntentService 23 | + 五+种布局 24 | + SharedPreference 25 | + SQLite 26 | + Merge, ViewStub 27 | + Activity、View、Window的理解 28 | + 各LaunchMode的使用场景 29 | + View的绘制流程 30 | + Touch事件机制 31 | + Android动画的原理 32 | + Handler, Looper的理解 33 | + Android跨进程通讯的方式 34 | + Binder的理解 35 | + Android Mashup设计的理解 36 | + 关于final用法,反射原理,注解原理,java编译过程,GC等一些常见问题 37 | + activity orientation change 38 | + 长连接,心跳机制 39 | + [安卓音频处理高性能方案](http://googlesamples.github.io/android-audio-high-performance/?linkId=19578000) 40 | 41 | + Android performance patterns, ~~S1, S2, S3~~([笔记](Android-Java/AndroidPerformancePatterns.md)), S4 42 | + [安卓逆向工程](http://www.fasteque.com/android-reverse-engineering-101-part-1/) 43 | 44 | + ~~Effective Java~~([笔记](Android-Java/EffectiveJava.md)) 45 | + 《Android源码设计模式解析与实战》 46 | + 《APP研发录》 47 | + [《深入理解安卓:卷一》](http://wiki.jikexueyuan.com/project/deep-android-v1/) 48 | + [《深入理解安卓:卷二》](http://wiki.jikexueyuan.com/project/deep-android-v2/) 49 | + savedInstanceState专题 50 | + [Advanced RxJava](http://akarnokd.blogspot.jp/) 51 | + 【源码】[RxJava](https://github.com/ReactiveX/RxJava) 52 | + 【源码】[Retrofit](https://github.com/square/retrofit) 53 | + 【源码】[OkHttp](https://github.com/square/okhttp) 54 | + 【源码】[Fresco](https://github.com/facebook/fresco) 55 | + 【源码】[EventBus](https://github.com/greenrobot/EventBus) 56 | 57 | ## 前沿 58 | + [Android weekly](http://androidweekly.net/),每周一期 59 | + [Github trending](https://github.com/trending),每周六 60 | + [Github feed](https://github.com/),每天早上 61 | + [Android Developers Blog](http://android-developers.blogspot.com/) 62 | + [Styling android](https://blog.stylingandroid.com) 63 | -------------------------------------------------------------------------------- /Android-Java/Canvas-Drawables.md: -------------------------------------------------------------------------------- 1 | ## [Canvas & Drawables](http://developer.android.com/guide/topics/graphics/2d-graphics.html) 2 | + 绘制4个基本元素 3 | + Bitmap:保存每个像素的数据 4 | + Canvas:提供draw*** API,通过draw系列函数(绘制线、矩形、圆、椭圆等),把绘制结果写入到bitmap对象中 5 | + Drawing primitive:Rect, Path, text, Bitmap... 6 | + Paint:画笔,描述绘制内容的属性(颜色、样式) 7 | + 两种用法 8 | + 绘制到View,只需要在View子类的onDraw方法中绘制即可,onDraw函数会传入一个Canvas对象,使用其进行绘制即可,由framework负责绘制流程;适用于静态自定义图形、简单动态图形; 9 | + 绘制到Canvas,绘制原语一样,但是最后需要显示的时候,要通过View/Surface来进行显示;适合复杂的动态图形绘制,例如视频游戏; 10 | + 绘制到View 11 | + 适用于静态、低帧率、简单动态图形的绘制,重写View的onDraw方法即可 12 | + 使用参数传入的Canvas对象,由framework负责调用onDraw函数 13 | + 通过invalidate函数请求重绘 14 | + onDraw、invalidate都需要在主线程执行,其他线程可以通过postInvalidate请求重绘 15 | + 16 | + 绘制到Canvas 17 | + Canvas记录(执行)draw操作,将操作记录到Bitmap上,最后将Bitmap显示在Surface上 18 | + 绘制到SurfaceView 19 | + SurfaceView是View的子类,支持他线程绘制,主线程不同步等待其绘制,不需要保证60 fps 20 | + Drawable 21 | + 2D图形的高度抽象,有一系列的子类 22 | 23 | ## [Drawable Resources](http://developer.android.com/intl/zh-cn/guide/topics/resources/drawable-resource.html) 24 | + BitmapDrawable,用图片(.png, .jpg, or .gif)创建drawable,xml定义为: 25 | ```xml 26 | 30 | ``` 31 | + NinePatchDrawable,点9图(.9.png),可以在两个方向上拉伸而不会变形 32 | + LayerDrawable,通过xml定义``,里面定义多个``来定义多层的drawable 33 | + StateListDrawable,通过xml定义``,里面定义多个``来定义具有不同状态的drawable,实现按钮点击态/激活态的常用方法 34 | + LevelListDrawable,通过xml定义``,里面有多个``来实现类似于wifi信号强度这样的drawable,例子: 35 | ```xml 36 | 37 | 38 | 39 | 40 | 41 | 42 | ``` 43 | ImageView有`setImageLevel(int)`方法,可以设置显示哪个强度 44 | + TransitionDrawable,通过xml定义``,里面有多个``,类似于``,但是支持不同layer之间的淡入淡出,通过TransitionDrawable的`startTransition(int)`、`resetTransition()`来显示某一层 45 | + InsetDrawable,把它设置为一个View的背景时,可以与View的背景区域小于其bound,但是应用场景呢? 46 | + ClipDrawable,把源drawable裁剪后显示,可以通过随时间改变裁剪区域来做出图片逐渐展开的效果 47 | + ScaleDrawable,把源drawable缩放后显示,类似还有RotateDrawable,GradientDrawable 48 | + ShapeDrawable,定义基本几何类型 49 | 50 | ## [AndroidFillableLoaders](http://jorgecastillo.xyz/2015/08/16/android-fillable-loaders/) 51 | + PNG可以导出为SVG 52 | + 通过SVGParser,可以将SVG指令转化为Path的指令,并将其绘制到Path对象中 53 | + DashPathEffect可以达到绘制加速边缘的效果 54 | + 填满的动画思路类似于[WashingMachineView](https://github.com/naman14/WashingMachineView/) -------------------------------------------------------------------------------- /Android-Java/JavaObjectMemoryUsage.md: -------------------------------------------------------------------------------- 1 | #Java对象内存的使用情况 2 | 3 | ##一般情况 4 | + primitive types 5 | ![java_primitive_types_mem_usage.png](../assets/java_primitive_types_mem_usage.png) 6 | + Object overhead for "housekeeping" information 7 | recording an object's class, ID and status flags such as whether the object is currently reachable, currently synchronization-locked etc. 8 | Hotspot JVM: 9 | + Object类实例:8字节; 10 | + Object类实例数组:12字节,比Object类实例多了个length域; 11 | + Object size granularity 12 | 为了便于寻址,对象内存会8字节对齐; 13 | 例子: 14 | + Object类实例8字节; 15 | + 只有一个boolean成员的类对象,16字节,有8个boolean成员的类对象,16字节; 16 | + 有两个long成员、三个int成员、一个boolean成员的类对象,40字节; 17 | 18 | ##数组 19 | + object header:12个字节,多了一个length域 20 | + 一维数组,数据区域,length * sizeof(element),此外,对于object是保存的引用,为4字节 21 | + 二维数组,每一行都是一个数组,故每一行都有object的开销 22 | + 例子:10*10 int二维数组: 23 | + 外层header:12字节 24 | + 外层数据(10个引用):10*4=40字节,加上padding,共56字节 25 | + 内层每一行,header12字节 26 | + 内层每一行,数据10*4=40字节,加上padding,每一行共56字节 27 | + 总共56*11=616字节 28 | + 更多维的数组逻辑一致 29 | 30 | ##String 31 | + 一个String对象实际上包含了不止一个对象 32 | + java的char占两个字节 33 | + Minimum String memory usage (bytes) = 8 * (int) ((((num chars) * 2) + 45) / 8) 34 | + String对象包含:char数组,offset,length,hashcode这四部分内容 35 | + 空String对象占用内存:8(header) + 3*4(上述三个int域) + 4(char数组引用) + 12(空char数组header) + 4(char数组padding) = 40字节 36 | + 包含17个字符的String:24 + (12 + 17*2 + 2) = 72字节 37 | + substring使用时的trick 38 | + 如果原string和substring都会被使用,则节省了内存 39 | + 如果原string不再被使用,有可能就会浪费内存 40 | + 例如: 41 | 原有的char数组并不会被回收,但是只使用了其中的一部分: 42 | ```java 43 | String str = "Some longish string..."; 44 | str = str.substring(5, 4); 45 | ``` 46 | 原有的char数据将来会被gc,避免了内存浪费: 47 | ```java 48 | String str = "Some longish string..."; 49 | str = new String(str.substring(5, 4)); 50 | ``` 51 | 52 | ##string buffers 53 | StringBuffer、StringBuilder等 54 | header: 8字节;length,char数组引用;底层char数组所占内存; 55 | 假设初始容量为16的string buffer:16 + 12 + 16*2 + 4 = 64字节 56 | 扩容策略是每次倍增容量 57 | 58 | ##减少String占用的内存 59 | + 通常不需要,只有确定String占用了太多内存后才有必要。 60 | + 一下情形并不需要String,有更节省内存的CharSequence实现。 61 | + storing the string in memory; 62 | + printing out the string or writing it to a stream; 63 | + retrieving individual characters/substrings from the string; 64 | + performing matches against regular expressions. 65 | + 不使用String的优化方案: 66 | + 使用string buffer,通过`trimToSize()`可以至少比String少8字节 67 | + 只用来保存ASCII字符,使用byte[]更高效,或自行实现CharSequence接口,使用更节省内存的底层结构,只在最后需要使用的时候转化为String 68 | + 必须使用String时的优化方案 69 | + 创建substring时,如果原string不再使用,那应该通过new一个新的String,而不是直接赋值给原引用 70 | + 当有很多内容相同的String对象时,可以通过两种方式节省内存: 71 | + 将内容定义为enum,使用时使用enum值 72 | + 规范化:使用`String.intern()`方法;使用HashMap等集合; 73 | ```java 74 | String employeeStatus = rs.getString(2); 75 | employeeStatus = employeeStatus.intern(); 76 | ``` 77 | + intern():将String的内容放到JVM的string pool中,此过程不可逆,很可能会导致内存泄漏;因此只有确定内容只有很少的几种时才可行; -------------------------------------------------------------------------------- /Android-Java/AndroidAnimation.md: -------------------------------------------------------------------------------- 1 | # 安卓系统动效专题 2 | 3 | ## demo 4 | + [animate](https://github.com/hitherejoe/animate) 5 | + [plaid](https://github.com/nickbutcher/plaid) 6 | + [android-topeka](https://github.com/googlesamples/android-topeka) 7 | 8 | ## [系统API](https://developer.android.com/training/material/animations.html#Transitions) 9 | 10 | ## [入门博客系列](http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html) 11 | 12 | ## [Backport lib](https://github.com/andkulikov/Transitions-Everywhere) 13 | 14 | ## [自定义Activity的过场动效](https://www.youtube.com/watch?v=CPxkoe2MraA) 15 | 16 | ## [Fragment过场动效](http://stackoverflow.com/a/17488542/3077508) 17 | 18 | ```java 19 | mFragmentManager.beginTransaction() 20 | .setCustomAnimations(R.anim.slide_down, R.anim.slide_up, R.anim.slide_down, R.anim.slide_up) 21 | .add(android.R.id.content, new SelfHomeFragment(), SelfHomeFragment.class.getName()) 22 | .commit(); 23 | ``` 24 | 25 | setCustomAnimations调用的顺序一定要是第一个,否则会不起效。 26 | 使用add、remove,则会有两层Fragment同时显示的效果。 27 | 28 | ## [Fragment share animation](https://medium.com/@bherbst/fragment-transitions-with-shared-elements-7c7d71d31cbb) 29 | 30 | ## Transition Animations 31 | + property animator 32 | + `View.animate()`方法返回一个[`ViewPropertyAnimator`](http://developer.android.com/intl/zh-cn/reference/android/view/ViewPropertyAnimator.html),它包含对View各种属性的动画改变API。 33 | + 通过动画API改变view的属性(位置),动画结束后,View会停留在结束时的状态(位置、大小等); 34 | + [`ObjectAnimator`](http://developer.android.com/intl/zh-cn/reference/android/animation/ObjectAnimator.html)、`ObjectAnimator.ofFloat(Object target, String propertyName, float... values)`,该方法将通过反射对View进行动画展示。 35 | + 基于Scene的动效:系统自动检测两个Scene之间的区别,然后用动效进行过渡; 36 | + Scene可以用代码创建,也可以由xml定义; 37 | + `ChanngeBounds`,`Fade`,`AutoTransition`,...,`TransitionSet`,`AutoTransition.setOrdering()`,`TransitionSet.setOrdering()`; 38 | + 变化的View要有公共的父Layout; 39 | + 无公共父Layout:`setReparent()`,但是效果并不是十分完美; 40 | + Material design的动画效果 41 | + [RippleDrawable](http://developer.android.com/intl/zh-cn/reference/android/graphics/drawable/RippleDrawable.html), [backport to API 14](https://github.com/balysv/material-ripple) 42 | + [CircularReveal](http://developer.android.com/intl/zh-cn/reference/android/view/ViewAnimationUtils.html), [backport to API 9](https://github.com/ozodrukh/CircularReveal) 43 | + [AnimatedVectorDrawable](http://developer.android.com/intl/zh-cn/reference/android/graphics/drawable/AnimatedVectorDrawable.html), [backport to API 14](https://github.com/wnafee/vector-compat) 44 | + [SharedElementEnterTransition](http://developer.android.com/intl/zh-cn/training/material/animations.html#Transitions), [backport to API 14](https://github.com/takahirom/PreLollipopTransition) 45 | + [AnimatedStateListDrawable](http://developer.android.com/intl/zh-cn/reference/android/graphics/drawable/AnimatedStateListDrawable.html) -------------------------------------------------------------------------------- /Android-Java/CustomViewViewGroup.md: -------------------------------------------------------------------------------- 1 | # 自定义View/ViewGroup以及高性能实现自定义UI 2 | 3 | ## [View绘制流程](http://a.codekk.com/detail/Android/lightSky/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20View%20%E7%BB%98%E5%88%B6%E6%B5%81%E7%A8%8B) 4 | 当 Activity 接收到焦点的时候,它会被请求绘制布局,该请求由 Android framework 处理。绘制是从根节点开始,对布局树进行 measure 和 draw。整个 View 树的绘图流程在ViewRoot.java类的performTraversals()函数展开,该函数所做的工作可简单概况为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw)。 5 | 6 | + measure 7 | + ViewGroup负责测量所有子View及自己,View负责测量自己; 8 | + View重写onMeasure以实现自己的测量逻辑; 9 | + View在onMeasure中,根据传入的widthMeasureSpec, heightMeasureSpec(父ViewGroup对自己的限制),以及自己的内容显示需要,计算出想要的宽高,通过setMeasuredDimension进行最终设置(该方法必须调用,否则抛异常),设置的宽高也是经过位运算的值; 10 | + MeasureSpec值都是经过位运算的,View类提供了一些方法从中读取信息 11 | + 推荐按照以下方式实现 12 | 13 | ```java 14 | @Override 15 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 16 | 17 | // Try for a width based on our minimum 18 | int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth(); 19 | int w = resolveSizeAndState(minw, widthMeasureSpec, 1); 20 | 21 | // Whatever the width ends up being, ask for a height that would let the pie 22 | // get as big as it can 23 | int minh = getPaddingBottom() + getPaddingTop() + getSuggestedMinimumHeight(); 24 | int h = resolveSizeAndState(minh, heightMeasureSpec, 0); 25 | 26 | setMeasuredDimension(w, h); 27 | 28 | } 29 | ``` 30 | + 计算自己所需高度、宽度则通过重写getSuggestedMinimumWidth,getSuggestedMinimumHeight来进行计算 31 | + 子View measure完之后,如果不符合父ViewGroup给出的约束(过大或过小),将触发父ViewGroup对子View进行第二次measure,此时传入的约束可能会发生变化,例如从限制最大值/最小值到给定具体值。 32 | 33 | + ViewGroup的measure过程包括:对于每个child,根据自己的measureSpec、child的LayoutParam、自己的padding,来计算child的measureSpec,然后传给child,让其自己进行上述的measure逻辑 34 | 35 | + layout 36 | + 首先要明确的是,子视图的具体位置都是相对于父视图而言的。View 的 onLayout 方法为空实现,而 ViewGroup 的 onLayout 为 abstract 的,因此,如果自定义的 View 要继承 ViewGroup 时,必须实现 onLayout 函数。 37 | + measure完成后,就是根据每个子View确定显示的大小,及其显示规则,来排布每个子View了,这个排布就是设置子View的left, top, right, bottom了,注意子View的这些值都是相对于父ViewGroup的,而不是屏幕坐标系的绝对位置。 38 | 39 | + draw 40 | + 应该重写的是onDraw方法,一定要重写draw方法时一定要首先调用`super.draw()` 41 | 42 | + 一定要注意 43 | + 自定义View/ViewGroup一定要避免调用requestLayout,会导致整个view hierarchy的重绘,影响性能 44 | 45 | 46 | ## [自定义View实例](https://medium.com/android-news/prefmatters-using-custom-views-in-android-to-improve-performance-part-1-4dc9bdd75396) 47 | 48 | ## [自定义ViewGroup实例](https://medium.com/android-news/perfmatters-introduction-to-custom-viewgroups-to-improve-performance-part-2-f14fbcd47c) 49 | 50 | ## View的位置相关的几个概念 51 | + X, Y, Z 52 | + View左上角在屏幕坐标系上的坐标 53 | + `getX: mLeft + getTranslationX()` 54 | + left, top, right, bottom 55 | + 相对于parent的位置(在parent中的位置),不建议代码修改 56 | + translationX, translationY, translationZ 57 | + View相对于left, top, elevation的位置 58 | + 对X, Y, Z的设置,实际上是通过对这三个属性设置实现的 59 | + 所以X, Y, Z可以由translationX, translationY, translationZ及left, top, elevation计算出来 60 | + height, measured height, width measured width 61 | + GlobalVisibleRect, LocalVisibleRect 62 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BestPractice4UserInput.md: -------------------------------------------------------------------------------- 1 | # Best Practices for User Input 2 | 3 | ## Using Touch Gestures 4 | + 手势检测 5 | + 监听touch事件 6 | + 根据当前及历史touch事件,判断是否符合支持的手势 7 | + 使用`MotionEventCompat`的辅助方法从`MotionEvent`类中读取信息, 8 | 使用`GestureDetectorCompat`进行兼容性的手势检测 9 | + `MotionEvent`类包含了触点id(多点触控支持),位置,压力等众多信息, 10 | 用于进行手势检测 11 | + 使用`GestureDetector`/`GestureDetectorCompat`类进行手势检测, 12 | 传入`GestureDetector.OnGestureListener`实现类,在View/Activity的 13 | `onTouchEvent`中把`MotionEvent`对象传入detector,系统将会在特定手势 14 | 触发时调用回调,例如:fling, long press等 15 | + [示例代码](http://developer.android.com/training/gestures/detector.html#detect) 16 | + 移动追踪 17 | + 会有一个touch slop的概念,触点在屏幕上移动超过一定距离才会被认为是移动 18 | + 常见的手势检测方式有:起止点,移动方向,历史轨迹,移动速度等 19 | + 速度追踪可以使用`VelocityTracker`和`VelocityTrackerCompat`类, 20 | [代码示例](http://developer.android.com/training/gestures/movement.html#velocity) 21 | + 为滚动手势添加动效 22 | + 大多数情况下,内容所需区域大于view大小的,都应该使用ScrollView或者 23 | ListView/RecyclerView,把滚动的处理交给系统 24 | + 但也有特殊情况,例如实现滚动手势时,如果有同样的效果,体验会更好, 25 | 此时就需要使用`Scroller`或者`OverScroller`了 26 | + 更建议使用`OverScroller`,因为它的兼容性更好,此外,对于内容的滚动需求, 27 | 应该使用`ScrollView`或者`HorizontalScrollView` 28 | + scroller和安卓平台独立,只用于记录位置随着滚动的变化,使用者需要按照一定 29 | 的频率去获取位置并应用到view上,以得到平滑的滚动效果 30 | + 滚动的不同类型 31 | + dragging,滚动内容的过程中手指不离开屏幕,只需重写 32 | `GestureDetector.OnGestureListener::onScroll()`方法即可 33 | + flinging,滚动内容的时候,快速滚动后手指离开屏幕,内容仍需保持一定速度继续滚动, 34 | 但是逐渐减速直至停止,这种情况下需要重写`GestureDetector.OnGestureListener:: 35 | onFling()`且结合scroller 36 | + [fling结合scroller的示例](http://developer.android.com/training/ 37 | gestures/scroll.html##scroll) 38 | + 多点触控 39 | + onTouchEvent回调的MotionEvent对象中,包含了多触点的信息 40 | + 额外触点的按下、抬起事件:`ACTION_POINTER_DOWN`, `ACTION_POINTER_UP` 41 | + 拖拽和缩放 42 | + 3.0以上版本有`View.OnDragListener`可供使用,包括drag drop 阴影等 43 | + 使用`ScaleGestureDetector`来进行view的缩放支持 44 | + ViewGroup的点击事件 45 | + 使用`onInterceptTouchEvent()`函数拦截对子View的点击事件 46 | + 使用`ViewConfiguration`类获取系统定义的一系列常量:间距,速度,时间等 47 | + 使用`TouchDelegate`类,扩展子View的点击热区,使其点击热区大于子View的边界 48 | 49 | ## Handling Keyboard Input 50 | + EditText等输入控件可以通过`inputType`属性设置输入类型,一方面控制输入法的输入模式, 51 | 另一方面限制输入内容 52 | + 拼写检查、自动纠正以及Input Method Action 53 | + `android:inputType="textCapSentences|textAutoCorrect"` 54 | + `android:imeOptions="actionSend"`, `actionSearch` 55 | + `editText.setOnEditorActionListener` 56 | + 键盘可见性变化 57 | + manifest中可以配置Activity启动时就显示键盘:`android:windowSoftInputMode="stateVisible"` 58 | + 代码主动显示键盘 59 | 60 | ```java 61 | public void showSoftKeyboard(View view) { 62 | if (view.requestFocus()) { 63 | InputMethodManager imm = (InputMethodManager) 64 | getSystemService(Context.INPUT_METHOD_SERVICE); 65 | imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); 66 | } 67 | } 68 | ``` 69 | 70 | + 此外,manifest中通过`windowSoftInputMode`来控制键盘弹起时Layout的变化: 71 | `adjustResize`, `adjustPan`, `adjustUnspecified`, `adjustNothing` 72 | + 此外,在activity的mode不为`adjustNothing`时,[KeyboardVisibilityEvent](https:// 73 | github.com/yshrsmz/KeyboardVisibilityEvent)库可以检测键盘弹出和收起事件,也提供了 74 | 手动弹出和收起的方法 75 | + 键盘导航:主要用于外接键盘的操作,并非跳转,而是焦点移动 76 | + tab键:View的`android:nextFocusForward`属性,指定当前View在获得焦点状态下,按下tab键, 77 | 焦点移动的下一个View的id 78 | + 方向键:`android:nextFocusUp`, `android:nextFocusDown`, `android:nextFocusLeft`, 79 | `android:nextFocusRight` 80 | + 直接响应键盘按键:Activity等类实现了`KeyEvent.Callback`接口可以监听键盘事件 81 | 82 | ## Supporting Game Controllers 83 | + Activity/View的输入事件回调有对游戏手柄等专业设备的支持 84 | -------------------------------------------------------------------------------- /Android-Java/MVP.md: -------------------------------------------------------------------------------- 1 | #MVP(Model-View-Presenter)模式 2 | 3 | ##[工作流程](http://hannesdorfmann.com/android/mosby/) 4 | ![MVP.png](../assets/MVP.png) 5 | + view通常会持有presenter的引用; 6 | + presenter持有view和model的引用; 7 | + model应该包括数据和对数据的获取或者修改操作; 8 | 9 | ##具体来说 10 | ![MVP1.png](../assets/MVP1.png) 11 | + MVP之间应该尽量解耦,可以通过定义接口来实现,相互持有的是接口引用; 12 | + view应该尽可能听从presenter的指令,而不是自己控制,如:接受presenter的showLoading指令之后才显示正在加载; 13 | + 与用户的交互由view负责,显示的细节由view负责; 14 | 15 | ##MVP in Android(Mosby) 16 | + 使用场景:单一的UI单元,例如fragment,或者一个ViewGroup,原则是这部分UI由一个presenter独立负责,不必相互干扰; 17 | + Acticity、Fragment更应该是view,而不是presenter; 18 | + core模块集成了Butterknife、FragmentArgs、Icepick等开源库; 19 | + MVP模块,定义了MvpPresenter,MvpActivity、MvpFragment类,作为presenter,view的基类; 20 | + MvpPresenter是一个接口,有一个MvpBasePresenter实现,使用WeakReference保存view的引用,所以presenter(子类)要调用view的接口时,需要调用isViewAttached()检查view是否有效,调用getView()获取其引用; 21 | + MvpLceView,模板化loading-content-error流程; 22 | + ViewState模块:保存view(并非android中的View)的状态,处理屏幕旋转、view(fragment、activity)重新创建时的状态恢复; 23 | + 还有针对dagger、retrofit、rx、testing等的模块; 24 | 25 | ##[一些建议](http://hannesdorfmann.com/android/mosby-playbook/) 26 | + Don’t “over-architect” 27 | In fact refactoring is a vital part of development, you simply can’t avoid it (but you can try to minimize the number of refactoring). Start with doing the simple thing and afterwards refactor and make it more extensible as app and requirements grow. 28 | + Use inheritance wisely 29 | 正确利用继承,基类拥有基本特点,子类增加新特性;避免“子类爆炸”; 30 | + Don’t see MVP as an MVC variant 31 | ![mvp-controller.png](../assets/mvp-controller.png) 32 | Controller负责控制View(UI)和用户交互时应该执行的动作(调用Presenter的哪个方法);当被Presenter调用显示方法时,如何显示(动效、数据使用方式); 33 | + Take separation of Model, View and Presenter serious 34 | 写代码的时候,认真思考每一行代码的功能应该属于哪个模块,目前的位置是否合适? 35 | + View负责UI显示,以及对UI、用户的响应 36 | + Model负责数据获取、存储、处理 37 | + Presenter负责配合/连接两者 38 | + Presentation Model 39 | + 当UI显示对于model的需求与数据源不一致时:最好的办法是加一层wrapper;其次是为model加一些方法,产生需要的域;最差的是为model加上这些域; 40 | + Navigation is a UI thing 41 | 不同界面之间的跳转应该由view负责 42 | + onActivityResult() and MVP 43 | 当result仅仅用于显示时,无需涉及presenter;当需要额外处理时,例如图片处理、上传等,则需要放到presenter中去; 44 | + MVP and EventBus - A match made in heaven 45 | EventBus常被用于业务逻辑事件、UI更新事件(fragment之间通信) 46 | + 前者presenter负责接收event,控制view显示; 47 | + 后者有一种常见做法:Activity实现一个listener接口,让fragment持有该接口引用(attach时赋值),使用该接口进行通信;然而使用EventBus更合适,解耦更彻底; 48 | + Optimistic propagation 49 | 用户的操作,首先给其成功的反馈,然后在后台进行处理,失败后给出失败的提示,并撤销成功的反馈(显示),这种做法对于用户体验或许更佳; 50 | 如何看待内存泄漏:避免activity/fragment的泄漏,但对于presenter,如果希望操作执行完之后再被GC,则subscriber/runnable持有的presenter引用,可以认为是合理的; 51 | + MVP scales 52 | MVP的V可以是整个屏幕显示的内容,也可以是屏幕上的某个模块显示的内容; 53 | + Not every View needs MVP 54 | 静态的页面并不需要MVP 55 | + Only display one Model per MVP view 56 | 一个V显示多个M会为代码增加复杂性,尤其是当需要保存ViewState时;合理的做法是将V拆分为独立的V,每个V只负责显示一个M;V可以是fragment,也可以是View/ViewGroup;例子: 57 | ![menu-refactored.jpg](../assets/menu-refactored.jpg) 58 | + Android Services 59 | Service显然属于业务逻辑部分,由presenter与之通信是合理的; 60 | + Use LCE only if you have LCE Views 61 | LCE还包含loadData,setData,如果没有这两个语义,就不要使用LceView,因为对接口的空实现,有违接口的规范; 62 | + Writing custom ViewStates 63 | 当需要保存view的状态时,定义好ViewState,并在view内维护、检查; 64 | + Testing custom ViewState 65 | View和ViewState都是纯Java代码,可以使用Java的单元测试方法进行测试; 66 | + ViewState variants 67 | 有可能View既显示了数据,又在进行刷新操作,但是ViewState始终只会处于一个状态,通常的做法是为原来的状态再加一层修饰,加以区分;也可以定义一个新的ViewState; 68 | + Not every UI representation is a ViewState 69 | 例如:显示空内容,并不是新状态,而是show content,只不过content为空; 70 | + Not every View needs a ViewState 71 | mosby的ViewState通过SavedInstance保存,限制大小为1MB;另外保存的数据有可能是过时的,需要注意; 72 | + Avoid Fragments on the backstack and child fragments 73 | fragment的生命周期中可能会有一些问题,所以尽量避免将fragment放入back stack,或者子fragment; 74 | + Mosby supports MVP for ViewGroups 75 | + Mosby provides Delegates 76 | 好处: 77 | + 可以把MVP模式应用到mosby未包含的类中,例如DialogFragment;甚至是第三方库; 78 | + 实现自定义的Delegate,可以改变默认Delegate的行为; -------------------------------------------------------------------------------- /Android-Java/Fragments.md: -------------------------------------------------------------------------------- 1 | #[使用Fragment](http://www.vogella.com/tutorials/AndroidFragments/article.html) 2 | 3 | ##优点 4 | + 简化对UI的重用,将UI和交互放到Fragment里面,可以方便的针对不同设备设置不同的UI(如pad显示两个fragment,phone显示一个); 5 | 6 | ##完整生命周期 7 | ![FullFragmentAndActivityLifeCycle.png](../assets/FullFragmentAndActivityLifeCycle.png) 8 | 9 | ##使用fragment 10 | + 直接在layout.xml里面声明一个Fragment 11 | + 使用FragmentManager动态添加、替换、移除; 12 | + 必须通过FragmentTransaction来完成动态改变; 13 | + addToBackStack()方法可以支持通过返回键回退到上一个Fragment; 14 | + Fragment的isInLayout()方法可以判断Fragment是否已经显示; 15 | + 可以通过放在不同文件夹下(values,values-land等)的xml文件中的变量值,在代码中动态获取值,来达到判断当前运行模式/设备的目的; 16 | + Fragment的Transaction支持动画; 17 | 18 | ##使用Fragment进行后台处理(Headless Fragment) 19 | + onCreateView返回null; 20 | + 结合`setRetainInstance()`方法,让Fragment在应用程序配置发生变化(横竖屏、屏幕尺寸)时,不会被destroy,从而继续执行后台任务; 21 | + 通过为Fragment设置tag,可以在之后获取fragment实例; 22 | + 效果类似于MVP中的ViewState; 23 | 24 | ##[Fragment不利于单元测试](https://corner.squareup.com/2014/10/advocating-against-android-fragments.html) 25 | + Fragment的life cycle很复杂 26 | + View controllers? Not so fast. 27 | 操作view,有很多view相关的代码,不利于单元测试 28 | + Fragment transactions 29 | 异步过程,当接收到多个click事件,或者configuration changes时,程序将处于未知状态 30 | + Fragment creation magic 31 | fragment manager可能会使用反射接口重新创建fragment,而fragment有可能会作为内部类被定义,没有public的构造函数,问题就来了 32 | + Fragments: lessons learned 33 | + Single Activity Interface:使用不同的Fragment实现界面,动效、生命周期将变得更容易 34 | + The backstack isn't an activity specific notion; you can implement a backstack within an activity. 35 | + There is no need for new APIs; everything we needed was there from the very beginning: activities, views, and layout inflaters. 36 | + Responsive UI: fragments vs custom views 37 | + 扩展阅读:使用custom views构建single activity app:[Simpler Android apps with Flow and Mortar](https://corner.squareup.com/2014/01/mortar-and-flow.html) 38 | 39 | ##[Fragment Transactions & Activity State Loss](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) 40 | ```java 41 | java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 42 | at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341) 43 | at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352) 44 | at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595) 45 | at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574) 46 | ``` 47 | + 原因:在activity的state被save之后,commit FragmentTransaction,会抛出这个异常 48 | + 当activity在后台被杀死之后,会通过onSaveInstanceState回调让程序可以保存数据(状态),其中dialog, fragment, view的状态由framework负责保存和恢复 49 | + 因为FragmentTransaction#commit()在onSaveInstanceState()后被调用了,系统为了防止activity state loss,抛出了该异常 50 | + 从3.1起,安卓系统对于Activity生命周期的维护发生了变化 51 | ![android_activity_life_cycle_change.png](../assets/android_activity_life_cycle_change.png) 52 | support库的行为: 53 | ![android_activity_life_cycle_change2.png](../assets/android_activity_life_cycle_change2.png) 54 | + 如何避免 55 | + Be careful when committing transactions inside Activity lifecycle methods 56 | 只在onCreate或者响应用户的输入时才会commit,不会遇到这个问题;但如果在onActivityResult、onStart、onResume等其他生命周期函数中调用,则会有风险,尤其是onResume,推荐使用FragmentActivity#onResumeFragments()/Activity#onPostResume()中调用,而不是onResume。 57 | ```java 58 | private boolean mReturningWithResult = false; 59 | 60 | @Override 61 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 62 | super.onActivityResult(requestCode, resultCode, data); 63 | mReturningWithResult = true; 64 | } 65 | 66 | @Override 67 | protected void onPostResume() { 68 | super.onPostResume(); 69 | if (mReturningWithResult) { 70 | // Commit your transactions here. 71 | } 72 | // Reset the boolean flag back to false for next time. 73 | mReturningWithResult = false; 74 | } 75 | ``` 76 | + Avoid performing transactions inside asynchronous callback methods. 77 | 回调内已经对Activity的状态没有保证了,强烈建议不要这么做; 78 | + Use commitAllowingStateLoss() only as a last resort. -------------------------------------------------------------------------------- /Android-Java/GoogleDev100Days.md: -------------------------------------------------------------------------------- 1 | #Google dev 100 days系列视频 2 | 3 | ##Day 1:[The Android Design Library](http://android-developers.blogspot.com/2015/05/android-design-support-library.html) 4 | compile 'com.android.support:design:22.2.0' 5 | + TextInputLayout 6 | EditText的hint将一直以浮动形式显示在输入框下面,并且可以通过setError()接口,提示错误信息; 7 | + FloatingActionButton 8 | 浮动操作按钮,默认设置为app theme的颜色;可以设置为mini大小;继承自ImageView,可以为其设置任何显示内容; 9 | + Snackbar 10 | 轻量、快速的用户反馈方式;显示在屏幕底部,支持一个可选的操作按钮,显示超时后自动消失,用户也可以滑动使其消失;API与toast基本一致,但功能更强大; 11 | ```java 12 | Snackbar 13 | .make(parentLayout, R.string.snackbar_text, Snackbar.LENGTH_LONG) 14 | .setAction(R.string.snackbar_action, myOnClickListener) 15 | .show(); // Don’t forget to show! 16 | ``` 17 | + TabLayout 18 | 多view tab支持;可以和ViewPager一起使用; 19 | + NavigationView 20 | 支持通过menu资源文件创建导航视图;把NavigationView放到DrawerLayout里面,通过`app:headerLayout`属性设置headerLayout,通过`app:menu`属性设置导航菜单内容,支持高亮显示当前选中的菜单项,支持多级菜单,通过`setNavigationItemSelectedListener()`接口设置菜单点击回调;需要注意的是NavigationView会负责状态栏的操作,在API 21+时,需要考虑状态栏的控制; 21 | + CoordinatorLayout 22 | + floating action buttons 23 | 将FloatingActionButton放到CoordinatorLayout中,在使用Snackbar时,将CoordinatorLayout传入到Snackbar的`Snackbar.make()`函数中,那么action button将会在snack bar显示和消失时,自动改变其位置,不需要任何代码;`layout_anchor`和`layout_anchorGravity`属性可以设置浮动view和其他view的位置; 24 | + app bar(以前的ActionBar) 25 | AppBarLayout可以和RecyclerView响应,RecyclerView通过`app:layout_behavior`属性指定Behavior子类,AppBarLayout的元素通过`app:layout_scrollFlags`属性指定对滑动事件的响应方式; 26 | + Collapsing Toolbars 27 | 把Toolbar直接作为AppBarLayout的子元素,无法满足Toolbar的不同元素以不同方式响应滑动操作的需求,为此,可以在中间加一层CollapsingToolbarLayout;可以实现顶部自定义布局,随滚动而变化为bar,再滚动则消失,反向滚动则显示的炫酷效果 28 | !video[patterns-scrolling-techniques_flex_space_image_xhdpi_003.webm](../assets/patterns-scrolling-techniques_flex_space_image_xhdpi_003.webm) 29 | + Custom views 30 | 自定义View可以通过定义[Coordinator.Behavior](http://developer.android.com/reference/android/support/design/widget/CoordinatorLayout.Behavior.html)的子类来与CoordinatorLayout进行合作,从而实现自定义的显示效果; 31 | 32 | ##Day 2:[LRUCache](http://developer.android.com/reference/android/util/LruCache.html) 33 | Android framework实现的LRU缓存算法类,对于Bitmap的使用场景非常合适; 34 | 35 | ##Day 3:[Google Play Services 7.5](http://android-developers.blogspot.com/2015/05/a-closer-look-at-google-play-services-75.html) 36 | + [Smart Lock for Passwords](https://developers.google.com/identity/smartlock-passwords/android/) 37 | 密码保存服务API; 38 | + Instance ID, Identity, and Authorization 39 | 提供应用识别、授权服务; 40 | + [Google Cloud Messaging](https://developers.google.com/cloud-messaging/) 41 | 消息推送、上报服务、基于话题订阅的推送、网络请求管理(优化); 42 | + [App invite](https://developers.google.com/app-invites) 43 | 集成的邀请新用户(分享APP)功能; 44 | + [Google Cast](http://www.google.com/cast/) 45 | 谷歌提供的多设备视频、音频播放功能; 46 | + Android Wear、Google Fit... 47 | 48 | ##Day 4:[Web app的推送通知](https://www.youtube.com/watch?v=Z_K8QPQe6oM) 49 | 50 | ##Day 5:[用户活动识别:步行、跑步、骑车等](https://developers.google.com/android/reference/com/google/android/gms/location/ActivityRecognitionApi) 51 | 52 | ##Day 6:[Android wear使用MAP API](https://developers.google.com/maps/documentation/android/wear) 53 | 54 | ##Day 7:[语音互动,不仅是启动命令,还能交互](http://io2015codelabs.appspot.com/codelabs/voice-interaction#1) 55 | 56 | ##Day 8:[谷歌地图的Lite模式,高性能显示多张静态地图](https://developers.google.com/maps/documentation/android/lite) 57 | 58 | ##Day 9:[使用Lint工具进行代码检查:性能、bug、内存泄漏等](http://developer.android.com/tools/debugging/improving-w-lint.html) 59 | 60 | ##Day 10:[逐步优化网页响应的速度](https://youtu.be/d5_6yHixpsQ) 61 | 62 | ##Day 11:[指纹验证API](https://youtu.be/VOn7VrTRlA4) 63 | 64 | ##Day 12:[Google Play商店的icon、描述文字-安装量统计功能](https://youtu.be/B6ydLpkhq04) 65 | 66 | ##Day 13:[优化APP在google搜索中的结果](https://youtu.be/NrcCUaAaHG4) 67 | 68 | ##Day 14:[Google play for work:为办公设备订制的APP](https://youtu.be/dH41OutAMNM) 69 | 70 | ##Day 15:[Google map的AnimateCamera API](https://youtu.be/gkze68UlHdk) 71 | 72 | ##Day 16:[Transparency & Alpha使用时的渲染性能优化](https://youtu.be/wIy8g8yNhNk) 73 | + 在动画开始时,`setLayerType(View.LAYER_TYPE_HARDWARE, null)`,动画结束后,`setLayerType(View.LAYER_TYPE_NONE, null)`;在API >= 16时,可以只调用`ViewPropertyAnimator.alpha(0.0f).withLayer()`接口即可; 74 | + 使用shadow时,重写View的`hasOverlappingRendering()`接口,返回false; 75 | + 只有当确定瓶颈是这部分view的渲染时,才有必要这样优化; 76 | 77 | ##Day 17:[Google Analytics SDK](https://youtu.be/ecAzIIvbKIU) 78 | 下载量、安装量、用户离开APP的页面、地区分布等多种统计信息; 79 | 80 | ##Day 18:[AdMob:APP广告分发平台](https://youtu.be/N-EXqPBaaKQ) 81 | 多种展示方式、自动获取费用最高的广告、Google Analytics集成等特点; 82 | 83 | ##Day 19:[designing for drivers](https://youtu.be/vG9c5egwEYY) 84 | 6步法则;只展示驾驶员真正想要的内容;使用语音控制; 85 | 86 | ##Day 20:[Android M中的Android for work API](https://youtu.be/vcSj8ln-BlE) 87 | 88 | ##Day 25:[Android M preview介绍](https://youtu.be/dcmNjbCNvA8) 89 | 90 | ##Day 26:[Chrome支持把web应用“安装”到桌面:去掉地址栏,有主题,有icon,直接启动](https://youtu.be/N1Bdu7ukN40) -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | + [关于](README.md) 2 | + 基础 3 | + [安卓基础](Android-Java/AndroidBasic.md) 4 | + [使用Fragment](Android-Java/Fragments.md) 5 | + [安卓系统点击事件处理框架](Android-Java/AndroidTouchSystem.md) 6 | + [官方Material design手册](Android-Java/AndroidOfficialMaterialDesignGuild.md) 7 | + [《50 Android Hacks》一书](Android-Java/50AndroidHacks.md) 8 | + 官方开发手册 9 | + [Getting Started](Android-Java/AndroidOfficialDevelopGuild-GettingStarted.md) 10 | + [Building Apps with Content Sharing](Android-Java/AndroidOfficialDevelopGuild-BuildingAppsWithContentSharing.md) 11 | + [Building Apps with Multimedia](Android-Java/AndroidOfficialDevelopGuild-BuildingAppsWithMultimedia.md) 12 | + [Building Apps with Graphics & Animation](Android-Java/AndroidOfficialDevelopGuild-BuildingAppsWithGraphicsAndAnimation.md) 13 | + [Building Apps with Connectivity & the Cloud](Android-Java/AndroidOfficialDevelopGuild-BuildAppWithConnectivity.md) 14 | + [Building Apps with Contacts & Sign-In](Android-Java/AndroidOfficialDevelopGuild-BuildAppWithContactsAndSignIn.md) 15 | + [Best Practices for Interaction and Engagement](Android-Java/AndroidOfficialDevelopGuild-BestPractice4InteractionAndEngagement.md) 16 | + [Best Practices for User Interface](Android-Java/AndroidOfficialDevelopGuild-BestPractice4UserInterface.md) 17 | + [Best Practices for User Input](Android-Java/AndroidOfficialDevelopGuild-BestPractice4UserInput.md) 18 | + [Best Practices for Background Jobs](Android-Java/AndroidOfficialDevelopGuild-BestPractice4BackgroundJob.md) 19 | + [Best Practices for Performance](Android-Java/AndroidOfficialDevelopGuild-BestPractice4Performance.md) 20 | + [Developer tools](Android-Java/AndroidOfficialDevelopGuild-Tools.md) 21 | + [官方分发手册](Android-Java/AndroidOfficialDistributeGuild.md) 22 | + [各个安卓版本引入的主要新特性](Android-Java/NewInAndroid.md) 23 | 24 | + 构建 25 | + [Gradle](Android-Java/Gradle.md) 26 | + [ProGuard](Android-Java/ProGuard.md) 27 | + [Multidex专题](Android-Java/MultiDex.md) 28 | 29 | + 测试 30 | + [AndroidTDD](Android-Java/AndroidTDD.md) 31 | + [《Test Driven Development: By Example》一书](Android-Java/TDD.md) 32 | 33 | + 定制 34 | + [MaterialDesign](Android-Java/MaterialDesign.md) 35 | + [使用style修饰View](Android-Java/StylingViews.md) 36 | + [安卓系统动效专题](Android-Java/AndroidAnimation.md) 37 | 38 | + 视野 39 | + [一些很棒的点子](Android-Java/CoolIdea.md) 40 | + [Android项目架构](Android-Java/AndroidProjectArch.md) 41 | + [安卓开发技能树](Android-Java/AndroidDevSkillTree.md) 42 | + [Google dev 100 days系列视频](Android-Java/GoogleDev100Days.md) 43 | + [WebRTC](Android-Java/WebRTC.md) 44 | + [Google IO 2015摘要](Android-Java/GoogleIO2015.md) 45 | + [Data binding(MVVM,Model-View-ViewModel)](Android-Java/MVVM.md) 46 | 47 | + 最佳实践 48 | + [MVP(Model-View-Presenter)模式](Android-Java/MVP.md) 49 | + [Rx在Android中的最佳实践](Android-Java/RxAndroidBestPractice.md) 50 | + [Futurice公司安卓团队的建议](Android-Java/android-best-practices.md) 51 | + [UI上的一些高效方式/最佳实践](Android-Java/EffectiveAndroidUI.md) 52 | 53 | + 深入 54 | + [安卓性能优化](Android-Java/AndroidPerformancePatterns.md) 55 | + [Memory leak专题](Android-Java/MemoryLeak.md) 56 | + [处理App运行时配置的变化(屏幕方向,语言等)](Android-Java/HandlingRuntimeChanges.md) 57 | + [Launch mode 和 Intent flags专题](Android-Java/LaunchModeIntentFlags.md) 58 | + [Canvas & Drawables](Android-Java/Canvas-Drawables.md) 59 | + [自定义View/ViewGroup以高性能实现自定义UI](Android-Java/CustomViewViewGroup.md) 60 | + [《深入理解JVM》](Android-Java/InsideJVM.md) 61 | + [《Effective Java 2nd Edition》](Android-Java/EffectiveJava.md) 62 | + [《Android源码设计模式解析与实战》](Android-Java/DesignPatternsInsideAndroid.md) 63 | + [深入Android frameworks](Android-Java/UnderstandAndroidSourceCode.md) 64 | + [Java对象内存使用](Android-Java/JavaObjectMemoryUsage.md) 65 | + [Java Memory model](Android-Java/JSR133.md) 66 | + [Java同步机制](Android-Java/JavaSynchorinization.md) 67 | + [Rx](Android-Java/Rx.md) 68 | + [依赖注入](Android-Java/DependencyInjection.md) 69 | + [AOP](Android-Java/AOP.md) 70 | + [《App研发录》一书](Android-Java/App-Dev-Notes.md) 71 | 72 | + 杂谈 73 | 74 | + [函数调用时,传递参数应该是不可变的(Immutable)](misc/BetterDesignWithImmutableParams.md) 75 | + [List.toArray()再强转是一定会失败的](Android-Java/3077508.md) 76 | + [使用词法分析、语法分析工具进行带语法文本处理](misc/Parcer.md) 77 | + [深入Java深浅拷贝、immutable、unmodifiable](misc/copy.md) 78 | + [面向对象六大原则(SOLID+)](misc/OOP6Principles.md) 79 | + [暗时间:学会正确思考](misc/DarkTime.md) 80 | 81 | + 后端 82 | + [Feed系统的设计](Backend/Feed.md) 83 | + [关系型数据库设计范式](Backend/DBNF.md) 84 | + [REST API设计](Backend/REST.md) 85 | 86 | + 前端 87 | + [React基础](Frontend/React-basic.md) 88 | + [深入浅出ES6系列](Frontend/ES6.md) 89 | + [Flux基础](Frontend/Flux.md) 90 | + [JSTips](https://github.com/loverajoel/jstips) 91 | + [JavaScript 语言精粹](Frontend/JavascriptGoodParts.md) 92 | 93 | + MOOC 94 | + [Effective Thinking Through Mathematics](MOOC/UTAustinX_UT.9.01x.md) 95 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BestPractice4UserInterface.md: -------------------------------------------------------------------------------- 1 | # Best Practices for User Interface 2 | 3 | ## Designing for Multiple Screens 4 | + 支持不同屏幕尺寸 5 | + layout中尽量使用`wrap_content`或`match_parent`,而不是固定的数值 6 | + 资源文件可以定义不同的Qualifier,根据屏幕尺寸(dp值,最小宽高dp值,large等),dpi级别,屏幕朝向等等 7 | + Nine-patch Bitmaps 8 | 9 | ## Adding the App Bar 10 | + 使用Toolbar,`setSupportActionBar`设置tool bar,`getSupportActionBar`可以返回一个`ActionBar`对象(尽管设置的是tool bar),可以对app bar进行相关操作 11 | + app bar的action通过option menu进行设置,响应在`onOptionsItemSelected`回调中 12 | + up navigation:在manifest中声明parent activity,同时设置`getSupportActionBar().setDisplayHomeAsUpEnabled(true);`;如此设置之后就无需在代码中进行响应操作了; 13 | + action view和action provider:例如search view 14 | 15 | ## Showing Pop-Up Messages 16 | + Snackbar,和Toast类似,但是更符合material design 17 | + 自动消失,可以加入action button 18 | + 使用在CoordinatorLayout中,还能让其他View在其显示时自动上移,协同显示 19 | 20 | ## Creating Custom Views 21 | + 设计良好的自定义view首先应该是一个设计良好的类,高效利用cpu和内存等 22 | + 且应该遵循安卓的标准 23 | + 提供xml属性,可以在layout中配置view 24 | + accessibility && compatibility 25 | + 在`onDraw`函数中进行自定义绘制 26 | + 在`onSizeChanged`函数中响应尺寸的变化,layout的变化 27 | + `onLayout`, `onMeasure`等 28 | + 响应输入事件:点击、滑动、fling等,`onTouchEvent` 29 | + `TouchEvent`的信息比较原始,通常可以使用系统提供的帮助类提供的高级API:`GestureDetector` 30 | + fling事件的处理需要结合`Scroller`,动画等方式,以达到真实世界的物理运动效果 31 | + 优化view的效率 32 | + 减小循环被调用的函数的执行时间,例如onDraw;减少其中的内存分配; 33 | + 在初始化,或者动画开始前进行对象的创建,避免在动画过程中进行内存分配; 34 | + 减少invalidate()的调用,但是要注意,当view的内容发生变化需要更新时,必须要调用,否则不会生效; 35 | + 减少requestLayout()的调用,它可能会导致多次遍历整个view树;同时也应该使得布局扁平化,不要太深的嵌套; 36 | + 如果ui过于复杂,可以考虑自定义ViewGroup以对ui进行加速处理;这时ViewGroup的子view是满足特定条件的,可以减少measure, layout的必要; 37 | 38 | ## Creating Backward-Compatible UIs 39 | + 通过抽象出接口,来定义UI的API,再根据不同的系统版本,选择不同的实现,以达到兼容的目的; 40 | + 新版系统直接proxy到新的系统API,旧的版本采取自定义实现方式; 41 | 42 | ## [Implementing Accessibility](http://developer.android.com/training/accessibility/index.html) 43 | ... 44 | 45 | ## Managing the System UI 46 | + 包括状态栏(顶部),导航栏(底部虚拟按键区域) 47 | + API 14+,让status bar可见性为gone,指定theme为`@style/Theme.AppCompat.Light.NoActionBar.FullScreen`或者`@android:style/Theme.Holo.NoActionBar.Fullscreen` 48 | 49 | ```xml 50 | 56 | ``` 57 | + API 14+,让navigation bar可见性为gone 58 | 59 | ```java 60 | View decorView = getWindow().getDecorView(); 61 | // Hide both the navigation bar and the status bar. 62 | // SYSTEM_UI_FLAG_FULLSCREEN is only available on Android 4.1 and higher, but as 63 | // a general rule, you should design your app to hide the status bar whenever you 64 | // hide the navigation bar. 65 | int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 66 | | View.SYSTEM_UI_FLAG_FULLSCREEN; 67 | decorView.setSystemUiVisibility(uiOptions); 68 | ``` 69 | 70 | + 注意,上述方式有以下问题 71 | + 用户点击任何区域,都会导致navigation bar出现,且保持可见(属性被清除); 72 | + 一旦属性被清除,需要重新设置,使得navigation bar重新隐藏; 73 | + 所以需要`decorView.setOnSystemUiVisibilityChangeListener`,监听system ui可见性变化,在可见时,通过postDelayed,再次隐藏之; 74 | + API 19+,以后,引入了`SYSTEM_UI_FLAG_IMMERSIVE`和`SYSTEM_UI_FLAG_IMMERSIVE_STICKY`,前者同样存在上述问题; 75 | + 更完整的可以参考最新版AndroidStudio(2.0 preview 7)创建的`FullscreenActivity`模板; 76 | + 响应system ui可见性的变化 77 | 78 | ```java 79 | View decorView = getWindow().getDecorView(); 80 | decorView.setOnSystemUiVisibilityChangeListener 81 | (new View.OnSystemUiVisibilityChangeListener() { 82 | @Override 83 | public void onSystemUiVisibilityChange(int visibility) { 84 | // Note that system bars will only be "visible" if none of the 85 | // LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set. 86 | if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { 87 | // TODO: The system bars are visible. Make any desired 88 | // adjustments to your UI, such as showing the action bar or 89 | // other navigational controls. 90 | } else { 91 | // TODO: The system bars are NOT visible. Make any desired 92 | // adjustments to your UI, such as hiding the action bar or 93 | // other navigational controls. 94 | } 95 | } 96 | }); 97 | ``` 98 | 99 | ## Material Design for Developers 100 | 更多参见material design专题,包括: 101 | 102 | + material theme及其定制; 103 | + CardView, RecyclerView, item animation; 104 | + Custom shadows and view clipping; 105 | + Vector drawables, tint, palette; 106 | + Custom animations: ripple, reveal, activity transition, shared element transition, curved motion, Animate View State Changes, Animate Vector Drawables; 107 | 108 | 兼容性维护: 109 | 110 | + 各种backport; 111 | + Define Alternative Styles; 112 | + Provide Alternative Layouts; 113 | + Support Library; 114 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialMaterialDesignGuild.md: -------------------------------------------------------------------------------- 1 | #Google官方Material Design手册([developer](http://developer.android.com/design/),[spec](http://www.google.com/design/spec)) 2 | 3 | ##Creative Vision 4 | 安卓系统、系统应用、谷歌核心应用,包括安卓开发者,都应牢记以下总体目标: 5 | + Enchant me:多层次的优美,流畅清晰的动画、松脆有意义的布局和排版、连图标都是一种艺术;应结合优美、简洁、功能目标,创造出使用简易但功能强大的APP; 6 | + Simplify my life:突出重点,简化操作; 7 | + Make me amazing:用新鲜技术打动用户,既简洁又优雅; 8 | 9 | ##Android Design Principles 10 | + Enchant Me 11 | + Delight me in surprising ways:漂亮的界面、精心设计的动画、适时的音效... 12 | + Real objects are more fun than buttons and menus:让用户可以直接接触、操作APP内的事物 13 | + Let me make it mine:除了提供有价值、优美的默认外观/设置外,也适当提供自定义途径,当然,不能妨碍主要的功能 14 | + Get to know me:记住用户的操作/设置/选择,尽量避免用户重复同样的选择,将以前的选择放在快捷的位置 15 | + Simplify My Life 16 | + Keep it brief:内容描述尽量用短句,可读性才强 17 | + Pictures are faster than words:图片更能吸引注意力,以及表达内容 18 | + Decide for me but let me have the final say:尽最大努力猜测用户要执行的操作,先设置为默认值,而不是询问用户是否使用之;但要提供方便的取消功能,让用户拥有最终决定权 19 | + Only show what I need when I need it:将内容分割为小块,方便接收;隐藏不重要的信息 20 | + I should always know where I am:不同界面有明显区别,完善的导航,用动画暗示不同界面的关系,为执行中的任务提供反馈 21 | + Never lose my stuff:记住用户的设置/创造,并能轻易访问,在不同设备之间提供同步功能 22 | + If it looks the same, it should act the same:将功能的区别在视觉上明显体现出来,不要使用“模式设置” 23 | + Only interrupt me if it's important:只在重要的时候提醒/打断用户 24 | + Make Me Amazing 25 | + Give me tricks that work everywhere:使用一些通用的快捷操作/手势/标志 26 | + It's not my fault:当用户操作不当时,提醒需要温和、易懂,掩盖技术细节,能自动修正就是最好的 27 | + Sprinkle encouragement:将复杂操作拆分为简单的小任务,即便作用细微,也要为操作提供反馈 28 | + Do the heavy lifting for me:让新手用得像专家一样 29 | + Make important things fast:将最主要的功能放置在最突出的位置 30 | 31 | ##Material disign principles 32 | + Material is the metaphor:给用户真实事物的感受,真实触感、纸与墨、贴近真实物理规律、光影、表面、移动、相互关系等,都是真实世界的暗示; 33 | + Bold, graphic, intentional 34 | The foundational elements of print-based design—typography, grids, space, scale, color, and use of imagery—guide visual treatments. These elements do far more than please the eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge-to-edge imagery, large-scale typography, and intentional white space create a bold and graphic interface that immerse the user in the experience. 35 | An emphasis on user actions makes core functionality immediately apparent and provides waypoints for the user. 36 | + Motion provides meaning:移动用来聚焦注意力、保持一致性;反馈很微妙;转变动画很有效; 37 | 38 | ##What is material? 39 | + Environment 40 | + 模拟3D世界,引入z坐标; 41 | + 模拟光影,结合key light和ambient light投射出的阴影; 42 | + Material properties 43 | + 物理属性 44 | + 只有宽高(xy)可以改变,厚度(z)固定为1dp; 45 | + 阴影体现的是元素的海拔高度(z),而不是颜色的变化; 46 | + 内容是显示在物质上的,而不是增加了物质的厚度; 47 | + 内容的行为独立于物质,但是受限于物质的边界; 48 | + 物质是实心的,输入事件(点击等)不能在物质之间传递,只能在最上层的物质上响应; 49 | + 多个物质元素不能同时在同一高度,要用阴影区分出它们的不同高度; 50 | + 物质不可穿过,改变高度时,不能穿过另一个物质; 51 | + 改变物质 52 | + 物质可以改变形状 53 | + 物质的变化都局限于其平面内 54 | + 物质不能折叠或者弯曲 55 | + 多个物质元素可以合并为同一个,只要在同一高度 56 | + 物质拆分、跨高度移动之后,又可以合并成一个 57 | + 物质的移动 58 | + 物质可以自发创建或销毁 59 | + 物质可以沿着任意轴移动 60 | + 沿着z轴移动通常暗示着和用户的交互 61 | + Objects in 3D space 62 | Material design中的元素和真实世界中的事物类似,可以叠起来,可以依附,但不能穿过,能投射阴影、反射光线; 63 | + 高度(Elevation) 64 | + 单位是dp,子元素的高度值是相对于父元素的,因为元素都是1dp厚,所以高度值是两个元素的顶部的距离; 65 | + 元素拥有默认的高度,App bar: 4dp,Floating Action Button: 6dp,Card: 2dp; 66 | + 默认高度不会改变,当改变一个元素的高度之后,应该尽快回到默认高度; 67 | + 同类元素的默认高度,在不同应用之间应该保持一致;但是在不同平台上可能不一致; 68 | + 响应式高度,改变高度以响应用户的输入(普通、聚焦、按下等),或者系统事件; 69 | + 动态高度变化,相对于默认高度,当需要响应时,变化一个偏移量,事件结束后回到默认高度; 70 | + 不同的阴影用来暗示不同的功能、区分不同的元素; 71 | + 阴影的变化可以用来暗示元素移动的方向; 72 | + Object relationships 73 | + 元素之间的关系:父子关系; 74 | + 每个元素有一个父元素(系统,或者其他元素),每个元素可以有多个子元素; 75 | + 子元素从父元素继承属性,兄弟之间继承的属性是相同的; 76 | 77 | ##Animation 78 | + Authentic motion 79 | 动效可以用来表示元素之间的空间关系,功能,表现其优美与流畅。 80 | + Mass and weight 81 | 动效的开始、结束、改变方向,都应该和真实世界一样,需要一个过程; 82 | + 加速、减速过程要自然,最好用平滑的(加速度的变化是均匀的)、对称的加速减速过程; 83 | + 进入和离开时的加速度,要能反映出元素的意图,例如:减速就暗示着会停下来;移动的速度、方向变化,会引起用户的注意,要考虑在动效的过程中,哪一部分应该引起用户最大的注意力; 84 | + 不同大小的元素,加速度理应不一样,小的元素加速度应大一些;尽量使用[curved motion](http://developer.android.com/training/material/animations.html#CurvedMotion); 85 | + Responsive interaction 86 | + 用户输入:点击、拖拽、声音、鼠标、键盘等,都应给出视觉上的响应; 87 | + Surface reaction:使用ripple API,仅仅产生view表面的视觉变化; 88 | + Material response:响应的动画要和触点有联系,如:动画的起止点都在触点处;产生和消失用大小动画;点击态高度增加; 89 | + Radial action:点击事件需要有视觉反馈;点击触发的动画要和点击有联系,例如动画的起止点都在触点处; 90 | + Meaningful transitions 91 | 好的动效设计,可以有效帮助用户理解场景的切换、界面内元素的布局与变化、界面内元素的层级关系; 92 | + Visual continuity 93 | 有助于引导用户的注意力;涉及到的元素:将进入场景的元素、将退出场景的元素、不同场景间共享的元素;相关阅读:[自定义Activity的切换动画](http://developer.android.com/training/material/animations.html#Transitions);当设计动效时,需要考虑:引导用户的注意力、通过颜色和共享元素把动效在视觉上关联起来、精确地使用动效; 94 | + Hierarchical timing 95 | 合理设计动效中元素之间的移动顺序、时间序列,要突出最重要的元素; 96 | + Consistent choreography 97 | 合理设计移动的路径;相关阅读:[Use curved motion](http://developer.android.com/training/material/animations.html#CurvedMotion); 98 | 最佳实践 99 | + 尽量避免线性空间路径,除非它就是需要这种限制; 100 | + 在不同的场景切换间,保持元素移动方向、路径的连贯性;避免冲突、交错的移动路径; 101 | + 元素移动时,它们处于哪一高度?为何这样设计? 102 | + 如果把每个元素的轨迹记录下来,形成的图像是否优美?是否清晰? 103 | + 使用连贯一致的进入、离开动效,强调元素之间的空间关系; 104 | + Delightful details 105 | 106 | ##Style 107 | 108 | ##Layout 109 | 110 | ##Components 111 | 112 | ##Patterns 113 | 114 | ##Usability 115 | 116 | ##Resources 117 | 118 | -------------------------------------------------------------------------------- /misc/DarkTime.md: -------------------------------------------------------------------------------- 1 | # 暗时间:学会正确思考 2 | 3 | ## 暗时间:专注,保持高效学习 4 | 5 | + 能迅速进入状态 6 | + 能够保持状态 7 | + 抗干扰 8 | 9 | ## 进度条:分治,有预估,有反馈 10 | 11 | + 不要过早退出循环,搜索引擎,前人们的经验 12 | + 兴趣遍地都是,专注和持之以恒才是真正稀缺的 13 | + 选择很多,敢于选择,别把不知道当成没有 14 | + 靠专业技能的成功是最具可复制性的,所以大家都可能靠此成功 15 | + 反思是让人得以改进自己的最重要的思维品质 16 | + 延迟选择是最差的选择 17 | + 一生的知识积累,自学起码占 90% 18 | 19 | ## 如何有效地记忆与学习 20 | 21 | + 拥有的知识不在于记得多少,而是它们能否在恰当的时候被回忆起来被正确使用 22 | + 理解记忆:理解知识的含义,并进行适当抽象 23 | + 从既有经验中总结知识时,要适当抽象来得出适用范围更广的知识,在遇到新问题时,同样进行抽象,这样就能更好的和已有的抽象知识建立联系,从而找到解决思路 24 | + 具体建议 25 | + 养成习惯,经常主动回顾 26 | + 创造回忆的机会 27 | + 经常和别人讨论,或者讲给别人听 28 | + 整理笔记 29 | + 书写(文章) 30 | + 设身处地“虚拟经历”别人经历过的事情 31 | + 抽象和推广 32 | + 联系/比较自身的经历 33 | + 样本大小很关键 34 | + 警惕“沉默的证据”、事后偏见、自利归因 35 | + 如果你想真正得到一些知识,最好过滤一下你的信息,否则你只是在被人的思考中得意着 36 | 37 | ## 学习密度与专注力 38 | 39 | + 真正的效率源自于内心对一个东西强烈的热忱,即追求(_我称之为压力,迫于生存的压力_) 40 | + 专注力是一种习惯,要注意培养 41 | + 做自己喜欢做的事情 42 | + 要事第一(重要但不紧迫,影响深远) 43 | 44 | ## 学习习惯 45 | 46 | ### 学习习惯 47 | 48 | + 只看经典 49 | + 做读书笔记,加深理解,自己思考。获得多少取决于思考了多少、多深,而不是读了多少。 50 | + 将思考养成习惯,大脑会持续(后台)进行思考,会有意想不到的收获。还能避免焦虑。 51 | + 多看心理学与思维的书,跨学科 52 | + 学习一项新知识,必须问自己三个重要问题: 53 | + 把握本质与知识结构 54 | + 学习和思考的过程中,常问自己几个问题: 55 | + 不要偏题 56 | + 定期总结 57 | + 分享检验 58 | + 辩证思维 59 | + 深入本质 60 | 61 | ### 时间和效率 62 | 63 | + 趁着有热情的时候,熬过最难的开头阶段(基础知识) 64 | + 要事第一 65 | + 重要的事情要营造比较大的时间块来完成 66 | + 善于利用碎片时间 67 | + 重视知识的本质(学会学习) 68 | + 提前积累!制定长远计划,计划越早,启动越早,就比别人有更多时间准备 69 | + 经常反思是否有学习成果,总结学习成果 70 | + 制定阅读计划 71 | 72 | ### 学习效率 73 | 74 | + 根据主题有意识查阅资料 75 | + 认清好资料:从问题出发,重点介绍方法背后的理念,注重直观解释,按照方法被发明的时间流程来介绍(遇到问题,分析问题,推理,方法) 76 | + 学习之前就进行一些思考,准备一些疑惑/问题 77 | + 有选择地阅读 78 | + 阅读分类:知识型与思维型 79 | 80 | ### 知识结构 81 | 82 | + 抓住不变量 83 | 84 | ### 习惯养成 85 | 86 | # 思维改变生活 87 | 88 | ## 逃出你的肖申克:突破思维囚笼 89 | 90 | ### 亲身经历才能明白? 91 | 92 | + 我们经常对事物做出错误的解释和归因,自利归因 93 | + 所谓亲身经历才能明白,是惰于思考的提现,让失败的教训替代提前的思考和推理 94 | + 普通人从自己的错误中学习,聪明人从被人的错误中学习 95 | + 人最重要的能力之一就是能否从被人的错误中学习 96 | 97 | ### 偏见 98 | 99 | + 我们大脑从外界接受到的信息是满含歧义的,但得益于基于漫长进化过程与生活积累的先验假设,大脑总是给出其中的一种解释 100 | + 日常生活中的事件,总有一个平凡的解释,和一个疯狂的解释 101 | + 但由于大脑的习惯,我们经常无法看到其他可能性:偏见 102 | + 打破这种偏见的唯一途径就是开阔视野,多积累知识,以及和具有不同知识背景的人讨论 103 | 104 | ### 情绪大脑与理性大脑 105 | 106 | + 我们常常做出我们明知不好的事情,这是因为情绪大脑(非理性)往往能胜过理性大脑 107 | + 我们发明了各种认知方法来诱使/要挟情绪大脑同意执行理性大脑的需求,例如加入互助学习小组,或者规定损失与监督 108 | + 经常动用理性思考也能锻炼理性大脑的实力 109 | 110 | ### 理智与情感 111 | 112 | + 我们的理性大脑非常善于对自己的行为做出立即的、看上去合理的解释,其实往往是经不起推敲的 113 | + 我们经常知道答案,却不知道求解的思维过程 114 | + 只要一件事情尚存在对自己有利的解释,我们的大脑便会毫不犹豫且掩耳盗铃地认为那是唯一的解释 115 | + 认知失调:我们的思想被迫对自己的行为作出合理的解释 116 | + 有利的证据不加细查,不利的证据却死缠烂打 117 | + 客观意味着承认存在未知信息的可能性,理性意味着能够从对立面的角度去看问题和思考 118 | + 学会质疑自己的判断,尝试理解错误的一方为什么会错 119 | + 能够改变既有的习惯,依靠的不是自制力,而是知识 120 | + 即便我们不是天生就知道如何对付情绪大脑,通过后天学习心理学知识,并且练习,也能具备成功绕过甚至客服大脑天生缺陷的能力 121 | 122 | ## 书写是为了更好地思考 123 | 124 | + 书写是对思维的备忘,人的记忆力有限 125 | + 书写是对思维的缓存,思维缓存是很小的,经常思此失彼 126 | + 和别人交流,一次书写,无限阅读 127 | + 书写的过程中,理清思路之后,很可能问题就会迎刃而解 128 | 129 | ## 写博客 130 | 131 | + 结交志同道合的朋友 132 | + 书写是更好的思考 133 | + 教是最好的学 134 | + 注重思维过程:前提,假设,逻辑,结论 135 | + 讨论是绝佳的反思 136 | + 激励自己持续学习和思考 137 | + 让自己成为一个持续学习和思考的人,并只写自己真正思考和总结之后的产物! 138 | + 养成思考的习惯,先通过书写来养成。习惯养成之后,就会思考有所得,然后书写 139 | 140 | ## 我不想与我不能:自利归因 141 | 142 | + 人总是倾向于把成功归因于自己,失败归因于他人 143 | + 我们的努力是为了增加结果发生的几率,而不是为了那个确定的结果 144 | 145 | ## 遇到问题应该自己动手 146 | 147 | + 如果你不知道一个东西,很可能你也不知道自己不知道它 148 | + 自己动手/思考解决问题,我们的收获远不止这个问题的解决 149 | + 困难的路越走越容易,容易的路越走越难 150 | 151 | ## 什么才是你的不可替代性和核心竞争力 152 | 153 | + 独特的个性知识经验组合 154 | + 应当最大限度加强和发挥自己独特的组合,而不是寻求单项的超越 155 | + 专业领域技能 156 | + 跨领域的技能 157 | + 学习能力,持续学习和思考新知识 158 | + 性格要素 159 | 160 | ## 跟波利亚学解题 161 | 162 | + 联想:把未知/未解决的问题转化为已知/已解决的问题 163 | + 启发式思维方法都是为了联想服务 164 | 165 | ### 解题方法/注意事项 166 | 167 | + 时刻不忘未知量,不要忘记目标,迷失方向 168 | + 用特例启发思考 169 | + 反过来推导,有时结论包含了很多重要信息 170 | + 试错 171 | + 调整题目的条件 172 | + 求解一个类似的题目 173 | + 列出所有可能跟问题有关的定理或性质 174 | + 考察反面,考察其他所有情况 175 | + 将问题泛化,并求解这个泛化后的 176 | + 意识孵化法,持续思考,让潜意识想到解决办法 177 | + 烫手山芋法,问别人 178 | 179 | + 联想的法则 180 | + 突破范畴陷阱 181 | + 获取知识优势的同时,防止被知识束缚住:抽象 182 | + 积累知识! 183 | + 好题目 184 | + 不需要未知的知识 185 | + 需要的未知知识可以从题目中分析出来 186 | + 考察解题的一般性思路,而不是特定技巧 187 | + 考察思维能力,联想、类比、抽象、演绎、归纳、观察、发散 188 | + 考察一般性思维方法,特例启发、试错、泛化、倒推... 189 | + 坏题目 190 | + 完全依赖特定知识 191 | + 搜索空间无限 192 | + 一个好习惯:把自己的思考过程清晰地写在纸上 193 | + 练习:把外显记忆转化为内隐记忆,增加领域知识 194 | + 总结的意义,练习解题最重要的目的是反思解题过程中的一般性的、跨问题的思维法则 195 | 196 | ## 锤子和钉子 197 | 198 | + 如果你手里有一把锤子,所有东西看上去都像钉子 199 | + 软件开发方法、工具、框架、范式,都不要走极端,应该掌握应用场景,按需使用 200 | + 任何工具都有其适用范畴和前提,仅仅将它看作我们工具箱中的一件工具,客观评估它,视具体情况而使用 201 | + 不要忘记自己要解决的问题是什么,why 永远在 how 之前 202 | + 如果你想钉一个钉子,所有东西看上去都像锤子 203 | + 专注于想要解决的问题,所看到的东西会呈现出以往看不到的一面 204 | + 持续思考,让潜意识持续思考,很可能获得顿悟 205 | 206 | ## 鱼是最后一个看到水的 207 | 208 | + 人倾向于在既有框架下去解决问题,在这个过程中很难察觉到框架约束的存在 209 | + 忽视既有框架的约束很容易导致 sub-optimal 的解决方案 210 | + 普通人遵守规则,牛人无视规则,伟人创造规则 211 | + first-class 的内建支持(简洁)永远优于补丁式的解决方案(绕来绕去) 212 | + 不要觉得不用设计模式就不够好不够强大,以尽可能简单地方式完成任务才是王道 213 | + 学习编程重在学习基本的概念和素养,这些是长期稳定不变的东西 214 | + 脱离语言思考,使用语言实现 215 | 216 | ## 知其所以然 217 | 218 | + 欧几里得式/瀑布流式/自上而下的解题/算法书,直接讲解结论,讲解算法,不讲解法产生的过程,不讲思维过程,弊病很多:只授鱼不授渔;记忆成本高;…… 219 | + 人的思维形式有两种 220 | + 联想 221 | + 先枚举知识,取决于知识量 222 | + 对知识的加工方法,是否抽象到本质 223 | + 对问题的理解,是否抽象到本质 224 | + 演绎 & 归纳 225 | + 是一种“必然”的推理,但不“必然”引向问题的结论 226 | + 讲述思维过程而非结果的重要价值 227 | + 內隐化:思维法则也是知识,是内隐的记忆,要将思维方法内隐化,需要不断练习 228 | + 跨情境运用:思维法则也是知识记忆,是问题的解决策略,收到提取线索的制约,也需要练习时加工、运用时联想 229 | + 对问题解的更多提取线索:过程虽然内容更多,但是更容易记忆 230 | + 包含了更多的知识,解决问题的思想、方法论 231 | + 重在分析推理,而不是联想,从问题本质入手,逐步分析推理,比生搬硬套联想要好得多 232 | + 怎么操作 233 | + 寻找算法的原始出处 234 | + 原始出处也许也不到位,那就参考讲得好的,也许也没有,那就要自己揣摩,自己问,自己思考,自己回答 235 | + 不仅学习别人的思路,整理自己的思路也是极其重要的 236 | + 波利亚《如何解题》,《数学的发现》 237 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BuildAppWithConnectivity.md: -------------------------------------------------------------------------------- 1 | # Building Apps with Connectivity & the Cloud 2 | 3 | ## Connecting Devices Wirelessly 4 | + [Using Network Service Discovery,局域网下的服务提供、发现框架](http://developer.android.com/intl/zh-cn/training/connect-devices-wirelessly/nsd.html) 5 | + 自己可以提供服务,然后通过NSD框架暴露在本地局域网(注册服务):`mNsdManager.registerService`; 6 | + 利用NSD框架,发现本地局域网的服务提供者:`mNsdManager.discoverServices`; 7 | + 解析发现的服务:`mNsdManager.resolveService`,解析成功的回调中,`NsdServiceInfo`参数携带了服务提供者(server)的信息,供建立连接; 8 | + 根据解析的结果(协议,地址),自行建立连接,进行通信即可 9 | + 当app退出,activity退出(onPause, onDestroy)时,需要将NSD服务取消注册 10 | + [利用Wi-Fi建立P2P连接](http://developer.android.com/intl/zh-cn/training/connect-devices-wirelessly/wifi-direct.html) 11 | + 声明权限:`ACCESS_WIFI_STATE`, `CHANGE_WIFI_STATE`, `INTERNET` 12 | + 监听系统广播事件: 13 | + 定义一个BroadcastReceiver,对收到的WifiP2p广播(wifi p2p状态变化、peer列表变化、p2p连接变化、peer信息变化等)进行响应 14 | + 在activity中注册BroadcastReceiver 15 | + 发现设备:`mManager.discoverPeers`,`mManager`为`WifiP2pManager`对象,通过`getSystemService`获得 16 | + 在BroadcastReceiver收到peer列表变化广播后,获取peer列表:`mManager.requestPeers` 17 | + 建立p2p连接:`mManager.connect` 18 | + 建立连接成功后,BroadcastReceiver会收到连接状态变化的广播,此时可以获取peer的详细地址信息,供建立socket连接用:`mManager.requestConnectionInfo` 19 | + [使用Wi-Fi P2P进行服务发现](http://developer.android.com/intl/zh-cn/training/connect-devices-wirelessly/nsd-wifi-direct.html) 20 | + NSD框架需要设备处于同一局域网下,而Wi-Fi P2P则无需设备接入网络 21 | + 声明权限:`ACCESS_WIFI_STATE`, `CHANGE_WIFI_STATE`, `INTERNET` 22 | + 注册自己提供的服务:`WifiP2pDnsSdServiceInfo.newInstance`,`mManager.addLocalService` 23 | + 发现附近的服务提供者 24 | + `WifiP2pManager.DnsSdTxtRecordListener` 25 | + `WifiP2pManager.DnsSdServiceResponseListener` 26 | + `mManager.setDnsSdResponseListeners(channel, dnsSdServiceResponseListener, dnsSdTxtRecordListener)` 27 | + `mManager.addServiceRequest` 28 | + `mManager.discoverServices` 29 | 30 | ## Performing Network Operations 31 | + 进行网络通信 32 | + 大部分都采用HTTP协议,选择HTTP client很重要,[OkHttp](https://github.com/square/okhttp)已被安卓6.0作为系统默认HTTP client 33 | + 进行网络连接之前,需呀检查一下网络是否可用(仅仅是检查是否接入网络,并不能检测是否可以访问互联网):`ConnectivityManager.getNetworkInfo(type)`,`ConnectivityManager.getActiveNetworkInfo()`和`NetworkInfo.isConnected()` 34 | + 也可以把网络访问出错的处理集中起来,总之,要么所有网络访问之前都进行网络检查,要么网络访问出错之后集中处理,这样代码更简洁优雅 35 | + 网络访问不能在UI线程上执行,使用[RxAndroid](https://github.com/ReactiveX/RxAndroid)可以方便的进行异步操作 36 | + 管理网络使用 37 | + 通常,为用户提供以下设置选项,比较好:同步频率,是否仅在wifi下联网等 38 | + 监听网络状态变化:使用一个监听`ConnectivityManager.CONNECTIVITY_ACTION`广播的`BroadcastReceiver`,接收网络状态变化事件 39 | + 在代码中注册/反注册`BroadcastReceiver`,可以控制其活跃期 40 | + 如果在manifest中声明,则其活跃期将是系统启动到系统关闭,此时可以通过`PackageManager.setComponentEnabledSetting(...)`方法启用/禁用组件 41 | + 解析XML 42 | + 安卓系统内置有xml解析器:[`XmlPullParser`](http://developer.android.com/intl/zh-cn/reference/org/xmlpull/v1/XmlPullParser.html) 43 | 44 | ## Transferring Data Without Draining the Battery 45 | + 典型手机蜂窝网络状态机 46 | 47 | ![典型手机蜂窝网络状态机](../assets/mobile_radio_state_machine.png) 48 | 49 | + APP的网络请求应该考虑蜂窝网络的特性:积攒多个请求,集中发送;适当预取;积攒多次请求的数据,但是使用同一次请求批量发送; 50 | + 使用DDMS/AndroidStudio的网络监控工具,可以查看APP网络请求的特征,并进行适当优化 51 | + 进行数据备份、数据同步时,可以把网络请求交给GSM,它会将任务延迟至充电时、连接wifi时、或者是进行上述打包操作 52 | + 适当缓存,以减小网络数据的传输 53 | + 调整网络请求的模式,wifi、高带宽时,集中请求大量数据,减少网络请求的频率和次数 54 | + 更多关于[电量优化](http://developer.android.com/training/monitoring-device-state/index.html)的内容,还需要在有实际开发需求的时候,进行实践和深入 55 | 56 | ## Backing up App Data to the Cloud 57 | + 安卓系统提供了自动同步的功能,不过是同步到google drive,每个app拥有25MB的空间 58 | + 同步有较好的开始策略(充电,连接wifi,处于空闲状态,或者24小时未同步),但是当25MB空间用完之后,没有备份替换策略,仅仅是停止备份 59 | + API 23起,系统默认备份APP的所有数据,可以在manifest中进行添加或排除,通过`android:fullBackupContent`指定配置文件 60 | + API 23之前的版本,系统提供了`BackupAgent`接口,同样也是备份到google服务器 61 | + google还提供了[Cloud Save service](http://developers.google.com/games/services/common/concepts/cloudsave),用于保存数据(不是备份应用数据),同一用户在不同设备保存数据时,会有冲突,Cloud Save service也提供了相应的冲突处理接口 62 | 63 | ## Transferring Data Using Sync Adapters 64 | + 安卓系统提供了一个用于和自己服务器同步数据的框架:Sync Adapter 65 | + 使用该框架可以享受系统的调度服务,提升电池性能,提升网络访问性能;该框架是异步的,对于实时同步需求,无法使用此框架; 66 | + 先创建一个`AbstractAccountAuthenticator`子类,以及一个`Service`用于连接`AccountAuthenticator`和Sync adapter框架,authenticator用于和自己的服务器进行认证,即便不需要认证,也需要实现一个空的authenticator 67 | + 为`AccountAuthenticator`创建一个metadata xml描述文件,把`Service`声明到manifest中,并且使用`AccountAuthenticator`的metadata 68 | + sync adapter框架需要content provider来与之配合使用;如果不想使用content provider保存数据,也需要提供一个空的content provider实现,然后用自己的方式进行数据存储; 69 | + 将content provider声明在manifest中 70 | + 实现`AbstractThreadedSyncAdapter`子类,用于响应系统发出的同步通知,执行自己的数据同步逻辑 71 | + 在`onPerformSync()`中执行数据同步 72 | + `onPerformSync()`在后台线程被调用,所以无需再次自行创建后台线程 73 | + 可以尝试在`onPerformSync()`中执行除了数据同步之外更多的网络请求,以集中网络请求,提示网络、电池效率 74 | + 创建一个`Service`,用于连接`ThreadedSyncAdapter`和Sync adapter框架 75 | + 和`AccountAuthenticator`类似,`ThreadedSyncAdapter`及其`Service`也需要定义metadata、声明到manifest文件中 76 | + 在Activity中创建一个account,它将用于Sync adapter框架 77 | + `AccountAuthenticator`、`ThreadedSyncAdapter`都使用了binder机制,用于系统进程进行跨进程调用,获取自定义的AccountAuthenticator和ThreadedSyncAdapter实例,并执行其方法 78 | + sync adapter框架支持多种触发机制,高效不失灵活 79 | + 基于服务器的消息推送,表明服务器数据发生了变化 80 | + 本地数据变化事件触发(需要使用content provider) 81 | + 本地网络事件,由系统发送,自动进行 82 | + 定期执行 83 | + 定时执行 84 | + 按需执行 85 | 86 | ## Transmitting Network Data Using Volley 87 | + volly是一个HTTP网络请求的封装库,支持schedule,并发,两级缓存,优先级,取消,可定制等特性 88 | + volly执行流程 89 | 90 | ![../assets/volley-request.png](../assets/volley-request.png) 91 | 92 | + 内置了`StringRequest`,`JsonObjectRequest`,`JsonArrayRequest`,`ImageRequest`请求类,用于把响应数据转换为目标数据格式 93 | + 也可以自定义实现Request 94 | 95 | ## Building Apps with Location & Maps 96 | + Google play service提供的相关服务:automated location tracking, geofencing, and activity recognition 97 | + ... 98 | -------------------------------------------------------------------------------- /Android-Java/Gradle.md: -------------------------------------------------------------------------------- 1 | # [Gradle:全新安卓构建系统](http://tools.android.com/tech-docs/new-build-system/user-guide) 2 | 3 | ## 优势 4 | + 支持多渠道、多APK打包; 5 | 6 | ## Multi-flavor 7 | + src目录下有main文件夹,这是app的base部分; 8 | + 支持多种flavor/variant,例如debug,release,paid,free等,分别在src目录下创建相应的文件夹,编写相应的代码;main文件夹下的代码是共用的,而相应文件下下的代码只在相应flavor/variant中使用; 9 | + 每个文件夹下都可以编写自己的java代码,使用自己的res资源文件等; 10 | + 在Android studio的build variants面板中可以选择构建的flavor; 11 | 12 | 文件结构如下图: 13 | 14 | ![xflavour_folder_structure.png](../assets/xflavour_folder_structure.png) 15 | 16 | ## Basic Project 17 | + 工程结构:创建新工程时,自动会生成main和androidTest目录,分别存放工程文件和测试代码;androidTest目录下不需要manifest文件,将自动生成; 18 | + 可以通过配置,修改/重新指定各种文件的位置: 19 | ```gradle 20 | android { 21 | sourceSets { 22 | main { 23 | manifest.srcFile 'AndroidManifest.xml' 24 | java.srcDirs = ['src'] 25 | resources.srcDirs = ['src'] 26 | aidl.srcDirs = ['src'] 27 | renderscript.srcDirs = ['src'] 28 | res.srcDirs = ['res'] 29 | assets.srcDirs = ['assets'] 30 | } 31 | 32 | androidTest.setRoot('tests') 33 | } 34 | } 35 | ``` 36 | + tasks:apply plugin时,会自动添加plugin定义的task(java和android包括:assemble,check,build,clean。build包括assemble和check); 37 | + 和make类似,上次执行之后没有改变的task下次将不会执行; 38 | + build的配置可以动态化: 39 | ```gradle 40 | def computeVersionName() { 41 | ... 42 | } 43 | 44 | android { 45 | compileSdkVersion 19 46 | buildToolsVersion "19.0.0" 47 | 48 | defaultConfig { 49 | versionCode 12 50 | versionName computeVersionName() 51 | minSdkVersion 16 52 | targetSdkVersion 16 53 | } 54 | } 55 | ``` 56 | 注意:方法名不能和默认的getter重名; 57 | + buildTypes && productFlavors 配置不同的打包类型,两者为组合关系; 58 | + Build Type + Product Flavor = Build Variant 59 | + 不同buildTypes的sourceSets可以重新指定;会创建对应的assemble<BuildTypeName> task; 60 | + main和不同类型的manifest文件将会合并;资源文件也将合并,同名的将以具体build type覆盖main中的资源; 61 | 62 | ## 依赖、安卓库、多工程配置 63 | + 声明依赖 64 | + 本地jar文件: 65 | ```gradle 66 | 67 | dependencies { 68 | compile files('libs/foo.jar') 69 | } 70 | ``` 71 | + 远程依赖: 72 | ```gradle 73 | repositories { 74 | mavenCentral() 75 | } 76 | 77 | 78 | dependencies { 79 | compile 'com.google.guava:guava:11.0.2' 80 | } 81 | ``` 82 | + library project依赖: 83 | ```gradle 84 | 85 | dependencies { 86 | compile project(':libraries:lib1') 87 | } 88 | ``` 89 | + 依赖类型 90 | + compile:用于主应用程序的依赖编译,所有的内容都会加入到classpath中,并且打包到APK文件中; 91 | + androidTestCompile:用于集成测试程序(或称InstrumentatinTest); 92 | + testCompile:用于单元测试; 93 | + provided:??TODO?? 94 | + debugCompile:debug Build Type; 95 | + releaseCompile:release Build Type; 96 | + APK文件总是使用两种依赖类型:compile和<buildtype>Compile; 97 | + 创建新的build type,就可以使用对应的<buildtype>Compile声明依赖; 98 | + library project最终将打包为.aar文件,包含编译后的代码、资源文件;library在被其他工程引用时,其依赖只有compile类型的才会被继承; 99 | 100 | ## 命令行运行 101 | + 如果不clean,将采用递增式编译,即之前已编译、且无修改的部分,将不会编译; 102 | + 第一次运行的时候加上`-–daemon`选项,将让gradle后台运行,后续编译将节省gradle初始化的时间; 103 | 104 | ## 创建自定义task 105 | + 添加task 106 | ```gradle 107 | 108 | task custom(description: 'This is our custom task') << { task -> 109 | println "Running task ${task.name}" 110 | } 111 | ``` 112 | + task的执行时机 113 | + Configuration stage 114 | + Execution stage 115 | + 默认task的执行时机均是Configuration stage,即便指定其他task,也会被触发执行 116 | + 可以通过doLast语句块,来保证只有在明确指定执行task时才会执行,而doLast块外的代码,将在任何任务执行时都会执行 117 | + `<<`操作符 118 | ```groovy 119 | task myTask2 << { 120 | println "Hello, World!" 121 | } 122 | ``` 123 | 相当于整个task都在doLast块内 124 | 125 | + 添加task的依赖 126 | ```gradle 127 | 128 | assemble.dependsOn 'custom' 129 | 130 | ``` 131 | `assemble`将依赖于`custom`,即`custom`执行后才能执行`assemble`;这样是将`custom`添加到了`assemble`的最后一个依赖; 132 | 133 | + 使用`rule`延迟创建task 134 | ```gradle 135 | assemble.dependsOn(‘customAssemble’) 136 | 137 | tasks.addRule(“Pattern: customAssemble“) { taskName -> 138 | println “Creating task ${taskName}” 139 | if (taskName.equals(“customAssemble”)) { 140 | android.applicationVariants.each { variant -> 141 | println “Adding dependency to assemble${variant.name}” 142 | def targetTask = project.tasks.findByName(“assemble${variant.name}”) 143 | if(targetTask != null) { 144 | targetTask.dependsOn(“customAssemble${variant.name}”) 145 | } 146 | } 147 | } 148 | task(taskName) << { task -> 149 | println “Running custom task ${task.name}” 150 | } 151 | } 152 | ``` 153 | 154 | ## 巧用Product Flavor 155 | 不同的版本,使用不同的服务器配置,不同的icon,不同的包名等,使用product flavor非常方便;可以在product flavor配置里面设置控制变量,主代码根据控制变量来控制行为; 156 | ```gradle 157 | productFlavors { 158 | rd { 159 | applicationId "zuul.com.android.rd" 160 | buildConfigField "boolean", "LOG_TIMBER", "true" 161 | buildConfigField "boolean", "LOG_STETHO", "true" 162 | buildConfigField "boolean", "SHOW_LOGGIN_PHONE", "true" 163 | } 164 | } 165 | ``` 166 | 资源文件(drawable,string,style等)、代码文件,都可以为不同的flavor创建相应的文件夹,进行个性化配置; 167 | 168 | 169 | ## [语法](http://trickyandroid.com/gradle-tip-2-understanding-syntax/) 170 | + 基于Groovy;closure是很常见的基本元素;调用方法如下: 171 | ![groovy_closure_invoke_syntax.png](../assets/groovy_closure_invoke_syntax.png) 172 | + build.gradle: 173 | + `buildscript`是在gradle系统中定义的一个方法,接受一个closure作为参数; 174 | + gradle脚本所有的顶层代码都是delegate到`Project`实例context上; 175 | + `script block`:把一个closure传递到一个方法中,同时调用该方法; -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-GettingStarted.md: -------------------------------------------------------------------------------- 1 | #[安卓官方开发指南](http://developer.android.com/training/index.html) 2 | 3 | ##Getting Started 4 | + Styling the Action Bar 5 | + ActionBar可以有tab,也可以有分离的底部bar,用于显示action items 6 | + 使用Theme 7 | + Theme.Holo,暗黑系主题 8 | + Theme.Holo.Light,光明系主题 9 | + Theme.Holo.Light.DarkActionBar,光明系主题,暗黑系ActionBar 10 | + `` 11 | + 使用support library时 12 | + Theme.AppCompat,暗黑系 13 | + Theme.AppCompat.Light 14 | + Theme.AppCompat.Light.DarkActionBar 15 | + [Android-Action-Bar-Icons](https://github.com/svenkapudija/Android-Action-Bar-Icons) 16 | + 自定义背景 17 | + standard: `android:actionBarStyle` ==> `android:background` 18 | + support: `actionBarStyle` ==> `background` 19 | + 示例 20 | ```xml 21 | 22 | 23 | 24 | 31 | 32 | 33 | 40 | 41 | ``` 42 | + 自定义文字颜色 43 | + standard 44 | + Action bar title: `android:actionBarStyle` ==> `android:titleTextStyle` ==> `android:textColor` 45 | + Action bar tabs: `android:actionBarTabTextStyle` ==> `android:textColor` 46 | + Action buttons: `android:actionMenuTextColor` 47 | + support: 规则一样,去掉`android:`前缀(`android:textColor`不需要去掉前缀) 48 | + 示例 49 | ```xml 50 | 51 | 52 | 53 | 64 | 65 | 66 | 73 | 74 | 75 | 80 | 81 | 82 | 87 | 88 | ``` 89 | + Customize the Tab Indicator(图标/左侧按钮) 90 | + standard: `android:actionBarTabStyle` ==> `android:background` 91 | + 示例 92 | ```xml 93 | 94 | 95 | 96 | 103 | 104 | 105 | 113 | 114 | ``` 115 | + [ActionBar全部style属性](http://developer.android.com/guide/topics/ui/actionbar.html#Style) 116 | + [ActionBar Style Generator](http://jgilfelt.github.io/android-actionbarstylegenerator/) 117 | + Overlaying the Action Bar 118 | + hide/show可以控制隐藏/显示,但是直接使用会导致Activity重新layout 119 | + 使用overlay mode可以避免重新layout,而使用带有透明度的ActionBar可以使得ActionBar底部的View也可见 120 | + 起用overlay mode 121 | ```xml 122 | 123 | 124 | 131 | 132 | ``` 133 | + 设置root layout的顶部边距,使得其完全可见 134 | + `android:paddingTop="?android:attr/actionBarSize"` 135 | + `android:paddingTop="?attr/actionBarSize"` 136 | + 多语言支持:创建`/values/strings.xml`,`/values-es/strings.xml`,`/values-fr/strings.xml`等资源文件夹,其中的资源定义为相同的id,则引用时会自动根据系统设置语言选择对应文件夹下的资源 137 | + 多尺寸支持:`drawable-xhdpi`,`drawable-hdpi/`...等等尺寸相关的资源 138 | + 多系统版本支持 139 | + minSdkVersion,targetSdkVersion 140 | + 运行时检查:`Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB` 141 | + Fragment之间通信 142 | + 通过Activity间接实现,Activity实现接口,Fragment获取(`getActivity()`)、cast、调用 143 | + EventBus 144 | -------------------------------------------------------------------------------- /Android-Java/Rx.md: -------------------------------------------------------------------------------- 1 | # Rx (Reactive eXtention) 2 | 3 | 四部分:事件流源头(source/observable),对事件流的操作(operator/transformer),对最终事件流进行响应(subscriber/observer),以及整个过程的调度(scheduler)。 4 | 5 | ## 再看文档 6 | + The Observable Contract 7 | + Notifications,observable与observer之间的通信包括:OnNext,OnCompleted,OnError,OnSubscribe;observer与observable之间的通信包括:Subscribe,Unsubscribe,Request; 8 | + observable可能会触发零次或多次OnNext,每次发出一个item,最终可能会有一次OnCompleted或OnError,这两者不可能都被触发,这两者之一一旦触发,就再也不会有任何事件了; 9 | + observable可能不会触发任何OnNext,也可能永远不会终止,也可能既不触发任何OnNext,也不终止; 10 | + observable发出item必须是串行的,可以是在不同的线程中,但是item之间必须遵循某种顺序(串行); 11 | + 如果observable并未触发OnCompleted/OnError,则observer仍可以和observable通信,即触发Unsubscribe/Request;但一旦observable已经终止了,observer就不应该再对observable发出任何指令了; 12 | + OnError不能传null,必须提供一个合适的error; 13 | + observable终止(释放资源)之前,必须向observer发出OnCompleted/OnError通知; 14 | + observable可能在收到observer的Subscribe通知之后,立即开始OnNext; 15 | + observer发出Unsubscribe后,observable会尝试停止向observer发出事件,但是并不保证! 16 | + 当observable发出OnCompleted/OnError之后,subscription就结束了,observer无需再Unsubscribe; 17 | + 当有多个observer先后subscribe同一个observable时,它们接收到的item序列没有保证,即可能从subscribe之后,始终相同,也可能会replay,甚至是没有任何关系; 18 | + Backpressure,是可选的,并不是所有的(语言)实现都包含,即便包含,也可能只是部分实现; 19 | + 如果observable实现了Backpressure,且observer使用backpressure,则observer subscribe时,observable不会立即发出item,相反,它会向observer发出一个OnSubscribe通知; 20 | + observer收到OnSubscribe通知后,可以向observable发出Request通知,主动请求一定数量的item; 21 | + observable收到Request后,正常情况下会向observer发出对应数量的item,但是也可能会发出OnCompleted/OnError,甚至在observer发出Request之前就终止自己; 22 | + 如果observable不支持backpressure,则收到observer的Request时,应该触发OnError,并终止自己; 23 | + 如果observable产生item的速度超过observer Request的速度,则对多余的item的处理,取决于observable的策略,或丢弃,或保存排队,或者由observer配置策略; 24 | + Hot & Cold Observable 25 | + observable合适开始发出item? 26 | + hot:创建之后立即开始,后续的observer将只能开到subscribe之后的序列; 27 | + cold:observer subscribe之后才开始,保证observer看到的是完整的序列; 28 | + 有的实现版本(语言)中,还存在Connectable的observable,它的开始时机既不是创建时,也不是被subscribe时,而是connect方法被调用时; 29 | + 注意:hot, cold, connectable和是否replay是两个概念,没有必然联系;此外,创建item的代码执行时机也是另外一个概念,它取决于代码的求值时机; 30 | + Operator,文档包含的是有哪些operator,以及如何选择operator,[详见文档](http://reactivex.io/documentation/operators.html)。 31 | + Single 32 | + RxJava的特殊实现,只会发出一个item,或者OnError,所以subscribe时,只需提供onSuccess和onError即可; 33 | + onSuccess和onError只有其中之一会被调用,且只会被调用一次;是否一定会有其中之一被调用?如果不考虑无限等待的话,可以说会; 34 | + Subject 35 | + Subject是某些Rx实现版本的内容,它既是observable,也是observer 36 | + 作为observer,它可以subscribe到其他的observable,作为observable,它可以被其他observer subscribe,它所subscribe的observable发出的item,它将转发出去,而且它也可以自行发出item; 37 | + 如果observable是cold的,当subject subsrcibe这个observable时,它将开始发出item,因此用一个subject去subscribe cold observable,可以达到把cold变hot的效果(此时subject作为observable,就是hot的了); 38 | + AsyncSubject,只在源observable结束时,发出源observable发出的最后一个item,如果源observable没有发出任何item,则它也不会发出任何item;如果有多个observer subscribe它,它会始终发出同一item;如果源observable发生错误,它也将发出错误,不会发出任何item; 39 | + BehaviorSubject,它被subscribe时,将会重放此前最后一个item(或者一个默认item);如果subscribe发生在onError之后,那observer将只会收到onError; 40 | + PublishSubject,不会重放;hot,创建之后就会开始发出item(如果有),所以可能会有item未被任何observer接收到;如果源observable已经终止(onCompleted/onError),则它对后续的observer只会发出相应的终止事件; 41 | + ReplaySubject,会重放所有的item;有的版本中,当历史item过多,或者时间过长,它会丢弃老的item;使用ReplaySubject时,不能在多线程调用onNext/onCompleted/onError方法,否则会导致非串行,有悖于observable contract,导致无法确定重放顺序; 42 | + [RxJava] SerializedSubject,可以把普通的subject转化为可以多线程调用onNext/onCompleted/onError的subject; 43 | + [RxJava] TestSubject,TestScheduler,用于测试、调试; 44 | + [Relay](https://github.com/JakeWharton/RxRelay) 45 | + 使用Subject可以把已有非Rx API适配为Rx API,但是一旦subject终止之后,就必须重新创建一个subject才能继续接受item了; 46 | + Relay和Subject类似,但是不会终止,它继承自Observable,实现了Action1(而不是Observer); 47 | 48 | ## 原理 49 | + `subscribe`原理,引用自[给 Android 开发者的 RxJava 详解](http://gank.io/post/560e15be2dca930e00da1083#toc_10) 50 | 51 | ![rx_subscribe.png](../assets/rx_subscribe.png) 52 | 53 | 注意,选中的部分,应该是`subscribe()`而不是`subscriber()`。 54 | 55 | + `lift`变换原理,引用自[给 Android 开发者的 RxJava 详解](http://gank.io/post/560e15be2dca930e00da1083#toc_19) 56 | 57 | ![rx_lift.png](../assets/rx_lift.png) 58 | ![rx_lift_2.png](../assets/rx_lift_2.png) 59 | 60 | + `subscribeOn`和`observeOn`原理:也是用`lift`实现,通过相应`Operator`实现线程的切换 61 | 62 | ## 细节 63 | + just, from等操作均是在创建时执行,而非subscribe时,很显然,因为java函数调用传递的是值,所以会先eval;create, defer等操作均是在subscribe时执行;~~create多次subscribe只会执行一次,defer多次subscribe会执行多次~~(create、defer,call函数内的代码每次subscribe均会被执行);[ref](https://github.com/Piasy/TestUnderstandRx/blob/242821254f/app%2Fsrc%2Ftest%2Fjava%2Fcom%2Fgithub%2Fpiasy%2Ftestunderstand%2Frx%2FHotColdObservableTest.java#L110) 64 | + hot v.s. cold Observable 65 | + cold:当Observable被subscribe时才开始发射item;(后来的subscriber同样会收到其subscribe之前发射的item;retrofit实现的是cold;每次subscribe时,发射item的代码都会被执行) 66 | + hot:创建之后就会开始发射item,不管是否被subscribe;(后来的subscriber不会收到其subscribe之前发射的item;) 67 | + map v.s. flatMap 68 | + 将cold observable通过cache转换成hot之后,再在别处subscribe他们,有其应用场景:第一次subscribe时并不关心结果,但是后面某处想要获取结果,又不希望再次执行创建observable的过程;类似于预取思想; 69 | + 怎么感觉有问题。。。 70 | + concat v.s. merge:concat不会让参数observable发射的item之间重叠,而merge可能会;concat传入的参数顺序是有影响的,merge没影响; 71 | + share v.s. replay 72 | + replay 73 | + 需要调用connect方法后,observable才开始发射; 74 | + 后subscribe的subscriber会在subscribe的瞬间先收到其subscribe之前发射的所有item,然后再正常收到剩下的item; 75 | + subscriber unsubscribe后将不会再接收到item,但也不会有onCompleted事件,且对其他subscriber不影响; 76 | + 实现的功能都需要测试验证,不能凭经验、也不能看博客,也不能仅看文档; 77 | 78 | ## [应用场景](http://blog.csdn.net/theone10211024/article/details/50435325) 79 | + Scheduler线程切换 80 | + Retrofit结合RxJava做网络请求框架 81 | + RxJava代替EventBus进行数据传递:RxBus 82 | + 解决嵌套回调(callback hell)问题 83 | + RxJava进行数组、list的遍历 84 | + 使用debounce做textSearch 85 | + 使用interval做周期性操作。当有“每隔xx秒后执行yy操作”类似的需求的时候,想到使用interval 86 | + 使用timer做定时操作。当有“x秒后执行y操作”类似的需求的时候,想到使用timer 87 | + 使用merge合并两个数据源 88 | + 使用combineLatest合并最近N个结点 89 | + 还存在另一个类似的operator:withLatestFrom,他们的使用场景区分如下([摘自](http://staltz.com/rx-glitches-arent-actually-a-problem.html)): 90 | + If I want to combine Observables that seem completely independent to each other, then I need combineLatest. 91 | + If I want to just map a certain Observable while using information from other Observables, then I need withLatestFrom. 92 | + 使用concat和first做缓存 93 | + 使用throttleFirst防止按钮重复点击 94 | + 使用schedulePeriodically做轮询请求 95 | + 使用timeInterval,获取本次事件与上次事件之间的时间间隔 96 | + 响应式的界面 97 | 98 | ## Code review 99 | + [part I](http://artemzin.com/blog/rxjava-code-review-part-1) -------------------------------------------------------------------------------- /Android-Java/AndroidTouchSystem.md: -------------------------------------------------------------------------------- 1 | # 安卓系统点击事件处理 2 | [参考PPT](http://devsbuild.it/content/Mastering-Android-Touch-System) 3 | 4 | ## 安卓系统点击事件处理框架 5 | + 用户的点击事件均被包装为MotionEvent 6 | + MotionEvent描述了用户的行为 7 | + ACTION_DOWN 8 | + ACTION_UP 9 | + ACTION_MOVE 10 | + ACTION_POINTER_DOWN 11 | + ACTION_POINTER_UP 12 | + ACTION_CANCEL 13 | + 使用`MotionEventCompat.getActionMasked(ev)`获取`MotionEvent`对应的action 14 | + MotionEvent还包括以下信息 15 | + 点击的位置(x, y坐标) 16 | + 触点的数量(手指) 17 | + 事件发生的时间戳 18 | + 任何一个手势,都是以ACTION_DOWN起始,ACTION_UP结束 19 | + 事件从Activity的dispatchTouchEvent()函数开始,沿着View层次树依次向下传递 20 | + 父元素把事件dispatch到子元素 21 | + 事件能在任意阶段被intercept 22 | + 事件会沿着View层次树依次向下传递,然后又反向向上传递,直到被“消费” 23 | + View如果对手势感兴趣,就必须消费掉ACTION_DOWN的事件 24 | + 出于性能的考虑,同一手势的后续事件将不会按照完整路径进行传递,而是直接传递到消费了ACTION_DOWN事件的View 25 | + 如果所有的View(ViewGroup)都没有消费掉事件,那它将传递到Activity的onTouchEvent()函数中,并结束传递过程,即如果没有被消费,也不会再继续传递了 26 | + 可选的OnTouchListener能在任一View(ViewGroup)上intercept事件,事件被intercept之后,后面的调用将被传入ACTION_CANCEL?啥意思?? 27 | + `Activity.dispatchTouchEvent()` 28 | + 总是首先被调用 29 | + Sends event to root view attached to Window 30 | + 如果所有的View(ViewGroup)都没有消费该事件,那么`Activity.onTouchEvent()`将被调用,而且这个函数是最后一个被调用的函数 31 | + `ViewGroup.dispatchTouchEvent()` 32 | + 首先调用`onInterceptTouchEvent()`函数,判断是否需要拦截 33 | + 检查是否应该替代子view的处理 34 | + Passes ACTION_CANCEL to active child 35 | + 如果要消费掉同一手势的所有后续事件,需要返回true 36 | + 对所有的孩子,以添加顺序的逆序进行遍历 37 | + 如果点击在孩子的边界内,则调用`child.dispatchTouchEvent()` 38 | + 如果没有被当前的孩子消费,则传递到下一个孩子 39 | + 如果所有的孩子都未消费该事件,则传递给listener,`OnTouchListener.onTouch()` 40 | + 如果没有listener,或者listener也未消费,则自己处理,调用`ViewGroup.onTouchEvent()` 41 | + Intercepted events jump over child step 42 | + `View.dispatchTouchEvent()` 43 | + 如果被设置了OnTouchListener,那么将先把事件发送到listener,调用`View.OnTouchListener.onTouch()` 44 | + 如果listener没有消费事件,将调用`View.onTouchEvent()`,即自己处理点击事件 45 | + 例子 46 | + View对事件不感兴趣 47 | ![ignorant_view_example.png](../assets/ignorant_view_example.png) 48 | + View对事件感兴趣 49 | ![interested_view_example.png](../assets/interested_view_example.png) 50 | + 事件被ViewGroup intercept 51 | ![intercept_example.png](../assets/intercept_example.png) 52 | + 小结 53 | + 手势以ACTION_DOWN起始,以ACTION_UP结束 54 | + ACTION_DOWN,在每一层View上都会调用`dispatchTouchEvent()`,该View会判断是否对接下来的手势感兴趣,后续的点击事件将直接传递到感兴趣的View 55 | + ViewGroup可以intercept一个手势,因为`onInterceptTouchEvent()`是在`dispatchTouchEvent()`函数中最先被调用的,如果`onInterceptTouchEvent()`返回true,它的孩子将不会收到该手势的后续事件 56 | 57 | ## 自定义点击事件处理 58 | + 途径 59 | + (View/ViewGroup子类,Target)重载`onTouchEvent()`函数 60 | + 为Target设置`OnTouchListener` 61 | + 消费事件(`onTouchEvent()`) 62 | + ACTION_DOWN:如果对手势感兴趣,那么ACTION_DOWN的event就要返回true,即便对于ACTION_DOWN不感兴趣 63 | + 后续的事件,同样返回true,结束事件的处理流程(不会再传递给其他view或者parent view) 64 | + ViewConfiguration的一些有用方法 65 | + `getScaledTouchSlop()`:判断一个移动距离是否为drag 66 | + `getScaledMinimumFlingVelocity()`:判断一个拖拽速度是否为fling 67 | + `getLongPressTimeout()`:判断一个touch时间段是否为long press 68 | + 传递点击事件:调用target的`dispatchTouchEvent()`,不要直接调用target的`onTouchEvent()` 69 | + ViewGroup拦截点击事件 70 | + 重载`onInterceptTouchEvent()` 71 | + 如果对当前的手势感兴趣,`onInterceptTouchEvent()`返回true,之后的点击事件将不再经过`onInterceptTouchEvent()`函数 72 | + 其他的target(之前消费事件的View/ViewGroup)将收到ACTION_CANCEL 73 | + 一些建议/警告 74 | + 尽量调用super的对应方法,父类中已经做了很多基础工作了 75 | + ACTION_MOVE的处理中,检查移动距离是否超过slop(`getScaledTouchSlop()`) 76 | + 处理ACTION_CANCEL事件,父View可能会拦截事件,ACTION_CANCEL后需要重置状态,且之后该手势将不会再收到任何事件 77 | + intercept之后,该手势之后的所有事件都将被拦截,所以不要轻易拦截 78 | + 多触点事件响应 79 | + `MotionEvent.getPointerCount()`:获取当前屏幕上的触点数量 80 | + ACTION_POINTER_DOWN,ACTION_POINTER_UP用来响应次触点的事件,`MotionEvent.getActionMasked()`,`MotionEvent.getActionIndex()` 81 | + MotionEvent的有些方法会有两个版本,带index参数的,用于获取第index个触点的数据;不带参数的,获取主触点(第一个触点)的数据 82 | + 批量处理 83 | + 出于效率的考虑,ACTION_MOVE可以被打包到一个MotionEvent进行处理 84 | + 最近一次(本次)事件的信息,通过标准的方法获取:`getX()`, `getY()`, `getEventTime()` 85 | + 本次和最早一次ACTION_MOVE的信息,通过相应historical的方法获取 86 | + `getHistorySize()`获取打包的数量 87 | + `getHistorical*(pos)`获取第一个触点的第pos个历史事件的信息 88 | + `getHistorical*(index, pos)`获取第index个触点的第pos个历史事件的信息 89 | + Can reconstruct all events as they occurred in time for maximum precision 90 | + System Touch Handlers 91 | + 不要首先就考虑使用自定义的事件处理方式 92 | + `OnClickListener` 93 | + `OnLongClickListener` 94 | + `OnTouchListener` 95 | + 监听每一个MotionEvent,而不需要编写子类 96 | + 可以在Listener中消费事件 97 | + View的onTouchEvent处理中,优先调用的是listener的处理函数 98 | + `OnScrollListener` / `View.onScrollChanged()` 99 | + `GestureDetector` 100 | + onDown(), onSingleTapUp(), onDoubleTap() 101 | + onLongPress() 102 | + onScroll() (缓慢滚动) 103 | + onFling() (快速滚动后释放手指) 104 | + `ScaleGestureDetector` 105 | + onScaleBegin(), onScale(), onScaleEnd() 106 | + 一个扩展的gesture detector库:[android-gesture-detectors](https://github.com/Almeros/android-gesture-detectors) 107 | + 通过`OnTouchListener`或者`onTouchEvent()`进行处理 108 | + 缺点 109 | + Consume UP events and exposes no interface for CANCEL events 110 | + May require added touch handling if these cases need special handling (e.g. reset a View's appearance) 111 | + Touch Delegate 112 | + Specialized object to assist in forwarding touches from a parent view to its child 113 | + Allows for the touch area of a specific view to be different than its actual bounds 114 | + Called in onTouchEvent() of attached View(Events have to make it that far without being consumed by a child or listener) 115 | + TouchDelegate is designed to be set on the PARENT and passed the CHILD view that touches should be forwarded to 116 | 117 | ```java 118 | ViewGroup parent; 119 | View child; 120 | Rect touchArea; 121 | parent.setTouchDelegate(new TouchDelegate(touchArea, child)); 122 | ``` 123 | 124 | ## ViewDragHelper 125 | 快速处理view拖拽的辅助类。[参考blog](http://fedepaol.github.io/blog/2014/09/01/dragging-with-viewdraghelper/)。 126 | 127 | + 创建ViewDragHelper: 128 | 129 | ```java 130 | mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); 131 | ``` 132 | 133 | + 把ViewGroup的点击事件传递给ViewDragHelper: 134 | 135 | ```java 136 | @Override 137 | public boolean onInterceptTouchEvent(MotionEvent event) { 138 | if (mDragHelper.shouldInterceptTouchEvent(event)) { 139 | return true; 140 | } 141 | return super.onInterceptTouchEvent(event); 142 | } 143 | 144 | @Override 145 | public boolean onTouchEvent(MotionEvent event) { 146 | mDragHelper.processTouchEvent(event); 147 | return true; 148 | } 149 | ``` 150 | 151 | + ViewDragHelper.Callback的实现类`DragHelperCallback`中,重载感兴趣的函数,实现自己的逻辑 152 | 153 | + 有一个用于边缘拖拽结束activity的库,边缘拖拽使用的就是ViewDragHelper:[Slidr](https://github.com/r0adkll/Slidr) 154 | -------------------------------------------------------------------------------- /Android-Java/AndroidProjectArch.md: -------------------------------------------------------------------------------- 1 | # Android项目架构 2 | 从功能需求、设计模式、最佳实践出发考虑 3 | 4 | ## MVP模式 5 | 数据、显示、控制解耦,[mosby](https://github.com/sockeqwe/mosby),作者[blog1](http://hannesdorfmann.com/android/mosby/),[blog2](http://hannesdorfmann.com/android/mosby-playbook/) 6 | 7 | ## 依赖注入 8 | + 普通数据注入,[Dagger](http://google.github.io/dagger/) 9 | + View注入[ButterKnife](http://jakewharton.github.io/butterknife/)。 10 | 11 | ## 数据存储 12 | + ~~[squidb](https://github.com/yahoo/squidb),基于注解,编译期生成DAO类,model类是编译期生成的,而不是定义表结构的类,对复杂SQL语句的支持非常好~~ 13 | + [DBFlow](https://github.com/Raizlabs/DBFlow),基于注解,编译期生成DAO类,对关系的支持很好 14 | + ~~[ActiveAndroid](https://github.com/pardom/ActiveAndroid),基于注解,运行时转换~~ 15 | + ~~[greenDAO](https://github.com/greenrobot/greenDAO),编译期生成辅助代码~~ 16 | + [StorIO](https://github.com/pushtorefresh/storio),与rx集成,响应式DBHelper框架 17 | 18 | ## 网络连接处理 19 | + [Retrofit](http://square.github.io/retrofit/),使用动态代理将java接口转化为REST API 20 | + [OkHttp](http://square.github.io/okhttp/),优化的HTTP client(共享连接,连接池,透明压缩,缓存,重试等),支持web socket协议 21 | 22 | ## 异步处理 23 | + 响应式编程([rx](https://github.com/ReactiveX/RxAndroid),在java7中使用lambda语法:[retrolambda](https://github.com/orfjackal/retrolambda)),消息的发出者和响应的接收者在同一模块内,局部性、单对单 24 | + 事件处理([EventBus](https://github.com/greenrobot/EventBus),~~[Otto](http://square.github.io/otto/)~~),全局性事件、一对多比较合适 25 | 26 | ## 测试 27 | + 单元测试([我的印象笔记](https://www.evernote.com/shard/s425/sh/ed5e5a9b-8ebf-4d72-8d4d-62bff3b57335/a172972c726caab6),[Robolectric](http://robolectric.org/),使得Android代码能够在PC的JVM上运行测试,加快速度) 28 | + 对rx的单元测试,[非官方RxAssertions](https://gist.github.com/ivacf/874dcb476bfc97f4d555),[官方TestSubscriber](http://reactivex.io/RxJava/javadoc/rx/observers/TestSubscriber.html) 29 | + [AndroidTDD](AndroidTDD.md) 30 | + 集成测试([我的印象笔记](https://www.evernote.com/shard/s425/sh/52ec6ce5-68ca-47d7-8fa1-14fdacfc3f1a/31fceed7211d8e13),[Espresso](https://code.google.com/p/android-test-kit/wiki/Espresso)) 31 | 32 | ## 持续集成 33 | + [jenkins-ci](http://jenkins-ci.org/),整合代码托管工具,commit、merge request自动触发构建 34 | 35 | ## 工具 36 | + 图片加载 37 | + [Fresco](https://github.com/facebook/fresco),多来源加载、缓存、内存管理(存放在安卓非堆特殊内存区域)、支持多种格式、多种功能(圆角) 38 | + [Glide](https://github.com/bumptech/glide),多来源加载、缓存、Object pool内存优化、Context生命周期加载优化、ListView、RecyclerView等加载优化 39 | + ~~[Picasso](http://square.github.io/picasso/),使用堆内存,格式稍少~~ 40 | + 模块热加载 41 | + [dynamic-load-apk](https://github.com/singwhatiwanna/dynamic-load-apk),通过代理实现启动、显示、执行安装时未定义的Activity,Service,实现模块热加载 42 | + 错误统计 43 | + [fabric](https://get.fabric.io/),提供crash统计、以及twitter集成 44 | + 调试 45 | + [XLog](https://github.com/promeG/XLog),函数调用追踪,log出参数、返回值、线程、执行时间,支持方法、类的注解; 46 | + [Fresco](https://github.com/facebook/fresco),chrome查看log,view heriachy,shared pref,db等; 47 | + [LeakCanary](https://github.com/square/leakcanary),memory leak检测工具; 48 | + 时间 49 | + [ThreeTenBP](https://github.com/ThreeTen/threetenbp),JSR310的java 8以前的兼容实现,比安卓的Date类等强大无数倍; 50 | + [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP),安卓的一个包装,init过程性能更好; 51 | + 导航 52 | + [FragmentArgs](https://github.com/sockeqwe/fragmentargs),Fragment启动时通过Argument传递参数 53 | + [Dart](https://github.com/f2prateek/dart),Activity之间通过Intent传递Extra参数 54 | + [Pocket Knife](https://github.com/hansenji/pocketknife),Activity的Extra传递参数,SavedInstance做状态保存/恢复 55 | + [Aftermath](https://github.com/MichaelEvans/Aftermath),A simple, annotation-based Android library for generating onActivityForResult handlers. 56 | + 动效 57 | + [rebound](https://github.com/facebook/rebound),通过Android系统的property transition,加上各种变换函数,实现模拟物理的动效; 58 | + 设计 59 | + sketch 60 | + [zeplin](https://zeplin.io/) 61 | 62 | # Develop maintainable apps 63 | + Libraries 64 | 选择第三方库时的考虑 65 | + Documentation: 文档是否全面 66 | + Repository Check-ins(Stability): 稳定性、是否保持维护 67 | + Fulfils a Need: 满足项目需求 68 | + Domain design 69 | + Keep code simple: OO,一个类只负责一件事 70 | + Use MVC/MVP/MVVM Pattern 71 | + Functional Test 72 | + Use the tools: IDE的重构工具很强大 73 | + Code quality 74 | + Readability matters 75 | + 工具 76 | + CheckStyle 77 | + Lint 78 | + Findbugs 79 | + PMD 80 | + Refactor Gradually 81 | + Testing 82 | + Unit test: Every single line of code that you write should be tested. Peroid. -- Robert Martin 83 | + Junit(...) + Mockito 84 | + Continuous Integration 85 | + Code coverage 86 | 87 | # [Android Clean Architecture](https://github.com/android10/Android-CleanArchitecture) 88 | ## 分层结构 89 | ![clean_architecture1.png](../assets/clean_architecture1.png) 90 | + Entities: These are the business objects of the application. 91 | + Use Cases: These use cases orchestrate the flow of data to and from the entities. Are also called Interactors. 92 | + Interface Adapters: This set of adapters convert data from the format most convenient for the use cases and entities. Presenters and Controllers belong here. 93 | + Frameworks and Drivers: This is where all the details go: UI, tools, frameworks, etc. 94 | + Dependency Rule: source code dependencies can only point inwards and nothing in an inner circle can know anything at all about something in an outer circle. 95 | 96 | ## 安卓项目层次结构示例 97 | ![clean_architecture_android.png](../assets/clean_architecture_android.png) 98 | 99 | ## 使用Rx后的层次结构示例 100 | ![clean_architecture_evolution.png](../assets/clean_architecture_evolution.png) 101 | + Presentation layer: UI tests with Espresso 2 and Android Instrumentation. 102 | + Presenter && View test: 针对接口进行单元测试;对UI简单交互结果逻辑的测试(如:Activity跳转,Fragment切换,相关接口调用); 103 | + Domain layer: JUnit + Mockito since it is a regular Java module. 104 | + Data layer: Migrated test battery to use Robolectric 3 + JUnit + Mockito. 105 | 106 | ## 代码组织(包组织) 107 | + Package by layer 108 | + Package by feature 109 | + Higher Modularity 110 | + Easier Code Navigation 111 | + Minimizes Scope 112 | 113 | 114 | # [Flux Architecture](http://lgvalle.github.io/2015/08/04/flux-architecture/) 115 | + 结构图 116 | ![flux-graph-simple.png](../assets/flux-graph-simple.png) 117 | + View: Application interface. It create actions in response to user interactions. Activity or Fragment 118 | + Dispatcher: Central hub through which pass all actions and whose responsibility is to make them arrive to every Store. An event bus. 119 | + Store: Maintain the state for a particular application domain. They respond to actions according to current state, execute business logic and emit a change event when they are done. This event is used by the view to update its interface. Simple POJOs with two main attributes: Type: a String identifying the type of event; Data: a Map with the payload for this action. 120 | + More about Stores 121 | + Stores contain the status of the application and its business logic. 122 | + Stores react to Actions emitted by the Dispatcher, execute business logic and emit a change event as result. 123 | + ... 124 | 125 | ## [RxFlux](https://medium.com/@marxallski/rxflux-android-architecture-94f77c857aa2) 126 | ![rxflux_arch.png](../assets/rxflux_arch.png) 127 | 128 | 129 | # [What is all this Clean Architecture jibber-jabber about?](http://pguardiola.com/blog/clean-architecture-part-1/) 130 | ## 基础 131 | + SOLID 132 | + Single Responsiblity 133 | + Open/Closed 134 | + Liskov Substitution 135 | + Interface Segregation 136 | + Dependency Inversion 137 | + Abstraction 138 | + 面向接口编程 139 | + 高内聚,低耦合 140 | + 内聚:单一模块内,各个部分之间的关系 141 | + 耦合:各个模块之间的关系 142 | + Vertical slicing 143 | + Horizontal slicing:传统方式,从服务提供方分包,例如DB, Network, ui... 144 | + Vertical slicing:源自敏捷,根据功能(use case)进行分包,而将与该功能相关的db, network, ui等部分放到同一个模块中 -------------------------------------------------------------------------------- /Android-Java/DesignPatternsInsideAndroid.md: -------------------------------------------------------------------------------- 1 | # 《Android源码设计模式解析与实战》一书 2 | 3 | ## [面向对象的六大原则](../misc/OOP6Principles.md) 4 | 5 | ## 单例模式 6 | + 建议实现方式 7 | + 无参数时,static inner holder class方式: 8 | 9 | ```java 10 | public class Singleton { 11 | private Singleton() { 12 | // singleton 13 | } 14 | 15 | public static getInstance() { 16 | return InstanceHolder.sInstance; 17 | } 18 | 19 | private static class InstanceHolder { 20 | private static final Singleton sInstance = new Singleton(); 21 | } 22 | } 23 | ``` 24 | 25 | + 有参数时,优化的double check lock (DCL)方式,注意`volatile`关键字,在JDK1.5及以后,用于解决this指针逃逸问题: 26 | 27 | ```java 28 | public class Singleton { 29 | private static volatile Singleton sInstance; 30 | 31 | private final Context mContext; 32 | 33 | private Singleton(Context context) { 34 | mContext = context; 35 | } 36 | 37 | public static getInstance(Context context) { 38 | if (sInstance == null) { 39 | synchronized(Singleton.class) { 40 | if (sInstance == null) { 41 | sInstance = new Singleton(context); 42 | } 43 | } 44 | } 45 | 46 | return sInstance; 47 | } 48 | } 49 | ``` 50 | 51 | + [this指针逃逸问题](http://blog.itpub.net/28912557/viewspace-762047/) 52 | 53 | 在DCL单例实现中,`sInstance = new Singleton(context);`实际上大致会进行三个操作: 54 | 55 | 1. 为`Singleton`的实例分配内存; 56 | 2. 调用`Singleton`的构造函数,初始化成员变量; 57 | 3. 把`sInstance`指向新分配的内存空间(此时`sInstance`就不是`null`了); 58 | 59 | 由于Java memory model的内存缓存模型(寄存器,cache,主存),以及允许指令重排,在缓存层面,上述三步中后两步顺序是无法保证的,有可能是`1-2-3`,也有可能是`1-3-2`。如果是`1-3-2`,在A线程执行,第三步执行完之后,切换到B线程,则B线程执行`getInstance`时将直接返回`sInstance`,因为B线程将直接命中缓存,但是它的成员变量仍未初始化,一旦使用就会出现问题。 60 | 61 | 而`volatile`关键字则能保证每次读数据都从主存读取,不会受到缓存的影响,所以B线程从主存读到的仍是null,就不会出现问题了。 62 | 63 | + 安卓系统的实践(部分) 64 | + 各种system service就是单例,虽然他们使用binder机制和真正的service跨进程通信,但是在本地的proxy就是单例的(APP进程中的单例); 65 | + 每个APP都有各种各样的`Context`,它的实现类是`ComtextImpl`,它在static代码块中初始化各种system service,并保存在一个map中,后续获取的时候将直接从map中获取;android 23起,初始化system service、缓存、单例逻辑的代码从`ContextImpl`转移到了`SystemServiceRegistry`中; 66 | + `LayoutInflater`工作原理 67 | + 实现类是`PhoneLayoutInflater` 68 | + 内置view,在xml定义时不用声明完整包名,因为`PhoneLayoutInflater`在`onCreateView`函数中会自动为其添加包名前缀 69 | + 最终view的创建都是通过`createView`函数完成,在其中通过反射创建view实例 70 | + `inflate`函数会解析xml布局文件,深度优先遍历,逐层创建view(通过`createViewFromTag`函数),并最终形成一棵view的树 71 | 72 | ## Builder模式 73 | + [AutoValue](https://github.com/google/auto/tree/master/value)及其在Android平台的版本[AutoParcel](https://github.com/frankiesardo/auto-parcel),不仅支持immutable,还支持builder模式 74 | + `WindowManager` 75 | + 实现类为`WindowManagerImpl` 76 | + Activity, Fragment, Dialog等组建的view显示,都是通过的`Window::setContentView()`方法来实现的 77 | + `Window`是抽象类,其实现类为`PhoneWindow`,**它的setContentView方法,怎么和WindowManager关联起来的??** 78 | + `WindowManager`添加、移除view的实现工作在`WindowManagerGlobal`类中 79 | + `ViewRootImpl`是framework层与native层通信的桥梁,继承自`Handler` 80 | + WMS只负责管理手机屏幕上View的z-order,即View的图层顺序,WMS管理的是属于某个window下的view 81 | 82 | ## 原型模式 83 | + 在已有对象的基础上构造新对象,通常是clone;clone的效率通常比new的效率高,但不绝对;需要注意深浅拷贝的问题。 84 | + Intent的查找与匹配 85 | + 系统启动之后,`PackageManagerService`会扫描系统内所有的APP,解析其manifest文件,得到所有APP注册的所有各类组件,保存在系统中(内存);后续使用Intent进行跳转时,通过查找保存的组件信息,得知应该启动哪个APP(组件); 86 | + 显式Intent:直接指定了响应Activity;隐式Intent:只指定了Action; 87 | + 启动Activity时,会向PackageManagerService查询intent对应的activity,在`PackageManagerService::queryIntentActivities(...)`方法中; 88 | 89 | ## 工厂方法 90 | + 用工厂去创建产品(对象),工厂和产品都可以提供抽象类,具体类,以达到实现解耦的目的 91 | + Activity的启动过程 92 | + `ActivityThread::main()`是app的执行起点 ==> 93 | + `thread.attach(false)`把ActivityThread绑定到ActivityManagerService,它调用了`ActivityManagerService::attachApplication`方法 ==> 94 | + `ActivityManagerService::attachApplication`方法中间接调用了`ActivityThread.ApplicationThread::bindApplication`和`ActivityManagerService::attachApplicationLocked`,前者把ApplicationThread对象绑定到ActivityManagerService ==> 95 | + `ActivityManagerService::attachApplicationLocked` ==> 96 | + `ActivityManagerService::realStartActivityLocked` ==> 97 | + `ActivityThread.ApplicationThread::scheduleLaunchActivity` ==> 98 | + `ActivityThread.H::handleMessage` ==> 99 | + `ActivityThread::handleLaunchActivity` ==> 100 | + `ActivityThread::performLaunchActivity` ==> 101 | + `Instrumentation::callActivityOnCreate` ==> 102 | + `Activity::performCreate` ==> 103 | + `Activity::onCreate` 104 | 105 | ## 抽象工厂 106 | 四种角色:抽象的工厂类,定义工厂的接口;具体的工厂类,实现生产产品;抽象的产品类,定义产品的接口;具体的产品类,实现产品的功能; 107 | 108 | ## 策略模式 109 | + 某一需求可以有多种实现算法,将每种算法独立封装,且它们之间可以相互替换(实现相同的接口)。策略模式让算法独立于使用它们的客户端而独立变化。 110 | + 安卓系统动画原理 111 | + `view.startAnimation()`,动画是怎么实现的?随时间改变view属性的值; 112 | + 整个过程是怎样的?绘制时利用`TimeInterpolator`获取绘制的时间百分比,再利用`TypeEvaluator`把百分比计算为属性值,设置给view; 113 | + 怎么做到随时间流逝持续进行上述过程? 114 | + `View::startAnimation(Animation animation)`在设置了animation之后,调用`invalidate`,invalidate最终调用到`ViewGroup::drawChild(Canvas canvas, View child, long drawingTime)` ==> 115 | + `View::draw(Canvas canvas, ViewGroup parent, long drawingTime)` ==> 116 | + `View::applyLegacyAnimation(ViewGroup parent, long drawingTime, Animation a, boolean scalingRequired)` ==> 117 | + `applyLegacyAnimation`函数中,完成了动画初始化、动画操作、界面刷新(本次动画操作完成后,再次invalidate) 118 | + 动画操作在`Animator::getTransformation(long currentTime, Transformation outTransformation)`函数中实现 119 | + `ValueAnimator`原理 120 | + 动画属性都保存在`PropertyValuesHolder`类中 121 | + `ValueAnimator::start()`调用后,将会把动画指令发送给内部的`ValueAnimator.AnimationHandler`类,而handler则使用`Choreographer`来进行定时的刷新 122 | + 刷新时调用了`ValueAnimator::doAnimationFrame(long frameTime)` ==> 123 | + `ValueAnimator::animationFrame(long currentTime)` ==> 124 | + `ValueAnimator::animateValue(float fraction)` ==> 125 | + 具体实现类(例如`ObjectAnimator`)的重载中,`ObjectAnimator::animateValue(float fraction)` ==> 126 | + `PropertyValuesHolder::setAnimatedValue(Object target)`,在其中通过反射,为view设置属性 127 | + `ValueAnimator`的代码使用反射工作,设置动画属性时传入的是字符串,容易产生错误,有两个不错的库对这一点进行了优化:[ViewAnimator](https://github.com/florent37/ViewAnimator), [AnimatorCompat](https://github.com/zzz40500/AnimatorCompat) 128 | 129 | ## 状态模式 130 | 一个对象的行为取决于它的状态,最直接的实现是各个函数中对状态进行判断(if-else或switch),采取不同的行为。状态模式则是把状态抽象为一个类,不同状态下的行为封装为不同的状态实现类。改变对象的状态时,只需要修改其状态对象,即可达到修改其行为的目的。 131 | 132 | ## 安卓事件输入系统 133 | + `InputReader`,从硬件的事件(CPU中断)中,读取输入事件,并封装为`Event`对象,然后分发给`InputDispatcher` 134 | + `InputDispatcher`负责接收来自`InputReader`的事件,并分发给合适的窗口,同时监控ANR 135 | + `InputReaderThread`,`InputDispatcherThread`,`InputManager`,`InputManagerService`,`WindowManagerService` 136 | 137 | ## 观察者模式 138 | 又称订阅模式,定义了对象间一对多的依赖关系,当特定的对象(subject)变化时,所有依赖它的对象(observer)都会得到通知并自动更新。 139 | 140 | + ListView中的观察者模式 141 | + 更改adapter数据集之后,会调用`notifyDataSetChanged` 142 | + 会调用其内部的observer的`onChanged`方法 143 | + 内部的observer是AdapterView的内部类(非静态),其onChanged方法会触发AdapterView重新布局,达到刷新UI的目的 144 | + BroadcastReceiver中的观察者模式 145 | + 代码注册receiver过程:`registerReceiver` ==> `ContextWrapper::registerReceiver` ==> 146 | + `ContextImpl::registerReceiver` ==> `ContextImpl::registerReceiverInternal` 147 | + `LoadedApk::getReceiverDispatcher`函数创建了一个`IIntentReceiver`对象(Binder对象) 148 | + `ActivityManagerNative.getDefault().registerReceiver`向ActivityManagerService注册receiver,并把`IIntentReceiver`传递进去,用于ActivityManagerService通知activity接收广播 ==> 149 | + `ActivityManagerProxy::registerReceiver` ===> 150 | + `ActivityManagerService::registerReceiver` 151 | + sticky处理:如果是sticky intent,最后一次发送此广播的时候,ActivityManagerService会把这个intent保存起来,后面注册相同action的接收器时,就会得到最后一次的广播,并重放 152 | + receiver会保存在ActivityManagerService中,并且会把receiver和filter进行关联 153 | + 发送广播 154 | + sendBroadcast会把广播通过binder发送给ActivityManagerService,后者通难过广播的action找到相应的接收器,然后把广播放进自己的消息队列中 155 | + ActivityManagerService的消息队列循环中会处理这个广播,通过binder分发给注册的ReceiverDispatcher,后者把这个广播放到注册Activity所在线程的消息队列中 156 | + ReceiverDispatcher的内部类Args在注册Activity所在线程的消息队列循环中处理这个广播,即调用`receiver.onReceive` 157 | + ActivityManagerService使用一个map保存了filter与receiver,注册就是加入map,发送就是查询map并完成分发 158 | -------------------------------------------------------------------------------- /Android-Java/InsideJVM.md: -------------------------------------------------------------------------------- 1 | # 深入理解Java虚拟机 2 | 3 | # 第二部分 自动内存管理机制 4 | 5 | ## 第二章 Java内存区域与内存溢出异常 6 | + JVM内存区域 7 | ![jvm_memory_area.png](../assets/jvm_memory_area.png) 8 | + 程序计数器:类似x86 EIP,每个线程都有一个程序计数器;执行native代码时计数器值为空;唯一不会抛出OOM的区域; 9 | + Java虚拟机栈:线程私有;即函数调用栈,保存函数局部变量; 10 | + Native方法栈:执行Native代码的函数调用栈; 11 | + Java堆:新创建的对象存放区域; 12 | + 方法区:保存已加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;有以永久代实现此区域的,也有以独立GC实现的; 13 | + 运行时常量池:是方法区的一部分;`String.intern()`方法有可能对此区域造成大的影响(依赖于JVM实现); 14 | + 直接内存:NIO模块,为了提高内存访问效率,直接分配堆外内存,直接操作; 15 | + HotSpot虚拟机对象创建 16 | 首先检查类的符号引用是否在常量池中,并检查该类是否已加载、解析、初始化;如果没有,则先进行加载; 17 | 然后为对象分配内存空间,对象所占空间在类加载完成后即可确定; 18 | 堆内存管理大致有两种方式:指针碰撞;空闲列表。依赖于GC时是否会对回收的内存进行压缩整理; 19 | 分配内存的同步性保证:分配过程原子化;将内存分配按照线程划分在不同的空间中(本地线程分配缓冲,TLAB)。后者可通过JVM参数控制; 20 | 内存分配完后,将内存空间初始化为零值,不包括“对象头”,初始化工作可以提前至TLAB分配时进行; 21 | 设置“对象头”,例如:对象是哪个类的实例,类的元数据信息地址,对象hash值,GC分代年龄等信息;根据虚拟机当前的运行状态不同,是否启用偏向锁等,对象头会有不同设置方式; 22 | 执行类的<init>方法; 23 | + HotSpot虚拟机对象的内存布局 24 | 分为三部分:Header,实例数据,对其填充; 25 | 实例数据部分,相同宽度的字段会分配到一起;满足该前提下,父类的变量出现在子类前;CompactFields参数为true时,子类中较窄的变量可能会插入到父类变量的空隙之中; 26 | 对象大小8字节对其; 27 | + 对象的访问定位 28 | 在堆上建立了对象,使用的时候基本都是通过栈上的reference来操作堆上的对象; 29 | 目前主流有两种访问方式:使用句柄;直接指针; 30 | + 使用句柄 31 | 在堆上划分一块内存作为句柄池,reference指向的是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息;句柄地址是稳定的,对象移动时只需修改句柄的内容,不必修改reference; 32 | + 直接指针 33 | reference直接指向堆上对象地址;访问速度快,只需一次指针定位;HotSpot使用直接指针方式; 34 | + 各种可能的OutOfMemoryError异常 35 | + 堆溢出 36 | ```java 37 | java.lang.OutOfMemoryError: Java heap space... 38 | ``` 39 | log中明确指出heap space,可通过JVM参数设置堆大小、最大大小、发生OOM后dump堆上数据; 40 | 一般通过分析dump数据,确认内存中的对象是否必要,即确定是内存泄漏还是内存溢出; 41 | 如果是内存泄漏,可进一步查看泄漏对象到GC Roots的引用链,根据引用链,基本就能定位泄漏代码的位置了; 42 | 如果是内存溢出,则可通过增加JVM堆内存、审查代码,减少对象生命周期,减少程序运行期间的内存消耗; 43 | + 虚拟机栈与本地方法栈溢出 44 | StackOverFlowError,OutOfMemoryError 45 | 当每个线程分配的栈容量越大时,发生StackOverFlowError时创建的线程数越少,因为每个进程的内存是有限的,栈容量越大,则允许的线程数越少;通过减少线程数、更换64位虚拟机(增加内存)、减少最大堆和栈容量,均可缓解这一错误; 46 | + 方法区与运行时常量池溢出 47 | ```java 48 | java.lang.OutOfMemoryError: PermGen space... 49 | ``` 50 | 永久代区域内存大小可以配置,如果方法区以永久代实现,则`String.intern()`方法就可能导致该错误; 51 | 类的卸载条件非常苛刻,如果运行时使用了大量的动态生成类,有可能导致该错误; 52 | + 直接内存溢出 53 | ```java 54 | java.lang.OutOfMemoryError: ... 55 | ``` 56 | 在dump文件中看不出明显异常; 57 | 直接内存区域可以通过JVM参数配置,默认与堆最大值一样; 58 | 59 | ## 第三章 垃圾收集器与内存分配策略 60 | + 是否可被回收 61 | + 引用计数法 62 | 为每个对象添加一个引用计数器,当计数器为0时,可被回收;Python等语言使用,Java不是;无法解决循环引用的问题; 63 | + 可达性分析 64 | 从GCRoots出发,对所有的对象引用关系图进行一次遍历,不可达的对象可被回收;Java使用;GCRoots包括:虚拟机栈中引用的对象;方法区中静态类属性引用的对象;方法区中常量引用的对象;本地方法栈中JNI引用的对象; 65 | + 四种引用类型 66 | + 强引用 67 | 普通赋值即为强引用;只要有强引用,就不会被GC; 68 | + 软引用(SoftReference) 69 | 用来引用那些有用,但非必须的对象;在系统将要发生内存溢出错误之前,会将软引用指向的对象列入回收范围并进行二次回收,如果仍内存不足,将抛出OOM; 70 | + 弱引用(WeakReference) 71 | 作用与软引用类似,但强度更弱;GC过程中,无论内存是否足够,只被弱引用指向的对象,都将被回收; 72 | + 虚引用(PhantonReference) 73 | 对对象的生存周期完全没有影响,也无法通过虚引用来获取对象实例,仅仅能在对象被回收时,得到一个系统通知(只能通过是否被加入到ReferenceQueue来判断是否被GC,这也是唯一判断对象是否被GC的途径); 74 | + finalize()方法 75 | 当可达性分析结果为不可达时,GC器首先会判断是否需要执行finalize()方法,只有当类重写了finalize方法,且该对象的finalize方法未被调用,才需要执行; 76 | finalize方法被放在一个虚拟机自动建立、低优先级的线程调用,且不保证执行完毕; 77 | 不需要执行finalize时,将被GC; 78 | 在finalize方法中,对象可以通过把自己挂到GCRoots引用链上进行GC逃逸,但只有一次机会; 79 | 而如果使用虚引用,则可以避免GC逃逸的问题出现,这也是虚引用的第二个使用场景; 80 | 千万不要把finalize()方法和C++的析构函数等同; 81 | + 回收方法区 82 | 包括常量、类,这两部分; 83 | 常量与普通对象类似,判断引用状态即可; 84 | 类的判断比较苛刻:该类的所有对象均已被回收;加载该类的ClassLoader已被回收;该类对应的`java.lang.Class`对象未在任何地方被引用。而且这只是充分条件; 85 | 在使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGI等频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以避免方法区溢出; 86 | + 垃圾收集算法 87 | + 标记-清除算法 88 | 第一阶段,判定对象可回收之后,将其标记,第二阶段,统一回收被标记的对象; 89 | 两个阶段效率都不高;另外存在碎片化问题; 90 | + 复制算法 91 | 将可用内存均分为两块,每次只使用其中一块,当一块用完之后触发GC,将存活的对象复制到另一块,再一次性清理已使用的一块; 92 | 效率高,不存在内存碎片;但是内存缩小一半,空间开销大; 93 | 现代商用JVM均使用复制算法来回收新生代,HotSpot虚拟机默认将内存分为8:1:1三块,一块Eden空间,两块较小的Survivor空间,每次使用Eden和一块Survivor,用完后将存活对象复制到另一块Survivor空间,集中回收已用部分;当一块Survivor不够复制时,将通过分配机制进入老年代; 94 | + 标记-整理算法 95 | 标记过程同“标记清除算法”,标记结束后,通过将所有存活对象都向一端移动,然后直接清理边界外的内存; 96 | + 分代收集算法 97 | 现代商用JVM均采用分代收集算法;一般把堆分为新生代和老年代; 98 | 新生代一般98%的对象都会很快被回收,使用复制算法; 99 | 老年代使用标记-清理,或者标记-整理算法; 100 | + HotSpot的算法实现 101 | ... 102 | 103 | ## 第七章 虚拟机类加载机制 104 | + 类的生命周期 105 | + 加载、验证、准备、解析、初始化、使用、卸载 106 | + 验证、准备、解析,通常称为链接过程 107 | + 加载、验证、准备、初始化、卸载,这五个阶段的开始顺序是确定的,解析阶段可能在初始化之后再开始,用于支持动态绑定;仅仅是开始顺序,并不是进行/完成的顺序; 108 | + 类加载的时机并未明确规定,但是初始化明确规定,当且仅当以下5中情况,若类未初始化,必须立即对类进行初始化: 109 | + 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时(new一个类,访问static成员/方法,`static final`成员除外,它们在编译期放入了常量池); 110 | + 使用`java.lang.reflect`包得方法对类进行反射调用的时候; 111 | + 初始化一个类,然而其父类未初始化时,先初始化父类; 112 | + 虚拟机启动时用户指定的要执行的主类; 113 | + 当使用JDK 1.7的动态语言支持时,如果一个`java.lang.invoke.MethodHandle`实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且方法句柄对应的类未初始化; 114 | + 这5种行为称为主动引用,可能会触发类的初始化;其他的行为均不会触发类的初始化,称为被动引用,例如: 115 | + 通过子类引用父类的静态成员,不会导致子类初始化 116 | + 通过数组定义(new一个数组),并不会触发数组元素类的初始化,但是虚拟机会自动生成一个直接继承于Object的子类,该类封装了对数组的访问 117 | + `static final`成员会被放入常量池,访问它们不会触发类的初始化 118 | + 接口的初始化触发条件与类基本一致,仅有第三点不同:一个接口初始化时,不要求父接口完成初始化,只有在真正使用父接口时才会触发父接口的初始化 119 | + 类加载的过程 120 | + 加载 121 | + 通过类的全限定名,获取定义此类的二进制字节流 122 | + 将字节流中的静态存储结构转化为方法区的运行时数据结构 123 | + 在内存中生成一个代表这个类的`java.lang.Class`对象,作为方法区这个类的各种数据的访问入口 124 | + 第一阶段最灵活,二进制流可以从任何位置、以任何方式获取,衍生了许多强大的技术(zip包、网络、动态代理),通过自定义类加载器即可利用这一灵活性 125 | + 数组的类型是虚拟机自动创建的,但也和数组元素的类加载器紧密相关 126 | + 验证 127 | + 编译器会对代码进行检查 128 | + 虚拟机会对字节码进行检查,包括:文件格式验证、元数据验证(修饰符、可见性、继承关系)、字节码验证(数据流、控制流分析,确定语义合法、符合逻辑,StackMapTable)、符号引用验证(可查找、可访问等) 129 | + 准备 130 | + 为static变量分配内存,设置零值(而非代码中定义的初始值,初始值在自动生成的()函数中进行);为static final变量直接设置初始值 131 | + 解析 132 | + 符号引用转换为直接引用,有些只有在运行时才能完成解析 133 | + 初始化 134 | + ()函数,将static代码块、static(无final修饰)变量的赋值组合起来而成;static代码块访问static变量时,变量必须已经定义(代码位置相关性,可写不可读) 135 | + 父类的()函数一定先于子类的执行,因此父类的static代码块先于子类的static变量赋值操作 136 | + ()函数对于接口来说非必须,()函数的线程安全性由虚拟机保证,static代码块中要避免耗时操作 137 | + 类加载器 138 | + 加载一个类到内存中 139 | + 比较两个类是否相等(equals()、isAssignableFrom()、inInstance()、instanceof操作),只有在两个类是由同一个类加载器加载的前提下才有意义 140 | + 双亲委派模型(Parents Delegation Model) 141 | BootstrapClassLoader、sum.misc.Launcher$ExtClassLoader、sun.misc.Launcher$App-ClassLoader 142 | ![java_class_loader_relation.jpeg](../assets/java_class_loader_relation.jpeg) 143 | 除了BootstrapClassLoader,其他所有的类加载器都应有父类加载器,而且父子关系不是以继承方式实现,而是以组合方式:一个类加载器收到类加载请求后,首先调用父类加载器的加载方法,每一层次均如此,如果父类加载器无法完成加载,子加载器才尝试自己加载。 144 | 保证Java类体系的正确性。自定义ClassLoader一般把加载逻辑放在findClass中。 145 | ```java 146 | protected Class loadClass(String className, boolean resolve) throws ClassNotFoundException { 147 | Class clazz = findLoadedClass(className); 148 | 149 | if (clazz == null) { 150 | ClassNotFoundException suppressed = null; 151 | try { 152 | clazz = parent.loadClass(className, false); 153 | } catch (ClassNotFoundException e) { 154 | suppressed = e; 155 | } 156 | 157 | if (clazz == null) { 158 | try { 159 | clazz = findClass(className); 160 | } catch (ClassNotFoundException e) { 161 | e.addSuppressed(suppressed); 162 | throw e; 163 | } 164 | } 165 | } 166 | 167 | return clazz; 168 | } 169 | ``` 170 | + 非双亲委派模型:JNDI、JDBC、JCE、JAXB、JBI;程序动态性,OSGI,代码热替换、模块热部署等; 171 | 172 | ## 第八章 虚拟机字节码执行引擎 173 | + 基于栈的执行引擎,而非基于寄存器 174 | + 即时编译本地代码执行,JIT 175 | + 局部变量表 176 | + slot 177 | + slot重用对垃圾回收可能会有影响,但是通过JIT可以解决 178 | + 局部变量不会被自动赋予零值,但编译器有些情况下会给提示 179 | + 操作数栈 180 | + 不同栈帧之间可能有重叠,避免数据复制开销 181 | + 动态链接,每个栈帧中都包含一个指向运行时常量池中该栈帧所属方法的引用,用于支持动态链接 182 | + 方法返回地址 183 | + 附加信息:调试信息等 184 | + 方法调用 185 | + 解析 186 | + invokestatic:静态方法 187 | + invokespecial:构造函数(())、私有方法、父类方法 188 | + invokevirtual:调用虚方法 189 | + invokeinterface:调用接口方法 190 | + invokedynamic:动态解析 191 | + 分派 192 | + 静态分派(Method Overload Resolution):方法重载,编译期分派,编译器选择最合适的重载版本;类似于C++的编译时多态:函数重载,模板; 193 | + 动态分派(Dynamic Dispatch):方法重写,运行期分派;类似于C++的运行时多态:虚函数; 194 | + 动态类型语言支持 195 | ... -------------------------------------------------------------------------------- /Android-Java/UnderstandAndroidSourceCode.md: -------------------------------------------------------------------------------- 1 | # 深入Android frameworks 2 | 《深入理解Android 5源代码》一书的内容实在太渣,转战博客系列:[老罗的Android之旅](http://blog.csdn.net/Luoshengyang/article/) 3 | 4 | ## Java Native Interface(JNI)系统 5 | + JNI函数注册:把Java中声明的native方法与C/C++代码一一对应起来 6 | + `JNINativeMethod`结构体,name, signature, fnPtr三个成员 7 | + signature部分:V - void - void, Z - jboolean - boolean, J - jlong - long, 8 | S - jshort - short, Ljava/lang/String; - jstring - String, 9 | Ljava/net/Socket; - jobject - Socket 10 | + 通过在C/C++代码中设置好Java native方法对应的结构体数据,就可以达到将其对应起来的目的 11 | + 所有的注册工作都在`frameworks/base/core/jni/AndroidRuntime.cpp`中完成 12 | + `AndroidRuntime::registerNativeMethods`函数调用了`JNIHelp.cpp`中的 13 | `jniRegisterNativeMethods`方法,=> `C_JNIEnv::RegisterNatives` 14 | + 如果不通过上述方式注册native函数,函数被调用时AndroidRuntime会从所有已载入的so库中 15 | 搜索被调用的函数,效率较低,而上述注册会加速native函数的查找,只需在JNINativeMethod数组 16 | 中查找即可 17 | + 而且由于上述注册机制,使得可以在运行时动态注册/hook 18 | + 动态注册 19 | + Java层调用`System.loadLibrary`加载so库 20 | + 查找so库中的`JNI_OnLoad`函数,如果存在,则执行之 21 | + 可以在JNI_OnLoad函数中进行动态注册 22 | 23 | ## Hardware Abstract Layer(HAL) 24 | + 将硬件抽象化,为操作系统提供虚拟硬件平台,操作系统的代码和硬件驱动的代码完全分离,软硬件完全分离, 25 | 便于保护硬件厂商的知识产权,也便于软硬件测试并行化 26 | + 主要包括`hw_module_t`, `hw_module_methods_t`, `hw_device_t` 27 | + `hw_module_methods_t`为所有硬件操作定义了统一的API 28 | + 硬件模块的编写有一定规范,HAL按照该规范载入硬件模块的SO库,获取绑定其中的符号、代码 29 | 30 | ## Binder机制(跨进程通信IPC) 31 | + [Android深入浅出之Binder机制](http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html) 32 | + 系统服务的初始化流程 33 | + 每个使用Binder机制进行IPC的进程,都需要打开一次`/dev/binder`设备,并进行内存映射,方便后续操作:`sp proc(ProcessState::self())`; 34 | + 每个进程也需要创建一个BpBinder对象,并将其转换为IServiceManager类型,保存到进程数据结构ProcessState中:`sp sm = defaultServiceManager()`; 35 | + 其实上面的`proc`和`sm`变量都没有使用,重要的工作是对两个函数的调用,而之所以要定义未使用的变量,是为了利用对象的析构机制自动释放资源,如果未定义变量,那返回的对象会立即析构,而如果定义了变量,则会在离开作用域时析构; 36 | + 初始化相应的服务:`MediaPlayerService::instantiate()`; 37 | + 创建线程池,加入线程池,并在其中开始循环,以便响应通过Binder发送的IPC请求:`ProcessState::self()->startThreadPool()`,`IPCThreadState::self()->joinThreadPool()`; 38 | + IBinder, BBinder, BpBinder, IInterface, BpInterface, IServiceManager, BpServiceManager, 39 | MediaPlayerService, BnMediaPlayerService 它们之间什么关系? 40 | + BBinder : IBinder : RefBase,定义了binder的API:`transact(...)`等基础API; 41 | + BpRefBase : RefBase,定义了基础的代理API:`remote()`; 42 | + BpBinder : IBinder,它也定义了代理的API:`remoteBinder()`; 43 | + IInterface : RefBase,定义了所有的service的公共API:`asBinder(...)`; 44 | + BnInterface : INTERFACE, BBinder,它是个模板类,用法是`BnInterface`,这样声明的类型就会同时继承IXXX和BBinder,`BnInterface`是IXXX的实现者,提供API:`queryLocalInterface(...)`; 45 | + BpInterface : INTERFACE, BpRefBase,它是个模板类,用法是`BpInterface`,这样声明的类型就会同时继承IXXX和BpRefBase,`BpInterface`是IXXX的代理服务提供者(代理); 46 | + IServiceManager : IInterface,service manager也是一个服务,只不过它比较特殊,它的功能是管理其他所有的服务,其他所有的服务都需要把自己注册到service manager的服务列表中; 47 | + `BnServiceManager : BnInterface`,是IServiceManager的实现者; 48 | + `BpServiceManager : BpInterface`,是IServiceManager的代理; 49 | + `ServiceManager.java`,是Java层对ServiceManager的定义,它通过`ServiceManagerNative.asInterface(BinderInternal.getContextObject())`获得`IServiceManager`实例,而该方法则是通过查询native层service manager的服务列表获取服务实例; 50 | + IMediaPlayerService: IInterface,定义了MediaPlayerService的API,包括远程调用,属性访问; 51 | + `BnMediaPlayerService: BnInterface`,是IMediaPlayerService的实现者; 52 | + `BpMediaPlayerService: BpInterface`,是IMediaPlayerService的代理; 53 | 54 | + IXXX是接口的定义,实现者是BnXXX,使用者通过代理BpXXX和接口(服务)打交道; 55 | + Bp ==> Binder proxy; 56 | + BpServiceManager可以理解为IServiceManager的代理,对用户透明,是仅供内部使用的API; 57 | + Bn ==> Binder native; 58 | + Bn和Bp相对,Bp是代理,则Bn就是和代理通信的另一端(实现端),Bp通过和Bn通信,代理IXXX向使用者提供服务; 59 | + addService的IPC过程 60 | + 调用的是`remote()->transact(...)`函数,而remote返回的实际上就是初始化ServiceManager时创建的BpBinder; 61 | + ==> `IPCThreadState::self()->transact(...)`,在其中通过binder设备发送命令和数据,同时读取返回的命令和数据; 62 | + 自行实现一整套service 63 | + 如果是纯native的service,没有应用层入口,则需要和MediaPlayerService一样,实现一个main函数,并在其他同类service的启动处启动自定义的service; 64 | + 定义service接口IXXX : IInterface,定义需要的API; 65 | + 定义BnXXX和BpXXX,完成service的实现和代理; 66 | + [浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路](http://blog.csdn.net/luoshengyang/article/details/6621566) 67 | + binder机制为service manager预留了通道,使其可以率先把自己注册为binder守护进程 68 | + binder IPC机制client和server通信只需要一次数据拷贝:client从用户空间拷贝到内核空间;server和内核空间(binder驱动)共享数据 69 | + 通过ioctl机制通知binder驱动自己成为守护进程之后,service manager进程将进入binder_loop,循环等待client的请求到来 70 | + [浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路](http://blog.csdn.net/luoshengyang/article/details/6627260) 71 | + `sp defaultServiceManager();`这一方法实现了单例模式,单例保存在`gDefaultServiceManager`中 72 | + 创建单例:`gDefaultServiceManager = interface_cast(ProcessState::self()->getContextObject(NULL));` 73 | + 简化为:`gDefaultServiceManager = new BpServiceManager(new BpBinder(0));` 74 | 75 | ## [Android启动运行在独立进程的Service](http://blog.csdn.net/luoshengyang/article/details/6677029) 76 | + 跨进程,当然就会使用binder机制 77 | + 包括三次跨进程通信 78 | + 从主进程(调用startService的进程)调用到ActivityManagerService进程中,完成新进程的创建,新进程开始执行 79 | + 从新进程调用到ActivityManagerService进程中,新进程告知ActivityManagerService自己已经准备就绪 80 | + 从ActivityManagerService进程又回到新进程中,传递了相关信息,最终将服务启动起来 81 | 82 | ## [Android应用程序的Activity启动过程](http://blog.csdn.net/luoshengyang/article/details/6685853) 83 | + step2: Activity.startActivity => 84 | + step3: Activity.startActivityForResult 85 | + step4: Instrumentation.execStartActivity => 86 | + step5: ActivityManagerProxy.startActivity == binder ==> 87 | + step6: ActivityManagerService.startActivity => 88 | + step6: ActivityManagerService.startActivityAsUser => 89 | + step7: ActivityStackSupervisor.startActivityMayWait => 90 | + step8: ActivityStackSupervisor.startActivityLocked => 91 | + step9: ActivityStackSupervisor.startActivityUncheckedLocked => 92 | + step10: ActivityStack.resumeTopActivityLocked => (此函数有两次调用,不同参数执行路径不同) 93 | + step11: ActivityStack.startPausingLocked => 94 | + step12: ApplicationThreadProxy.schedulePauseActivity == binder ==> 95 | + step13: ActivityThread$ApplicationThread.schedulePauseActivity => 96 | + step14: ActivityThread.queueOrSendMessage => (PAUSE_ACTIVITY) 97 | + step15: ActivityThread$H.handleMessage => 98 | + step16: ActivityThread.handlePauseActivity => (在此处调用前一activity的onPause回调) 99 | + step17: ActivityManagerProxy.activityPaused == binder ==> 100 | + step18: ActivityManagerService.activityPaused => 101 | + step19: ActivityStack.activityPaused => 102 | + step20: ActivityStack.completePauseLocked => 103 | + step21: ActivityStack.resumeTopActivityLocked => 104 | + step22: ActivityStack.startSpecificActivityLocked => (如果对应进程不存在,则创建进程) 105 | + step23: ActivityManagerService.startProcessLocked => 106 | + step24: ActivityThread.main => 107 | + step25: ActivityManagerProxy.attachApplication == binder ==> 108 | + step26: ActivityManagerService.attachApplication => 109 | + step27: ActivityManagerService.attachApplicationLocked => 110 | + step28: ActivityStack.realStartActivityLocked => 111 | + step29: ApplicationThreadProxy.scheduleLaunchActivity == binder ==> 112 | + step30: ApplicationThread.scheduleLaunchActivity => 113 | + step31: ActivityThread.queueOrSendMessage => (LAUNCH_ACTIVITY) 114 | + step32: ActivityThread$H.handleMessage => 115 | + step33: ActivityThread.handleLaunchActivity => 116 | + step34: ActivityThread.performLaunchActivity => (此处通过反射创建新activity对象,然后通过mInstrumentation调用新activity的onCreate/onResume等回调) 117 | 118 | + Step1 - Step 11:Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity; 119 | + Step 12 - Step 16:ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态; 120 | + Step 17 - Step 24:Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例(**how?**),即将要启动的Activity就是在这个ActivityThread实例中运行; 121 | + Step 25 - Step 27:ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信; 122 | + Step 28 - Step 35:ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。 123 | -------------------------------------------------------------------------------- /Frontend/ES6.md: -------------------------------------------------------------------------------- 1 | # [ECMAScript 6](http://www.infoq.com/cn/es6-in-depth/) 2 | 3 | ## 迭代器和for-of循环 4 | + 不要用for-in循环遍历数组,它是用于遍历普通对象的各个属性的key的; 5 | + for-of循环可以用来遍历数组,没有for-in的缺陷,也没有forEach的缺陷:无法break, continue, return; 6 | + for-in循环用来遍历对象属性,for-of循环用来遍历数据:例如数组中的值; 7 | + for-of还支持其他集合的遍历(`Map`, `Set`),也能用于字符串遍历(视其为Unicode字符数组); 8 | 9 | ```javascript 10 | for (var value of myArray) { 11 | console.log(value); 12 | } 13 | 14 | var uniqueWords = new Set(words); 15 | for (var word of uniqueWords) { 16 | console.log(word); 17 | } 18 | 19 | for (var [key, value] of phoneBookMap) { 20 | console.log(key + "'s phone number is: " + value); 21 | } 22 | ``` 23 | 24 | + for-of循环语句通过方法调用(迭代器方法)来遍历各种集合 25 | + 迭代器对象 26 | + 拥有`[Symbol.iterator]()`的对象被称为可迭代的 27 | + 迭代器对象可以是任意具有`.next()`方法的对象 28 | + for-of循环将重复调用这个方法,每次循环调用一次 29 | 30 | ```javascript 31 | var zeroesForeverIterator = { 32 | [Symbol.iterator]: function () { 33 | return this; 34 | }, 35 | next: function () { 36 | return {done: false, value: 0}; 37 | } 38 | }; 39 | ``` 40 | 41 | ## 生成器 Generators 42 | ```javascript 43 | function* quips(name) { 44 | yield "你好 " + name + "!"; 45 | yield "希望你能喜欢这篇介绍ES6的译文"; 46 | if (name.startsWith("X")) { 47 | yield "你的名字 " + name + " 首字母是X,这很酷!"; 48 | } 49 | yield "我们下次再见!"; 50 | } 51 | ``` 52 | + 普通函数使用`function`声明,而生成器函数使用`function*`声明。 53 | + 在生成器函数内部,有一种类似`return`的语法:关键字`yield`。二者的区别是,普通函数只可以`return`一次,而生成器函数可以`yield`多次(当然也可以只`yield`一次)。在生成器的执行过程中,遇到`yield`表达式立即暂停,后续可恢复执行状态。 54 | + 这就是普通函数和生成器函数之间最大的区别,普通函数不能自暂停,生成器函数可以。 55 | + 执行效果: 56 | 57 | ```javascript 58 | > var iter = quips("jorendorff"); 59 | [object Generator] 60 | > iter.next() 61 | { value: "你好 jorendorff!", done: false } 62 | > iter.next() 63 | { value: "希望你能喜欢这篇介绍ES6的译文", done: false } 64 | > iter.next() 65 | { value: "我们下次再见!", done: false } 66 | > iter.next() 67 | { value: undefined, done: true } 68 | ``` 69 | + 调用一个生成器时,它并非立即执行,而是返回一个已暂停的生成器对象,以后对其每调用一次`.next()`方法,函数调用将其自身解冻并一直运行到下一个`yield`表达式,再次暂停。 70 | + 调用最后一个`iter.next()`时,我们最终抵达生成器函数的末尾,所以返回结果中`done`的值为`true`。 71 | + 每当生成器执行`yield`语句,生成器的堆栈结构(本地变量、参数、临时值、生成器内部当前的执行位置)被移出堆栈。然而,生成器对象保留了对这个堆栈结构的引用(备份),所以稍后调用`.next()`可以重新激活堆栈结构并且继续执行。 72 | + 值得特别一提的是,生成器不是线程,当生成器运行时,它和调用者处于同一线程中,拥有确定的连续执行顺序,永不并发。 73 | + 与系统线程不同的是,生成器只有在其函数体内标记为`yield`的点才会暂停。 74 | + 生成器的作用 75 | + 生成器是迭代器:所有的生成器都有内建`.next()`和`[Symbol.iterator]()`方法的实现。你只须编写循环部分的行为。 76 | + 使任意对象可迭代。编写生成器函数遍历这个对象,运行时`yield`每一个值。然后将这个生成器函数作为这个对象的`[Symbol.iterator]`方法。 77 | + 简化数组构建函数。 78 | + 获取异常尺寸的结果。例如无限长的数组。 79 | + 重构复杂循环。 80 | + 构建与迭代相关的工具。 81 | + lazy计算。仅当需要时进行计算。 82 | + `.next`可选参数 83 | + `.return` 84 | + `.throw` 85 | + `yield*` 86 | 87 | ## 模板字符串 88 | ```javascript 89 | function authorize(user, action) { 90 | if (!user.hasPrivilege(action)) { 91 | throw new Error( 92 | `用户 ${user.name} 未被授权执行 ${action} 操作。`); 93 | } 94 | } 95 | ``` 96 | + 反撇号(`)基础知识 97 | + 模板占位符中的代码可以是任意JavaScript表达式,所以函数调用、算数运算等这些都可以作为占位符使用,你甚至可以在一个模板字符串中嵌套另一个,我称之为模板套构(template inception)。 98 | + 如果这两个值都不是字符串,可以按照常规将其转换为字符串。例如:如果`action`是一个对象,将会调用它的`.toString()`方法将其转换为字符串值。 99 | + 如果你需要在模板字符串中书写反撇号、`$`和`{`,你必须使用反斜杠将其转义。 100 | + 模板字符串可以多行书写,模板字符串中所有的空格、新行、缩进,都会原样输出在生成的字符串中。 101 | + 反撇号的未来 102 | + 它们不会为你自动转义特殊字符 103 | + 它们无法很好地与国际化库相配合 104 | + 它们不能替代模板引擎的地位 105 | 106 | ## 不定参数和默认参数 107 | + 以前的版本中有“神奇的arguments对象”,ES6引入了与其他语言类似的不定参数语法: 108 | 109 | ```javascript 110 | function containsAll(haystack, ...needles) { 111 | for (var needle of needles) { 112 | if (haystack.indexOf(needle) === -1) { 113 | return false; 114 | } 115 | } 116 | return true; 117 | } 118 | ``` 119 | + 如果没有额外的参数,不定参数就是一个空数组,它永远不会是undefined。 120 | + 默认参数也与c++类似,但有几点需要注意 121 | + 默认值表达式在函数调用时自左向右求值,这一点与Python不同。这也意味着,默认表达式可以使用该参数之前已经填充好的其它参数值。 122 | + 传递undefined值等效于不传值,将使用定义的默认值 123 | + 没有默认值的参数隐式默认为undefined 124 | + 停止使用arguments 125 | 126 | 127 | ## 解构(Destructuring) 128 | + 数组与迭代器的解构 129 | 130 | ```javascript 131 | [ variable1, variable2, ..., variableN ] = array; 132 | ``` 133 | 134 | + 可以对任意深度的嵌套数组进行解构 135 | + 可以在对应位留空来跳过被解构数组中的某些元素 136 | 137 | ```javascript 138 | var [,,third] = ["foo", "bar", "baz"]; 139 | ``` 140 | + 还可以通过“不定参数”模式捕获数组中的所有尾随元素 141 | + 当访问空数组或越界访问数组时,对其解构与对其索引的行为一致,最终得到的结果都是:undefined 142 | + 数组解构赋值的模式同样适用于任意迭代器 143 | 144 | ```javascript 145 | function* fibs() { 146 | var a = 0; 147 | var b = 1; 148 | while (true) { 149 | yield a; 150 | [a, b] = [b, a + b]; 151 | } 152 | } 153 | var [first, second, third, fourth, fifth, sixth] = fibs(); 154 | console.log(sixth); 155 | // 5 156 | ``` 157 | 158 | + 对象的解构 159 | 160 | ```javascript 161 | var robotA = { name: "Bender" }; 162 | var { name: nameA } = robotA; 163 | console.log(nameA); 164 | // "Bender" 165 | 166 | var { foo, bar } = { foo: "lorem", bar: "ipsum" }; 167 | console.log(foo); 168 | // "lorem" 169 | 170 | var { missing } = {}; 171 | console.log(missing); 172 | // undefined 173 | ``` 174 | 175 | + 首先指定被绑定的属性,然后紧跟一个要解构的变量。 176 | + 当属性名与变量名一致时,可以通过一种实用的句法简写。 177 | + 可以随意嵌套并进一步组合对象解构 178 | + 解构一个未定义的属性时,得到的值为undefined 179 | 180 | + 解构值不是对象、数组或迭代器 181 | 182 | ```javascript 183 | var {blowUp} = null; 184 | // TypeError: null has no properties(null没有属性) 185 | 186 | var {wtf} = NaN; 187 | console.log(wtf); 188 | // undefined 189 | ``` 190 | 191 | + 当你尝试解构null或undefined时,你会得到一个类型错误 192 | + 可以解构其它原始类型,例如:布尔值、数值、字符串,但是你将得到undefined 193 | 194 | + 当你要解构的属性未定义时你可以提供一个默认值: 195 | 196 | ```javascript 197 | var [missing = true] = []; 198 | console.log(missing); 199 | // true 200 | ``` 201 | 202 | + 解构的实际应用 203 | + 函数参数定义:函数接收一个对象,将不同的实际参数作为对象属性,以避免让API使用者记住多个参数的使用顺序。 204 | + 配置对象参数:通过默认值实现 205 | + 与ES6迭代器协议协同使用:`for (var [key, value] of map) { //... }` 206 | + 多重返回值 207 | 208 | ## 箭头函数(Arrow Functions) 209 | 符号 | 含义 210 | --- | --- 211 | `` | “趋向于”操作符 213 | `<=` | 小于等于 214 | `=>` | 这又是什么? 215 | 216 | + 箭头函数:`=>`,用于lambda语法 217 | + 使用了块语句的箭头函数不会自动返回值,你需要使用return语句将所需值返回。 218 | + 当使用箭头函数创建普通对象时,你总是需要将对象包裹在小括号里。 219 | + `puppy => {}`会被解析为没有任何行为并返回undefined的箭头函数。 220 | + 箭头函数没有它自己的`this`值 221 | + 通过`object.method()`语法调用的方法使用非箭头函数定义,这些函数需要从调用者的作用域中获取一个有意义的`this`值。 222 | + 其它情况全都使用箭头函数。 223 | + 箭头函数不会获取它们自己的arguments对象 224 | + ES6的方法语法 225 | 226 | ```javascript 227 | // ES6 228 | { 229 | ... 230 | addAll: function addAll(pieces) { 231 | _.each(pieces, piece => this.add(piece)); 232 | }, 233 | ... 234 | } 235 | 236 | // ===>>> 237 | 238 | // ES6的方法语法 239 | { 240 | ... 241 | addAll(pieces) { 242 | _.each(pieces, piece => this.add(piece)); 243 | }, 244 | ... 245 | } 246 | ``` 247 | 248 | ## Symbols 249 | 250 | ```javascript 251 | // 创建一个独一无二的symbol 252 | var isMoving = Symbol("isMoving"); 253 | ... 254 | if (!element[isMoving]) { 255 | smoothAnimations(element); 256 | } 257 | element[isMoving] = true; 258 | ``` 259 | 260 | + `Symbol`是JavaScript的第七种原始类型:Undefined 未定义,Null 空值,Boolean 布尔类型,Number 数字类型,String 字符串类型,Object 对象类型 261 | + 以symbol为键的属性属性与数组元素类似,不能被类似obj.name的点号法访问,你必须使用方括号访问这些属性。 262 | + 只有当isMoving在当前作用域中时才会生效 263 | + JavaScript中最常见的对象检查的特性会忽略symbol键,例如:for-in循环,`Object.keys(obj)`,`Object.getOwnPropertyNames(obj)` 264 | + `Object.getOwnPropertySymbols(obj)`可以列出对象的symbol键; 265 | + `Reflect.ownKeys(obj)`,会同时返回字符串键和symbol键 266 | + symbol被创建后就不可变更,你不能为它设置属性(在严格模式下尝试设置属性会得到TypeError的错误)。他们可以用作属性名称,这些性质与字符串类似。 267 | + 每一个symbol都独一无二,不与其它symbol等同,即使二者有相同的描述也不相等 268 | + symbol不能被自动转换为字符串,这和语言中的其它类型不同。尝试拼接symbol与字符串将得到TypeError错误。通过String(sym)或sym.toString()可以显示地将symbol转换为一个字符串。 269 | + 获取symbol的三种方法 270 | + 调用`Symbol()` 271 | + 调用`Symbol.for(string)`,如果同一个描述的symbol已经存在,将返回已存在的symbol对象 272 | + 使用标准定义的symbol,例如:`Symbol.iterator`。标准根据一些特殊用途定义了少许的几个symbol。 273 | 274 | ## 集合 275 | + 已经有了一种类似哈希表的东西:对象(Object)。 276 | + 作为查询表使用的对象,不能既支持方法又保证避免冲突。 277 | + 因而,要么得用`Object.create(null)`而非直接写`{}`,要么得小心地避免把`Object.prototype.toString`之类的内置方法名作为键名来存储数据。 278 | + 对象的键名总是字符串(当然,ES6 中也可以是`Symbol`)而不能是另一个对象。 279 | + 没有有效的获知属性个数的方法。 280 | + 纯粹的对象不可遍历,也就是,它们不能配合`for-of`循环或`...`操作符等语法。 281 | + 集合数据的访问,不能再通过属性方式了,只能通过暴露出来的接口(`get`等)。 282 | + `Set`, `Map`, `WeakMap`, `WeakSet` 283 | 284 | ## 代理(Proxy) 285 | + 对象的14种内部方法。 286 | + `var proxy = new Proxy(target, handler);` 287 | + 代理的行为很简单:将代理的所有内部方法转发至目标。简单来说,如果调用`proxy.[[Enumerate]]()`,就会返回`target.[[Enumerate]]()`。 288 | + 句柄对象的方法可以覆写任意代理的内部方法。 289 | 290 | ## 类(Class),继承 291 | ... 292 | + 实例属性,方法? 293 | + 静态属性,方法? 294 | + `prototype`? 295 | 296 | ## `let`和`const` 297 | + ES6之前,只有两种作用域:全局,函数,没有代码块作用域 298 | + `var`是函数作用域 299 | + 变量提升(hoisting) 300 | + ES6引入了新的作用域:代码块作用域;`let`和`const`都是这一作用域; 301 | 302 | ## 模块 303 | + export: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export 304 | + import: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import 305 | + module: https://hacks.mozilla.org/2015/08/es6-in-depth-modules/, 译文:http://www.infoq.com/cn/articles/es6-in-depth-modules 306 | + 另一篇博文:http://wengeezhang.com/?p=241 307 | + import, export, default 308 | + import as, import _, import * 309 | + export as, export * 310 | -------------------------------------------------------------------------------- /Android-Java/AndroidTDD.md: -------------------------------------------------------------------------------- 1 | # 安卓测试驱动开发/安卓测试验证 2 | 安卓测试主要包括两种:单元测试;集成测试;测试主要通过mock依赖,验证行为。 3 | Mock框架、单元测试框架、集成测试框架是TDD所需的主要工具。 4 | 5 | ## Mock框架:[Mockito](http://mockito.org/) 6 | + stubbed的方法没有必要verify 7 | + 当被测代码对返回值不关心时,不要stub 8 | + mock对象的方法默认将返回空值(null,空集合,默认基本类型值) 9 | + stub可以被重载,但当出现这种需求时,就说明stub已经太多了,需要改进设计 10 | + stub的顺序有影响,但不应依赖其顺序的影响 11 | + 默认使用Java原生equals进行判断,也支持ArgumentMatcher,有内置, 12 | 也支持自定义/Hamcrest,但最后者不建议使用(影响可读性) 13 | + 一旦使用了ArgumentMatcher,则所有的参数都要使用ArgumentMatcher 14 | + `times(int)`,`never()`,`atLeastOnce()`,`atLeast(int)`, 15 | `atMost(int)`验证调用次数,verify默认的是`times(1)`,因此无需显式指定 16 | + 验证无返回值函数抛出异常:`doThrow(new RuntimeException()).when(mockedList).clear();` 17 | + `InOrder`可以验证调用的顺序(不同语句),原则上只需要验证关键逻辑,没必要 18 | 所有调用都需要验证、甚至其顺序 19 | + `only()`的语义:仅有该方法被调用,且仅被调用一次 20 | + `never()`的语义:该方法未被调用过 21 | + `verifyZeroInteractions(...)`的语义:mock对象没有任何方法调用 22 | + `verifyNoMoreInteractions(...)`:验证没有其他的调用(除了此前验证的方法) 23 | + 对多次调用进行stub,最后一次将一直有效 24 | + doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() family of methods 25 | + spy,对真实对象进行spy,部分mock。但是spy对象的操作和原有真实对象的操作是独立的 26 | + Capturing arguments for further assertions,在verify中可以捕获调用的参数,后续进行验证 27 | + reset mock,不建议使用 28 | + BDD,given, when, then 29 | + mock对象可以序列化 30 | + Verification with timeout 31 | + (new) Better generic support with deep stubs (Since 1.10.0) 32 | + [How to mock dependencies in Unit, Integration and Functional tests; 33 | Dagger, Robolectric and Instrumentation](http://artemzin.com/blog/ 34 | how-to-mock-dependencies-in-unit-integration-and-functional-tests- 35 | dagger-robolectric-instrumentation/) 36 | + [ 37 | Testing made sweet with a Mockito](https://speakerdeck.com/jeroenmols/testing-made-sweet-with-a-mockito) 38 | 39 | ## Assertion框架 40 | + [Google出品的Truth](https://github.com/google/truth), 41 | 及其[使用介绍blog](http://jeremie-martinez.com/2015/11/05/truth-android/) 42 | 43 | ## 单元测试:[The Square Way](http://www.philosophicalhacker.com/2015/04/10/against-android-unit-tests/) 44 | + 单元测试是方法级别的测试,需要测试的是一个类的公开接口/方法,测试其逻辑正确性, 45 | 如果发现一个类的某个方法所使用的依赖难以mock,it's a smell,重构吧 46 | + 基础理论部分 47 | + The three steps of a unit test: arrange, act, and assert 48 | + check the return value of the method 49 | + get a reference to some publicly accessible property of the object being tested 50 | + check the state of the object’s injected dependencies 51 | + 所以编码时,应该尽量将代码逻辑的依赖通过注入的方式(或提供public setter, 52 | 或将被测函数的依赖作为参数传入)提供,以便于测试 53 | + post-act-state的验证要注意单元测试与集成测试的区别:单元测试只验证 54 | 一个方法的逻辑正确性,不验证多个方法(模块)一起工作的逻辑正确性; 55 | + static方法对测试非常不利,因为其对象依赖、方法依赖无法mock, 56 | 而其post-act-state也通常无法assert,开发过程中应尽量避免 57 | + 实践部分 58 | 架构图 59 | ![androidstack-02.png](../assets/androidstack-02.png) 60 | Remove all business logic from app component classes (e.g., 61 | Activitys, Fragments, Services) and place that logic into 62 | "business objects", POJO objects whose dependencies are injected, 63 | android-specific implementations of android-agnostic interfaces. 64 | Delegate all application specific behavior to POJO objects 65 | whose dependencies are Android-specific implementations of 66 | Android-agnostic interfaces. 67 | + Non-UI App Components 68 | + 把业务逻辑(比如根据数据类型执行不同操作、检查数据合法性等)从组件类中 69 | 抽离出来,业务逻辑类对于组件类的依赖也不要直接使用,而是通过定义接口来 70 | 进行转发调用,这样就能使得业务逻辑类与SDK解耦。 71 | + 业务逻辑类的依赖通过依赖注入框架注入,便于测试时mock。 72 | + 而剩下的组件类工作简单,只负责转发业务逻辑类的功能请求,就没必要进行测试了。 73 | + UI App Component Classes 74 | + 通过MVP模式,将UI组件类和业务逻辑类解耦,同时移除对SDK组件类的依赖; 75 | + pre-act-state,post-act-state,测试对象的依赖中,如果mock框架 76 | (如Mockito)可以mock,OK;如果不能mock(例如Activity,BroadcastReceiver) 77 | ,则可以通过定义接口的形式替换这些依赖,而接口的实现则是简单直接的转发,无需测试; 78 | pre-act-state便可以设置完毕,调用被测函数后,验证post-act-state即可。 79 | + Dependency Inject 80 | + 可以使用Constructor inject的类就不要使用Field inject。前者更利于单元测试。 81 | 其实除了SDK组件类,其他的类基本上都可以使用Constructor inject。 82 | + 无需依赖第三方框架(Robolectric,Dagger) 83 | + 单元测试的目标 84 | + 在非安卓系统组件相关的代码,直接每个方法进行测试,很好理解 85 | + 系统组件相关的代码,主要测:试依赖于生命周期函数的逻辑;由简单UI交互引发的逻辑; 86 | 自定义接口的逻辑(如MVP中的View); 87 | + 与其他框架/工具的整合 88 | + RxJava 89 | + Rx为异步而生,但测试往往需要同步进行验证 90 | + 可通过`myObservable.toBlocking().first();`进行同步 91 | + 也可通过官方的`TestSubscriber`,该类提供了众多实用的方法,例如同步获取 92 | Observable发射的所有对象,断言没有onError,等待onComplete等 93 | + 处理生产代码中的异步问题,可以在测试时hook `Schedulers.io()`为 94 | `Schedulers.immediate()`,但在hook之前需要reset RxJavaPlugins, 95 | 在RxJava中这个方法是package private,所以做到这点有点hack,在RxAndroid中则 96 | 无需如此麻烦,因为或者RxAndroid提供了很好的hook支持。 97 | 98 | ```java 99 | package rx.plugins; 100 | 101 | public class RxJavaTestPlugins extends RxJavaPlugins { 102 | RxJavaTestPlugins() { 103 | super(); 104 | } 105 | 106 | public static void resetPlugins(){ 107 | getInstance().reset(); 108 | } 109 | } 110 | 111 | ... 112 | RxJavaTestPlugins.resetPlugins(); 113 | RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() { 114 | @Override 115 | public Scheduler getIOScheduler() { 116 | return Schedulers.immediate(); 117 | } 118 | }); 119 | ``` 120 | 另外有一点需要指出的是,要想RxJava的hook起作用,必须要在`Schedulers` 121 | 类初始化之前进行hook,那么在测试的时候,只能通过实现自定义的TestRunner来做到了 122 | ,在TestRunner的构造函数中进行reset和hook就OK了。而RxAndroid的hook则没这 123 | 么麻烦,在测例的setUp函数中进行就OK。 124 | + RxJava还能设置`ObservableExecutionHook`,示例: 125 | `RxJavaPlugins.getInstance().registerObservableExecutionHook(new DebugHook(new DebugNotificationListener() {...}` 126 | + 网络库:Retrofit,OkHttp 127 | + Retrofit提供了retrofit-mock模块,用于测试retrofit,详情可见 128 | [retrofit-mock测例](https://github.com/square/retrofit/blob/master/ 129 | retrofit-mock%2Fsrc%2Ftest%2Fjava%2Fretrofit%2FMockRetrofitTest.java)。 130 | + 对于2.0中的adapter-rxjava,retrofit也提供了adapter-rxjava-mock模块, 131 | 详情可见[adapter-rxjava-mock测例](https://github.com/square/retrofit/ 132 | blob/master/retrofit-adapters%2Frxjava-mock%2Fsrc%2Ftest%2Fjava%2 133 | Fretrofit%2Fmock%2FRxJavaBehaviorAdapterTest.java)。 134 | + 另外,OkHttp也提供了MockWebServer,也可以利用起来,详见 135 | [converter-gson测例](https://github.com/square/retrofit/blob/master/ 136 | retrofit-converters%2Fgson%2Fsrc%2Ftest%2Fjava%2Fretrofit%2FGsonConverterFactoryTest.java)。 137 | 138 | ## 集成测试 139 | + 集成测试的级别是什么?wiki定义为:把各个经过单元测试的模块组合起来,作为一个 140 | 整体进行测试,是验证测试的前一步。 141 | + 那么需要组合多少个unit呢?这个应该视具体情况和实际操作来决定,目前来看,对于 142 | 安卓开发来说,以Activity作为集成测试的单位比较合适,但是也不尽然,例如需要测试不 143 | 同Activity之间的状态同步时,Activity1进入Activity2,并在其中进行了操作, 144 | 改变了应用的状态,回到Activity1也希望能够响应该变化,那么测试范围就涉及多个Activity了。 145 | + 测试框架 146 | + Espresso 147 | + ViewMatchers/ViewActions/ViewAssertions 148 | + 同步问题:自动处理UI Event/AsyncTask。当使用Retrofit时,可以为 149 | 测试代码生成测试用的RestAdapter,指定Excutor为AsyncTask: 150 | 151 | ```java 152 | new RestAdapter.Builder().setExecutors(AsyncTask.THREAD_POOL_EXECUTOR, 153 | new MainThreadExecutor()) 154 | .build(); 155 | ``` 156 | 157 | + idle resources 158 | + https://github.com/JakeWharton/okhttp-idling-resource 159 | + https://github.com/AzimoLabs/ConditionWatcher 160 | + 测试Save and restore state,触发代码: 161 | 162 | ```java 163 | private void rotateScreen() { 164 | Context context = InstrumentationRegistry.getTargetContext(); 165 | int orientation 166 | = context.getResources().getConfiguration().orientation; 167 | 168 | Activity activity = activityRule.getActivity(); 169 | activity.setRequestedOrientation( 170 | (orientation == Configuration.ORIENTATION_PORTRAIT) ? 171 | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : 172 | ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 173 | } 174 | ``` 175 | 176 | + [Espresso测试RecyclerView](http://blog.egorand.me/testing-a- 177 | sorted-list-with-espresso/),[recycler view的espresso view 178 | matcher](https://github.com/dannyroa/espresso-samples)及 179 | [stackoverflow问题](http://stackoverflow.com/questions/34138945/ 180 | espresso-matcher-for-nested-recyclerview) 181 | 182 | + Retrofit 183 | + 不要mock所有的对象,在集成测试阶段,直接mock service 返回的数据即可,https://github.com/andrzejchm/RESTMock 184 | 185 | + UIAutomator 186 | + https://medium.com/@hitherejoe/handling-android-runtime-permissions-in-ui-tests-981f9dc11a4e 187 | + 和一些其他框架的整合 188 | + Dagger2 189 | + 生产代码的依赖都是通过依赖注入框架注入,本应是有利于测试的,但是如何通过依 190 | 赖注入框架把mock的依赖注入进去呢? 191 | + 总的来说,都是通过使得main和test使用不同的module,test的module provide 192 | mock的依赖,main的module provide真实的依赖 193 | + 思路1:利用flavor/build variant,专门创建一个用于注入测试依赖的variant, 194 | 其中维护测试依赖的component;不用打破生产代码的封装特性;但是增加了维护成本, 195 | 有两套component需要维护,而且这两套component必须切换AS的build variant才能切换,不能同时维护; 196 | + 思路2:修改生产代码的component创建/获取途径,使得测试时可以设置测试用的 197 | component(能够注入测试依赖);后期不用切换AS的build variant就能进行重构; 198 | 一定程度上打破了封装性; 199 | + 思路3:集成测试代码使用测试专用的Application类(生产代码app的子类), 200 | 自定义test runner,使得测试运行时使用测试app类,在其中初始化dagger component,完美。 201 | + http://blog.sqisland.com/2015/12/mock-application-in-espresso.html 202 | + http://blog.egorand.me/reliable-functional-tests-with-espresso-and-dagger/ 203 | + [DaggerMock](https://github.com/fabioCollini/DaggerMock),将mockito和dagger结合起来 204 | + StorIO 205 | 206 | ## 回归测试 207 | + 验证功能无回退 208 | + 对于视觉检查来说,[screenshot-tests-for-android](https://github.com/facebook/screenshot-tests-for-android)似乎不错 209 | + 首先需要确认,当前版本是正确的(通过了单元测试与集成测试) 210 | + 在此基础上,对渲染数据的静态场景,生成截屏 211 | + 以后在相同场景下(测例),再次截屏,并进行对比 212 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BuildingAppsWithContentSharing.md: -------------------------------------------------------------------------------- 1 | #Building Apps with Content Sharing 2 | + Sharing Simple Data 3 | + Intent && ActionProvider 4 | + 发送数据(发起intent调起其他app处理) 5 | + Send Text Content 6 | + Send Binary Content 7 | + Send Multiple Pieces of Content 8 | + 接收数据 9 | + AndroidManifest.xml中为Activity定义`` 10 | + 在Activity的onCreate中调用`getIntent()`获取action、数据,并进行处理 11 | + Sharing Files 12 | + 唯一“安全”的方式就是:将文件对应的URI通过Intent发送出去,并为该URI提供临时的访问权限。而这些步骤都可以通过`FileProvider`完成 13 | + 在AndroidManifest.xml中声明provider 14 | ```xml 15 | 17 | 19 | 24 | 27 | 28 | ... 29 | 30 | 31 | ``` 32 | + ``指定描述要分享的目录的xml文件 33 | + 指定要分享的目录 34 | ```xml 35 | 36 | 37 | 38 | ``` 39 | + ``标签可以有多个子标签,``指定app的files目录下的分享目录名,``指定外部存储(`Environment.getExternalStorageDirectory()`)的分享目录名,``指定app的cache目录下的分享目录名;分享路径只能在xml中描述; 40 | + 如上配置后,需要访问files/images/default_image.jpg时,对应uri为:`content://com.example.myapp.fileprovider/myimages/default_image.jpg` 41 | + Receive File Requests 42 | + 定义一个Selection Activity,响应Intent action,例如:`ACTION_PICK` 43 | ```xml 44 | 47 | 48 | 50 | 52 | 54 | 55 | 56 | 57 | 58 | ``` 59 | + 其他app发起该intent,通过`startActivityForResult()`和`onActivityResult()`中发起请求、处理结果 60 | + 在Selection Activity的onCreate函数中,解析其他app的请求 61 | ```java 62 | File requestFile = new File(mImageFilename[position]); 63 | // Use the FileProvider to get a content URI 64 | try { 65 | fileUri = FileProvider.getUriForFile( 66 | MainActivity.this, 67 | "com.example.myapp.fileprovider", 68 | requestFile); 69 | } catch (IllegalArgumentException e) { 70 | Log.e("File Selector", 71 | "The selected file can't be shared: " + 72 | clickedFilename); 73 | } 74 | ``` 75 | + 赋予临时访问权限 76 | ```java 77 | mResultIntent = new Intent("com.example.myapp.ACTION_RETURN_FILE"); 78 | if (fileUri != null) { 79 | // Grant temporary read permission to the content URI 80 | mResultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 81 | } 82 | ``` 83 | + 使用`setFlags()`赋予临时访问权限更安全,`Context.grantUriPermission()`赋予的权限只有手动调用`Context.revokeUriPermission()`才会被移除 84 | + 返回结果 85 | ```java 86 | mResultIntent.setDataAndType( 87 | fileUri, 88 | getContentResolver().getType(fileUri)); 89 | // Set the result 90 | MainActivity.this.setResult(Activity.RESULT_OK, 91 | mResultIntent); 92 | finish(); 93 | ``` 94 | + Requesting a Shared File 95 | + 通常流程是:app发起一个带有请求的intent,分享文件的app的相应Activity被启动,该Activity显示文件列表,用户选择文件后返回被选中的文件的Uri 96 | + 发起请求 97 | ```java 98 | mRequestFileIntent = new Intent(Intent.ACTION_PICK); 99 | mRequestFileIntent.setType("image/jpg"); 100 | startActivityForResult(mRequestFileIntent, 0); 101 | ``` 102 | + 访问返回的文件 103 | ```java 104 | @Override 105 | public void onActivityResult(int requestCode, int resultCode, 106 | Intent returnIntent) { 107 | // If the selection didn't work 108 | if (resultCode != RESULT_OK) { 109 | // Exit without doing anything else 110 | return; 111 | } else { 112 | // Get the file's content URI from the incoming Intent 113 | Uri returnUri = returnIntent.getData(); 114 | /* 115 | * Try to open the file for "read" access using the 116 | * returned URI. If the file isn't found, write to the 117 | * error log and return. 118 | */ 119 | try { 120 | /* 121 | * Get the content resolver instance for this context, and use it 122 | * to get a ParcelFileDescriptor for the file. 123 | */ 124 | mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r"); 125 | } catch (FileNotFoundException e) { 126 | e.printStackTrace(); 127 | Log.e("MainActivity", "File not found."); 128 | return; 129 | } 130 | // Get a regular file descriptor for the file 131 | FileDescriptor fd = mInputPFD.getFileDescriptor(); 132 | ... 133 | } 134 | } 135 | ``` 136 | + Retrieving File Information 137 | + MIME Type 138 | ```java 139 | Uri returnUri = returnIntent.getData(); 140 | String mimeType = getContentResolver().getType(returnUri); 141 | ``` 142 | + File's Name and Size 143 | ```java 144 | Uri returnUri = returnIntent.getData(); 145 | Cursor returnCursor = 146 | getContentResolver().query(returnUri, null, null, null, null); 147 | int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); 148 | int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); 149 | nameView.setText(returnCursor.getString(nameIndex)); 150 | sizeView.setText(Long.toString(returnCursor.getLong(sizeIndex))); 151 | ``` 152 | + Sharing Files with NFC 153 | + Android Beam,大文件传输,from 4.1 API 16 154 | + Android Beam NDEF,小数据传输,from 4.0 API 14 155 | + 发送文件 156 | + 权限:``,`` 157 | + NFC feature:`` 158 | + minSdkVersion >= 16 159 | + 检查是否支持 160 | ```java 161 | if (PackageManager.hasSystemFeature(PackageManager.FEATURE_NFC) && 162 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 163 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 164 | } 165 | ``` 166 | + 创建提供文件的回调 167 | ```java 168 | // List of URIs to provide to Android Beam 169 | private Uri[] mFileUris = new Uri[10]; 170 | ... 171 | /** 172 | * Callback that Android Beam file transfer calls to get 173 | * files to share 174 | */ 175 | private class FileUriCallback implements 176 | NfcAdapter.CreateBeamUrisCallback { 177 | public FileUriCallback() { 178 | } 179 | /** 180 | * Create content URIs as needed to share with another device 181 | */ 182 | @Override 183 | public Uri[] createBeamUris(NfcEvent event) { 184 | return mFileUris; 185 | } 186 | } 187 | 188 | ... 189 | // Android Beam file transfer is available, continue 190 | ... 191 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 192 | /* 193 | * Instantiate a new FileUriCallback to handle requests for 194 | * URIs 195 | */ 196 | mFileUriCallback = new FileUriCallback(); 197 | // Set the dynamic callback for URI requests. 198 | mNfcAdapter.setBeamPushUrisCallback(mFileUriCallback,this); 199 | ... 200 | ``` 201 | + 设置需要发送的文件 202 | ```java 203 | /* 204 | * Create a list of URIs, get a File, 205 | * and set its permissions 206 | */ 207 | private Uri[] mFileUris = new Uri[10]; 208 | String transferFile = "transferimage.jpg"; 209 | File extDir = getExternalFilesDir(null); 210 | File requestFile = new File(extDir, transferFile); 211 | requestFile.setReadable(true, false); 212 | // Get a URI for the File and add it to the list of URIs 213 | fileUri = Uri.fromFile(requestFile); 214 | if (fileUri != null) { 215 | mFileUris[0] = fileUri; 216 | } else { 217 | Log.e("My Activity", "No File URI available for file."); 218 | } 219 | ``` 220 | + 接收文件 221 | + 设置intent-filter 222 | ```xml 223 | 226 | ... 227 | 228 | 229 | 230 | ... 231 | 232 | 233 | ``` 234 | + 权限:`` 235 | + 获取接收文件的路径 236 | ```java 237 | // Get the Intent action 238 | mIntent = getIntent(); 239 | String action = mIntent.getAction(); 240 | /* 241 | * For ACTION_VIEW, the Activity is being asked to display data. 242 | * Get the URI. 243 | */ 244 | if (TextUtils.equals(action, Intent.ACTION_VIEW)) { 245 | // Get the URI from the Intent 246 | Uri beamUri = mIntent.getData(); 247 | /* 248 | * Test for the type of URI, by getting its scheme value 249 | */ 250 | if (TextUtils.equals(beamUri.getScheme(), "file")) { 251 | mParentPath = handleFileUri(beamUri); 252 | } else if (TextUtils.equals( 253 | beamUri.getScheme(), "content")) { 254 | mParentPath = handleContentUri(beamUri); 255 | } 256 | } 257 | ``` 258 | + 读取 259 | ```java 260 | String fileName = beamUri.getPath(); 261 | File copiedFile = new File(fileName); 262 | ``` 263 | + 根据不同的content provider读取文件 264 | ```java 265 | ... 266 | public String handleContentUri(Uri beamUri) { 267 | // Position of the filename in the query Cursor 268 | int filenameIndex; 269 | // File object for the filename 270 | File copiedFile; 271 | // The filename stored in MediaStore 272 | String fileName; 273 | // Test the authority of the URI 274 | if (!TextUtils.equals(beamUri.getAuthority(), MediaStore.AUTHORITY)) { 275 | /* 276 | * Handle content URIs for other content providers 277 | */ 278 | // For a MediaStore content URI 279 | } else { 280 | // Get the column that contains the file name 281 | String[] projection = { MediaStore.MediaColumns.DATA }; 282 | Cursor pathCursor = 283 | getContentResolver().query(beamUri, projection, 284 | null, null, null); 285 | // Check for a valid cursor 286 | if (pathCursor != null && 287 | pathCursor.moveToFirst()) { 288 | // Get the column index in the Cursor 289 | filenameIndex = pathCursor.getColumnIndex( 290 | MediaStore.MediaColumns.DATA); 291 | // Get the full file name including path 292 | fileName = pathCursor.getString(filenameIndex); 293 | // Create a File object for the filename 294 | copiedFile = new File(fileName); 295 | // Return the parent directory of the file 296 | return new File(copiedFile.getParent()); 297 | } else { 298 | // The query didn't work; return null 299 | return null; 300 | } 301 | } 302 | } 303 | ... 304 | ``` 305 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /Android-Java/AndroidOfficialDevelopGuild-BestPractice4InteractionAndEngagement.md: -------------------------------------------------------------------------------- 1 | # Best Practices for Interaction and Engagement 2 | 3 | ## Designing Effective Navigation 4 | + Planning Screens and Their Relationships 5 | + 实体关系图(Entity-relationship diagram),数据、用户之间的关系、操作 6 | + 详尽的用例场景 7 | + 界面关系图 8 | + Planning for Multiple Touchscreen Sizes 9 | + 界面组合技术,在平板电脑、电视上,屏幕很大,可以把列表界面和详情界面左右排布一起显示 10 | + Providing Descendant and Lateral Navigation 11 | + Descendant,父界面前往子界面 12 | + Lateral,兄弟界面之间的跳转 13 | + list和section 14 | + Providing Ancestral and Temporal Navigation 15 | + Temporal(时间性),按下返回键,应该回到上一界面 16 | + Ancestral(父子性),ActionBar/ToolBar上的返回按钮,返回父界面(通常是上一界面,但并不全是,容纳WebView的Activity就是很好的例子),需要注意的是,一定要清除backstack 17 | + ... 18 | 19 | ## Implementing Effective Navigation 20 | + Creating Swipe Views with Tabs 21 | + `ViewPager`,`PagerTitleStrip`,`FragmentPagerAdapter`,`FragmentStatePagerAdapter` 22 | + Creating a Navigation Drawer 23 | + drawer layout里面可以显式一个菜单列表 24 | + `ActionBarDrawerToggle`(appcompat-v7中)可以监听drawer的开启与关闭事件,也可以用代码控制drawer的开启与关闭 25 | + [完整样例](http://developer.android.com/intl/zh-cn/training/implementing-navigation/nav-drawer.html) 26 | + Providing Up Navigation 27 | + 首先在manifest里面为activity声明其parent activity,用于up navigation 28 | + 然后在`onOptionsItemSelected`回调中处理up navigation: 29 | 30 | ```java 31 | @Override 32 | public boolean onOptionsItemSelected(MenuItem item) { 33 | switch (item.getItemId()) { 34 | // Respond to the action bar's Up/Home button 35 | case android.R.id.home: 36 | Intent upIntent = NavUtils.getParentActivityIntent(this); 37 | if (NavUtils.shouldUpRecreateTask(this, upIntent)) { 38 | // This activity is NOT part of this app's task, so create a new task 39 | // when navigating up, with a synthesized back stack. 40 | TaskStackBuilder.create(this) 41 | // Add all of this activity's parents to the back stack 42 | .addNextIntentWithParentStack(upIntent) 43 | // Navigate up to the closest parent 44 | .startActivities(); 45 | } else { 46 | // This activity is part of this app's task, so simply 47 | // navigate up to the logical parent activity. 48 | NavUtils.navigateUpTo(this, upIntent); 49 | } 50 | return true; 51 | } 52 | return super.onOptionsItemSelected(item); 53 | } 54 | ``` 55 | 56 | + Providing Proper Back Navigation 57 | + 安卓系统都有一个物理返回键,所以不应该在UI上额外添加一个返回键 58 | + 安卓系统的back stack通常情况下都可以应对back navigation 59 | + 但是以下情况需要特殊考虑 60 | + 从通知栏消息、widget、navigation drawer直接进入一个深层次的activity 61 | 62 | ```java 63 | // Intent for the activity to open when user selects the notification 64 | Intent detailsIntent = new Intent(this, DetailsActivity.class); 65 | 66 | // Use TaskStackBuilder to build the back stack and get the PendingIntent 67 | PendingIntent pendingIntent = 68 | TaskStackBuilder.create(this) 69 | // add all of DetailsActivity's parents to the stack, 70 | // followed by DetailsActivity itself 71 | .addNextIntentWithParentStack(upIntent) 72 | .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 73 | 74 | NotificationCompat.Builder builder = new NotificationCompat.Builder(this); 75 | builder.setContentIntent(pendingIntent); 76 | ``` 77 | 78 | + fragment之间的导航 79 | 80 | ```java 81 | // Works with either the framework FragmentManager or the 82 | // support package FragmentManager (getSupportFragmentManager). 83 | getSupportFragmentManager().beginTransaction() 84 | .add(detailFragment, "detail") 85 | // Add this transaction to the back stack 86 | .addToBackStack() 87 | .commit(); 88 | ``` 89 | 90 | 需要注意的是,当fragment是在ViewPager中水平切换时,不应该把transaction 加入到 backstack中。 91 | 92 | + WebView 93 | 94 | ```java 95 | @Override 96 | public void onBackPressed() { 97 | if (mWebView.canGoBack()) { 98 | mWebView.goBack(); 99 | return; 100 | } 101 | 102 | // Otherwise defer to system default behavior. 103 | super.onBackPressed(); 104 | } 105 | ``` 106 | 107 | + Implementing Descendant Navigation 108 | + 通常都是`Intent`加上`startActivity(intent)`,或者`FragmentTransaction`来完成向子界面的导航 109 | + 需要注意的是,如果app将打开其他app的界面,为了防止用户中途离开,再次从launcher打开app时,却是其他app的界面,可以为intent设置`FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET` flag 110 | 111 | ```java 112 | Intent externalActivityIntent = new Intent(Intent.ACTION_PICK); 113 | externalActivityIntent.setType("image/*"); 114 | externalActivityIntent.addFlags( 115 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 116 | startActivity(externalActivityIntent); 117 | ``` 118 | 119 | + Notifying the User 120 | + Building a Notification 121 | + 使用[`NotificationCompat.Builder`](http://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html)创建通知栏消息 122 | + 响应用户对通知栏消息的点击,启动相应Activity,同时要考虑是否提供返回的导航设计 123 | + Preserving Navigation when Starting an Activity 124 | + 点击通知栏消息,可能会启动两种类型的Activity:正常使用流程会启动的Activity;仅仅是把通知栏消息展开的Activity; 125 | + 前者通常需要提供back导航支持,在manifest中声明Activity父子关系,同时构造PendingIntent时构造back stack,使用`stackBuilder.getPendingIntent`创建PendingIntent 126 | + 后者通常不需要加入到back stack中,无需手动构造back stack,使用`PendingIntent.getActivity`创建PendingIntent,同时在manifest中设置以下选项: 127 | ```xml 128 | 134 | 135 | ``` 136 | + 完整示例: 137 | 138 | ```java 139 | Intent resultIntent = new Intent(this, ResultActivity.class); 140 | TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); 141 | // Adds the back stack 142 | stackBuilder.addParentStack(ResultActivity.class); 143 | // Adds the Intent to the top of the stack 144 | stackBuilder.addNextIntent(resultIntent); 145 | // Gets a PendingIntent containing the entire back stack 146 | PendingIntent resultPendingIntent = 147 | stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 148 | 149 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) 150 | .setSmallIcon(R.drawable.notification_icon) 151 | .setContentTitle("My notification") 152 | .setContentText("Hello World!") 153 | .setContentIntent(resultPendingIntent); 154 | 155 | // Sets an ID for the notification 156 | int mNotificationId = 001; 157 | // Gets an instance of the NotificationManager service 158 | NotificationManager mNotifyMgr = 159 | (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 160 | // Builds the notification and issues it. 161 | mNotifyMgr.notify(mNotificationId, mBuilder.build()); 162 | ``` 163 | 164 | + Updating Notifications 165 | + 创建通知栏消息时,如果指定了id,以后可以通过id对已经显示的消息进行修改/更新、移除操作; 166 | + 修改/更新只需以相同的id,新的`Notification`对象,调用`mNotifyMgr.notify`即可; 167 | + 如果消息支持被移除,则用户可以手动移除(单个或者所有),`setAutoCancel()`会让消息在用户点击时自动消失;`cancel(id)`和`cancelAll()`可以代码移除通知栏消息; 168 | + Using Big View Styles 169 | + Android 4.1之后,通知栏消息引入了action的支持,方便用户快捷操作 170 | 171 | ```java 172 | // Sets up the Snooze and Dismiss action buttons that will appear in the 173 | // big view of the notification. 174 | Intent dismissIntent = new Intent(this, PingService.class); 175 | dismissIntent.setAction(CommonConstants.ACTION_DISMISS); 176 | PendingIntent piDismiss = PendingIntent.getService(this, 0, dismissIntent, 0); 177 | 178 | Intent snoozeIntent = new Intent(this, PingService.class); 179 | snoozeIntent.setAction(CommonConstants.ACTION_SNOOZE); 180 | PendingIntent piSnooze = PendingIntent.getService(this, 0, snoozeIntent, 0); 181 | 182 | // Constructs the Builder object. 183 | NotificationCompat.Builder builder = 184 | new NotificationCompat.Builder(this) 185 | .setSmallIcon(R.drawable.ic_stat_notification) 186 | .setContentTitle(getString(R.string.notification)) 187 | .setContentText(getString(R.string.ping)) 188 | .setDefaults(Notification.DEFAULT_ALL) // requires VIBRATE permission 189 | /* 190 | * Sets the big view "big text" style and supplies the 191 | * text (the user's reminder message) that will be displayed 192 | * in the detail area of the expanded notification. 193 | * These calls are ignored by the support library for 194 | * pre-4.1 devices. 195 | */ 196 | .setStyle(new NotificationCompat.BigTextStyle() 197 | .bigText(msg)) 198 | .addAction (R.drawable.ic_stat_dismiss, 199 | getString(R.string.dismiss), piDismiss) 200 | .addAction (R.drawable.ic_stat_snooze, 201 | getString(R.string.snooze), piSnooze); 202 | ``` 203 | 204 | + Displaying Progress in a Notification 205 | + `setProgress (int max, int progress, boolean indeterminate)`可以为通知栏消息设置进度条,支持实时进度、持续模式 206 | 207 | + Supporting Swipe-to-Refresh 208 | + 官方有[SwipeRefreshLayout](http://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html) 209 | + 民间有[Ultra Pull To Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh) 210 | + 滑到底部触发加载更多可以使用[Mugen](https://github.com/vinaysshenoy/mugen) 211 | 212 | + Adding Search Functionality 213 | + 在App Bar中添加[SearchView](http://developer.android.com/reference/android/widget/SearchView.html),注意它有support版本 214 | 215 | res/menu/options_menu.xml: 216 | 217 | ```xml 218 | 219 | 220 | 225 | 226 | ``` 227 | 228 | ```java 229 | @Override 230 | public boolean onCreateOptionsMenu(Menu menu) { 231 | MenuInflater inflater = getMenuInflater(); 232 | inflater.inflate(R.menu.options_menu, menu); 233 | 234 | return true; 235 | } 236 | ``` 237 | 238 | + 创建可搜索的配置 239 | 240 | res/xml/searchable.xml,配置SearchView的label,hint 241 | 242 | ```xml 243 | 244 | 247 | ``` 248 | 249 | manifest 250 | 251 | ```xml 252 | 253 | ... 254 | 256 | 257 | 258 | ``` 259 | 260 | ```java 261 | @Override 262 | public boolean onCreateOptionsMenu(Menu menu) { 263 | MenuInflater inflater = getMenuInflater(); 264 | inflater.inflate(R.menu.options_menu, menu); 265 | 266 | // Associate searchable configuration with the SearchView 267 | SearchManager searchManager = 268 | (SearchManager) getSystemService(Context.SEARCH_SERVICE); 269 | SearchView searchView = 270 | (SearchView) menu.findItem(R.id.search).getActionView(); 271 | searchView.setSearchableInfo( 272 | searchManager.getSearchableInfo(getComponentName())); 273 | 274 | return true; 275 | } 276 | ``` 277 | 278 | + 当用户提交一个query时,SearchView会发出一个`ACTION_SEARCH` intent,需要在manifest文件中声明响应此intent 279 | 280 | + Making Your App Content Searchable by Google 281 | + Google对如何优化对app内的内容、网站的搜索,提供了[相应的建议](http://developer.android.com/intl/zh-cn/training/app-indexing/enabling-app-indexing.html) 282 | + Deep Links,通过在manifest中声明感兴趣的Intent,可以在用户在其他app内触发此intent时启动自己的app,intent可以设置action, category, scheme来进行过滤 283 | 284 | + [优化app的assistant内容](http://developer.android.com/intl/zh-cn/training/articles/assistant.html) 285 | 286 | + [app link,让自己的app成为自己网站uri的默认打开方式](http://developer.android.com/intl/zh-cn/training/app-links/index.html) 287 | --------------------------------------------------------------------------------