├── project ├── Empty.java ├── AS │ ├── img │ │ ├── tip.gif │ │ ├── filed.gif │ │ ├── params.gif │ │ ├── smart.png │ │ ├── split.png │ │ ├── all_body.png │ │ ├── condition.gif │ │ ├── one_debug.png │ │ ├── runnable.gif │ │ ├── set_name.png │ │ ├── smart_new.gif │ │ ├── smart_old.gif │ │ ├── template.gif │ │ ├── command_copy.png │ │ ├── extract_xml.gif │ │ ├── find_class.png │ │ ├── method_pre.png │ │ ├── recent_file.png │ │ ├── extract_method.gif │ │ ├── mul_condition.gif │ │ └── mul_no_condition.gif │ └── README.md ├── git │ ├── rebase_1.png │ ├── rebase_2.png │ └── README.md └── design-pattern │ ├── 瞰-设计模式与Android(篇三) │ ├── UML_Bridge.png │ ├── UML_Facade.png │ ├── UML_Proxy.png │ ├── proxy-ams.png │ ├── UML_Mediator.png │ ├── fly-message.png │ ├── UML_Composite.png │ ├── UML_Decorator.png │ ├── UML_Flyweight.png │ ├── composite-view.png │ ├── Seq_LayoutInflate.png │ └── README.md │ ├── 瞰-设计模式与Android(篇二) │ ├── UML_Bridge.png │ ├── UML_Cursor.png │ ├── UML_State.png │ ├── UML_Command.png │ ├── UML_Iterator.png │ ├── UML_Memento.png │ ├── UML_Template.png │ ├── Seq_LayoutInflate.png │ ├── elevator_sequence.png │ └── README.md │ └── 瞰-设计模式与Android(篇一) │ ├── imgs │ ├── UML_ALL.png │ ├── UML_Bridge.png │ ├── UML_Cursor.png │ ├── UML_Facade.png │ ├── UML_Proxy.png │ ├── UML_State.png │ ├── proxy-ams.png │ ├── UML_Builder.png │ ├── UML_Command.png │ ├── UML_Factory.png │ ├── UML_Iterator.png │ ├── UML_Mediator.png │ ├── UML_Memento.png │ ├── UML_Strategy.png │ ├── UML_Template.png │ ├── fly-message.png │ ├── UML_AbsFactory.png │ ├── UML_Animation.png │ ├── UML_Composite.png │ ├── UML_Decorator.png │ ├── UML_Flyweight.png │ ├── UML_Prototype.png │ ├── composite-view.png │ ├── test_absFactory.png │ ├── Seq_LayoutInflate.png │ └── elevator_sequence.png │ └── README.md └── README.md /project/Empty.java: -------------------------------------------------------------------------------- 1 | class Empty{} 2 | -------------------------------------------------------------------------------- /project/AS/img/tip.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/tip.gif -------------------------------------------------------------------------------- /project/AS/img/filed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/filed.gif -------------------------------------------------------------------------------- /project/AS/img/params.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/params.gif -------------------------------------------------------------------------------- /project/AS/img/smart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/smart.png -------------------------------------------------------------------------------- /project/AS/img/split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/split.png -------------------------------------------------------------------------------- /project/git/rebase_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/git/rebase_1.png -------------------------------------------------------------------------------- /project/git/rebase_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/git/rebase_2.png -------------------------------------------------------------------------------- /project/AS/img/all_body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/all_body.png -------------------------------------------------------------------------------- /project/AS/img/condition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/condition.gif -------------------------------------------------------------------------------- /project/AS/img/one_debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/one_debug.png -------------------------------------------------------------------------------- /project/AS/img/runnable.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/runnable.gif -------------------------------------------------------------------------------- /project/AS/img/set_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/set_name.png -------------------------------------------------------------------------------- /project/AS/img/smart_new.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/smart_new.gif -------------------------------------------------------------------------------- /project/AS/img/smart_old.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/smart_old.gif -------------------------------------------------------------------------------- /project/AS/img/template.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/template.gif -------------------------------------------------------------------------------- /project/AS/img/command_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/command_copy.png -------------------------------------------------------------------------------- /project/AS/img/extract_xml.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/extract_xml.gif -------------------------------------------------------------------------------- /project/AS/img/find_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/find_class.png -------------------------------------------------------------------------------- /project/AS/img/method_pre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/method_pre.png -------------------------------------------------------------------------------- /project/AS/img/recent_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/recent_file.png -------------------------------------------------------------------------------- /project/AS/img/extract_method.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/extract_method.gif -------------------------------------------------------------------------------- /project/AS/img/mul_condition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/mul_condition.gif -------------------------------------------------------------------------------- /project/AS/img/mul_no_condition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/AS/img/mul_no_condition.gif -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/UML_Bridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/UML_Bridge.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/UML_Facade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/UML_Facade.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/UML_Proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/UML_Proxy.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/proxy-ams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/proxy-ams.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/UML_Bridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/UML_Bridge.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/UML_Cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/UML_Cursor.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/UML_State.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/UML_State.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_ALL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_ALL.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/UML_Mediator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/UML_Mediator.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/fly-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/fly-message.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/UML_Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/UML_Command.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/UML_Iterator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/UML_Iterator.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/UML_Memento.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/UML_Memento.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/UML_Template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/UML_Template.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Bridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Bridge.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Cursor.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Facade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Facade.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Proxy.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_State.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_State.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/proxy-ams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/proxy-ams.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/UML_Composite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/UML_Composite.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/UML_Decorator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/UML_Decorator.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/UML_Flyweight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/UML_Flyweight.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/composite-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/composite-view.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Builder.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Command.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Factory.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Iterator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Iterator.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Mediator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Mediator.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Memento.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Memento.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Strategy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Strategy.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Template.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/fly-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/fly-message.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/Seq_LayoutInflate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇三)/Seq_LayoutInflate.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/Seq_LayoutInflate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/Seq_LayoutInflate.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/elevator_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇二)/elevator_sequence.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_AbsFactory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_AbsFactory.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Animation.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Composite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Composite.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Decorator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Decorator.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Flyweight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Flyweight.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/UML_Prototype.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/composite-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/composite-view.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/test_absFactory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/test_absFactory.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/Seq_LayoutInflate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/Seq_LayoutInflate.png -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/imgs/elevator_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suzeyu1992/repo/HEAD/project/design-pattern/瞰-设计模式与Android(篇一)/imgs/elevator_sequence.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > Final 2 | 3 |
起始于2016年11月20日
已废...
4 | 5 | 6 | 7 | 8 | * **`Android`** 9 | * **`常规知识`** 10 | * [5.0特性Verctor2SVG](http://suzeyu.com/2016/05/19/5.0%E7%89%B9%E6%80%A7Verctor2SVG/) 11 | * [Android 中如何使用annotion替代Enum](http://suzeyu.com/2016/05/20/Android-%E4%B8%AD%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8annotion%E6%9B%BF%E4%BB%A3Enum/) 12 | * [mac系统android编译源码](http://suzeyu.com/2016/07/12/mac%E7%B3%BB%E7%BB%9Fandroid%E7%BC%96%E8%AF%91%E6%BA%90%E7%A0%81/) 13 | * [Android Studio 快捷键 mac版](http://suzeyu.com/2016/05/20/Android-Studio-%E5%BF%AB%E6%8D%B7%E9%94%AE-mac/) 14 | * [Retrofit(一)-入门使用](http://suzeyu.com/2016/07/18/Retrofit-%E4%B8%80-%E5%85%A5%E9%97%A8%E4%BD%BF%E7%94%A8/) 15 | * [Android绘图篇-01 Canvas和Paint的基础使用](http://suzeyu.com/2016/07/05/Android-%E7%BB%98%E5%9B%BE%E7%AF%87-1-Canvas%E5%92%8CPaint%E7%9A%84%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/) 16 | * **`framework`** 17 | * [深入WindowManager](http://suzeyu.com/2016/11/28/%E6%B7%B1%E5%85%A5WindowManager/) 18 | * [认知WindowManagerService](http://suzeyu.com/2016/11/28/WindowManagerService/) 19 | * [了解Binder是如何撑起Android系统运行](http://suzeyu.com/2017/02/08/%E9%9D%A2%E8%AF%95%E4%B9%8BBinder%E7%9A%84%E8%AE%A4%E7%9F%A5/) 20 | * [Android系统的启动简述](http://suzeyu.com/2017/02/09/Android%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/) 21 | * **`Java`** 22 | * **`小技巧小姿势`** 23 | * [git命令小手册](https://github.com/suzeyu1992/repo/tree/master/project/git) 24 | * [Android Studio高效使用](http://suzeyu.com/2016/12/17/Android-Studio-%E5%B8%B8%E7%94%A8%E6%89%8B%E5%86%8C/) 25 | * [shell命令常用小手册](http://suzeyu.com/2016/12/26/shell%E5%B8%B8%E7%94%A8%E5%B0%8F%E6%89%8B%E5%86%8C/) 26 | * [vim常用手册](http://suzeyu.com/2016/12/27/vim%E5%B8%B8%E7%94%A8%E7%AC%94%E8%AE%B0/) 27 | * **`数据结构`** 28 | * [算法的介绍](http://suzeyu.com/2017/01/01/%E8%AE%A4%E7%9F%A5%E7%AE%97%E6%B3%95/) 29 | * [结构之-线性表](http://suzeyu.com/2017/01/04/%E7%BA%BF%E6%80%A7%E8%A1%A8/) 30 | * [结构之-栈和队列](http://suzeyu.com/2017/01/06/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97/) 31 | * **`设计模式`** 32 | * [面向对象的六大原则解析](http://suzeyu.com/2016/11/28/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%85%AD%E5%A4%A7%E5%8E%9F%E5%88%99%E8%A7%A3%E6%9E%90/) 33 | * [设计模式与Android(篇一)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%80)) 34 | * [设计模式与Android(篇二)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%BA%8C)) 35 | * [设计模式与Android(篇三)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%89)) 36 | * **`mySql`** 37 | * [MySQL(一)--数据库入门](http://suzeyu.com/2016/05/15/MySQL-%E4%B8%80-%E6%95%B0%E6%8D%AE%E5%BA%93%E5%85%A5%E9%97%A8/) 38 | * [MySQL(二)--基本查询语句](http://suzeyu.com/2016/05/16/MySQL-%E4%BA%8C-%E5%9F%BA%E6%9C%AC%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5/) 39 | * [MySQL(三)--子查询](http://suzeyu.com/2016/05/17/MySQL-%E4%B8%89-%E5%AD%90%E6%9F%A5%E8%AF%A2/) 40 | * [MySql(四)--数据控制语言控制器等](http://suzeyu.com/2016/05/18/MySql-%E5%9B%9B-%E6%95%B0%E6%8D%AE%E6%8E%A7%E5%88%B6%E8%AF%AD%E8%A8%80%E6%8E%A7%E5%88%B6%E5%99%A8%E7%AD%89/) 41 | * **`PHP`** 42 | * [PHP(一)-环境搭建](http://suzeyu.com/2016/04/15/PHP(%E4%B8%80)-%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/) 43 | * [PHP(二)-php基础语法](http://suzeyu.com/2016/04/16/PHP(%E4%BA%8C)-php%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95/) 44 | * [PHP(三)-载入文件和错误处理](http://suzeyu.com/2016/04/17/PHP(%E4%B8%89)-%E8%BD%BD%E5%85%A5%E6%96%87%E4%BB%B6%E5%92%8C%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/) 45 | * [PHP(四)-函数 数组介绍](http://suzeyu.com/2016/04/18/PHP-%E5%9B%9B-%E5%87%BD%E6%95%B0-%E6%95%B0%E7%BB%84%E4%BB%8B%E7%BB%8D/) 46 | * 太监了... 47 | * **`js`** 48 | * [基础篇(一)-JS简单介绍](http://suzeyu.com/2016/04/10/%E5%9F%BA%E7%A1%80%E7%AF%87(%E4%B8%80)-JS%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D/) 49 | * [基础篇(二)-JS基本语法](http://suzeyu.com/2016/04/11/%E5%9F%BA%E7%A1%80%E7%AF%87(%E4%BA%8C)-JS%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95/) 50 | * [基础篇(三)-JS对象使用](http://suzeyu.com/2016/04/12/%E5%9F%BA%E7%A1%80%E7%AF%87(%E4%B8%89)-JS%E5%AF%B9%E8%B1%A1%E4%BD%BF%E7%94%A8/) 51 | * [基础篇(四)-JS-BOM](http://suzeyu.com/2016/04/13/%E5%9F%BA%E7%A1%80%E7%AF%87(%E5%9B%9B)-JS-BOM/) 52 | * [基础篇(五)-JS-DOM](http://suzeyu.com/2016/04/14/%E5%9F%BA%E7%A1%80%E7%AF%87(%E4%BA%94)-JS-DOM/) 53 | * [基础篇(六)-HTML5介绍](http://suzeyu.com/2016/04/16/%E5%9F%BA%E7%A1%80%E7%AF%87(%E5%85%AD)-HTML5%E4%BB%8B%E7%BB%8D/) 54 | * **`抄书笔记`** 55 | * [《Android 开发艺术探索》 01-Activity的生命周期和模式](http://suzeyu.com/2016/08/01/Android%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2-%E7%AC%94%E8%AE%B001-Activity%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%92%8C%E6%A8%A1%E5%BC%8F/) 56 | * [《Android 开发艺术探索》 02-IPC机制](http://suzeyu.com/2016/08/02/Android%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2-%E7%AC%94%E8%AE%B002-IPC%E6%9C%BA%E5%88%B6/) 57 | * [《Android 开发艺术探索》 03-View的事件体系](http://suzeyu.com/2016/08/08/Android%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2-%E7%AC%94%E8%AE%B003-View%E7%9A%84%E4%BA%8B%E4%BB%B6%E4%BD%93%E7%B3%BB/) 58 | * [《Android 开发艺术探索》 04-View的工作原理](http://suzeyu.com/2016/08/10/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B04-View%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/) 59 | * [《Android 开发艺术探索》 05-理解RemoteViews](http://suzeyu.com/2016/08/11/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-05-%E7%90%86%E8%A7%A3RemoteViews/) 60 | * [《Android 开发艺术探索》 06-Android的Drawable](http://suzeyu.com/2016/08/12/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-06-Android%E7%9A%84Drawable/) 61 | * [《Android 开发艺术探索》 07-Andriod动画深入分析](http://suzeyu.com/2016/08/13/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-07-Andriod%E5%8A%A8%E7%94%BB%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90/) 62 | * [《Android 开发艺术探索》 08-理解Window和WindowManager](http://suzeyu.com/2016/08/15/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-08-%E7%90%86%E8%A7%A3Window%E5%92%8CWindowManager/) 63 | * [《Android 开发艺术探索》 09-四大组件的工作过程](http://suzeyu.com/2016/08/16/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-09-%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6%E7%9A%84%E5%B7%A5%E4%BD%9C%E8%BF%87%E7%A8%8B/) 64 | * [《Android 开发艺术探索》 10-Android的消息机制](http://suzeyu.com/2016/08/22/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-10-Android%E7%9A%84%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6/) 65 | * [《Android 开发艺术探索》 11-Android的线程和线程池](http://suzeyu.com/2016/08/22/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-11-Android%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%E6%B1%A0/) 66 | * [《Android 开发艺术探索》 12-Bitmap的加载和Cache](http://suzeyu.com/2016/08/23/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-12-Bitmap%E7%9A%84%E5%8A%A0%E8%BD%BD%E5%92%8CCache/) 67 | * [《Android 开发艺术探索》 13-综合技术](http://suzeyu.com/2016/08/25/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-13-%E7%BB%BC%E5%90%88%E6%8A%80%E6%9C%AF/) 68 | * [《Android 开发艺术探索》 14-JNI和NDK编程](http://suzeyu.com/2016/08/26/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-14-JNI%E5%92%8CNDK%E7%BC%96%E7%A8%8B/) 69 | * [《Android 开发艺术探索》 15-Android性能优化](http://suzeyu.com/2016/08/27/%E3%80%8AAndroid-%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B-15-Android%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/) 70 | * [《Android 编程实战》01-完善开发环境和优化](http://suzeyu.com/2016/09/20/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B%E5%AE%8C%E5%96%84%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E5%92%8C%E4%BC%98%E5%8C%96/) 71 | * [《Android 编程实战》02-Android上编写高效Java](http://suzeyu.com/2016/09/21/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B02-Android%E4%B8%8A%E7%BC%96%E5%86%99%E9%AB%98%E6%95%88Java/) 72 | * [《Android 编程实战》03-组件 清单 资源和UI闲聊](http://suzeyu.com/2016/09/21/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B03-%E7%BB%84%E4%BB%B6-%E6%B8%85%E5%8D%95-%E8%B5%84%E6%BA%90%E5%92%8CUI%E9%97%B2%E8%81%8A/) 73 | * [《Android 编程实战》04-手势操作进阶, 重识Service](http://suzeyu.com/2016/09/26/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B04-%E7%95%8C%E9%9D%A2%E6%93%8D%E4%BD%9C-Service/) 74 | * [《Android 编程实战》05-重识 IPC](http://suzeyu.com/2016/09/28/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B05-%E9%87%8D%E8%AF%86-IPC/) 75 | * [《Android 编程实战》06-重识 BroadcastReceiver](http://suzeyu.com/2016/09/29/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B06-%E9%87%8D%E8%AF%86-BroadcastReceiver/) 76 | * [《Android 编程实战》07-序列化说明](http://suzeyu.com/2016/10/02/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B07-%E5%BA%8F%E5%88%97%E5%8C%96%E8%AF%B4%E6%98%8E/) 77 | * [《Android 编程实战》08-高级音频,视频及相机应用](http://suzeyu.com/2016/10/03/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B08-%E9%AB%98%E7%BA%A7%E9%9F%B3%E9%A2%91-%E8%A7%86%E9%A2%91%E5%8F%8A%E7%9B%B8%E6%9C%BA%E5%BA%94%E7%94%A8/) 78 | * [《Android-编程实战》09-Android应用安全问题](http://suzeyu.com/2016/10/04/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B09-Android%E5%BA%94%E7%94%A8%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98/) 79 | * [《Android-编程实战》10-隐藏的Android API](http://suzeyu.com/2016/10/04/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B10-%E9%9A%90%E8%97%8F%E7%9A%84Android-API/) 80 | * [《Android-编程实战》11-网络 Web服务](http://suzeyu.com/2016/10/08/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B11-%E7%BD%91%E7%BB%9C-Web%E6%9C%8D%E5%8A%A1/) 81 | * [《Android-编程实战》12-远程设备其余通信方式](http://suzeyu.com/2016/10/08/%E3%80%8AAndroid-%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%E3%80%8B12-%E8%BF%9C%E7%A8%8B%E8%AE%BE%E5%A4%87%E5%85%B6%E4%BD%99%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F/) 82 | * [《Android编程权威指南》随记一 零散知识整理](http://suzeyu.com/2016/09/07/%E3%80%8AAndroid%E7%BC%96%E7%A8%8B%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97%E3%80%8B%E9%9A%8F%E8%AE%B0%E4%B8%80/) 83 | * [《Android编程权威指南》随记二 Fragment的概括](http://suzeyu.com/2016/09/08/%E3%80%8AAndroid%E7%BC%96%E7%A8%8B%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97%E3%80%8B%E9%9A%8F%E8%AE%B0%E4%BA%8C-Fragment%E7%9A%84%E6%A6%82%E6%8B%AC/) 84 | * [《Android编程权威指南》随记三 媒体与Intent等](http://suzeyu.com/2016/09/09/%E3%80%8AAndroid%E7%BC%96%E7%A8%8B%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97%E3%80%8B%E9%9A%8F%E8%AE%B0%E4%B8%89-%E5%AA%92%E4%BD%93%E4%B8%8EIntent%E7%AD%89/) 85 | * [《Android编程权威指南》随记四 组件等](http://suzeyu.com/2016/09/10/%E3%80%8AAndroid%E7%BC%96%E7%A8%8B%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97%E3%80%8B%E9%9A%8F%E8%AE%B0%E5%9B%9B-%E7%BB%84%E4%BB%B6%E7%AD%89/) 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /project/AS/README.md: -------------------------------------------------------------------------------- 1 | 2 | > 整理`Android群英传`和网上出现的比较完整`AS`的快捷小技巧. `mac`版已测, `win`大部分已测试. 最后有一个整理的完整`快捷键表格`. win也好mac也好, 如果快捷被修改还是错误, 都可以通过提供的`Keymap中的关键字`来进行重设. 3 | > 图中`gif`如果看不清, 放大页面即可. 4 | 5 | O(∩_∩)O~~ 喜欢就撒个星. 转载表明出处[https://github.com/suzeyu1992/repo](https://github.com/suzeyu1992/repo) 6 | 7 | 8 | * [常用小操作](#1) 9 | * [单词选择](#2) 10 | * [显示最近操作,修改的文件](#3) 11 | * [文件查找](#4) 12 | * [操作记录](#5) 13 | * [移动行](#6) 14 | * [查找方法调用处](#7) 15 | * [方法的跟进](#8) 16 | * [显示方法的参数](#9) 17 | * [行的快速操作](#10) 18 | * [多行操作](#11) 19 | * [快速补全完成](#12) 20 | * [代码提示](#13) 21 | * [变量的快速操作](#14) 22 | * [代码折叠](#15) 23 | * [预览方法定义](#16) 24 | * [粘贴板管理](#17) 25 | * [拆分窗口](#18) 26 | * [变量命令风格](#19) 27 | * [查看大纲](#20) 28 | * [书签](#21) 29 | * [快速重构](#22) 30 | * [重构入口](#23) 31 | * [surround with](#24) 32 | * [Extract](#25) 33 | * [代码模块](#26) 34 | * [代码分析](#27) 35 | * [Insepect Code & Code cleanup](#28) 36 | * [Dependencies](#29) 37 | * [方法调用栈](#30) 38 | * [断点调试](#31) 39 | * [快速调试](#32) 40 | * [快捷键查找表](#33) 41 | 42 | 43 | ## 常用小操作 44 | 45 | 46 | ### 单词选择 47 | 48 | 一般`IDE`通过`option + 方向键左右`(win为`ctrl + 左右方向键`)对光标进行单词的跳动, 但是由于我们使用驼峰式命名较多, 默认的会导致由多个单词组成的驼峰式的首尾跳转. 还好`AS`可以对这种风格进行设定. 49 | 50 | **默认效果** 51 | 52 | ![](img/smart_old.gif) 53 | 54 | **修改后的效果** 55 | 56 | ![](img/smart_new.gif) 57 | 58 | 59 | **修改步骤** 60 | 61 | 打开**偏好设置**如图片设置 62 | 63 | ![](img/smart.png) 64 | 65 | > 虽然单词跳着爽了, 但是当你想复制这个变量名双击的时候. 只会复制变量名中的某一个单词. 而不是像以前会复制空格分割两边的完整字符串. 反正我是用了不到一天有些不舒服就切换回原来的风格了. 66 | > 但是`option + 左右方向键`这个小技巧用熟了, 比特意用鼠标点,或者光标一下下移动到目标位置会高效很多. 67 | 68 | 69 | ### 显示最近操作, 修改的文件 70 | 71 | 例如: 我打开了`main_activity.xml`布局文件, 然后关闭页面. 这个时候`Command + E`, 就会如下显示之前的操作文件. 72 | 73 | ![](img/recent_file.png) 74 | 75 | 76 | 那么如果要查看之前修改过的文件, `Command + Shift + E`即可. 可以配合`Control + Tab`进行快速界面的切换. 77 | 78 | **快捷键** 79 | 80 | * 最近操作 81 | * `Command + E` **(mac)** 82 | * `ctrl + E` **(win)** 83 | * 最近修改 84 | * `Command + Shift + E` **(mac)** 85 | * `ctrl + shift + E` **(win)** 86 | 87 | 88 | 89 | ### 文件查找 90 | 91 | 92 | 93 | `search Everywhere` 94 | 95 | 项目中的全局查找功能文件功能 96 | 97 | **查找类文件** 98 | 99 | * `command + o` **(mac)** 100 | * `ctrl + n` **(win)** 101 | 102 | 默认只是在项目代码中查找类文件, 如果需要查找`sdk`或者类库中的相关类. 再按一次快捷键, 或者勾选下图选项搜索全局类文件 103 | 104 | ![](img/find_class.png) 105 | 106 | --- 107 | 108 | **查找文件** 109 | 110 | 这个级别比上面那个更广. 包括所有文件, 如`xml`等. 111 | 112 | * `command + shift + o` **(mac)** 113 | * `ctrl + shift + n` **(win)** 114 | 115 | 如上如果需要搜索类库或者源码, 请在按一次快捷键, 或者勾选`查找类`给出图中的选项. 116 | 117 | 118 | 119 | ### 操作记录 120 | 121 | 在浏览代码结构的时候, 很多时候总是会跟进许多类里面, 如果想回到之前浏览调转点. 那么这个快捷键你就必须要记住. 122 | 123 | * `Command + Option + Left\Right` 或者 `command + [`, `command + ]` **(mac)** 124 | * `ctrl + alt + Left\Right`**(win)** 125 | 126 | 127 | ### 移动行 128 | 129 | 这个不用太多介绍, 就是**整行**的上下移动. 130 | 131 | * `Option + shift + 方向键上\方向键下` **(mac)** 132 | * `alt + shift + 方向键上\方向键下` **(win)** 133 | 134 | 135 | ### 查找方法调用处 136 | 137 | 场景: 查找一个方法在何处被调用, 或者一个ID在哪里被引用. 选中这个方法**右键**, 选择`Find Usages`即可. 138 | 139 | 快捷键: 140 | 141 | * `option + F7` **(mac)** 142 | * `alt + F7` **(win)** 143 | 144 | 145 | ### 方法的跟进 146 | 147 | 方法总是伴随着调转, 我们也总是会对方法的内部查看细节. 常用的办法是`按住Command 并点击方法名`的方式. 148 | 149 | 也可以在光标所在处直接通过`Command + B`进行方法内部跟进. 150 | 151 | * `Command + B` **(mac)** 152 | * `ctrl + alt + B` **(win)** 153 | 154 | 155 | ### 显示方法的参数 156 | 157 | 当我们使用一个方法的时候, 会在刚开始的时候显示出所有的参数. 但是过一会可能就不存在了. 这个时候如果你想知道下一个参数是什么类型, 或者看一下所有参数. 158 | 159 | ![](img/params.gif) 160 | 161 | 快捷键: 162 | 163 | * `command + P` **(mac)** 164 | * `ctrl + P` **(win)** 165 | 166 | 如果你还想查看一下方法的文档, 不用跟进源码, 直接`F1`键即可显示出文档. 167 | 168 | 169 | 170 | ### 行的快速操作 171 | 172 | 快速删除行, 其实使用剪切功能就可以`Command + X` 173 | 174 | **快速复制行**, 使用`command + D`**mac**, `ctrl + D`**win** 175 | 176 | 177 | 178 | ### 多行操作 179 | 180 | 可能有时候我们会声明做一系列相关的变量为`float`类型, 但是真实的需要的是`int`类型. 这个时候就可以进行`多重选择`进行整体修改. 如下图: 181 | 182 | ![](img/mul_condition.gif) 183 | 184 | 按住`option`(win为`alt`)键, 然后鼠标进行区域的选择. 然后进行对应修改即可. 185 | 186 | 你可能认为上面的必须要连续的才可以. 那么如果可以设置`多个光标点`是否就可以满足你的问题了? 187 | 188 | 看下图: 189 | 190 | ![](img/mul_no_condition.gif) 191 | 192 | * `option + shift + 鼠标点击` **(mac)** 193 | * `alt + shift + 鼠标点击`**(win)** 194 | 195 | 即可添加一个`编辑光标`. 最后如果不需要那么就`esc`即可关闭多余光标. 196 | 197 | 198 | ### 快速补全完成 199 | 200 | 创建一个对象. 或者`findViewById()`有时总需要移动鼠标或者光标到末尾进行`分号`的补全. 这些都可以快捷键搞定. 当你用习惯之后, 会非常的方便好用. 201 | 202 | * `command + shift + enter`**(mac)** 203 | * `ctrl + shift + enter`**(win)** 204 | 205 | 实现的功能 206 | 207 | * 方法体大括号的添加 208 | * 行尾分号的添加 209 | * 自动格式化改行操作等 210 | 211 | 212 | 213 | ### 代码提示 214 | 215 | 任何地方都可以调出的代码提示. 216 | 217 | `control + option + /`(不是默认, mac默认为`control + space`). 这里要说的是. 代码提示之后一般都会`回车键`确定. 当按`回车键`的时候. 会保留光标之后的内容. 当有的时候我们并不需要. 这个时候你就可以使用`table`键进行选择. 它会将后面的输入内容删掉. 218 | 219 | win的快捷方式`ctrl + 空格`(win同样大部分和输入法冲突, 请手动在AS设置里面`keymap`项查找` completion basic`手动修改快捷键) 220 | 221 | ![](img/tip.gif) 222 | 223 | 224 | **快速生成变量** 225 | 226 | 比如在方法体中`logi`直接快速生成一个`log`日志输出的模板, 但是`TAG`这个常量还没有声明. 这个时候用光标移动到`TAG`上. 使用快捷键提示即可快速生成. 227 | 228 | * `option + enter`**(mac)** 229 | * `alt + enter`**(win)** 230 | 231 | 232 | ### 变量的快速操作 233 | 234 | ![](img/filed.gif) 235 | 236 | 上图使用了两次快捷键, 237 | 238 | * 第一次: 跳转到变量的`声明处` 239 | * `command + B` **(mac)** 240 | * `ctrl + b` **(win)** 241 | * 第二次: 跳转到变量类型的`定义处` 242 | * `command + shift + B` **(mac)** 243 | * `ctrl + shift + b` **(win)** 244 | 245 | 246 | 247 | ### 代码折叠 248 | 249 | 对代码块进行折叠和展开. 250 | 251 | * `command + 加号\减号`**(mac)** 252 | * `ctrl + 加号\减号` **(win)** 253 | 254 | 255 | ### 预览方法定义 256 | 257 | 如果只想大体了解方法的定义, 而不需要进行方法所在源码类的跳转显示. 可以通过`Command + Y` (win快捷键为`ctrl + shift + I`)来进行预览 258 | 259 | ![](img/method_pre.png) 260 | 261 | 262 | 263 | ### 粘贴板管理 264 | 265 | 对于复制粘贴. 我们习惯只是`单次`的`c+v`, 那么如果你想查看之前被覆盖了的`复制过的`内容. `AS`同样有`复制粘贴板`来管理. 展示最近几次的复制内容. 266 | 267 | ![](img/command_copy.png) 268 | 269 | * `command + shift + v` **(mac)** 270 | * `ctrl + shift + v` **(win)** 271 | 272 | 273 | ### 拆分窗口 274 | 275 | ![](img/split.png) 276 | 277 | 只需要在标签页上`右键`. 选择`split vertical\horizontal`. 278 | 279 | 280 | 281 | 282 | ### 变量命名风格 283 | 284 | 对于`成员变量`应该`m`开头. 对于`静态成员`通常是`s`开头. 可以这样设置这个风格. 285 | 286 | ![](img/set_name.png) 287 | 288 | 这样在输入一个变量的名字时, 就可以自动补全`m`或者`s`. 同时在`Extra`代码的时候, 生成的代码都可以自动根据这个规则重构. 289 | 290 | 291 | ### 查看大纲 292 | 293 | 当一个类很大的时候. 可以通过`command + F12`(win为`ctrl + F12`) 打开大纲界面. 展示全部的方法和成员变量列表. 294 | 295 | ![](img/all_body.png) 296 | 297 | 并且支持模糊搜索. 来进行筛选显示. 可以快速的找到要搜索的方法集. 298 | 299 | 300 | 301 | ### 书签 302 | 303 | 在浏览大型代码, 或者调试时. 有时可能会需要记住一些关键的代码或者方法. 这个时候就可以使用`书签`. 来记录此关键代码. 只需要在点击需要标记的行, 并按`F3`就会在左侧出现一个`小对勾`. 这就说明已经打上了一个书签. 304 | 305 | 同时在`Favorites`标签中, 也可以找到对应的`Bookmarks` 306 | 307 | 可以通过快捷键`command + F3` (win为`shift + F11`)快速调出书签面板, 进行后续的操作. 308 | 309 | 310 | 311 | ## 快速重构 312 | 313 | 314 | ### 重构入口 315 | 316 | 当一个代码片段准备重构的时候. 可以使用`control + T` (win为`ctrl + alt + shift + T`)打开重构入口. 或者`右键单击`选择`Refactor`显示重构界面. 317 | 318 | 这里有很多方便的功能. 提成方法等等. 319 | 320 | 321 | ### surround With 322 | 323 | 当对一个段代码进行, 条件包裹, 捕捉异常, 循环, Runnable等. 可以直接使用快捷的方式, 而不需要手动生成条件在进行代码块的`复制-粘贴` 324 | 325 | ![](img/runnable.gif) 326 | 327 | 快捷键 328 | 329 | * `command + option + T`**(mac)** 330 | * `ctrl + alt + T`**(win)** 331 | 332 | 333 | ### Extract 334 | 335 | 你可以经常使用它,提取出一个方法. 336 | 337 | ![](img/extract_method.gif) 338 | 339 | 不仅仅如此, 你还可以对`xml`中的多个控件相同的属性进行抽取为`style` 340 | 341 | ![](img/extract_xml.gif) 342 | 343 | 是不是很爽? 344 | 345 | 这是其一, 对于`xml`不仅可以抽取`Style`, 还可以抽取布局`Layout` 346 | 347 | 而代码中, 可以提取各种变量, 参数, 长廊. 348 | 349 | 350 | 351 | ## 代码模板 352 | 353 | `AS`中已经内置了很多代码模板, 你可以很嗖嗖嗖的就完成之前倒背如流的代码. 如下一小部分 354 | 355 | ![](img/template.gif) 356 | 357 | 通过快捷键可调出这些代码模板 358 | 359 | * `command + J`**(mac)** 360 | * `ctrl + J`**(win)** 361 | 362 | 363 | ## 代码分析 364 | 365 | `Google`提供了很多代码分析工具, 这些工具都集中在顶部菜单栏的`Analyze`中. 366 | 367 | 368 | ### Inspect Code & Code cleanup 369 | 370 | `Inspect Code`可以让`IDE`分析整个工程. 类似于`Lint`分析, 并会给出大致修改意见等 371 | 372 | `Code cleanup`功能可以进行自动的代码修复. 373 | 374 | 375 | ### Dependencies 376 | 377 | 通过`Analyze`中的几个`Dependencies`选项. 可以快速分析项目的依赖. 378 | 379 | 380 | ### 方法调用栈 381 | 382 | 对于某些方法来说, 查看它被调用的地方和调用的顺序是非常重要的. 可通过`control + option + H`快速查找方法调用栈. 383 | 384 | 385 | ## 断点调试 386 | 387 | 388 | ### 快速断点 389 | 390 | `条件断点`. 主要用在循环体内. 一个循环10次的`for`. 只想在第`8`次进入断点. 391 | 392 | 和`普通断点`相似, 一样先声明一个断点. 然后在`普通断点`上`单击鼠标右键`. 在弹出的菜单填写断点条件即可. 如下: 393 | 394 | ![](img/condition.gif) 395 | 396 | 上面图中`Enable`可以控制`启用`, 还是`停用`一个断点. 397 | 398 | --- 399 | 400 | `临时断点`. 如果需要一个只执行一次的断点. 执行完之后断点自动取消. 那么可以通过快捷点将当前行作为临时断点. 401 | 402 | ![](img/one_debug.png) 403 | 404 | * `command + option + shift + F8` 405 | * `ctrl + alt + shift + F8` 406 | 407 | 408 | 为了篇幅不是很长, 调试的部分贴一个讲解很详细的帖子 409 | 410 | * [Android Studio代码调试大全](http://blog.csdn.net/dd864140130/article/details/51560664) 411 | 412 | 413 | 414 | 415 | ## 快捷键查找表 416 | 417 | 速查表列出了`keymap`快捷键的名称. 可以直接通过`AS setting`界面搜索`keymap`. 通过对应的快捷键名称来添加或者修改快捷键. 418 | 419 | 420 | | 功能描述 | keymap对应名字 | Mac | Win/Linux | 421 | | --- | --- | --- | --- | 422 | | **提示错误解决方案** | **Show Intention Actions** | `option + enter` | `alt + enter` | 423 | | **AS配置界面** | **Preferences** | `command + ,` | `control+alt+S` | 424 | | **工程项目配置界面** | **Project Structure** | `command + ;` | `Control+Alt+Shift+S` | 425 | | **快速构成代码** | **Code Generate** | `command + N` | `alt + insert` | 426 | | **代码提示** | **Completion/Basic** | `control + space`(mac会冲突, 手动修改) | `ctrl + 空格`(win同样大部分和输入法冲突, 手动修改) | 427 | | **选择视图** | **select in any view** | `option + F1` | `alt + F1` | 428 | | **添加书签标识** | **Toggle Bookmark** | `option + F3` | `ctrl + F11` | 429 | | **向下移动一行** | **Move Line Down** | `option + shift + Down` | `alt + shift + Down` | 430 | | **向上移动一行** | **Move Line Up** | `option + shift + Up` | `alt + shift + Up` | 431 | | **注释代码** | **Comment with Line Comment** | `command + /` | `ctrl + /` | 432 | | **用代码模板包裹代码** | **surround with Live Template** | `command + option + J` | `ctrl + alt + J` | 433 | | **格式化代码** | **Reformat Code** | `command + option + L` | `ctrl + alt + L` | 434 | | **Copy Reference** | **Copy Reference** | `command + option + shift + C` | `ctrl + alt + shift + C` | 435 | | **if/try等包裹代码** | **Surround With..** | `command + option + T` | `ctrl + alt + T` | 436 | | **查看声明** | **Declaration** | `command + B` | `ctrl + B` | 437 | | **快捷向下复制行** | **Duplicate Line or Block** | `command + D` | `ctrl + D` | 438 | | **删除行** | **Delete Line** | `command + delete` | `ctrl + Y` | 439 | | **快捷最近打开** | **Recent Files** | `command + E` | `ctrl + E` | 440 | | **查找** | **Edit/Find/Find** | `command + F` | `ctrl + F` | 441 | | **文件方法结构** | **File Structure** | `command + F12` | `ctrl + F12` | 442 | | **显示书签** | **Show Bookmarks** | `command + F3` | `shift + F11` | 443 | | **代码高亮向下查找** | **Move To Next Occurrence** | `command + G` | `F3` | 444 | | **代码高亮向上查找** | **Move To Previous Occurrence** | `command + shift + G` | `shift + F3` | 445 | | **按照模板生成代码** | **Insert Live Template** | `command + J` | `ctrl + J` | 446 | | **定位到行** | **Navigate/Line** | `command + L` | `ctrl + G` | 447 | | **快速到行首/尾** | **Move Caret to Line Start/End** | `command + Left/Right` | `ctrl + Left/Right` | 448 | | **代码折叠/展开** | **Collapse/Expand** | `command + 减号/加号` | `ctrl + 减号/加号` | 449 | | **查找类** | **Navigate/Class** | `command + O` | `ctrl + N` | 450 | | **多行注释** | **Code/Comment ** | `command + option + /` | `ctrl + alt + /` | 451 | | **格式化代码** | **Reformat Code** | `command + option + L` | `ctrl + alt + L` | 452 | | **提示参数类型** | **Parameter Info** | `command + P` | `ctrl + P` | 453 | | **查找替换** | **Replace** | `command + R` | `ctrl + R` | 454 | | **查找命令** | **Find Action** | `command + shift + A` | `ctrl + shift + A` | 455 | | **拷贝文件路径** | **Copy Paths** | `command + shift + C` | `ctrl + shift + C` | 456 | | **移动代码块** | **Move Statement Up/Down** | `command + shift + down/up` | `ctrl + shift + down/up` | 457 | | **代码补全** | **Complete Current Statement** | `command + shift + enter` | `ctrl + shift + enter` | 458 | | **全路径查找** | **Find in Path** | `command + shift + F` | `ctrl + shift + F` | 459 | | **代码高亮** | **Highlight Usages in File** | `command + shift + F7` | `alt + J` | 460 | | **窗口内所有代码折叠/展开** | **Collapse/Expand All** | `command + shift + 减号/加号` | `ctrl + shift + 减号/加号` | 461 | | **查找文件** | **Navigate/File** | `command + shift + O` | `ctrl + shift + N` | 462 | | **全路径中替换** | **Replace in Path** | `command + shift + R` | `ctrl + shift + R` | 463 | | **大小写转换** | **Toggle Case** | `command + shift + U` | `ctrl + shift + U` | 464 | | **显示粘贴版历史** | **Paste from History** | `command + shift + V` | `ctrl + shift + V` | 465 | | **快速查找定义** | | `command + space` | `ctrl + shift + I` | 466 | | **粘贴** | **Paste** | `command + V` | `ctrl + V` | 467 | | **复制** | **Copy** | `command + C` | `ctrl + C` | 468 | | **去除无效包引用** | **Optimize Imports** | `control + option + O` | `ctrl + alt + O` | 469 | | **显示类关系继承体系** | **Type Hierarchy** | `control + H` | `ctrl + H` | 470 | | **快速覆写方法** | **override Methods** | `control + O` | `ctrl + O` | 471 | | **查找调用的位置** | **Call hierarchy** | `control + option + H` | `ctrl + alt + H` | 472 | | **添加书签** | **Toggle Bookmark** | `F3` | `F11` | 473 | | **扩大缩小选中范围** | **Extend Selection/Shrink Selection** | `option + UP/Down` | `ctrl + W + shift + W` | 474 | | **跳转到父类** | **Super method** | `command + U` | `ctrl + U` | 475 | | **关闭当前tab标签** | **Editor Tabs / Close** | `command + W` | `ctrl + shift + a` | 476 | | **关闭除编辑窗口的其余窗口** | **Hide All Tool Windows** | `command + shift + F12` | `ctrl + shift + F12` | 477 | | **预览方法定义** | **Quick Definition** | `command + Y` | `ctrl + shift + I` | 478 | | **返回光标最后编辑位置** | **Last Edit Location** | `command + shift + delete` | `control + shift + Backspace` | 479 | | **调到代码块首部/尾部** | **Move Caret To Code Block** | `Option + Command + [ 或者 ]` | `Control+[ 或者 ]` | 480 | | **重命名** | **Rename** | `shift + F6` | `shift + F6` | 481 | | **抽取方法** | **Extract Method** | `command + option + M` | `control+alt+M` | 482 | | **抽取变量** | **Extract Variable** | `command + option + V` | `control+alt+V` | 483 | | **抽取字段** | **Extract Field** | `command + option + F` | `control+alt+F` | 484 | | **抽取常量** | **Extract Constant** | `command + option + C` | `control+alt+C` | 485 | | **抽取参数** | **Extract Parameter** | `command + option + P` | `control+alt+P` | 486 | | **重构入口** | **Refactor This** | `control + T` | `ctrl + alt + shift + T` | 487 | 488 | 489 | 490 | 491 | * [作者GitHub](https://github.com/suzeyu1992/repo.git) 492 | 493 | 494 | 495 | -------------------------------------------------------------------------------- /project/git/README.md: -------------------------------------------------------------------------------- 1 | > Git再识 拥抱开始 2 | > 这些应该可以让你对Git的使用更进一步 ^*^ 3 | 4 | 5 | * [安装Git](#安装git) 6 | * [配置文件](#配置文件) 7 | * [查看config](#查看config) 8 | * [设置用户信息](#设置用户信息) 9 | * [Git别名](#git别名) 10 | * [.gitignore文件](#gitignore文件) 11 | * [仓库的基础操作](#仓库的基础操作) 12 | * [初始化仓库](#初始化仓库) 13 | * [添加文件到暂存区](#添加文件到暂存区) 14 | * [status仓库](#status仓库) 15 | * [diff仓库](#diff仓库) 16 | * [提交更新](#提交更新) 17 | * [移除文件](#移除文件) 18 | * [移动文件](#移动文件) 19 | * [查看历史提交](#查看历史提交) 20 | * [撤销操作](#撤销操作) 21 | * [标签](#标签) 22 | * [分支操作](#分支操作) 23 | * [分支的创建](#分支的创建) 24 | * [分支的切换](#分支的切换) 25 | * [分支合并](#分支合并) 26 | * [删除分支](#删除分支) 27 | * [分支管理](#分支管理) 28 | * [变基](#变基) 29 | * [远程分支](#远程分支) 30 | * [跟踪分支](#跟踪分支) 31 | * [远程仓库操作](#远程仓库操作) 32 | * [克隆仓库](#克隆仓库) 33 | * [查看远程仓库](#查看远程仓库) 34 | * [添加远程仓库](#添加远程仓库) 35 | * [远程仓库的拉取推送](#远程仓库的拉取推送) 36 | * [远程仓库移除重命名](#远程仓库移除重命名) 37 | 38 | 39 | 40 | 41 | ## 安装Git 42 | 43 | **Linux** 44 | 45 | `$ sudo yum install git` 46 | 47 | 或者 48 | 49 | `$ sudo apt-get install git` 50 | 51 | **Mac** 52 | 53 | 直接在`Terminal`执行`git`命令, 如果没有会提示安装方法. 54 | 55 | 如果喜欢安装程序的话, [点这里](http://git-scm.com/download/mac) 56 | 57 | ## 配置文件 58 | 59 | 第一次安装, 千万别要忘了配置你的git的使用者名字和邮箱. 这个道理很简单, 每一次的提交时需要记录提交者的, 如果没有提交者, 那么bug出现找谁来背锅?? 60 | 61 | 62 | ### 查看config 63 | 64 | Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。 65 | 66 | **`/etc/gitconfig` 文件: 包含系统上每一个用户及他们仓库的通用配置。** 67 | 68 | `$ git config --system --list` 69 | 70 | --- 71 | 72 | **`~/.gitconfig` 或 `~/.config/git/config` 文件:只针对当前用户。** 73 | 74 | `$ git config --global --list` 75 | 76 | --- 77 | 78 | 79 | 当前使用仓库的 Git 目录中的 config 文件(就是 `.git/config`):针对该仓库。 80 | 81 | 每一个级别覆盖上一级别的配置,所以`.git/config`的配置变量会覆盖`/etc/gitconfig`中的配置变量。 82 | 83 | --- 84 | 85 | **当然, 也可以查看某一项配置信息.** 86 | 87 | * 形式:`git config [--global|--system] ` 88 | 89 | *查看最终的配置属性值* 90 | `$ git config user.name` 91 | 92 | *查看当前系统的属性值* 93 | `$ git config --global user.name` 94 | 95 | 96 | --- 97 | 98 | ### 设置用户信息 99 | 100 | `$ git config --global user.name "name"` 101 | 102 | `$ git config --global user.email xxx@163.com` 103 | 104 | 使用`--global`选项, 当以后在该系统上做的任何操作都会使用此属性. 如果你需要在某一个特定的项目使用其他名字和邮箱, 那么可以通过设置**项目中的config文件**, 这样config中的属性会覆盖掉**global的全局属性**, 并且当在其他项目中并不会造成影响. 使用方式只需要去掉`--global`参数即可修改项目中的`.git/config`文件 105 | 106 | `$ git config user.name "name"` 107 | 108 | 109 | --- 110 | 111 | **获取帮助手册** 112 | 113 | 形式: `git help ` 或者 `git --help` 114 | 115 | 比如查看config手册 116 | 117 | `$ git help config` 118 | 119 | ### Git别名 120 | 121 | 例如: 生成别名之后可以在日后用简短的表示来使用 122 | 123 | ``` 124 | $ git config --global alias.br branch 125 | $ git config --global alias.ci commit 126 | $ git config --global alias.st status 127 | ``` 128 | 129 | 130 | ### .gitignore文件 131 | 132 | 对于自动生成的文件, 日志, 编译的临时文件等. 可以对其进行配置, 让git不追踪这些文件 133 | 134 | 规范如下: 135 | 136 | * 所有空行或者以 # 开头的行都会被 Git 忽略。 137 | * 可以使用标准的 glob 模式匹配。 138 | * 匹配模式可以以(/)开头防止递归。 139 | * 匹配模式可以以(/)结尾指定目录。 140 | * 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反 141 | 142 | `glob模式`是指shell所使用的简化了的正则表达式. 143 | 144 | * `*` :匹配零个或多个任意字符 145 | * `[abc]` :只匹配括号内的任意一个字符 146 | * `[0-9]` :使用短划线表示范围, 可以匹配0到9之间的任何字符. 147 | * `?` :匹配任意一个字符 148 | * `**`:匹配任意的中间目录,例如`a/**/z`可以匹配`a/z`,`a/b/z`,`a/b/c/z`等 149 | 150 | 如下给出一个样板: 151 | 152 | 153 | ``` 154 | # 忽略所有以 .c结尾的文件 155 | *.c 156 | 157 | # 但是 stream.c 会被git追踪 158 | !stream.c 159 | 160 | # 只忽略当前文件夹下的TODO文件, 不包括其他文件夹下的TODO例如: subdir/TODO 161 | /TODO 162 | 163 | # 忽略所有在build文件夹下的文件 164 | build/ 165 | 166 | # 忽略 doc/notes.txt, 但不包括多层下.txt例如: doc/server/arch.txt 167 | doc/*.txt 168 | 169 | # 忽略所有在doc目录下的.pdf文件 170 | doc/**/*.pdf 171 | ``` 172 | 173 | 174 | ## 仓库的基础操作 175 | 176 | 177 | ### 初始化仓库 178 | 179 | 180 | ``` 181 | $ git init 182 | ``` 183 | 184 | 185 | 186 | 187 | ### 添加文件到暂存区 188 | 189 | 190 | ``` 191 | # 添加全部暂存区和历史区不存在的或者有更改的 `.c`结尾的文件 192 | $ git add *.c 193 | 194 | # 添加全部暂存区和历史区不存在的或者有更改的文件 195 | $ git add . 196 | 197 | # 指定文件添加 198 | $ git add test.c 199 | ``` 200 | 201 | 202 | 203 | --- 204 | 205 | 206 | ### status仓库 207 | 208 | 209 | 210 | ``` 211 | $ git status 212 | 213 | # 如果需要显示一种紧凑式格式 214 | $ git status --short # 等价于 $ git status -s 215 | ``` 216 | 217 | 紧凑式中字母的表示含义如下: 218 | 219 | * `??` :表示新添加的未追踪的文件 220 | * ` M` :M出现在右边,表示该文件被修改但是还没有放入暂存区 221 | * `M ` :M出现在左边,表示文件被修改已经放入了暂存区 222 | * `MM` :出现两个,代表此文件在工作区修改已经放入了暂存区, 但之后有进行了修改,没有添加到暂存区 223 | 224 | 225 | --- 226 | 227 | 228 | 229 | 230 | ### diff仓库 231 | 232 | 如果你想知道文件具体修改的内容, 那么`diff`会很有用 233 | 234 | 235 | ``` 236 | # 查看以追踪但尚未暂存的文件更新了哪些部分, 不添加额外参数 237 | $ git diff 238 | 239 | # 对使用了add添加到了暂存区的内容, 使用--staged参数或者--cached 240 | $ git diff --staged 241 | 242 | ``` 243 | 244 | 245 | ### 提交更新 246 | 247 | 248 | ``` 249 | # 常规做法 250 | $ git commit -m "commit message" 251 | 252 | # 如果不使用-m参数添加提交信息, git会使用默认的编译器如vi进行提交描述编写. 253 | # 可通过$ git config --global core.edit 设定喜欢的编译器 254 | 255 | # 跳过暂存区操作, 直接从工作区提交到历史区 256 | $ git commit -a -m "" #等价于: $ git commit -am "" 257 | 258 | 259 | 260 | ``` 261 | 262 | 263 | ### 移除文件 264 | 265 | 如果在工作区间对一个文件进行删除, 需要先进行`add`,然后才可以提交. 使用`git rm`可以直接在工作区间删除文件, 并提交到暂存区. 266 | 267 | 268 | ``` 269 | $ git rm fileName 270 | 271 | # 如果文件修改,并添加了暂存区, 需要使用-f参数来强制删除(force) 272 | $ git rm -f fileName 273 | 274 | # 可以使用glob模式,如下 275 | $ git rm log/\*.log # 删除log目录下所有名称是.log结尾文件 276 | $ git rm \*~ # 删除以~结尾的所有文件 277 | ``` 278 | 279 | 280 | ### 移动文件 281 | 282 | 同样使用`git rm`会方便很多, 并且如果相对文件重命名也可以如此 283 | 284 | 285 | ``` 286 | $ git mv file_from file_to 287 | ``` 288 | 当执行了这条语句之后, 只需要在下一次`commit`即可, 不需要考虑额外操作. 等价于如下: 289 | 290 | ``` 291 | $ mv file_from file_to 292 | $ git rm file_from 293 | $ git rm file_to 294 | 295 | ``` 296 | 297 | 298 | ### 查看历史提交 299 | 300 | 如果是最基本的`git log`, 那么会按提交时间列出所有更新, 包括提交的SHA-1校验和, 作者名称,邮箱,提交时间,提交说明. 下面说说常用选项. 301 | 302 | 303 | ``` 304 | # -p:仅显示最近x次的提交 格式 $ git log -p -x 305 | $ git log -p -2 # 查看最近的两次提交内容 306 | 307 | 308 | # --stat:简略的显示每次提交的内容更改, 如哪些文件变更,多少删除,多少添加等 309 | $ git log --stat 310 | 311 | # --pretty: 指定默认不同格式展示信息 312 | $ git log --pretty=oneline #每次提交用一行显示 313 | 314 | $ git log --pretty=format:"%h - %an, %ar : %s" 315 | # 效果:1a99c42 - 苏, 19 hours ago : 无意义提交 316 | 317 | 318 | ``` 319 | **关于`format`对应的常用占位符的写法和意义** 320 | 321 | |选项 |说明| 322 | |---|---| 323 | |%H|提交对象(commit)的完整哈希字串| 324 | |%h|提交对象的简短哈希字串| 325 | |%T|树对象(tree)的完整哈希字串| 326 | |%t|树对象的简短哈希字串| 327 | |%P|父对象(parent)的完整哈希字串| 328 | |%p|父对象的简短哈希字串| 329 | |%an|作者(author)的名字| 330 | |%ae|作者的电子邮件地址| 331 | |%ad|作者修订日期(可以用 --date= 选项定制格式)| 332 | |%ar|作者修订日期,按多久以前的方式显示| 333 | |%cn|提交者(committer)的名字| 334 | |%ce|提交者的电子邮件地址| 335 | |%cd|提交日期| 336 | |%cr|提交日期,按多久以前的方式显示| 337 | |%s|提交说明| 338 | 339 | --- 340 | 341 | **图形展示分支的合并历史** 342 | ``` 343 | $ git log --graph --oneline #oneline只是让输出看起来比较舒服 344 | ``` 345 | 346 | --- 347 | 348 | **git log的一些其他操作** 349 | 350 | |选项 |说明| 351 | |---|---| 352 | |-p|按补丁格式显示每个更新之间的差异。| 353 | |--stat|显示每次更新的文件修改统计信息。| 354 | |--shortstat|只显示 --stat 中最后的行数修改添加移除统计。| 355 | |--name-only|仅在提交信息后显示已修改的文件清单。| 356 | |--name-status|显示新增、修改、删除的文件清单。| 357 | |--abbrev-commit|仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。| 358 | |--relative-date|使用较短的相对时间显示(比如,“2 weeks ago”)。| 359 | |--graph|显示 ASCII 图形表示的分支合并历史。| 360 | |--pretty|使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。| 361 | 362 | 363 | **查找一个字符串的出现和删除的提交** 364 | 365 | ``` 366 | # 使用限制符-S后面紧跟要查询的字符串 367 | $ git log -Smethod_name 368 | 369 | # 或者针对一个文件进行更改的提交查询, 只需要在后面追加文件名称即可 370 | $ git log fileName 371 | ``` 372 | 373 | 哈哈,即使如果你不小心写个隐藏bug不管几个月之后,如果老大要想找出问题是写引发的其实很简单.例如这样 374 | `$ git log -p fileName` 想甩锅?算了吧,还是认错以后多注意吧. 375 | 376 | **还有一些限制log输出的选项** 377 | 378 | 379 | 380 | |选项| 说明| 381 | |---|---| 382 | |-(n)|仅显示最近的 n 条提交| 383 | |--since, --after|仅显示指定时间之后的提交。| 384 | |--until, --before|仅显示指定时间之前的提交。| 385 | |--author|仅显示指定作者相关的提交。| 386 | |--committer|仅显示指定提交者相关的提交。| 387 | |--grep|仅显示含指定关键字的提交| 388 | |-S|仅显示添加或移除了某个关键字的提交| 389 | 390 | 一个实际的例子,如果要查看 Git 仓库中,2016 年 11 月1号到7号,作者叫苏的提交文件,可以用下面的查询命令: 391 | 392 | ``` 393 | git log --pretty="%h - %s" --author=苏 --since="2016-11-01" \ 394 | --before="2016-11-07" 395 | ``` 396 | 397 | 398 | ### 撤销操作 399 | 400 | **amend重新提交** 401 | 402 | 当我们`commit`之后突然发现漏掉了一个文件, 这个时候不可能对一个文件再进行`commit`一次, 这样做就显得很多余, 而如果版本回退之前再添加也比较麻烦. 这个时候就可以使用这个`amend`命令.如下: 403 | 404 | 405 | ``` 406 | $ git commit -m "版本1.5开发代码" 407 | 408 | # 正当你松了一口气的时候发现配置文件忘记修改了, 你赶紧修改,并适合用add到暂存区 409 | $ git add project.property 410 | $ git commit --amend 411 | # 你会神奇的发现你没有增加任何多余的操作就把漏掉的文件补齐到最后一次提交中 412 | ``` 413 | --- 414 | 415 | **取消暂存的文件** 416 | 417 | 就是对文件进行了`git add`操作. 这个时候可以`reset` 418 | 419 | 420 | ``` 421 | # 让暂存区的文件变成文件修改但是没有添加暂存区的状态 422 | $ git reset HEAD fileName 423 | ``` 424 | 425 | **撤销对文件的修改** 426 | 427 | 场景: 当文件修改了, 但是还没有进行`git add`的时候还只是在工作区间, 还原成最后一次提交的内容 428 | 429 | 430 | ``` 431 | $ git checkout -- filename 432 | ``` 433 | 要注意使用, 使用之前确定是否要抛弃已经添加的内容. 因为这个动作可能让你最新添加的内容彻底丢失. 因为没有进行`commit`, 一般来说进行了`commit`的内容都是可以恢复的. 434 | 435 | 436 | 437 | ### 标签 438 | 439 | >Git可以给历史中的某一个提交打上标签, 以示重要. 比如每次正式版本的上线等. 440 | 441 | **列出标签** 442 | 443 | 444 | ``` 445 | $ git tag 446 | 447 | # 如果你只关心某一部分的标签, 例如只对v2.x的标签感兴趣,你可以这样做 448 | $ git tag -l 'v2.*' 449 | ``` 450 | 451 | --- 452 | 453 | **创建标签** 454 | 455 | 标签分为两种一种是`附加标签`另一种是`轻量标签`. 456 | 457 | * 附加标签: 会保存打标签者的信息, 时间和附加信息. 最后更随打标签的提交 458 | * 轻量标签: 只是在一个提交上做一个标记. 存储在一个专门保存标签的文件,指向提交的hash值 459 | 460 | 先来看附加标签: 461 | 462 | 463 | ``` 464 | $ git tag -a v1.0 -m '附加信息' 465 | ``` 466 | 467 | 轻量标签 468 | 469 | 470 | ``` 471 | $ git tag v1.0 472 | ``` 473 | 474 | 后期打标签, 就是对已经提交某次提交进行追加标签设置 475 | 476 | 477 | ``` 478 | # 可以先使用git log --oneline获取提交历史的hash值 479 | $ git log --oneline 480 | 481 | # 然后把hash值赋值到标签语句之后 482 | $ git tag -a v1.1 a6b4c97 483 | ``` 484 | 485 | **共享标签** 486 | 487 | 默认情况下, `git push`不会把标签传递到远程服务器. 需要显示的推送标签共享到服务器 488 | 例如: `git push origin [tagname]` 489 | 490 | ``` 491 | $ git push origin v1.4 492 | 493 | # 如果想把本地上所有在远程服务器上的不存在标签删除掉,可以这样 494 | $ git push origin --tags 495 | ``` 496 | --- 497 | 498 | **检出标签** 499 | 500 | git中不能真正的检出一个标签, 但是可以在标签处创建一个新的分支.如下 501 | 502 | 503 | ``` 504 | $ git checkout -b checkbranch2 v2.0 505 | ``` 506 | --- 507 | 508 | **查看标签对应的信息** 509 | 510 | 使用`git show <标签名>`可以查看对应标签的详细信息, 如果`git show`这样的命令只是显示上次提交的内容 511 | 512 | ``` 513 | $ git show v1.4 514 | ``` 515 | 516 | 517 | 518 | ## 分支操作 519 | 520 | 521 | ### 分支的创建 522 | 523 | 分支的创建, 其实本质就是创建一个可以移动的指针,这个指针名就是新的分支名 524 | 525 | 526 | ``` 527 | $ git branch dev 528 | 529 | # 上面的只是创建了一个分支. 并没有切换, 可以使用一条命令创建并且换到新分支 530 | $ git checkout -b dev 531 | 532 | # 可以通过`git log`命令查看各个分支所指向的对象 533 | $ git log --oneline --decorate 534 | 535 | # 如果你想查看图形式的分叉历史,可以这样: 536 | $ git log --oneline --decorate --graph --all 537 | ``` 538 | 539 | 540 | ### 分支的切换 541 | 542 | 所谓的分支切换就是`HEAD`指针的指向的改变 543 | 544 | 545 | ``` 546 | $ git checkout dev 547 | ``` 548 | 549 | 550 | ### 分支合并 551 | 552 | 利用`git merge <要合并到的目标分支>`, 这条命令会把当前所在分支与目标分支的内容合并, 可以这样理解, 如果当你试图向目标分支合并时, 如果当前分支可以顺着一个分支走下去, 那么本质上其实只是当前指针的向前移动, 由于这种情况下的合并并没有需要解决的分期, 所以git会称这个是`fast-forward`快速前进. 553 | 554 | 555 | ``` 556 | git merge dev 557 | ``` 558 | 559 | 560 | ### 删除分支 561 | 562 | 当一个功能分支开发完毕之后, 并进行了合并, 通常这个分支也就被删除,以保证仓库中的干净. 563 | 564 | 565 | ``` 566 | # 删除dev分支 567 | $ git branch -d dev 568 | 569 | # 如果dev分支还有未提交的内容,为了保证你的数据安全git默认是不允许删除,可以使用`-D`强制删除 570 | $ git branch -D dev 571 | 572 | ``` 573 | 574 | 575 | ### 分支管理 576 | 577 | `git branch`命令不只是可以创建于删除分支. 如果不添加任何参数, 那么会得到所有分支的一个列表 578 | 579 | ``` 580 | $ git branch # 其中一个分支前面的*代表,目前检出的分支就是HEAD指针所指向的分支 581 | 582 | # 追加-v参数 可以展示每一个分支最后一次提交 583 | $ git branch -v 584 | 585 | # --merged:查看那些分支已经合并到当前分支 586 | $ git branch --merged # 一般这个列表展示的除了*号的其他分支, 都可以删除 587 | 588 | # --no-merged:查看所有包含未合并工作的分支 589 | $ git branch --no-merged 590 | ``` 591 | 592 | 593 | 594 | ### 变基 595 | 596 | > 和合并`merge`相似的效果都是合并分支, 但是使用变基`rebase`可以让提交历史变得更简洁. 如下 597 | 598 | ![](rebase_1.png) 599 | ![](rebase_2.png) 600 | 601 | 图片1是`merge`合并效果, 图片2是`rebase`合并效果. 明显变基会让提交历史看起来更加干净. 使用如下: 602 | 603 | 604 | ``` 605 | # rebase <目标分支名> [需要移动变基底的分支] 606 | $ git rebase master experiment 607 | 608 | # 此时目标分支后面会追加另一个分支的提交. 此时只需要切换到master分支,合并分支即可. 609 | $ git checkout master 610 | $ git merge experiment 611 | 612 | ``` 613 | 614 | `rebase`原理就是, 从目标分支和要变基的分支向上查找出共同祖先节点就是`c2`, 然后把要变基的分支到`c2`节点的所有提交,提取出相应的修改生成一个副本, 并追加到目标分创建相对应的提交. 此时变基的分支指向`目标分支master`的后面某一次提交. 此时只要使用修改`master`指向指针使用`merge`即可. 615 | 616 | 617 | 618 | ### 远程分支 619 | 620 | 621 | ``` 622 | # 获得远程的仓库列表 623 | $ git ls-remote origin 624 | 625 | # 如果想获得更多的信息 626 | $ git remote show origin 627 | 628 | # 查看远程分支和本地分支 629 | $ git branch -a 630 | ``` 631 | 632 | --- 633 | 634 | **拉取远程分支** 635 | 636 | 假设远程如果有一个`dev`分支, 你使用`fetch`进行抓取. 这个时候, 本地不会自动生成一个可编辑的副本, 换句话说就是这种情况下, 不会有一个新的`dev`本地分支, 只有一个不可以修改的`origin/dev`指针. 这个时候可以运行`git merge origin/dev`命令, 将这些远程dev分支的工作合并到当前分支. 如果想要在自己本地的dev分支上工作, 可以将其建立在远程分支之上. 637 | 638 | 639 | ``` 640 | $ git checkout -b dev origin/dev 641 | ``` 642 | 643 | --- 644 | 645 | **创建远程分支** 646 | 647 | 如果你的本地有一个新建的`dev`分支, 并且你进行了提交, 此时你想把这个分支也提交到远程的`dev`分支, 但是远程还没有创建`dev`, 这个时候可以使用如下命令: `git push <远程仓库名> <要推送的本地分支>` 648 | 649 | 650 | ``` 651 | $ git push origin dev 652 | ``` 653 | 654 | --- 655 | 656 | **删除远程分支** 657 | 658 | 659 | ``` 660 | # 删除远程dev分支 661 | $ git push origin --delete dev 662 | ``` 663 | 664 | 665 | ### 跟踪分支 666 | 667 | 从一个远程跟踪分支检出一个本地分支会自动创建一个叫做 `跟踪分支`(有时候也叫做 “上游分支”)。 跟踪分支是与远程分支有直接关系的本地分支。 如果在一个跟踪分支上输入` git pull`,`Git` 能自动地识别去哪个服务器上抓取、合并到哪个分支。 668 | 669 | 当克隆一个仓库时,它通常会自动地创建一个跟踪 `origin/master` 的 `master` 分支。 然而,如果你愿意的话可以设置其他的跟踪分支 - 其他远程仓库上的跟踪分支,或者不跟踪 `master` 分支。 最简单的就是之前看到的例子,运行 `git checkout -b [branch] [remotename]/[branch]`。 这是一个十分常用的操作所以 Git 提供了 `--track` 快捷方式: 670 | 671 | 672 | ``` 673 | $ git checkout --track origin/dev 674 | 675 | # 如果想要自定义本地分支名字 676 | $ git checkout -b 任意分支名字 origin/dev 677 | ``` 678 | 679 | --- 680 | 681 | **设置已有的本地分支跟踪一个刚刚拉取下来的远程分支**,或者想要修改正在跟踪的上游分支,你可以在任意时间使用 -u 或 --set-upstream-to 选项运行 git branch 来显式地设置。 682 | 683 | 684 | ``` 685 | # 设置HEAD指向的分支的上游为远程dev分支 686 | $ git branch -u origin/dev 687 | ``` 688 | 689 | --- 690 | 691 | **查看设置的所有跟踪分支** 692 | 693 | 694 | ``` 695 | $ git branch -vv 696 | ``` 697 | 698 | 699 | ## 远程仓库操作 700 | 701 | 702 | 703 | ### 克隆仓库 704 | 705 | 706 | ``` 707 | $ git clone 708 | 709 | # 如果你想创建自己的仓库名, 就是本地根文件夹的名称, 那么可以如下追加名称 710 | $ git clone [dirName] 711 | ``` 712 | 713 | 714 | ### 查看远程仓库 715 | 716 | 717 | ``` 718 | # 默认查看远程仓库的方式 719 | $ git remote 720 | 721 | # 查看远程仓库的读写权限. 如果可以看到(push)说明提交的推送 722 | $ git remote -v 723 | ``` 724 | 725 | 如果需要查看某一个仓库更多的信息时, 使用`git remote show ...` 726 | 727 | ``` 728 | $ git remote show origin 729 | ``` 730 | 731 | 732 | ### 添加远程仓库 733 | 734 | 735 | ``` 736 | # 格式: git remote add [shortName] 同时可以指定一个仓库的引用名称,例如 737 | $ git remote add rp git@github.com:suzeyu1992/GitOperateDemo.git 738 | 739 | # 此时你想对新加的远程进行拉取动作, 只需要使用之前的引用名称就可以 740 | $ git fetch rp 741 | 742 | ``` 743 | 744 | 745 | ### 远程仓库的拉取推送 746 | 747 | **拉取** 748 | 749 | 大家常用的可能是`git pull`这个指令. 这个指令的通常会从服务器上抓取数据自动尝试合并到当前所在分支. 750 | 751 | 而我们可以也可以利用`git fetch `进行本地分支所追踪的远程分支没有提交. 然后我们可以手动的进行合并. 752 | 753 | **推送** 754 | 755 | `git push [remote-name] [branch-name]` 例如: 756 | 757 | ``` 758 | $ git push origin master 759 | ``` 760 | 761 | 只有当对服务器有写入权限, 并且之前没有人提交, 这条命令才会生效. 762 | 763 | 上面的这两个推送也好拉取也好. 如果所在分支设置了远程服务器分支的追踪, 那么可以省略掉后面的仓库名和分支名. 如果没有设置那么必须显示的指定. 764 | 765 | 766 | 767 | 768 | ### 远程仓库移除重命名 769 | 770 | 对远程仓库的名称进行修改 771 | 772 | ``` 773 | $ git remote rename oldName newName 774 | ``` 775 | 776 | 想要移除一个远程仓库,例如服务器搬走了,不再使用一个特定镜像,或者一个贡献者不再贡献 777 | 778 | 779 | ``` 780 | $ git remote rm 仓库名 781 | ``` 782 | 783 | 784 | -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇三)/README.md: -------------------------------------------------------------------------------- 1 | > 对于设计模式这是牛人们对代码中的一定场景而进行提炼的结果, 对于一个进阶的开发人员这是一个必不可少的技能. 当代码越写越好, 更易扩展更加灵活. 这对于Coder来说是最酷的事情. 2 | 3 | > 通过`设计模式`和`Android源码`中的关系, 可以更加清楚的记住各个模式的特点, 和源码中的实现方式. **多练多分析之间的关系**这是必不可少的一步! 4 | 5 | 6 | * [设计模式与Android(篇一)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%80)) 7 | * [设计模式与Android(篇二)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%BA%8C)) 8 | * [设计模式与Android(篇三)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%89)) 9 | 10 | 文章只是对`设计模式`做一个场景比对, 实现. 和简单说明. 最好可以自己动手代码实现, 分析代码之间的关系, 才能真正的学以致用. 11 | 12 | 13 | 14 | 15 | * [中介者模式 Mediator](#1) 16 | * [模式介绍](#2) 17 | * [模式范例](#3) 18 | * [Android源码模式实现](#4) 19 | * [代理模式 Proxy](#5) 20 | * [模式介绍](#6) 21 | * [模式范例](#7) 22 | * [Android源码模式实现](#8) 23 | * [组合模式 Composite](#9) 24 | * [模式介绍](#10) 25 | * [模式范例](#11) 26 | * [Android源码模式实现](#12) 27 | * [适配器模式 Adapter](#13) 28 | * [模式介绍](#14) 29 | * [模式范例](#15) 30 | * [Android源码模式实现](#16) 31 | * [装饰模式 Decorator](#17) 32 | * [模式介绍](#18) 33 | * [模式范例](#19) 34 | * [Android源码模式实现](#20) 35 | * [享元模式 Flyweight](#21) 36 | * [模式介绍](#22) 37 | * [模式范例](#23) 38 | * [Android源码模式实现](#24) 39 | * [外观模式 Facade](#25) 40 | * [模式介绍](#26) 41 | * [模式范例](#27) 42 | * [Android源码模式实现](#28) 43 | * [桥接模式](#29) 44 | * [模式介绍](#30) 45 | * [模式范例](#31) 46 | * [Android源码模式实现](#32) 47 | 48 | 49 | 50 | 51 | ### 中介者模式 Mediator 52 | 53 | 54 | #### 模式介绍 55 | 56 | > 也称为调节者模式或者调停者模式 57 | 58 | * `定义`: 包装了一系列对象互相作用的方式, 使得这些对象不必互相明显作用. 从而使他们可以松散耦合. 当某些对象之间的作用发生改变时, 不会立即影响其他的一些对象之间的作用. 保证这些作用可以彼此独立的变化. 中介者模式将多对多的关系转化为一对多的相互作用. 中介者模式将对象的行为和协作抽象化, 把对象在小尺度的行为上与其他对象的相互作用分开处理. 59 | * `场景`: 当对象之间的交互操作很多且每个对象的行为都依赖彼此时, 为防止在修改一个对象的行为会涉及修改很多其他对象的行为, 可采用中介者模式, 来解决紧耦合问题. 该模式将对象之间的多对多关系变成了一对多关系, 中介者对象将系统从网状结构变成以调停者为中心的星形结构, 达到降低系统的复杂性, 提高可扩展的作用. 60 | 61 | 62 | #### 模式范例 63 | 64 | 场景: 以电脑为例, 电脑主要部分为:CPU, 内存, 显卡, IO设备. 通常需要一个东西把这些组件连接起来共同工作,这就是主板的工作. 任何的两块模块之间的通信都会经过主板去协调. 这里以读取光盘为例.看主板是如何充当这个中介者角色的. 65 | 66 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/mediator) 67 | 68 | ![](UML_Mediator.png) 69 | 70 | 代码使用 71 | 72 | 73 | ```java 74 | public static void main(String arg[]){ 75 | 76 | // 构造主板对象 77 | MainBoard mainBoard = new MainBoard(); 78 | 79 | // 构造各个零件同事 80 | CDDevice cdDevice = new CDDevice(mainBoard); 81 | CPU cpu = new CPU(mainBoard); 82 | GraphicsCard graphicsCard = new GraphicsCard(mainBoard); 83 | SoundCard soundCard = new SoundCard(mainBoard); 84 | 85 | // 将各个部件安装到主板 86 | mainBoard.setCdDevice(cdDevice); 87 | mainBoard.setCpu(cpu); 88 | mainBoard.setGraphicsCard(graphicsCard); 89 | mainBoard.setSoundCard(soundCard); 90 | 91 | // 完成后开始放片 92 | cdDevice.load(); 93 | } 94 | ``` 95 | 96 | 97 | 从图片可以看出, 虽然彼此间会互相交互, 但是通过中介者模式, 会让一个网状的关系, 转成一个以`中介者`为中心的星状图. 98 | 99 | 100 | #### Android源码对应实现 101 | 102 | 中介者模式在`Android`源码中比较好的例子是`Keyguard`锁屏的实现. 103 | 104 | 105 | ```java 106 | public class KeyguardViewMediator extends SystemUI { 107 | private AlarmManager mAlarmManager; 108 | private AudioManager mAudioManager; 109 | private StatusBarManager mStatusBarManager; 110 | private boolean mSwitchingUser; 111 | 112 | private boolean mSystemReady; 113 | private boolean mBootCompleted; 114 | private boolean mBootSendUserPresent; 115 | // .... 116 | } 117 | ``` 118 | 119 | 可以看到类中存在很多`XXManager`的变量, 这些各种各样的管理器就是各个具体的实现类, `Android`使用`KeyguardViewMediator`充当这个中介者协调这些管理器的状态改变, 同样也会定义很多方法来处理这些管理器的状态, 以解锁或锁屏时声音的播放为例, 对应的方法`playSounds()`来协调音频这一状态. 120 | 121 | 而其他管理器的协调同样可以在此类找到. 122 | 123 | --- 124 | 125 | 而另一个中介者模式的例子就是`Binder`机制, 在`Binder`机制中有3个非常重要的组件`ServiceManager`,`Binder Driver`和`Bp Binder`. 其中`Bp Binder`是`Binder`的一个代理角色, 其提供了`IBinder`接口给各个客户端服务使用, 这三者就扮演了一个中介者角色 126 | 127 | 当手机启动后, `ServiceManager`会先向`Binder Driver`进行注册, 同样`ServiceManager`也是一个服务, 但特殊性在于, 它在`Binder Driver`中是最先被注册的, 其注册`ID`为0, 当其他的服务想要注册到`Binder Driver`时, 会先通过这个`0号ID获取到ServiceManager`所对应的`IBinder`接口, 该接口实质上的实现逻辑是由`Bp Binder`实现的, 获取到对应的接口后就回调其中的`transact()`方法, 此后就会在`Binder Driver`中注册一个`ID 1`来对应这个服务, 如果客户端想要使用这个服务, 那么他会先获取`ID 0`的接口, 也就是`ServiceManager`所对应的接口, 并调用其`transact()`要求连接到刚才的服务, 这个时候`Binder Driver`就会将`ID 1`的服务回传给客户端并将相关信息反馈给`ServiceManager`完成连接. 这里`ServiceManger`和`Binder Driver`就相当于一个中介者, 协调各个服务器和客户端. 128 | 129 | 130 | 131 | ### 代理模式 Proxy 132 | 133 | 134 | #### 模式介绍 135 | 136 | > 也称委托模式, 结构性设计模式. 生活中也是有很多常见的代理例子, 代理上网, 叫外卖, 通过律师打官司都是一种代理 137 | 138 | * `定义`: 为其他对象提供一种代理以控制对这个对象的访问 139 | * `场景`: 当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问, 为了保证客户端使用的透明性, 委托对象与代理对象需要实现相同的接口. 140 | 141 | 142 | 143 | #### 模式范例 144 | 145 | 场景: 公司拖欠工资, 员工通过律师来间接的和公司要钱. 146 | 147 | [范例源码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/proxy) 148 | 149 | ![](UML_Proxy.png) 150 | 151 | 使用时代码 152 | 153 | 154 | ```java 155 | public static void main(String args[]){ 156 | // 构造一个起诉者 157 | ConcreteLawsuit concreteLawsuit = new ConcreteLawsuit(); 158 | 159 | // 构造一个律师, 被代理者 160 | Lawyer lawyer = new Lawyer(concreteLawsuit); 161 | 162 | // 律师代理 163 | lawyer.submit(); 164 | lawyer.burden(); 165 | lawyer.defend(); 166 | lawyer.finish(); 167 | } 168 | ``` 169 | 170 | 代理模式大致分为两个部分, 一个是`静态代理`,还有一个是`动态代理`. 171 | 172 | * `静态代理`如上述示例那样, 代理者的代码由程序员自己或者通过自动化工具生成固定的代码再对其进行编译, 也就是说在我们的代码运行前`代理类class`编译文件就已经存在 173 | * `动态代理`则与静态代理相反, 通过反射机制动态生成代理者对象, 也就是说我们在`code阶段`压根就不需要知道代理谁, 代理谁将会在执行阶段决定, 而`Java`也给我们提供了一个便捷的动态代理接口`InvocationHandler`, 并复写`invoke()` 174 | 175 | 动态代理最终的调用方式: 176 | 177 | 178 | ```java 179 | // 构造一个动态代理 180 | DynamicProxy dynamicProxy = new DynamicProxy(concreteLawsuit); 181 | 182 | // 获取被代理者的ClassLoader 183 | ClassLoader classLoader = concreteLawsuit.getClass().getClassLoader(); 184 | 185 | // 动态构造一个代理者律师 186 | ILawsuit law = (ILawsuit) Proxy.newProxyInstance(classLoader, new Class[]{ILawsuit.class}, dynamicProxy); 187 | 188 | // 动态调用 189 | law.submit(); 190 | law.burden(); 191 | law.defend(); 192 | law.finish(); 193 | ``` 194 | 195 | 196 | #### Android源码对应实现 197 | 198 | `Android`源码中的代理模式实现有很多, 如源码中的`ActivityManagerProxy`代理类, 其具体代理的是`ActivityManagerNative`的子类`ActivityManagerService`. `ActivityManagerProxy`与`ActivityManagerNative`处于同一个文件. 199 | 200 | 而`ActivityManagerProxy`和`ActivityManagerNative`都继承了`IActivityManager` 201 | 202 | ![](proxy-ams.png) 203 | 204 | 可以很明显的看出这三个类构成的`代理模式`, 但是由于`AMN`是抽象类, 所以具体的实现交由了子类`AMS`去实现. 而`AMS`是系统级的`Service`并且运行于独立的进程空间中, 可以通过`ServiceManager`来获取它. 而`AMP`也运行于自己所处的进程空间中, 两者并不相同, 因此`AMS`和`AMP`的通信必定是通过跨进程来进行的, 所以此处源码中所实现的实质为远程代理. 205 | 206 | `AMP`在实际的逻辑处理中并未过多地被外部类使用, 因为在`Android`中管理与维护`Activity`相关信息的是另一个叫做`ActivityManager`的类, `ActivityManager`虽说管理着相关信息, 但是实质上其大多数逻辑都是由`AMP`承担的. 207 | 208 | 209 | ### 组合模式 Composite 210 | 211 | 212 | 213 | #### 模式介绍 214 | 215 | > 结构性设计模式, 比较简单, 把一组相似的对象看做一个对象来处理, 并根据一个树状结构来组合对象, 然后提供一个统一的方法去访问相应的对象, 以此忽略掉对象与对象集合之间的差别. 216 | 217 | * `定义`: 将对象组合成树形结构以表示`整体-部分`的层次结构, 使得用户对单个对象和组合对象的使用一致性 218 | * `场景`: 219 | * 表示对象的部分-整体层次结构 220 | * 从一个整体中能够独立出部分模块或功能的场景 221 | 222 | 223 | #### 模式范例 224 | 225 | 一个很好的组合例子就是文件夹和文件之间的关系. 以此为例, 看看一个简单文件系统是如何构成的. 226 | 227 | [范例源码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/composite) 228 | 229 | ![](UML_Composite.png) 230 | 231 | 使用代码和结果 232 | 233 | 234 | ```java 235 | public static void main(String arg[]){ 236 | // 构造一个目录对象表示c盘目录 237 | Folder diskC = new Folder("C"); 238 | 239 | // C盘根目录下有一个文件 Log.txt 240 | diskC.addDir(new File("Lag.txt")); 241 | 242 | // C盘下还有3个子目录 243 | diskC.addDir(new Folder("目录1")); 244 | 245 | Folder dirs = new Folder("目录2"); 246 | dirs.addDir(new File("null.txt")); 247 | diskC.addDir(dirs); 248 | 249 | diskC.addDir(new Folder("目录3")); 250 | 251 | // 打印文件结构 252 | diskC.print(); 253 | } 254 | 255 | // =========> 结果 256 | C (Lag.txt,目录1 (),目录2 (null.txt),目录3 ()) 257 | ``` 258 | 从根节点依次延伸可以很明显看出这是一个树状的嵌套结构. 这就是组合模式 259 | 260 | 261 | 262 | #### Android源码对应实现 263 | 264 | 这个模式在`Android`有一个很经典的表示, 我们一直再使用, 就是`View`和`ViewGroup`结构. 265 | 266 | ![](composite-view.png) 267 | 268 | 由于`View`的视图层级中使用的是`安全的设计模式`, 所以只能是`ViewGroup`才可以包含`View`,反之则不可以, 而上面的范例使用的是`透明的组合模式`. 可以观察一下具体有哪些不同. 269 | 270 | 271 | ### 适配器模式 Adapter 272 | 273 | 274 | #### 模式介绍 275 | 276 | > 这也是一个我们从始至终都在使用的模式, `ListView`,`GridView`,`RecycleView`. 适器就是将两个不兼容的类融合在一起, 有点像粘合剂. 277 | 278 | 279 | * `定义`: 把一个类的接口转换成客户端所期待的另一个接口, 从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 280 | * `场景`: 281 | * 系统需要使用现有的类, 而此类的接口不符合系统的需要, 即接口不兼容 282 | * 想要建立一个可以重复使用的类, 用于与一些比起之间没有太大关联的一些类, 包括一些可能在将来引进的类一起工作 283 | * 需要一个统一的输出接口, 而输入端的类型不可预知 284 | 285 | 286 | #### 模式范例 287 | 288 | 软件开发有一句话: `任何问题都可以加一个中间层来解决`. 正式对适配器模式的描述, 最常见的就是笔记本电脑一般用的5V电压, 但是生活中的电压都是标准的220V. 所以我们笔记本都通过一个电源适配器来解决此问题. 289 | 290 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/adapter) 291 | 292 | 293 | 类图关系很简单就不贴出来了 294 | 295 | 代码中有两种实现: 296 | 297 | * `类适配器模式` : 主要是`Adapter`角色是**继承需要适配**的角色. 298 | * `对象适配器模式`: 通过在构造适配器的时候**传入适配对象**. 使用组合的形式实现接口兼容. 299 | 300 | 相比较, 使用`对象适配器`更加的灵活, 另一个好处就是被适配对象的方法不会暴露出来, 而`类适配器`由于继承了被适配的对象, 因此被适配对象类在`Adapter`类中同样存在, 这就使得`Adapter`出现了一些奇怪的方法, 用户的使用成本也较高. 301 | 302 | 303 | #### Android源码对应实现 304 | 305 | 不用说`Adapter`大家都知道. `Android`的做法增加了个`Adapter`层来隔离变化, 将`ListView`需要的关于`Item View`接口抽象到`Adapter`对象中, 并且在`ListView`内部调用了`Adapter`这些接口完成布局等操作. 这样只要用户实现了`Adapter`的接口, 并且将该`Adapter`设置给`ListView`, `ListView`就可以按照用户设定的`UI`效果, 数量, 数据来显示每一项数据. 306 | 307 | 308 | 309 | ### 装饰模式 Decorator 310 | 311 | 312 | #### 模式介绍 313 | 314 | > 也称包装模式, 结构性设计模式, 使用一种对客户端透明的方式来动态的扩展对象的功能, 同时他也是继承关系的一种替代方案之一 315 | 316 | * `定义`: 动态地给一个对象添加一些额外的职责. 就增加功能来说, 装饰模式相比生成子类更加灵活. 317 | * `场景`: 需要透明且动态地扩展类的功能时 318 | 319 | 320 | #### 模式范例 321 | 322 | 人穿衣服的例子 323 | 324 | [范例源码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/decorator) 325 | 326 | ![](UML_Decorator.png) 327 | 328 | 其实可以这种扩展并非是直接修改原有方法逻辑或者结构, 更恰当的说, 仅仅是在另一个类中将原有方法和逻辑进行封装整合. 329 | 330 | `装饰模式`和`代理模式`有点类似, 比较容易混淆的是会把装饰模式当成代理模式. `装饰模式`是以对客户端透明的方式扩展对象的功能, 是继承关系的一个替代方案. 而`代理模式`则是给一个对象提供一个对象代理, 并由代理对象来控制对原有对象的引用. `装饰模式`应该为所装饰的对象增强功能; `代理模式`对代理的对象施加控制, 但不对对象本身的功能增强. 331 | 332 | 333 | 334 | #### Android源码对应实现 335 | 336 | `Context`, 是不是熟悉的不能再熟悉了. 它的本质就是一个抽象类. 在装饰模式中相当于`抽象组件`. 虽然`Activity`继承了`Context`但是其中的`startActivity()`,`startService()`这些都是由另一个继承者来处理的的. 这个`Context`的另一个继承者就是`ContextImpl`. 337 | 338 | `ContextImpl`内部实现了`Context`的抽象方法. 而`Activity`等组件只是将其中的方法进行了转发调用. 339 | 340 | 341 | 342 | ### 享元模式 Flyweight 343 | 344 | 345 | #### 模式介绍 346 | 347 | > 用尽可能减少内存使用量, 它适合用于可能存在大量重复对象的场景, 来缓存可共享的对象, 达到对象共享, 避免创建过多对象的效果. 就可以提升性能, 避免内存抖动 348 | 349 | * `定义`: 使用共享对象可有效地支持大量的相似对象 350 | * `场景`: 351 | * 系统中存在大量的相似对象 352 | * 细粒度的对象都具备比较接近的外部状态, 而且内部状态与环境无关, 也就是说对象没有特定身份 353 | * 需要缓冲池的场景 354 | 355 | 356 | #### 模式范例 357 | 358 | 通过售票口的出票来为例 359 | 360 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/flyweight) 361 | 362 | ![](UML_Flyweight.png) 363 | 364 | 代码使用和结果 365 | 366 | 367 | ```java 368 | public class Client { 369 | 370 | public static void main(String arg[]){ 371 | Ticket ticket = TicketFactory.getTicket("青岛", "北京"); 372 | ticket.showTicketInfo("上铺"); 373 | 374 | Ticket ticket1 = TicketFactory.getTicket("青岛", "上海"); 375 | ticket1.showTicketInfo("上铺"); 376 | 377 | Ticket ticket2 = TicketFactory.getTicket("青岛", "北京"); 378 | ticket2.showTicketInfo("上铺"); 379 | } 380 | } 381 | 382 | // ========> 383 | 创建对象--> 青岛-北京 384 | 购买 从青岛 到 上铺 的北京火车票, 价格: 293 385 | 创建对象--> 青岛-上海 386 | 购买 从青岛 到 上铺 的上海火车票, 价格: 20 387 | 使用缓存--> 青岛-北京 388 | 购买 从青岛 到 上铺 的北京火车票, 价格: 141 389 | ``` 390 | 391 | 其实主要思想就是: 让可复用的对象实现复用, 减少无用的重复创建的步骤. 392 | 393 | 394 | 395 | #### Android源码对应实现 396 | 397 | `Message`对象. 在使用`Handler`传递数据的时候. 不可避免的需要使用`Message`. 即使你通过`Handler.post(Runnable)`传递一个接口, 在源码内部同样会通过`Message`为载体挂到`callback`变量上传递. 看一下. 源码中是如何维护一个频繁需要使用对象的 398 | 399 | 400 | ```java 401 | private static Message sPool; // 静态! 402 | 403 | // 获取一个Message 404 | public static Message obtain() { 405 | synchronized (sPoolSync) { 406 | if (sPool != null) { 407 | Message m = sPool; 408 | sPool = m.next; 409 | m.next = null; 410 | m.flags = 0; // clear in-use flag 411 | sPoolSize--; 412 | return m; 413 | } 414 | } 415 | return new Message(); 416 | } 417 | 418 | // 回收, 实现缓存的方法 419 | public void recycle() { 420 | if (isInUse()) { 421 | if (gCheckRecycle) { 422 | throw new IllegalStateException("This message cannot be recycled because it " 423 | + "is still in use."); 424 | } 425 | return; 426 | } 427 | recycleUnchecked(); 428 | } 429 | 430 | void recycleUnchecked() { 431 | flags = FLAG_IN_USE; 432 | what = 0; 433 | arg1 = 0; 434 | arg2 = 0; 435 | obj = null; 436 | replyTo = null; 437 | sendingUid = -1; 438 | when = 0; 439 | target = null; 440 | callback = null; 441 | data = null; 442 | 443 | synchronized (sPoolSync) { 444 | if (sPoolSize < MAX_POOL_SIZE) { 445 | next = sPool; 446 | sPool = this; 447 | sPoolSize++; 448 | } 449 | } 450 | } 451 | ``` 452 | 453 | 454 | `Android`是在调用了`recycle()`方法的时候实现了缓存, 在`obtain()`的时候取缓存如果没有, 那么就会创建新的对象. 缓存实现的方式是一个`单向链表`, 每次调用`recycle()`会把这个对象挂在`链表头`.看一下如下的图. 455 | 456 | ![](fly-message.png) 457 | 458 | 459 | 460 | ### 外观模式 Facade 461 | 462 | 463 | #### 模式介绍 464 | 465 | > 使用频率很高, 也可以说是第三方SDK都会使用, 本质就是加上一个中间层的传递, 既可以做到统一一个高层类, 降低用户的使用成本, 也能屏蔽一些实现细节. 可能你不经意间使用很多次此模式, 只是没有在理论层面认知它的存在. 466 | 467 | 468 | * `定义`: 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行. 门面模式也就是`Facade模式`提供了一个高层次的接口. 469 | * `场景`: 470 | * 为一个复杂子系统提供一个简单接口. 471 | * 当需要构建一个层次结构的子系统时. 使用外观模式定义子系统的每层的入口点. 如果子系统相互依赖可以仅通过`facade`进行通信. 472 | 473 | 474 | #### 模式范例 475 | 476 | 以手机的外观模式为例 477 | 478 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/facade) 479 | 480 | ![](UML_Facade.png) 481 | 482 | 483 | 484 | #### Android源码对应实现 485 | 486 | 还是`Context`, `Context`对于开发者来说是最重要的高层接口. `Context`只是定义了很多接口的抽象类, 这些接口的功能实现并不是在`Context`以及子类中, 而是通过其他的子系统来完成的, 例如`startActivity()`的真正实现是通过`AMS`, 获取应用包信息是通过`PMS`. 而`Centext`只是做了一个高层次的统一封装. 487 | 488 | 好处显而易见, 对于开发者, 你只要知道这个高层类即可. 不需要知道太多的子系统就能完成开发. 489 | 490 | 491 | 492 | ### 桥接模式 493 | 494 | 495 | #### 模式介绍 496 | 497 | > 结构性设计模式 498 | 499 | * `定义`: 将抽象部分与实际部分分离, 使他们都可以独立地进行变化 500 | * `场景`: 501 | * 一个类存在两个独立变化的维度, 且这两个维度都需要进行扩展 502 | * 对于那些不想使用继承或者因为多层次继承导致系统类的个数的急剧增加的系统, 也可以考虑使用此模式 503 | * 如果一个系统需要在构件的抽象化角色和具体化角色之间更加灵活, 避免在两个层次之间建立静态的继承联系, 可以通过桥接模式使他们在抽象层建立一个关联关系 504 | 505 | 506 | #### 模式范例 507 | 508 | 以喝咖啡为例子, 一个咖啡馆中咖啡有四种, 分别是大杯加糖, 小杯加糖, 大杯无糖, 小杯无糖. 但是对于一杯咖啡来说这4种状态中实际上就是`两种变化`. 糖的状态和杯的状态. 509 | 510 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/bridge) 511 | 512 | 代码使用以及结果: 513 | 514 | 515 | ```java 516 | public static void main(String args[]){ 517 | 518 | // 原汁原味 519 | Ordinary ordinary = new Ordinary(); 520 | 521 | // 准备糖类 522 | Sugar sugar = new Sugar(); 523 | 524 | // 大杯咖啡原味 525 | LargeCoffee largeCoffee = new LargeCoffee(ordinary); 526 | largeCoffee.makeCoffee(); 527 | 528 | // 小杯咖啡 原味 529 | SmallCoffee smallCoffee = new SmallCoffee(ordinary); 530 | smallCoffee.makeCoffee(); 531 | 532 | // 大杯咖啡 加糖 533 | LargeCoffee larSugar = new LargeCoffee(sugar); 534 | larSugar.makeCoffee(); 535 | 536 | // 小杯咖啡 加糖 537 | LargeCoffee smallSugar = new LargeCoffee(sugar); 538 | smallSugar.makeCoffee(); 539 | } 540 | //=========>结果 541 | 大杯的 原味 咖啡 542 | 小杯的 原味 咖啡 543 | 大杯的 加糖 咖啡 544 | 大杯的 加糖 咖啡 545 | 546 | ``` 547 | 548 | 这里`CoffeeAdditives`相当于作为了`实现部分`, 而`Coffee`则对应抽象部分, 模式中定义所谓的`抽象`和`实现`实质上对应的是两个独立变化的维度. 也就是说**任何多维度变化或者说多个树状类之间的耦合都可以使用桥接模式来解耦**. 范例中的这两个基类, 并不一定就是所谓的对应的角色, 两者各自为一维度,独立变化. 549 | 550 | 如果需要增加口味的种类, 只需要继承`CoffeeAdditives`实现不同的子类即可完成加奶,加盐的新功能的添加. 不管是这两个角色谁变化了, 相对于对方而言都是独立的没有过多的交际. 551 | 552 | 553 | 554 | #### Android源码对应实现 555 | 556 | `桥接模式`在`Android`中应用的比较广泛. 一般都是作用于大范围. 557 | 558 | * `View`的具体控件都定义了不同类型控件的所拥有的基本属性和行为, 但是将它们绘制到屏幕上的部分是与`View`相关的功能类`DisplayList`,`Hardwarelayer`,`Canvas`负责. 这俩个部分可以看做桥接 559 | * `Adapter`与`AdapterView`之间也可以看做是桥接 560 | * `Window`和`WindowManager`之间的关系. `Window`和`PhoneWindow`构成窗口的抽象部分; `WindowManager`和`WindowManagerImpl`为实现部分; 实现部分的具体实现类`WMI`使用`WindowManagerGlobal`通过`IWindowManager`接口与`WMS`进行交互. 并由`WMS`完成具体的窗口工作. 561 | 562 | 563 | 564 | -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇二)/README.md: -------------------------------------------------------------------------------- 1 | 2 | > 对于设计模式这是牛人们对代码中的一定场景而进行提炼的结果, 对于一个进阶的开发人员这是一个必不可少的技能. 当代码越写越好, 更易扩展更加灵活. 这对于Coder来说是最酷的事情. 3 | 4 | > 通过`设计模式`和`Android源码`中的关系, 可以更加清楚的记住各个模式的特点, 和源码中的实现方式. **多练多分析之间的关系**这是必不可少的一步! 5 | 6 | 7 | * [设计模式与Android(篇一)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%80)) 8 | * [设计模式与Android(篇二)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%BA%8C)) 9 | * [设计模式与Android(篇三)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%89)) 10 | 11 | 12 | 文章只是对`设计模式`做一个场景比对, 实现. 和简单说明. 最好可以自己动手代码实现, 分析代码之间的关系, 才能真正的学以致用. 13 | 14 | 15 | 16 | 17 | * [状态模式 State](#1) 18 | * [模式介绍](#2) 19 | * [模式范例](#3) 20 | * [Android源码模式实现](#4) 21 | * [责任链模式](#5) 22 | * [模式介绍](#6) 23 | * [模式范例](#7) 24 | * [Android源码模式实现](#8) 25 | * [解释器模式 Interpreter](#9) 26 | * [模式介绍](#10) 27 | * [模式范例](#11) 28 | * [Android源码模式实现](#12) 29 | * [命令模式 Command](#13) 30 | * [模式介绍](#14) 31 | * [模式范例](#15) 32 | * [Android源码模式实现](#16) 33 | * [观察者模式 Observer](#17) 34 | * [模式介绍](#18) 35 | * [模式范例](#19) 36 | * [Android源码模式实现](#20) 37 | * [备忘录模式 Memento](#21) 38 | * [模式介绍](#22) 39 | * [模式范例](#23) 40 | * [Android源码模式实现](#24) 41 | * [迭代器模式 Iterator](#25) 42 | * [模式介绍](#25) 43 | * [模式范例](#26) 44 | * [Android源码模式实现](#28) 45 | * [模板模式 Template](#29) 46 | * [模式介绍](#30) 47 | * [模式范例](#31) 48 | * [Android源码模式实现](#32) 49 | * [访问者模式 Visitor](#33) 50 | * [模式介绍](#34) 51 | * [模式范例](#35) 52 | * [Android源码模式实现](#36) 53 | 54 | 55 | 56 | 57 | ### 状态模式 State 58 | 59 | 60 | #### 模式介绍 61 | 62 | > 状态模式中的行为是由状态来决定的, 不同的状态有不同的行为, 状态模式和策略模式的结构几乎一模一样, 但他们的目的, 本质却完全不一样. 状态模式的行为是平行的不可替换的. 策略模式的行为是彼此独立, 可相互替换的. 总结一句话表述: 状态模式是把对象的行为包装在不同的状态对象里, 每一个状态对象都有一个共同的抽象状态基类, 状态模式的意图是让一个对象在其内部状态改变的时候, 其行为也随之改变 63 | 64 | * `定义`: 当一个对象的内在状态改变时允许改变其行为, 这个对象看起来像是改变了其类. 65 | * `场景`: 66 | 1. 一个代码的行为取决于它的装填, 并且必须在运行时根据其状态改变它的行为 67 | 2. 代码中包含大量与对象状态有关的条件语句, 同样可以去除分支语句的效果 68 | 69 | 70 | #### 模式范例 71 | 72 | 例如电视开关机状态下的**频道切换**或者**音量调节**, 不同的状态下的各种功能行为是不同的. `关机`: 功能音量频道切换是无效的, `开机`: 却可以实现. 实现这样一个关系, 如果最简单暴力的方法就是一个类实现, 里面充斥了各种条件判断来实现不同场景的功能. 73 | 74 | [范例代码实现](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/state) 75 | 76 | 范例类图: 77 | 78 | ![](UML_State.png) 79 | 80 | 客户端的实现: 81 | 82 | 83 | ```java 84 | public static void main(String arg[]){ 85 | TvController tvController = new TvController(); 86 | 87 | // 开机 88 | tvController.powerOn(); 89 | 90 | // 下一个频道 91 | tvController.nextChannel(); 92 | 93 | // 调高音量 94 | tvController.turnUp(); 95 | 96 | // 关机 97 | tvController.powerOff(); 98 | 99 | // 关机状态下调低音量 100 | tvController.turnDown(); 101 | } 102 | 103 | /** 输出结果--> 104 | * 开机了-- 105 | * 下一个频道 106 | * 调高音量 107 | * 关机了-- 108 | **/ 109 | ``` 110 | 其实有多重行为, 但代码中却不存在了`条件分支语句` 111 | 112 | 113 | #### Android源码对相应实现 114 | 115 | `WiFi`管理 其中的实现就使用了`状态模式` 116 | 117 | 在`WiFi`复杂的调用中, 存在一个`State`的状态类, 它代表了`WiFi`的某个状态, 定义如下: 118 | 119 | 120 | ```java 121 | public class State implements IState { 122 | // 进入当前状态之后调用该函数 123 | @Override 124 | public void enter() { 125 | } 126 | 127 | // 退出该状态后改用该函数 128 | @Override 129 | public void exit() { 130 | } 131 | 132 | // 处理消息 133 | @Override 134 | public boolean processMessage(Message msg) { 135 | return false; 136 | } 137 | } 138 | ``` 139 | 140 | 状态之间并不是可以随意切换的, 他们有一种层级关系, 这些层级关系`StateMachine`的构造函数中被定义的, 代码如下: 141 | 142 | 143 | ```java 144 | // WiFiStateMachine 145 | public WifiStateMachine(Context context, String wlanInterface, 146 | WifiTrafficPoller trafficPoller){ 147 | super("WifiStateMachine"); 148 | 149 | addState(mDefaultState); 150 | addState(mInitialState, mDefaultState); 151 | addState(mSupplicantStartingState, mDefaultState); 152 | addState(mSupplicantStartedState, mDefaultState); 153 | addState(mDriverStartingState, mSupplicantStartedState); 154 | addState(mDriverStartedState, mSupplicantStartedState); 155 | addState(mScanModeState, mDriverStartedState); 156 | addState(mConnectModeState, mDriverStartedState); 157 | addState(mL2ConnectedState, mConnectModeState); 158 | addState(mObtainingIpState, mL2ConnectedState); 159 | addState(mVerifyingLinkState, mL2ConnectedState); 160 | addState(mConnectedState, mL2ConnectedState); 161 | addState(mRoamingState, mL2ConnectedState); 162 | addState(mDisconnectingState, mConnectModeState); 163 | addState(mDisconnectedState, mConnectModeState); 164 | addState(mWpsRunningState, mConnectModeState); 165 | addState(mWaitForP2pDisableState, mSupplicantStartedState); 166 | addState(mDriverStoppingState, mSupplicantStartedState); 167 | addState(mDriverStoppedState, mSupplicantStartedState); 168 | addState(mSupplicantStoppingState, mDefaultState); 169 | addState(mSoftApStartingState, mDefaultState); 170 | addState(mSoftApStartedState, mDefaultState); 171 | addState(mTetheringState, mSoftApStartedState); 172 | addState(mTetheredState, mSoftApStartedState); 173 | addState(mUntetheringState, mSoftApStartedState); 174 | // 初始化模式为mInitialState 175 | setInitialState(mInitialState); 176 | } 177 | ``` 178 | 179 | 在构造函数中调用了`addState()`函数, 这些函数最终会调用`SmHandler#addState()`函数. 这个函数就是在状态之间建立一个层级关系, 这是一个树形的层级关系. 状态之间并不是跨越式的转换, 当前状态只能转换到上一个状态或者下一个状态. 180 | 181 | 上面说的比较抽象, 列举书中的例子. 一个电梯的状态有`停止`, `运行`, `开门`, `关门`. 在运行状态只能到停止状态. 不会直接开门状态,这会出人命的, 关门状态也是不合乎常理的. 所以就如下关系图片: 182 | 183 | ![](elevator_sequence.png) 184 | 185 | 正如上图, 不同状态对于不同的指令的反应是完全不一样的, `WiFi`工作状态机制也是同理, 除了对状态之间的转换进行控制之外, 还通过状态模式来对不同的命令进行不同的处理. `State`类就是状态的基类, 它与`Wifi`相关的子类都定义在`WifiStateMachine`中. 186 | 187 | `State`的类有`enter`,`exit`,`processMessage`三个函数, 进入状态之后会调用`enter()`, 退出时调用`exit()`, 处理具体消息时调用`processMessage()`. 而`状态模式的核心就是当一个对象的内在状态改变时允许改变其行为`, 所以我们关注`processMessage()`不同的状态下就是依赖这个函数实现不同行为的. 188 | 189 | 例如: 在请求扫描Wifi时, 如果在`初始化状态(InitialState)`下, 说明Wifi驱动还没有进行加载和启动, 扫描的请求会被会被忽略. 而在`驱动加载状态下`, 请求会被添加到延迟处理的消息队列中, 等待驱动加载完毕进行扫描请求. 190 | 191 | 总结起来: 就是将请求的处理封装到状态类中, 在不同的状态类中对同一个请求进行不同的处理. 它能够消除一些重复的`if-else`逻辑, 使得程序的结构更加清晰, 可扩展性和稳定性也有了一定的提高 192 | 193 | #### 实战场景 194 | 195 | 例如新浪微博首页, 任何状态下可以看微博, 当点击转发后, 如果是`登录状态`那么就可以直接调转转发页面, 如果是`未登录状态`那么需要调转到登录界面. 这就可以使用状态模式进行逻辑的分离. 196 | 197 | 198 | 199 | 200 | ### 责任链模式 201 | 202 | 203 | #### 模式介绍 204 | 205 | > 行为型设计模式, 将每一个对象看做一个节点, 并把所有节点串成一条链式, 从链头开始传递事件, 如果无法处理交给节点的下一个节点位置上, 直到有节点处理了这个事件. 206 | 207 | * `定义`: 使多个对象都有机会处理请求, 从而避免了请求的发送者和接收者之间的耦合关系. 将这些对象连成一条链, 并沿着这条链传递该请求, 直到对象处理它为止 208 | * `场景`: 多个对象都可以处理一个请求时, 但具体由哪个对象处理是在运行时决定. 209 | 210 | 211 | #### 模式范例 212 | 213 | 出差是需要经费的, 那么肯定需要找领导签字才会批下钱, 但是如果经费较多, 你的上一级可能无权签字,这个时候上一级领导就会把这个审批带向他的上级提交.. 直到有可以批准的为止. 从始至终出差人只需要知道自己的上一级即可. 不需要知道其他的审批人. 214 | 215 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/iterator) 216 | 217 | 范例类图 218 | 219 | ![](UML_Iterator.png) 220 | 221 | 看一下客户端的调用--> 222 | 223 | 224 | ```java 225 | public static void main(String args[]){ 226 | 227 | // 构造3个处理者对象 228 | Handler1 handler1 = new Handler1(); 229 | Handler2 handler2 = new Handler2(); 230 | Handler3 handler3 = new Handler3(); 231 | 232 | // 构造3个请求者对象 233 | Request1 re1 = new Request1("请求1"); 234 | Request2 re2 = new Request2("请求2"); 235 | Request3 re3 = new Request3("请求3"); 236 | 237 | // 设置当前处理者对象下一个节点的处理者对象 238 | handler1.nextHandler = handler2; 239 | handler2.nextHandler = handler3; 240 | 241 | // 准备开始请求 242 | // 总是从链式的首端发起请求 243 | handler1.handleRequest(re1); 244 | handler1.handleRequest(re2); 245 | handler1.handleRequest(re3); 246 | } 247 | 248 | 249 | // 执行结果===================> 250 | 处理者 1 处理请求, 请求的等级为: 1 251 | 处理者 2 处理请求, 请求的等级为: 2 252 | 处理者 3 处理请求, 请求的等级为: 3 253 | ``` 254 | 255 | 对于每个处理者其内部的逻辑是完全灵活的, 比如可以进行跳级传递等... 256 | 257 | 258 | #### Android源码对应实现 259 | 260 | 责任链模式在`Android`中比较类似的就是事件的分发处理, 每当用户接触屏幕时, `Android`就会将对应的事件包装成一个事件对象从`ViewTree`的顶部之上而下地分发传递. 261 | 262 | `ViewGroup`事件投递的递归调用就类似一条责任链, 一旦其寻到责任者, 那么就由责任者持有并消费掉该次事件 具体的体现在`View#onTouchEvent()`方法返回值的设置, 如果返回false, 那么就意味着当前`View`不会是该次事件的责任人, 将不会对其持有, 如果返回`true`, 则相反, 此时`View`会持有该事件并不在向外传递. 263 | 264 | 265 | ### 解释器模式 Interpreter 266 | 267 | 268 | 269 | #### 模式介绍 270 | 271 | > 这是较少使用的行为型模式, 其提供了一种解释语言的语法或表达式的方式, 该模式定义了一个表达式接口, 通过该接口解释一个特定的上下文. 272 | 273 | * `定义`: 给定一个语言, 定义它的文法的一种表示, 并定义一个解释器, 该解释器使用该表示来解释语言中的句子. 274 | * 文法? 如`他很高`,`他很胖`,`他很瘦`. 这三个语句可以看做一个`他很[形容词]`这样的结构, 可以看做是一条文法 275 | * `场景`: 276 | 1. 如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象语法树时可以考虑使用解释器模式 277 | 2. 在某些特定的领域出现不断重复的问题时, 可以将该领域的问题转化为一种语法规则下的语句, 然后构建解释器来解释该语句. 278 | 279 | 280 | #### 模式范例 281 | 282 | 不好理解看看是通过代码形式的表示是否可以清楚一些? 283 | 284 | 比如一个场景是算术表达式的解释, 如`m + n + p`, 如果使用解释器模式对该表达式进行解释, 那么代表数字的`mnp`3个字母我们可以看成是`终结符号`, 而`+`这个算术运算符则可以当做非终结符号. 285 | 286 | [代码范例](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/interpreter) 287 | 288 | 如最终调用方式: 289 | 290 | 291 | ```java 292 | public static void main(String arg[]){ 293 | Calculator calculator = new Calculator("12 + 11 + 13 + 14"); 294 | System.out.println(calculator.calculate()); 295 | } 296 | 297 | // 结果如下: 298 | 50 299 | ``` 300 | 301 | 这个例子只是先了对加减法的解释计算, 如果要实现更多的运算规则, 乘除取余, 只需要创建对应解释器即可, 但是混合运算的复杂是要考虑各种符号的优先级的问题,这个就比较麻烦. 302 | 303 | 将一个具体的文法通过一个解释器解释, 把复杂的文法规则分离为简单的功能进行解释, 最后将其组合成一颗抽象的语法树解释执行, 至此, 可以看到解释器模式的原理和本质: 将复杂的问题简单化, 模块化, 分离实现, 解释执行 304 | 305 | 306 | 307 | #### Android源码对应实现 308 | 309 | `Android源码中`的解释器模式并不多见, 虽然没有经典实现, 但是可以在一些地方看到对解释器模式原理的应用. `AndroidManifest.xml`这个清单文件 310 | 311 | 整理一下大体过程. 关于读取配置文件, 那么就需要一个很重要的类`PackageParser`. 该类对`AndroidManifest.xml`中每一个组件标签创建了对应的类, 用于存储相应的消息. 312 | 313 | `PackageParser`为`Activity`,`Service`,`Provider`,`Permission`等构件在其内部以内部类的方式创建了对应的类, 按照解释器模式的定义, 这些类其实都对应`AndroidManifest.xml`中的一个标签, 也就是一条文法, 其在对该配置文件解析时充分运用了解释器模式分离实现, 解释器执行的特性. 314 | 315 | 对一个`APK`文件的解析会调用`PackageManagerService#scanPackageLI()`方法, 这个方法有两种实现 316 | 317 | 318 | ```java 319 | private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user); 320 | 321 | private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,int scanFlags, long currentTime, UserHandle user) 322 | ``` 323 | 324 | 两者的唯一区别是第一个参数, 第一种实现为`File`第二种为`PackageParser.Package`. 在具体解析某个文件时会先调用第一种实现解析`apk`文件, 在调用第二种实现将解析后的信息保存至`PMS`中. 而这两种方法中同样会调用一个函数名相同但参数不同的函数. `ParserPackage(...)`. 对于参数1为`File`类型的其中主要逻辑就是为了第二种`参数为Resources`实现准备好需要的参数, 然后可以调用第二种`ParserPackage(Resource ...)`. 325 | 326 | 而`ParserPackage`的第二种实现逻辑比较复杂, 内部主要对整个`AndroidManifest.xml`配置文件的每个子节点进行具体的解析. 327 | 328 | 例如`parseApplication`方法的会对`application`节点进行解析, 对于不同的子标签会调用不同的解析方法来对其内部进行解析. 如碰到了`activity`标签, 那么会调用`parseActivity()`进行内部解析. 而`parseActivity()`不仅承担着对`Activity`的解析, 其同样承担着`Broadcast`的解析. 并会继续调用方法对内部标签进行解析如`parseIntent`和`parseMetaData`等. 329 | 330 | 331 | 332 | ### 命令模式 Command 333 | 334 | 335 | #### 模式介绍 336 | 337 | > 行为型设计模式, 如当我们点击关机键的时候, 系统就会执行一系列的操作, 保存程序的进度, 结束程序, 调用内核命令关机. 用户不关心命令做了什么, 只需要点击关机即可达到效果. 338 | 339 | * `定义`: 将一个请求封装成一个对象, 从而让用户使用不同的请求把客户端参数化; 对请求排队或者记录请求日志, 以及支持可撤销操作. 340 | * `场景`: 341 | * 需要抽象出待执行的操作, 然后以参数的形式提供出来-- 类似于过程设计中的回调机制, 而命令模式正式回调机制的一个面向对象的替代品 342 | * 在不同的时刻指定, 排列和执行请求. 一个命令对象可以有与初始请求无关的生存期 343 | * 需要支持取消操作 344 | * 支持修改日志的功能, 这样当系统崩溃的时候, 这些修改可以重做一遍 345 | * 需要支持事务的操作 346 | 347 | 348 | 349 | #### 模式范例 350 | 351 | 把俄罗斯方块的大体逻辑模拟成代码, 向左,向右,变形,加速下落这四个按钮相当于`请求者`, 执行具体按钮命令的逻辑方法可以看做是命令角色. 352 | 353 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/command) 354 | 355 | 范例类图 356 | 357 | ![](UML_Command.png) 358 | 359 | 代码测试 360 | 361 | 362 | ```java 363 | public static void main(String arg[]){ 364 | 365 | // 创建游戏 366 | TetrisMachine machine = new TetrisMachine(); 367 | 368 | // 根据游戏构造四个命令 369 | LeftCommand leftCommand = new LeftCommand(machine); 370 | RightCommand rightCommand = new RightCommand(machine); 371 | FastCommand fastCommand = new FastCommand(machine); 372 | TransformCommand transformCommand = new TransformCommand(machine); 373 | 374 | // 按钮可以执行不同的命令 375 | Buttons buttons = new Buttons(); 376 | buttons.setmLeftCom(leftCommand); 377 | buttons.setmRightCom(rightCommand); 378 | buttons.setmFastCom(fastCommand); 379 | buttons.setmTransformCom(transformCommand); 380 | 381 | // 具体按下那个按钮玩家说的算 382 | buttons.toLeft(); 383 | buttons.toRight(); 384 | buttons.fast(); 385 | buttons.transform(); 386 | } 387 | ``` 388 | 389 | 其实调用逻辑做的很复杂, 完全可以直接创建`TetrisMachine`类直接调用的. 这样做的主要原因是后续开发方便, 比如如果需要增加或修改游戏功能只需要修改`TetrisMachine`类就可以. 然后修改一下`Player`类. 但是事物是相对的对开发者方便了, 但是如果别人负责了这个项目看到这个功能可能会花更多时间去理解,反而简单的事情没有很直接的表达. 390 | 391 | 除此之外, 使用命令模式的另一个好处是可以实现命令记录的功能, 如上面代码中, 如果要`Button`请求者角色中使用一个数据结构来存储执行过的命令对象, 以此可以很方便地知道刚刚执行过哪些命令动作, 并可以在需要时恢复 392 | 393 | 394 | #### Android源码对应实现 395 | 396 | `Android`中关于命令模式的使用虽然不少, 但都不是典型, 很多方面的应用与其他大多数设计模式一样都有一定的变种, 一个比较经典的例子是`Android的事件机制中底层逻辑对事件的转发处理`, `Android`的每一种事件在屏幕上产生后都会经过底层逻辑将其封装转换为一个`NotifiArgs`对象. 397 | 398 | #### 实战场景 399 | 400 | 很好的一个场景就是, 对画板模块的使用, 可以很方便的实现重画,撤销等功能. 401 | 402 | 403 | 404 | ### 观察者模式 Observer 405 | 406 | 407 | #### 模式介绍 408 | 409 | > 一个使用率非常高的模式, 常用的地方GUI系统, 订阅--发布系统. 最明显的特点就是解耦, 将被观察者和观察者进行解耦, 使得依赖性更小. 410 | 411 | * `定义`: 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态, 则所有依赖于它的对象都会得到通知并被自动更新. 412 | * `场景`: 413 | * 关联行为场景, 需要注意的是, 关联行为是可拆分的, 而不是`组合`的关系 414 | * 事件多级触发场景 415 | * 跨系统的消息交换场景, 如消息队列,事件总线的处理机制 416 | 417 | 418 | #### 模式范例 419 | 420 | 例如一个简单的订阅, 订阅者可以在被观察者更新的时候收到通知. 421 | 422 | [范例源码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/observer) 423 | 424 | 类图就不需要了, 因为`JDK`已经内置了此模式的实现, 看一下范例的调用方式和结果 425 | 426 | 427 | ```java 428 | public static void main(String arg[]){ 429 | // 创建被观察对象 430 | DecTechFrontier decTechFrontier = new DecTechFrontier(); 431 | 432 | // 创建几个观察者 433 | Coder co1 = new Coder("张飞"); 434 | Coder co2 = new Coder("李逵"); 435 | Coder co3 = new Coder("关羽"); 436 | Coder co4 = new Coder("孙悟空"); 437 | 438 | // 将观察者注册到被观察的对象 439 | decTechFrontier.addObserver(co1); 440 | decTechFrontier.addObserver(co2); 441 | decTechFrontier.addObserver(co4); 442 | decTechFrontier.addObserver(co3); 443 | 444 | // 发布消息 445 | decTechFrontier.postNewPublication("葵花宝典"); 446 | } 447 | // =====> 输出结果 448 | 你好, 关羽, 你订阅的东西有更新了: 葵花宝典 449 | 你好, 孙悟空, 你订阅的东西有更新了: 葵花宝典 450 | 你好, 李逵, 你订阅的东西有更新了: 葵花宝典 451 | 你好, 张飞, 你订阅的东西有更新了: 葵花宝典 452 | 453 | ``` 454 | 455 | `Observer`和`Observable`是`JDK`中的内置类型, 可见观察者模式是非常重要的, 这里`Observer`是抽象观察者角色, `范例Coder`类扮演的是具体观察者角色; `Observable`对应的是抽象主题角色, `范例DecTechFrontier`是具体的主题角色. `主题角色`通过`setChange()`标识主题发生改变,并通过`notifyObservable()`通知所有的`观察者角色`. 而`观察者`都过复写`update()`方法来实现主题更新时需要做的事情 . 至此这两个角色并没有耦合. 456 | 457 | 458 | 459 | #### Android源码对应实现 460 | 461 | `ListView`中的`Adapter#notifyDataSetChange()`就是通过观察者模式实现的子View的更新. 462 | 463 | 首先是`notifyDataSetChange()`方法为入口. 这个方法定义在`BaseAdapter`中. 464 | 465 | 466 | ```java 467 | public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { 468 | // 数据集观察者 469 | private final DataSetObservable mDataSetObservable = new DataSetObservable(); 470 | public void registerDataSetObserver(DataSetObserver observer) { 471 | mDataSetObservable.registerObserver(observer); 472 | } 473 | 474 | public void unregisterDataSetObserver(DataSetObserver observer) { 475 | mDataSetObservable.unregisterObserver(observer); 476 | } 477 | 478 | /** 479 | * 数据发生改变是, 调用所有观察者 480 | **/ 481 | public void notifyDataSetChanged() { 482 | mDataSetObservable.notifyChanged(); 483 | } 484 | 485 | } 486 | ``` 487 | 488 | 很明显的`BaseAdapter`是一个观察者模式, 那么接着看一下如何运作, 以及这个观察者是什么. 489 | 490 | 491 | ```java 492 | public class DataSetObservable extends Observable { 493 | // 调用每个观察者的onChange函数来通知他们被观察者发生了改变 494 | public void notifyChanged() { 495 | synchronized(mObservers) { 496 | // 调用所有的观察者onChange() 497 | for (int i = mObservers.size() - 1; i >= 0; i--) { 498 | mObservers.get(i).onChanged(); 499 | } 500 | } 501 | } 502 | } 503 | ``` 504 | 505 | 可以看到我们调用的`notifyDataSetChanged()`会遍历所有的观察者中的`onChange()`. 506 | 507 | 这些观察者就是在`ListView`通过`setAdapter()`方法设置`Adapter`产生的. 508 | 509 | 510 | ```java 511 | @Override 512 | public void setAdapter(ListAdapter adapter) { 513 | // 如果已经有了一个Adapter, 那么先注销该Adapter对应的观察者 514 | if (mAdapter != null && mDataSetObserver != null) { 515 | mAdapter.unregisterDataSetObserver(mDataSetObserver); 516 | } 517 | super.setAdapter(adapter); 518 | 519 | if (mAdapter != null) { 520 | mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); 521 | mOldItemCount = mItemCount; 522 | // 获取数据的数量 523 | mItemCount = mAdapter.getCount(); 524 | checkFocus(); 525 | // *** 创建一个数据集观察者 526 | mDataSetObserver = new AdapterDataSetObserver(); 527 | // 将这个观察者注册到Adapter中, 实际上注册到了 DataSetObservable中 528 | mAdapter.registerDataSetObserver(mDataSetObserver); 529 | 530 | } 531 | 532 | requestLayout(); 533 | } 534 | ``` 535 | 536 | 可以看出, 在设置`Adapter`时会构建一个`AdapterDataSetObserver`, 这就是之前说的观察者, 最后将这个观察者注册到`Adapter`中 537 | 538 | 那么`AdapterDataSetObserver`是什么? 是如何运作的? 首先这个这个类定义在了`ListView`的父类`AbsListView`中, 而这个类又继承了`AbsListView`的父类`AdapterView的AdapterDataSetObserver`.如下 539 | 540 | 541 | ```java 542 | class AdapterDataSetObserver extends DataSetObserver { 543 | 544 | private Parcelable mInstanceState = null; 545 | 546 | // 核心方法 547 | @Override 548 | public void onChanged() { 549 | mDataChanged = true; 550 | mOldItemCount = mItemCount; 551 | // 获取adapter的数量 552 | mItemCount = getAdapter().getCount(); 553 | 554 | if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null 555 | && mOldItemCount == 0 && mItemCount > 0) { 556 | AdapterView.this.onRestoreInstanceState(mInstanceState); 557 | mInstanceState = null; 558 | } else { 559 | rememberSyncState(); 560 | } 561 | checkFocus(); 562 | // 重新布局 563 | requestLayout(); 564 | } 565 | } 566 | ``` 567 | 568 | 这回应该很清楚了, 当`ListView`的数据发生变化时, 调用了`Adapter#notifyDataSetChanged()`函数, 这个函数又会调用`DataSetObserver#notifyChange()`函数, 这个函数会遍历所有的观察者`AdapterDataSetObserver#onChange()` 在`onChange()`方法中又会调用`ListView`重新布局, 使得`ListView`刷新界面 569 | 570 | 571 | 572 | 573 | #### 实战场景 574 | 575 | 事件总线! 576 | 577 | 578 | 579 | ### 备忘录模式 Memento 580 | 581 | 582 | 583 | #### 模式介绍 584 | 585 | > 一种行为模式, 该模式用于保存对象, 并且在之后可以再次恢复到此状态 586 | 587 | * `定义`: 在不破坏封闭的前提下, 捕获一个对象的内部状态,并在该对象之外保存这个状态, 以后就可将该对象恢复到原先保存的状态. 588 | * `场景`: 589 | 1. 需要保存一个对象在某一个时刻的状态或部分状态 590 | 2. 如果用一个接口来让其他对象得到这些状态, 将会暴露对象的实现细节并破坏对象的封装性, 一个对象不希望外界直接访问其内部状态, 通过中间对象可以间接访问其内部状态. 591 | 592 | 593 | 594 | #### 模式范例 595 | 596 | 比如一个游戏, 在退出时候保存进度, 在进入的时候恢复进度的场景 597 | 598 | [范例源码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/memento) 599 | 600 | 范例类图 601 | 602 | ![](UML_Memento.png) 603 | 604 | 范例的使用--> 605 | 606 | 607 | ```java 608 | public static void main(String arg[]){ 609 | 610 | // 构建游戏对象 611 | CallOfDuty game = new CallOfDuty(); 612 | 613 | // 1 打游戏 614 | game.play(); 615 | 616 | Caretaker caretaker = new Caretaker(); 617 | // 2 游戏存档 618 | caretaker.archive(game.createMemo()); 619 | 620 | // 3 退出游戏 621 | game.quit(); 622 | 623 | // 4 恢复游戏 624 | CallOfDuty newGame = new CallOfDuty(); 625 | newGame.restore(caretaker.getMemo()); 626 | } 627 | 628 | // =======> 运行结果 629 | 玩游戏: 第1关 奋战杀敌中 630 | 进度升级中 631 | 到达 第2关 632 | ----- 633 | 退出前的游戏属性: 当前游戏信息: checkpoint=2 ,mLifeValue=90 ,mWeapon=沙漠之鹰 634 | 退出游戏 635 | ----- 636 | 恢复后的游戏属性--> 当前游戏信息: checkpoint=2 ,mLifeValue=90 ,mWeapon=沙漠之鹰 637 | ``` 638 | 639 | 可以看到`CallOfDuty`在这里为`Originator`角色, 也就是需要存储的对象, 在这里并没有直接存储对象, 而是通过`Memo`对`CallOfDuty`对象的数据进行存储, 然后在存储`Memo`对象, 最终对`Memo`的存储操作交给`Caretaker`对象. 在这个过程中, 各个角色职责清晰, 单一, 即对外屏蔽了对`CallOfDuty`角色的直接访问, 在满足了对象状态存取功能的同时也使得该模块的结构清晰, 整洁. 640 | 641 | 642 | 643 | #### Android源码对应实现 644 | 645 | 在`Android`源码中的状态模式应用是`Activity`中的状态保存. 646 | 647 | 在这里, `Activity`扮演了`Caretaker`角色, 负责存储和恢复UI的状态信息; `Activity`,`Fragment`,`View`,`ViewGroup`等对象为`Originator`角色, 也就是需要存储状态的角色. `Memo`则由`Bundle`类扮演. 648 | 649 | 650 | 651 | ### 迭代器模式 Iterator 652 | 653 | 654 | #### 模式介绍 655 | 656 | > 也成为游标模式, 行为性设计模式. 源于对容器的访问. 657 | 658 | * `定义`: 提供了一种方法顺序访问一个容器对象中的各个元素, 而不需要暴露该对象的内部表示 659 | * `场景`: 遍历一个容器对象 660 | 661 | 662 | #### 模式实现 663 | 664 | 场景: 如两个部门, 老板想要对两个部门的统计数据, 但是如果两个部门的内部实现存储如果是一个用数组, 一个集合, 那么老板访问就需要了解其内部的数据结构. 使得老板的职责过多, 这个时候如果用迭代器模式实现,统一遍历方式, 那么就会很方便,也不会对外暴露内部的实现细节. 如下: 665 | 666 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/cursor) 667 | 668 | ![](UML_Cursor.png) 669 | 670 | 使用情况: 671 | 672 | 673 | ```java 674 | public static void main(String args[]){ 675 | CompanSu companSu = new CompanSu(); 676 | check(companSu.iterator()); 677 | 678 | CompanLi companLi = new CompanLi(); 679 | check(companLi.iterator()); 680 | } 681 | 682 | private static void check(Iterator iterator) { 683 | while (iterator.hasNext()){ 684 | System.out.println(iterator.next().toString()); 685 | } 686 | } 687 | 688 | // ======> 结果 689 | Employee{name='小敏', age=99, sex='男', position='程序员'} 690 | Employee{name='小李', age=98, sex='男', position='程序员'} 691 | Employee{name='小往', age=11, sex='女', position='程序员'} 692 | Employee{name='小爱', age=9, sex='女', position='程序员'} 693 | Employee{name='大敏', age=66, sex='妖', position='未知'} 694 | Employee{name='大李', age=66, sex='妖', position='未知'} 695 | ``` 696 | 697 | 这个例子只是列举个思想, 可以看到通过迭代器实现, 就可以对外通过一个统一的接口, 来对不同的内部细节不一样的容器进行访问. 这也是`List`,`Map`都实现迭代器的意义. 698 | 699 | 700 | 701 | #### Android源码对应实现 702 | 703 | 几乎开发者不会自己去实现一个迭代器, 例如`Android`中, 除了各种数据结构体, 最典型的就是数据库查询使用了`Cursor`. 当使用`SQLiteDatabase#query()`方法查询数据时, 会返回一个`Cursor`对象. 该对象实质就是一个迭代器. 704 | 705 | 所以可以看出迭代器模式, 特点很明显也很单一, 支持以不同的方式去遍历一个容器对象, 也可以有多个遍历, 弱化了容器与遍历算法之间的关系. 几乎每一种高级语言都有对应的内置迭代器实现. 706 | 707 | 708 | 709 | ### 模板模式 Template 710 | 711 | 712 | #### 模式介绍 713 | 714 | > 某一个算法所需要的关键步骤是已知的, 但是某一步的具体实现是未知的需要子类去实现 715 | 716 | * `定义`: 定义一个操作中的算法框架, 而将一些步骤延迟到子类中, 使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤. 717 | * `场景`: 718 | * 多个子类有共有的方法, 并且逻辑基本相同 719 | * 重要, 复杂的算法, 可以把核心算法设计为模板方法, 周边的相关细节由子类去实现 720 | * 重构时, 模板方法模式是一个经常使用的模式, 把相同的代码抽取到父类, 然后通过钩子函数约束其行为 721 | 722 | 723 | #### 模式范例 724 | 725 | 模板方式实际上是封装一个固定流程, 然后暴露某一个步骤方法, 这里以计算机开机为例子, 726 | 727 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/template) 728 | 729 | ![](UML_Template.png) 730 | 731 | 代码使用: 732 | 733 | 734 | ```java 735 | public static void main(String arg[]){ 736 | 737 | CodeComputer codeComputer = new CodeComputer(); 738 | codeComputer.startUp(); 739 | 740 | MilitaryComputer militaryComputer = new MilitaryComputer(); 741 | militaryComputer.startUp(); 742 | } 743 | 744 | // ========> 运行结果 745 | --------- 开机 start ----------- 746 | 开启电源 747 | 硬件检测 748 | 载入操作系统 749 | 需要密码 750 | ---------- 关机 end --------------- 751 | --------- 开机 start ----------- 752 | 开启电源 753 | 硬件检测 754 | >> 需要检测防火墙 755 | 载入操作系统 756 | 需要进行眼膜验证 757 | ---------- 关机 end --------------- 758 | ``` 759 | 760 | 上面代码不管什么情况四个步骤是必须的, 开启电源是刚需,不需要子类实现, `startUp()`设置方法是`final`因为调用流程是必须的. 而其余的方法根据不同的需求来进行改造. 761 | 762 | 763 | #### Android源码对应实现 764 | 765 | `Android`中`AsyncTask`就是一个比较明显的模板方法模式. 766 | 767 | 其内部调用顺序就是 `execute`-->`onPreExecute`-->`doInBackground`-->`onPostExecute` 768 | 769 | 或者比如`Activity`的声明周期方法. 770 | 771 | 772 | 773 | ### 访问者模式 Visitor 774 | 775 | 776 | 777 | #### 模式介绍 778 | 779 | > 访问者模式是一种将数据操作与数据结构分离的设计模式,它是23种设计模式最复杂的一个, 但使用率不高. 大体思想, 软件系统中拥有一个由许多对象构成的, 比较稳定的对象结构, 这些对象的类都拥有一个`accept`方法用来接收访问者对象的访问. 访问者是一个接口, 他拥有`visit`方法, 这个方法对访问到的对象结构中不同的类型元素做出不同的处理. 780 | 781 | * `定义`: 封装一些作用于某种数据结构中的各个元素的操作, 它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作 782 | * `场景`: 783 | * 对象结构比较稳定, 但经常需要在此对象结构上定义新的操作 784 | * 需要对一个对象结构中的对象进行很多不同的并且不相关的操作, 而需要避免这些操作污染这些对象的类, 也不希望在增加新操作时修改这些类 785 | 786 | 787 | #### 模式范例 788 | 789 | 场景: 公司的年度考核, 评定员工分别是`CEO`和`CTO`, 而`CEO`只关注工程师的代码量和经理的新产品数; `CTO`关注的是工程师的`KPI`和经理的`KPI`. 这时`CEO`和`CTO`对于不同员工的关注点是不一样的. 这就需要对不同的员工类型进行不同的处理. 访问者模式此时可以派上用场了. 790 | 791 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/visitor) 792 | 793 | 代码使用结果: 794 | 795 | 796 | ```java 797 | public static void main(String args[]){ 798 | // 构建报表 799 | BusinessReport report = new BusinessReport(); 800 | 801 | System.out.println("----------给CEO看的报表----------"); 802 | // 设置访问者, 这里是CEO 803 | report.showReport(new CEOVisitor()); 804 | 805 | System.out.println("----------给CTO看的报表----------"); 806 | // 注入另一访问者CTO 807 | report.showReport(new CTOVisitor()); 808 | } 809 | // ======> 结果 810 | ----------给CEO看的报表---------- 811 | CEO访问--> 经理王经理 . KPI : 9 , 新产品数量: 0 812 | CEO访问--> 工程师工程师-jake . KPI : 2 813 | CEO访问--> 工程师工程师-小李 . KPI : 5 814 | CEO访问--> 工程师工程师-小张 . KPI : 0 815 | ----------给CTO看的报表---------- 816 | CTO访问--> 经理 王经理 , 新产品数量: 0 817 | CTO访问--> 工程师 工程师-jake . 代码行数 : 14290 818 | CTO访问--> 工程师 工程师-小李 . 代码行数 : 2183 819 | CTO访问--> 工程师 工程师-小张 . 代码行数 : 83422 820 | ``` 821 | 822 | 823 | 范例中`Staff`扮演了`Element`角色, 而`Enginner`和`Manager`都是`ConcreteElement`; `CEOVisitor`和`CTOVistor`都是具体的`Vistor`对象, 而`BusinessReport`就是`ObjectStructure`; `Client`就是客户端 824 | 825 | 访问者最大的优点就是增加访问者非常容易, 如果要增加一个访问者, 只需要创建一个实现了`Visitor`接口的类, 然后实现两个`visi`函数来对不同的元素进行不同的操作, 从而达到数据对象与数据操作相分离的效果. 826 | 827 | 828 | 829 | #### Android源码对应实现 830 | 831 | `APT`的注解. 简单记录一下. 首先编译器将代码抽象成一个代码元素的树, 然后在编译时对整棵树进行遍历访问, 每个元素都有一个`accept()`接收访问者的访问, 每个访问者中都有对应的`visit()`函数, 例如`visitType()`函数就是对类型元素的访问, 在每个`visit`函数中对不同的类型进行不同的处理, 这样就达到了差异处理效果, 同时将数据结构与数据操作分离, 使得每个类型的职责单一, 易于升级维护. `JDK`还特意预留了`visitUnknown()`接口应对`Java`语言后续发展可能添加的元素类型问题, 灵活的将访问者模式的缺点化解. 832 | 833 | -------------------------------------------------------------------------------- /project/design-pattern/瞰-设计模式与Android(篇一)/README.md: -------------------------------------------------------------------------------- 1 | > 试图看懂 --> 试图模仿 --> 试图记住 --> 试图熟练 2 | 3 | > 对于设计模式这是牛人们对代码中的一定场景而进行提炼的结果, 对于一个进阶的开发人员这是一个必不可少的技能. 当代码越写越好, 更易扩展更加灵活. 这对于Coder来说是最酷的事情. 4 | 5 | > 通过`设计模式`和`Android源码`中的关系, 可以更加清楚的记住各个模式的特点, 和源码中的实现方式. **多练多分析之间的关系**这是必不可少的一步! 6 | 7 | 8 | * [设计模式与Android(篇一)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%80)) 9 | * [设计模式与Android(篇二)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%BA%8C)) 10 | * [设计模式与Android(篇三)](https://github.com/suzeyu1992/repo/tree/master/project/design-pattern/%E7%9E%B0-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8EAndroid(%E7%AF%87%E4%B8%89)) 11 | 12 | 本篇就是``一书的缩减版. 这本书挺不错的. 片中类图讲解出自`<大话设计模式>` 13 | 14 | 15 | 16 | * [灵活之路 - 面向对象六大原则](#1) 17 | * [启航之路 - UML类图说明](#2) 18 | * [发现之路 - 23种设计模式](#3) 19 | * [单例模式 Singleton](#4) 20 | * [模式介绍](#5) 21 | * [模式范例](#6) 22 | * [Android源码模式实现](#7) 23 | * [建造者模式 Builder](#8) 24 | * [模式介绍](#9) 25 | * [模式范例](#10) 26 | * [Android源码模式实现](#11) 27 | * [实战场景](#12) 28 | * [原型模式 Prototype](#13) 29 | * [模式介绍](#14) 30 | * [模式范例](#15) 31 | * [Android源码模式实现](#16) 32 | * [实战场景](#17) 33 | * [工厂方法模式 Factory](#18) 34 | * [模式介绍](#19) 35 | * [模式范例](#20) 36 | * [Android源码模式实现](#21) 37 | * [实战场景](#22) 38 | * [抽象工厂模式 Abstract Factory](#23) 39 | * [模式介绍](#24) 40 | * [模式范例](#25) 41 | * [Android源码模式实现](#26) 42 | * [策略模式 Strategy](#27) 43 | * [模式介绍](#28) 44 | * [模式范例](#29) 45 | * [Android源码模式实现](#30) 46 | * [实战场景](#31) 47 | 48 | 49 | 50 | ## 灵活之路 - 面向对象六大原则 51 | 52 | `如果下面文字描述,不是很明白那么跳转到后面链接有详细代码说明`[原则解析](http://szysky.com/2016/11/28/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%85%AD%E5%A4%A7%E5%8E%9F%E5%88%99%E8%A7%A3%E6%9E%90/) 53 | 54 | * `单一职责原则SRP(Single Responsibility Principle)` 55 | * `定义`: 就一个类而言, 应该仅有一个引起它的变化的原因. `通俗点`就是一个类应该是相关性很高数据封装 56 | * `举例`: 现在有一个图片加载类. 但是这个类内部包含了**图片下载的逻辑**,**图片缓存的逻辑**这样就使得这个类的职责过多, 随着功能的不断完善, 这个类的代码和逻辑也变得纵横交错混合在了一起. 对于后续的修改维护扩展都是不利的. 所以让两个类组合起来, 一个类内部只负责**图片下载**,另一个类内部负责**图片缓存**. 保持每个类的**单一职责** 57 | * `开闭原则OCP(Open Close Principle)` 58 | * `定义`: 软件中的对象应该对于**扩展**是**开放**的 但是对于**修改**是**封闭**的. `通俗点` : 尽量通过**扩展的方式**来实现变化, 而不是通过修改已有的代码来实现. 59 | * `举例`: 此时我们实现了一个**双缓存类**和**单缓存类**. 在**图片加载类**中进行这两个缓存类的实例. 并对外暴露一个布尔值让用户设置是否使用双缓存来决定内部缓存的逻辑. ok. 目前看可能没有问题. 但是如果有一个更好的缓存算法类, 这时候每次都需要在**图片加载类中修改代码**. 这就违反了`OCP`原则, 利用**继承,接口**的特性可以让此类问题得以解决. 比如: 我们可以定义一个`缓存接口`, 在**加载类**中使用的个这个接口中的方法. 而这个接口的具体实现通过暴露一个方法让外部调用的时候传入, 以后如果有新的缓存类只需要调用方法传入接口的子类就可以. 这样就对于原始代码修改进行了关闭, 而对于扩展是开放的. 60 | * `里氏替换原则LSP(Liskov Substitution Principle)` 61 | * `定义`: 所有引用基类的地方必须能透明地使用其子类. `通俗点`:是基于继承,多态两大特性. 再简单点**抽象** 62 | * `举例`: `Window#show(View view)`这个方法接收一个**View**, 但是我们可以`Button`,`TextView`等等. 其实很简单. 我们常用只不过不知道这个名字而已. 所以`LSP`的原则的核心就是**抽象**. 抽象又依赖于继承这个特性. **通常开闭原则和里氏替换是不离不弃的**例如上面`OCP`中举得例子. 在外部调用就是利用了继承的特性, 也就是**里氏替换** 63 | * `依赖倒置原则DIP(Dependence Inversion Principle)` 64 | * `定义`: 指代了一种特定的解耦形式, 使得高层次的模块不依赖于低层次的模块的实现细节的目的, 依赖模块被颠倒了. `通俗点`: 在Java中依赖抽象(接口,抽象类), 而不依赖具体实现类. 模块之间的依赖通过**抽象**发生, 实现类之间不发生直接的依赖关系, 其依赖关系是通过接口或抽象类产生. 65 | * `举例`: 还是在`OCP`中的例子, 内部加载类依赖于也就是成员变量是`缓存接口`, 而不是具体的某一个`单缓存`或者`双缓存`的实现类. 66 | * `接口隔离原则ISP(Interface Segregation Principles)` 67 | * `定义`: 接口的依赖关系应该建立在最小的接口上. `通俗点`:接口隔离原则的目的是系统解开耦合, 从而容易重构, 更改和重新部署. 68 | * `举例`: 在操作一些**IO文件,网络**的时候我们总是伴随着`try...catch...finally`. 在最终调用块中调用`close()`确保资源可以正确的释放. 但这样这样的代码不仅可读性差可以每次都是写一些冗余的模板代码. 其实可以提供一个静态方法, 而根据java中的的特性,之上操作的对象都会实现一个**标识接口Closeable**,这个接口标识了一个可关闭的对象有一个`close()`. 所以这个静态方法的形参接收一个`Closeable`接口,并在方法内调用`close()`即可. 仔细想想: 这个方法的形参在调用的时候传入的实参是**里氏替换原则**, 而方法内部调用的是一个接口的`close()`方法,但传入的可能是某一个实现类,那么这不就是**依赖导致原则**,并且建立在最小化的依赖基础上, 只要知道这个对象是可关闭的, 别的一概不关心, 这就是**接口隔离原则**. 69 | * `迪米特原则LOD(Law of Demeter)` 70 | * `定义`: 一个对象应该对其他对象有**最少**的了解. `通俗点`: 一个类应该对自己需要耦合或调用的类知道的最少, 类的内部如果实现与调用者或者依赖者没有关系, 调用者或者依赖者只需要知道他需要的方法即可, 其他一概不管. 71 | * `举例`: 房间类, 中介类, 上班族类. 可以**上班族**应该只关心**中介类**, 而不需要关注**房间类**. 只需要**中介类**返回房子的地址即可. 而不需要通过调用**中介类**返回一个**房间类** . 这也就是代码中需要注意的. 不要过度耦合, 要降低类之间的关系. 72 | 73 | 74 | ## 启航之路 - UML类图说明 75 | 76 | > 对于许多类组成的庞大关系网, 最好的办法是通过图来表示出其关系. 可以直观的看出组合的元素, 元素直接是如何存在的, 元素与哪些元素直接存在着联系等. 表示出来的图就是`UML类图`. 77 | 78 | 可以看如下一个稍微完整的一个`UML类图` 79 | 80 | ![](imgs/UML_ALL.png) 81 | 82 | --- 83 | 84 | **组成元素** 85 | 86 | * `类和接口`: 通过黄色的矩形框来表示一个类, 例如上面鸟就是一个**普通类**, 如果类名是斜体那么就是**抽象类**, 如果和**飞翔**或者**唐老鸭**的表示法那么就是接口. 87 | * `访问权限`: 通过`+ 公共权限`, `- 私有权限`, `# 保护权限` 88 | * `变量和方法`: 分别在第二行, 和第三行表示,抽象方法同样斜体表示, 静态属性的用下划线表示. 89 | 90 | --- 91 | 92 | **关系结构** 93 | 94 | * `继承关系`: 类与类之间的关系, 通过**空心三角+实线**表示, 通过**箭头的方向指向父类**表述关系. 95 | * `实现关系`: 类与接口直接的关系, 通过**空心三角+虚线**表示, 通过**箭头的方向指向接口**表述关系. 96 | * `关联关系`: 当一个类知道另一个类的时候,可以使用**关联**, 比如企鹅和气候两个类中, `企鹅类的变量有气候类的引用`, 这个时候就如上图之间的关系. **实线箭头**表示, **箭头指向被知道的类** 97 | * `依赖关系`: 例如**动物**是依赖**氧气和水的**, 就如`动物类中的方法形参类型依赖这两个类型`. 如上图动物和水之间关系. 使用**虚线箭头**, **箭头指向被依赖的类** 98 | * `聚合关系`: 表示一种弱拥用, A可以包含B, 但B不可以包含A. 如大雁和雁群两个类. 雁群类中会有一个数组,数组的元素是大雁类型. 这之间就是`聚合`. 使用**空心菱形+实线箭头** 99 | * `合成关系`: 也可以认为是`组合`. 是一种强拥有关系. 例如鸟类和翅膀类, 鸟类是整体, 翅膀类是部分. 并且其生命周期相同, 对应着就是**在鸟类初始化的时候,翅膀类也会随之初始化**. 并且, 上图中的鸟到翅膀还有`1..2`的字样. 这称为**基数**. 表明一段会有几个实例, 例如一个鸟会有两个翅膀. 如果一个类有无数个实例那就用`n`表示. `关联关系`,`聚合关系`也是可以有**基数**的. 使用**实心菱形+实线箭头**表示. 100 | 101 | 102 | > 编程是门技术, 更加是一门艺术, 不能只满足代码结果运行正确就完事, 时常考虑如果让代码更加简练, 更加容易维护, 更易扩展和复用, 这样才可以真正提高. 103 | 104 | 105 | ## 发现之路 - 23种设计模式 106 | 107 | 108 | ### 单例模式 Singleton 109 | 110 | 111 | #### 模式介绍 112 | 113 | * `定义`: 确保某个类只有一个实例, 而且自行实例化并向整个系统提供这个实例. 114 | * `场景`: 确保一个类只会有一个对象实例, 避免产生多个对象消耗过多的资源, 或者某种类型的对象只应该有且只有一个. 如创建一个对象需要消耗的资源过多, 访问IO和数据库等资源时就可以考虑单例. 115 | 116 | 117 | #### 模式范例 118 | 119 | 单例模式的实现有5种. 120 | 121 | * 饿汉式单例 --> [实现代码](https://github.com/suzeyu1992/AlgorithmTraining/blob/master/src/design/single/HungrySingle.java) 122 | * 懒汉式单例 --> [实现代码](https://github.com/suzeyu1992/AlgorithmTraining/blob/master/src/design/single/LazySingle.java) 123 | * 静态内部类单例 --> [实现代码](https://github.com/suzeyu1992/AlgorithmTraining/blob/master/src/design/single/InnerStaticSingle.java) 124 | * 枚举单例 --> [实现代码](https://github.com/suzeyu1992/AlgorithmTraining/blob/master/src/design/single/EnumSingle.java) 125 | * 容器实现单例 --> [实现代码](https://github.com/suzeyu1992/AlgorithmTraining/blob/master/src/design/single/CollectionSingle.java) 这种方式在`android`源码中存在. 126 | 127 | 128 | --- 129 | 130 | **知识扩展** 131 | 132 | `枚举实现法`最大的优点就是实现简单, 但是在`android`却比较消耗内存. 有一点与其他单例模式不同的是: 默认枚举实例的创建`是线程安全的`. 为什么? 因为其他的单例在一种特定的场合下会重新创建对象,那就是`反序列化`. 133 | 134 | `反序列化`是从磁盘读回数据并创建一个新的对象. 即使构造函数是私有的, 反序列化依然可以通过特殊的途径去创建一个实例, 相当于调用了构造函数. 反序列化提供了一个很特别的`钩子函数`, 类中具有一个私有的, 被实例化的方法`readResolver()`, 这个方法可以让开发人员控制对象的反序列化. 例如上面的几个单例模式, 如果想杜绝单例对象在被反序列化时重新生成对象, 那么必须加入如下方法: 135 | 136 | 137 | ```java 138 | private Object readResolve() throws ObjectStreamException(){ 139 | return sInstent; // 返回单例中的实例对象 140 | } 141 | ``` 142 | 143 | 这样在反序列化的时候就不是默认的重新生成一个新对象. 而对于枚举,并不存在这个问题. 因为即使反序列化它也不会重新生成新的实例. 144 | 145 | 146 | #### Android源码对应模式 147 | 148 | 149 | 我们经常会在`Activity`中通过`getSystemService(String name)`这个函数来获取系统的服务, 比如说`WMS`,`AMS`,`LayoutInflater`等等. 这些服务都会在某一时刻以`容器单例`的形式保存在应用中. 150 | 151 | 以`Adapter#getView()`中使用布局加载器`LayoutInflate.from(context).inflate(layoutId,null)`为例 152 | 153 | 会调用`ContextImpl#getSystemService(String)`方法获取服务, 而方法内部只是从一个`SYSTEM_SERVICE_MAP`名字的集合中获取了一个`ServiceFetcher`对象, 并从其中获取具体的服务返回. 154 | 155 | 那么我们可以缕一下应用的启动, 并定位到何时保存的这些服务到这个集合的. 156 | 157 | 1. 首先应用的入口为`ActivityThread#main()`,在这个函数里面会创建`ActivityThread`对象, 并启动消息循环(UI)线程, 调用`attach(boolean)`函数 158 | 2. 在`attach(boolean)`中通过`Binder`机制与`ActivityManagerService`通信, 最终回调本类的`handlelaunchActivity()`函数. 159 | 3. 然后执行`PerformLaunchActivity()`函数, 开始创建`Application`,`Context`,`Activity`, 并把上下文关联到`Activity`中, 最终调用`Activity#onCreate()` 160 | 161 | ok刚才大概流程是这样的, 通过之前的分析我们知道, 各个系统服务是保存在`ContextImpl类中的`, 这个类是在上面的第3步中被初始化的. 看如下代码, 就是服务被注册的代码, 时机也就是第一个`Context`被创建的时候. 162 | 163 | 164 | ```java 165 | class ContextImpl extends Context { 166 | // 存储所有系统服务的集合 167 | private static final HashMap SYSTEM_SERVICE_MAP =new HashMap(); 168 | 169 | // 一个注册服务的并添加到结合的方法 170 | private static void registerService(String serviceName, ServiceFetcher fetcher) { 171 | if (!(fetcher instanceof StaticServiceFetcher)) { 172 | fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++; 173 | } 174 | SYSTEM_SERVICE_MAP.put(serviceName, fetcher); 175 | } 176 | 177 | // 静态语句块, 只在类第一次被加载的时候调用, 保证了服务只被添加一次. 178 | static { 179 | // 注册了LayoutInflate服务 180 | registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { 181 | public Object createService(ContextImpl ctx) { 182 | return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); 183 | }}); 184 | 185 | registerService(INPUT_SERVICE, new StaticServiceFetcher() { 186 | public Object createStaticService() { 187 | return InputManager.getInstance(); 188 | }}); 189 | 190 | /** 191 | * 后面省略一大坨的注册的服务代码 192 | **/ 193 | } 194 | 195 | } 196 | ``` 197 | 198 | 199 | 200 | ### 建造者模式 Builder 201 | 202 | 203 | #### 模式介绍 204 | 205 | > 一个复杂的对象有很多组成成分, 如汽车, 车轮, 方向盘, 发动机,等等. 为了在构建过程中对外部隐藏实现细节, 就可以使用`Builder`模式将部件和组装过程分离, 使得构建过程和部件都可以自由扩展, 两者之间的耦合也将到了最低. 206 | 207 | * `定义`: 将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示. 208 | * `场景`: 209 | 1. 当初始化一个队形特别复杂, 参数特别多, 且有很多参数都具有默认值时. 210 | 2. 相同的方法, 不同的执行顺序, 产生不同的事件结果时 211 | 3. 多个部件或零件, 都可以装配到一个对象中, 但是产生的运行结果又不相同. 212 | 213 | 214 | #### 模式范例 215 | 216 | [范例代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/builder) 217 | 218 | 范例的UML类图 219 | 220 | ![](imgs/UML_Builder.png) 221 | 222 | 上例中通过具体`MacbookBuilder`类构建`Macbook`对象, 而`Director`封装了构建复杂产品对象的过程, 对外隐藏了构建的细节. `Builder`于`Director`一起将一个复杂对象的构建与它的表示分离, 是的同样的构建过程可以创建不同的对象. 223 | 224 | 可能你会觉得`唉? 怎么和我见过的Builder模式不一样呢?` ,这是因为`Director`这个角色经常会被忽略. 而直接使用一个`Builder`来进行对象的封装, 并且这个`Builder`通常为**链式调用**, 它的每个`setter`方法都会返回`this`自身, 比如我们常用的`AlertDialog`. 下节介绍. 225 | 226 | 227 | #### Android源码模式实现 228 | 229 | 在Android中最经典的`Builder`实现就是`AlertDialog`. 看一下开发中的使用: 230 | 231 | 232 | ```java 233 | // 一个粗略的创建dialog 234 | // 创建构建者builder角色 235 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 236 | builder.setIcon(android.R.drawable.sym_def_app_icon) 237 | .setTitle("标题") 238 | .setMessage("message") 239 | // 设置点击等.. 240 | .setPositiveButton("确定", null); 241 | 242 | // 构建 243 | AlertDialog alertDialog = builder.create(); 244 | 245 | // 显示 246 | alertDialog.show(); 247 | ``` 248 | 249 | 从类名就可以看出这是一个`Builder模式`, 通过`Builder`对象来组装`Dialog`的各个部分. 将`Dialog`的构造和表示进行了分离. 250 | 251 | 接下来看一下`AlertDialog`的源码: 252 | 253 | 254 | ```java 255 | public class AlertDialog extends Dialog implements DialogInterface { 256 | // AlertController 这个对象会保存Builder对象中的各个参数 257 | private AlertController mAlert; 258 | 259 | // 实际上操作的是上面这个变量中的属性 260 | @Override 261 | public void setTitle(CharSequence title) { 262 | super.setTitle(title); 263 | mAlert.setTitle(title); 264 | } 265 | 266 | public void setMessage(CharSequence message) { 267 | mAlert.setMessage(message); 268 | } 269 | // 省略一坨代码如各种setter等 270 | // Builder以内部类的形式存在 271 | public static class Builder { 272 | // 1.存储AlertDialog的各个参数 如title,icon等 273 | private final AlertController.AlertParams P; 274 | 275 | // 构造函数 276 | public Builder(Context context) { 277 | this(context, resolveDialogTheme(context, 0)); 278 | } 279 | 280 | // 2. 设置参数, 我们构建的Builder设置的参数就是这些方法 281 | public Builder setTitle(int titleId) { 282 | P.mTitle = P.mContext.getText(titleId); 283 | return this; 284 | } 285 | 286 | public Builder setTitle(CharSequence title) { 287 | P.mTitle = title; 288 | return this; 289 | } 290 | 291 | // .... 292 | 293 | // 3.构建AlertDialog, 传递参数 294 | public AlertDialog create() { 295 | // 4.因为已经通过builder设置了参数, 接下来就可以创建真正需要的AlertDialog对象 296 | final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); 297 | 298 | // 5.将Builder类中的成员变量P应用到AlertDialog类中 299 | P.apply(dialog.mAlert); 300 | dialog.setCancelable(P.mCancelable); 301 | if (P.mCancelable) { 302 | dialog.setCanceledOnTouchOutside(true); 303 | } 304 | dialog.setOnCancelListener(P.mOnCancelListener); 305 | dialog.setOnDismissListener(P.mOnDismissListener); 306 | if (P.mOnKeyListener != null) { 307 | dialog.setOnKeyListener(P.mOnKeyListener); 308 | } 309 | return dialog; 310 | } 311 | } 312 | } 313 | ``` 314 | 315 | 对, 最后还调用了`AlertDialog#show()`函数, 这个函数主要做了如下几件事情: 316 | 317 | 1. 通过`dispatchOnCreate()`函数来调用`AlertDialog#onCreate()`函数 318 | 2. 然后调用`AlertDialog#onStart()`函数 319 | 3. 最后将`Dialog`的`DecorView`添加到`WindowManager`中. 320 | 321 | 那么在看一下`onCreate()`函数的源码及后续调用. 322 | 323 | 324 | ```java 325 | // AlertDialog类 326 | protected void onCreate(Bundle savedInstanceState) { 327 | super.onCreate(savedInstanceState); 328 | mAlert.installContent(); 329 | } 330 | 331 | // AlertController类 332 | public void installContent() { 333 | // 设置窗口, 没有title类型 334 | mWindow.requestFeature(Window.FEATURE_NO_TITLE); 335 | int contentView = selectContentView(); 336 | // 设置窗口的内容视图 337 | mWindow.setContentView(contentView); 338 | // 初始化AlertDialog其他子视图的内容 339 | setupView(); 340 | setupDecor(); 341 | } 342 | ``` 343 | 344 | 这部分比较重要, 通过`Window#setContentView()`和Activity是一样的过程, 设置了内容布局, 通过`AlertController`的构造函数可以发现加载布局资源就是`com.android.internal.R.layout.alert_dialog`这个文件, 之前的Builder中的各种`setter`方法就是把设置的内容传入到这个布局当中. 345 | 346 | 347 | --- 348 | 349 | 可以看到`Android源码中的AlertDialog`并没有遵循`GOF设计模式`中经典的实现方式, 而是进行了变种, 但却使其使用更加的方便. 这里`AlertDialog.Builder`这个类同时扮演了范例中的`builder`,`具体实现builder`,`Director`的角色. 简化了`Builder`设计模式, 因为模块比较稳定不会存在变化, 根据具体场景简化模式, 正是体现了灵活运用设计模式的实例. 350 | 351 | 352 | #### 实战场景 353 | 354 | 就如`Picasso`,`Glide`等链式的调用, 你可以通过链式设置很多配置属性, 也可以仅调用两三此传入必要参数即可. 是的调用实现更加灵活. 355 | 356 | 357 | ### 原型模式 Prototype 358 | 359 | 360 | #### 模式介绍 361 | 362 | > **创建性**模式, 从一个样板对象中复制出一个内部属性一致的对象, 其实就是**克隆**. 而被复制的对象就叫做**原型**, 多用于创建复杂的或者构造耗时的实例 363 | 364 | * `定义`: 用原型实例指定创建对象的种类, 并通过拷贝这些原型创建新的对象. 365 | * `场景`: 366 | 1. 类初始化需要消耗非常多的资源, 这个资源包括数据,硬件资源等, 可通过原型拷贝避免这些消耗 367 | 2. 通过`new`产生一个对象需要非常繁琐的数据准备或访问权限, 同样可以使用原型模式 368 | 3. 一个对象需要提供给其他对象访问, 并且会能会对其修改属性, 可以用原型拷贝多个对象提供使用 369 | 370 | 其实这个模式很简单, 就是利用`Object#clone()`方法可以复制一份提供使用(clone是一个`native`方法). 但是需要注意, 通过实现`Cloneable`接口的原型模式在调用`clone`函数构造并不一定就比通过`new`方式的快, 只有当通过`new`构造对象较为耗时或者说成本较高时, 通过`clone`方法才能获得效率提升. 371 | 372 | **UML类图** 373 | 374 | ![](imgs/UML_Prototype.png) 375 | 376 | 377 | #### 模式范例 378 | 379 | 这里模式实现很简单, 实现也比较少, 这里就贴出代码 380 | 381 | 382 | ```java 383 | public class WordDocument implements Cloneable{ 384 | 385 | // 文本 386 | public String mText; 387 | 388 | // 图片名列表 389 | public ArrayList mImages = new ArrayList(); 390 | 391 | public WordDocument(){ 392 | System.out.println("-----------WordDocument构造函数-----------"); 393 | } 394 | 395 | @Override 396 | protected WordDocument clone() { 397 | try { 398 | // 通过本地方法特殊途径, 构建一个对象 399 | WordDocument doc = (WordDocument) super.clone(); 400 | doc.mText = this.mText; 401 | 402 | // 因为Image是引用类型, 这样直接赋值属于浅拷贝, 再次对集合进行clone. 实现wordDocument的深拷贝 403 | doc.mImages = (ArrayList) this.mImages.clone(); 404 | return doc; 405 | }catch (Exception ex){} 406 | 407 | return null; 408 | } 409 | 410 | /** 411 | * 打印文档内容 412 | */ 413 | public void showDocument(){ 414 | System.out.println("------------开始输出内容---------------------"); 415 | System.out.println("Text: "+mText); 416 | System.out.println("List: "+mImages.toString()); 417 | System.out.println("------------输出结束------------------------"); 418 | } 419 | } 420 | ``` 421 | 422 | 与标准的原型模式相比`WordDocument`就是一个**具体实现的原型**对象. 而实现的`Cloneable`接口为**抽象的原型对象**. 423 | 424 | 其实`Cloneable`这个接口内部没有任何方法, 所以其本质就是`标识接口`,只是表明这个类的对象是`可拷贝的`, 而`clone()`这个方法是`Objec`类中的, 如果没有标识这个接口, 那么调用会抛出异常. 425 | 426 | --- 427 | 428 | **深拷贝浅拷贝** 429 | 430 | 例如上面的代码中进行修改一下 431 | 432 | ```java 433 | @Override 434 | protected WordDocument clone() { 435 | try { 436 | // 通过本地方法特殊途径, 构建一个对象 437 | WordDocument doc = (WordDocument) super.clone(); 438 | doc.mText = this.mText; 439 | 440 | // 这里进行修改 那么此时属于浅拷贝 441 | doc.mImages = this.mImages; 442 | return doc; 443 | } 444 | ``` 445 | 446 | 你可能应该发现了什么, 其实本质不过就是通过`super.clone()`构建了一个本类对象的初始状态, 然后把被拷贝的对象的各个属性值进行**赋值**操作而已. 447 | 448 | 的确, 就是如此. 就如上面两处不同的代码, 449 | 450 | * `浅拷贝`: 也称`影子拷贝`, 拷贝出来的对象并不是完全一份独立的对象, 新的对象某些属性如**引用传递**可能会`引用`原始对象的对应属性值, 也就是说, 对浅拷贝的属性可能会影响到原始数据的属性. 451 | * `深拷贝`: 拷贝出一份原始对象, 并对原始对象的属性值, 进行**复制添加**到新拷贝的对象的各个属性上. 这样拷贝出来的对象与原始对象不存在任何关联, 只作为一个数据的副本存在. 452 | 453 | 454 | 上面因为`mImages`的类型是`ArrayList`如果直接进行赋值那么属于引用传递, 共享的一份数据源, 而如果在对`ArrayList`进行一次`clone`, 那么相当于又构建了一个集合并进行数据的复制. 455 | 456 | 而`mText`虽然是对象, 但是因为是`String`类型, 属于安全类型, 由于final类,实例不可更改的特性. 如果对副本进行字符串的修改, 只不过是把原引用删除,重新指向了新的字符串. 457 | 458 | 459 | 460 | #### Android源码对应实现 461 | 462 | 上面我们说了通过对集合再次调用`clone()`即可完成深拷贝. 那么看一下`ArrayList`源码 463 | 464 | 465 | ```java 466 | public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable{ 467 | 468 | transient Object[] elementData; 469 | private int size; 470 | 471 | public Object clone() { 472 | try { 473 | ArrayList v = (ArrayList) super.clone(); 474 | v.elementData = Arrays.copyOf(elementData, size); 475 | v.modCount = 0; 476 | return v; 477 | } catch (CloneNotSupportedException e) { 478 | throw new InternalError(e); 479 | } 480 | } 481 | ``` 482 | 483 | `ArrayList`的内部克隆实现很简单, 我们都知道`ArrayList`内部是通过数组的数据结构来实现的. 通过`Arrays`工具类对原始集合的数据进行赋值并添加到一个新的数组并返回, 而返回的数组挂到了克隆出来对象上的`elementData`变量上. 484 | 485 | 而集合的大小`size`没有被进行赋值? 因为其类型是整型, 属于**值传递**, 在clone之后原始值通过值传递到了新对象中, 即使修改也不会对原始对象有任何的影响. 486 | 487 | --- 488 | 489 | 那么`Android`源码中的实现是什么? 490 | 491 | `Intent`, 我们看如下代码 492 | 493 | 494 | ```java 495 | Intent intent = new Intent("某一个activity的action"); 496 | intent.putExtra("result", "Successful"); 497 | 498 | // 调用克隆方法 499 | Intent clone = (Intent) intent.clone(); 500 | startActivity(clone); 501 | ``` 502 | 503 | 这样同样没问题, 一样的效果. 那么看一下`Intent#clone()`内部是如何实现的. 504 | 505 | 506 | ```java 507 | @Override 508 | public Object clone() { 509 | return new Intent(this); 510 | } 511 | 512 | /** 513 | * Copy constructor. 514 | */ 515 | public Intent(Intent o) { 516 | this.mAction = o.mAction; 517 | this.mData = o.mData; 518 | this.mType = o.mType; 519 | this.mPackage = o.mPackage; 520 | this.mComponent = o.mComponent; 521 | this.mFlags = o.mFlags; 522 | this.mContentUserHint = o.mContentUserHint; 523 | if (o.mCategories != null) { 524 | this.mCategories = new ArraySet(o.mCategories); 525 | } 526 | if (o.mExtras != null) { 527 | this.mExtras = new Bundle(o.mExtras); 528 | } 529 | if (o.mSourceBounds != null) { 530 | this.mSourceBounds = new Rect(o.mSourceBounds); 531 | } 532 | if (o.mSelector != null) { 533 | this.mSelector = new Intent(o.mSelector); 534 | } 535 | if (o.mClipData != null) { 536 | this.mClipData = new ClipData(o.mClipData); 537 | } 538 | } 539 | ``` 540 | 541 | 很简单不需要解释了, 手动`new`的并进行数据复制. 相当于封装了一下复制的细节而已. 542 | 543 | 但是为什么没有调用`super.clone()`来实现拷贝呢? 之前说过使用`clone`还是`new`关键字是需要根据构造对象的成本来决定的, 如果对象的构造成本比较复杂或者麻烦, 那么`clone`则是一种更优的选择, 否则就可以使用`new`的形式. 这和`c++`拷贝构造函数是一样的. 544 | 545 | 546 | 547 | 548 | #### 实战场景 549 | 550 | 当登录模块登录成功之后, 会把一些个人信息,token等信息在保存类中的某个数据结构上, 并通过一个方法对外暴露出去, 提供其他模块使用. 但是如果你返回的是一个数据结构也就是一个对象, 这个对象包含了很多个人信息, 但是正常来说, 对于外部应该只提供查看数据的能力, 不应该提供修改的能力. 551 | 552 | 所以这个使用, 就可以对登录模块对外暴露的方法进行修改, 利用`原型模式`对外返回的是一个内部数据的`深拷贝`, 这样就把可能出现的隐患彻底的隔绝了. 553 | 554 | **说明** 555 | 556 | `原型模式`是通过内存中二进制流的方式拷贝, 要比直接通过`new`一个对象性能更好, 特别是循环体内产生大量对象是. 但是注意, 因为是`二进制流的拷贝`, 所以构造函数是不会执行的. 这点要明确记牢. 557 | 558 | 559 | ### 工厂方法模式 Factory 560 | 561 | 562 | #### 模式介绍 563 | 564 | > 创建型设计模式, 其实这个模式可能在开发中出现很多回了, 只是并不了解什么是工厂模式的概念. 565 | 566 | * `定义`: 定义一个用于创建的对象的接口, 让子类决定实例化哪个类 567 | * `场景`: 在任何需要生成复杂对象的地方, 都可以使用工厂方法模式. 复杂对象适合使用工厂模式, 用`new`就可以完成创建的对象无需使用工厂模式. 568 | 569 | 工厂方法模式完全符合设计模式原则, 降低了对象之间的耦合度, 而且, 工厂方法模式依赖于抽象的架构, 将实例化的任务交由了子类实现. 570 | 571 | 572 | #### 模式范例 573 | 574 | [实现代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/factory) 575 | 576 | 这是范例的UML类图. 577 | 578 | ![](imgs/UML_Factory.png) 579 | 580 | 其实这里, 可以去掉抽象的工厂类, 只需要一个工厂即可. 这样会更加简洁直观. 581 | 582 | 583 | #### Android源码对应实现 584 | 585 | `List`和`Set`不陌生, 都继承`Collection`接口, 而`Collection`接口继承`Iterable`接口, 而这个接口很简单就一个`iterator()`方法, 如下 586 | 587 | 588 | ```java 589 | public interface Collection extends Iterable { 590 | // .... 591 | } 592 | 593 | public interface Iterable { 594 | Iterator iterator(); 595 | 596 | // 可能JDK1.8之后添加两个默认方法, 这里我们不需要关心 597 | } 598 | ``` 599 | 600 | 关于`List`和`Set`迭代器的方法遍历元素应该都用过. 那么看一下源码实现. 601 | 602 | 603 | ```java 604 | public class ArrayList extends AbstractList implements Cloneable, Serializable, RandomAccess { 605 | 606 | @Override public Iterator iterator() { 607 | return new ArrayListIterator(); 608 | } 609 | 610 | private class ArrayListIterator implements Iterator { 611 | private int remaining = size; 612 | 613 | private int removalIndex = -1; 614 | 615 | private int expectedModCount = modCount; 616 | 617 | public boolean hasNext() { 618 | return remaining != 0; 619 | } 620 | 621 | @SuppressWarnings("unchecked") public E next() { 622 | ArrayList ourList = ArrayList.this; 623 | // 返回集合大小元素, 还有几个未遍历 624 | int rem = remaining; 625 | if (ourList.modCount != expectedModCount) { 626 | throw new ConcurrentModificationException(); 627 | } 628 | if (rem == 0) { 629 | throw new NoSuchElementException(); 630 | } 631 | remaining = rem - 1; 632 | return (E) ourList.array[removalIndex = ourList.size - rem]; 633 | } 634 | 635 | public void remove() { 636 | Object[] a = array; 637 | int removalIdx = removalIndex; 638 | if (modCount != expectedModCount) { 639 | throw new ConcurrentModificationException(); 640 | } 641 | if (removalIdx < 0) { 642 | throw new IllegalStateException(); 643 | } 644 | System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining); 645 | a[--size] = null; // Prevent memory leak 646 | removalIndex = -1; 647 | expectedModCount = ++modCount; 648 | } 649 | } 650 | 651 | } 652 | ``` 653 | 654 | 655 | ```java 656 | // hashSet 复写逻辑 657 | public class HashSet extends AbstractSet implements Set, Cloneable,Serializable { 658 | public Iterator iterator() { 659 | return backingMap.keySet().iterator(); 660 | } 661 | } 662 | ``` 663 | 664 | 665 | ```java 666 | // HashMap 复写逻辑 667 | public class HashMap extends AbstractMap implements Cloneable, Serializable { 668 | Iterator newKeyIterator() { return new KeyIterator(); } 669 | 670 | private final class KeyIterator extends HashIterator 671 | implements Iterator { 672 | public K next() { return nextEntry().key; } 673 | } 674 | 675 | } 676 | ``` 677 | 678 | * `HashSet`的`iterator`方法会返回成员变量`backingMap`中对应`HashSet`对象元素的迭代器对象, 最终返回的是`KeySet`中的一个迭代器对象 679 | * `ArrayList`和`HashMap`中的`iterator()`就相当一个工厂方法, **专为new对象而生**! 680 | 681 | --- 682 | 683 | 而`Android`中, 看一下如下代码 684 | 685 | 686 | ```java 687 | public class MainActivity extends Activity { 688 | protected void onCreate(Bundle savedInstanceState) { 689 | super.onCreate(savedInstanceState); 690 | setContentView(new FrameLayout(this)); 691 | } 692 | } 693 | ``` 694 | 通过`onCreate()`这个方法, 我们可以构建出任何样式的根布局, 如`LinearLayout`,`TextView`等等. 我们在不同的`Activity#onCreate()`方法将设置的布局通过`setContentView()`函数传递给`frameworks`并显示出来. 这不就是一个工厂模式的结构. 方法内部可以创建不同的对象, 产生不同的实例. 695 | 696 | 697 | #### 实战场景 698 | 699 | 例如对数据的持久化, 可以通过的途径有`SP`,`File`,`SQLite`等. 但是对数据的操作无非就是`增删改查`, 那么我们可以抽象一个抽象类并定义CURD抽象方法. `SP`, `File`,`SQLite`分别继承抽象类, 并在抽象方法实现自己的处理逻辑. 然后就可以创建一个`工厂类`, 工厂类有一个方法, **形参为产品实现类的字节码**, 返回一个**泛型上限限定是产品的抽象类**对象, 方法内部通过字节码反射具体的产品类实例. 700 | 701 | 这样在使用的使用, 我们只需有通过`工厂方法`传入的不同`产品Class`就可以构建不同的实例, 而数据的CRUD通过**依赖倒置**抽象特性, 高层不需要依赖底层的类. 702 | 703 | 704 | ### 抽象工厂模式 Abstract Factory 705 | 706 | 707 | #### 模式介绍 708 | 709 | > 创建型设计模式, 之前工厂模式会生产某一个产品, 但是如果说, 不同的操作系统图形的场景下的两个产品**按钮**和**文本框**. 对于每一个操作系统, 其本身就构成了一个单独的产品. 两种产品两种变化, 这种情况就较之前的普通工厂升级了复杂度, 如: `Android`中的`Button`和`TextView`, `iOS`中的`Button`和`TextView`或者`WindowPhone`场景... 710 | 711 | * `定义`: 为创建一组相关或者是相互依赖的的对象提供一个接口, 而不需要指定他们的具体类 712 | * `场景`: 一个对象族有相同的约束时可以使用**抽象工厂**, 如`android`和`iOS`都有打电话软件和短信软件, 两者都属于`软件的范畴`, 但是他们的操作平台不同, 实现逻辑也不会相同. 这个时候就可以使用`抽象工厂方法模式` 713 | 714 | 715 | #### 模式范例 716 | 717 | [实现代码](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/absfactory) 718 | 719 | 范例UML图 720 | 721 | ![](imgs/UML_AbsFactory.png) 722 | 723 | 看一下运行结果: 724 | 725 | ![](imgs/test_absFactory.png) 726 | 727 | 如果这是时候, 如果想创建一种使用`普通轮胎`, `新款发动机`的车型. 只需要继承抽象工厂, 并使用原有的普通轮胎类, 并继承`IEngfine`实现一款新的发动机类. 即可完成扩展. 这就是通过接口扩展. 728 | 729 | 上面的范例, 对于每一个造车的工厂, 内部使用的零件不管哪个车场都是具有抽象的轮胎和发送机类. 这样可以达到一种自由组合的状态. 730 | 731 | 但是弊端也显示出来了, 不仅需要扩展新的`工厂类`还要扩展`新的组件类`. 732 | 733 | 734 | #### Android源码对应实现 735 | 736 | 抽象工厂在`Android`实现较少, 上一节说`onCreate()`方法就相当于一个工厂方法. 那么对于另外一个组件`Service#onBind()`同样也可以看做一个工厂方法. 737 | 738 | 如果从`frameworks`层的角度来看`Activity`和`Service`可以看做一个具体的工厂, 这样来看相当于一个抽象方法模式的雏形也没错. 739 | 740 | 另一个更像的例子是`Android`底层对`MediaPlayer`使用. 这里书上噼里啪啦一堆C语言. 我就不抄了.... 741 | 742 | 743 | ### 策略模式 Strategy 744 | 745 | 746 | #### 模式介绍 747 | 748 | > 开发中可能有这样的情况: 实现某一个功能可以有多中算法或者策略, 我们根据不同的功能来选择不同的算法. 针对这种情况, 1.可以在一个类中封装多个方法, 每个方法实现不同算法. 2.通过`if..else if..else..`条件判断来决定使用哪种算法. 但是这两种都是`硬编码`实现. 并且随着算法的增多类也就变得臃肿, 维护的成本随之变高. 如果需要增加一种新的算法, 必然需要对算法类进行修改. 这就违反了`OCP`原则和`单一职责`的原则. 749 | 750 | 751 | * `定义`: 策略模式定义了一系列的算法, 并将每一个算法封装起来, 而且使它们还可以相互替换. 策略模式让算法独立于使用它的客户而独立变化. 752 | * `场景`: 753 | * 针对同一类型问题的多种处理方式, 仅仅是具体行为有差别时 754 | * 需要安全地封装多种同一类型的操作时 755 | * 出现同一抽象类有多个子类, 而又不需要使用`if-else`或者`switch`等来选择具体子类. 756 | 757 | 758 | #### 模式范例 759 | 760 | 最方便的记忆法就是记住, `策略模式`可以去掉`if-else`或者`switch`. 语句, 即使后续会扩展通过接口来进行扩展, 不会对源代码进行修改. 满足了`OCP开闭原则`. 761 | 762 | 看一下范例代码: --> 对于交通费用的计算, 计算的算法可能会有公交, 地铁等... 763 | 764 | [代码地址](https://github.com/suzeyu1992/AlgorithmTraining/tree/master/src/design/strategy) 765 | 766 | 范例类图: 767 | 768 | ![](imgs/UML_Strategy.png) 769 | 770 | 在看一下代码的使用以及结果--> 771 | 772 | 773 | ```java 774 | public static void main (String arg[]){ 775 | // 创建操作策略的环境类 776 | TranficCalculator calculator = new TranficCalculator(); 777 | // 设置公交车的策略, 并准备计算 778 | calculator.setStrategy(new BusStrategy()); 779 | System.out.println("公交车-->计算9公里价格: "+calculator.calculatePrice(9)); 780 | 781 | // 设置地铁的策略, 并准备计算 782 | calculator.setStrategy(new SubwayStrategy()); 783 | System.out.println("地铁-->计算9公里价格: "+calculator.calculatePrice(9)); 784 | } 785 | 786 | // 结果--> 787 | 公交车-->计算9公里价格: 1 788 | 地铁-->计算9公里价格: 4 789 | ``` 790 | 791 | 你应该可以发现, 这种方式在隐藏实现的同时, **可扩展性**变得很强, 如果此时需要增加一个出租车的计算策略, 那么只需要添加一个实现了`计算策略接口`即可. 对原始代码的修改进行了`关闭`, 并对扩展`开放`. 792 | 793 | 794 | #### Android源码对应实现 795 | 796 | 动画里面的`插值器Interpolator`利用了策略模式, 利用`Interpolator`策略的抽象, `LinearInterpolator`,`CycleInterpolator`等插值器为具体的实现策略, 通过注入不同的插值器实现不同的动态效果. 797 | 798 | 看一下大概的类图 799 | 800 | ![](imgs/UML_Animation.png) 801 | 802 | * 动画中的`TimeInterpolator`时间插值器, 它的作用是根据时间流逝的百分比计算出当前属性值改变的百分比, 内置的插值器有如下几种 803 | * `线性插值器(LinearInterpolator)`用于匀速动画 804 | * `加速减速插值器(AccelerateDecelerateInterpolator)`:起始时动画加速, 结尾时动画减速 805 | * `减速插值器(DecelerateInterpolator)`: 用于随着时间的推移动画越来越慢. 806 | * 动画中的`TypeEvalutor`类型估值器: 根据当前属性改变的百分比来计算改变后的属性值. 内置的类型估值器有如下几种 807 | * `整型估值器(IntEvalutor)` 808 | * `浮点型估值器(FloatEvalutor)` 809 | * `Color估值器(ArgbEvalutor)` 810 | 811 | 接下来就开始回忆一下从一个动画开始后, 代码究竟做了什么? 812 | 813 | 对于源码的起始点入口就是调用`View的startAnimation()` 814 | 815 | 816 | ```java 817 | public void startAnimation(Animation animation) { 818 | // 1.初始化动画的开始时间 819 | animation.setStartTime(Animation.START_ON_FIRST_FRAME); 820 | // 2.对View设置动画 821 | setAnimation(animation); 822 | // 3.刷新父类缓存 823 | invalidateParentCaches(); 824 | // 4.刷新View本身及子View 825 | invalidate(true); 826 | } 827 | ``` 828 | 829 | 这里首先设置了动画的起始时间, 然后将该动画设置到`View`中, 最后再向`ViewGroup`请求刷新视图, 随后`ViewGroup`会调用`dispatchDraw()`方法对这个`View`所在的区域进行重绘. 其实对于某一个`View`的重绘最终是调用其`ViewGroup`的`drawChild(...)`方法. 跟入一下 830 | 831 | 832 | ```java 833 | protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 834 | // 简单的转发 835 | return child.draw(canvas, this, drawingTime); 836 | } 837 | 838 | boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { 839 | // .... 840 | // 查看是否需要清除动画信息 841 | final int flags = parent.mGroupFlags; 842 | // 省略无关代码 843 | 844 | // 获取设置的动画信息 845 | final Animation a = getAnimation(); 846 | 847 | if (a != null) { 848 | // 绘制动画 849 | more = drawAnimation(parent, drawingTime, a, scalingRequired); 850 | //... 851 | } 852 | } 853 | ``` 854 | 855 | 父类会调用子类的`draw`方法, 其中会先判断是否设置了清除动画的标记, 然后再获取该`View`动画信息, 如果设置了动画, 就会调用`View#drawAnimation()`方法. 856 | 857 | 858 | ```java 859 | private boolean drawAnimation(ViewGroup parent, long drawingTime, 860 | Animation a, boolean scalingRequired) { 861 | Transformation invalidationTransform; 862 | final int flags = parent.mGroupFlags; 863 | final boolean initialized = a.isInitialized(); 864 | // 1. 判断动画是否已经初始化过 865 | if (!initialized) { 866 | a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight()); 867 | a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop); 868 | if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler); 869 | // 如果设置了动画的监听, 则触发对应的回调 870 | onAnimationStart(); 871 | } 872 | // 获取Transformation对象, 存储动画的信息 873 | final Transformation t = parent.getChildTransformation(); 874 | // 2. 调用Animation#getTransformation, 通过计算获取动画的相关值 875 | boolean more = a.getTransformation(drawingTime, t, 1f); 876 | 877 | 878 | if (more) { 879 | // 3. 根据具体实现, 判断当前动画类型是否需要进行调整位置大小, 然后刷新不同的区域 880 | if (!a.willChangeBounds()) { 881 | // ... 882 | } else { 883 | // 获取重绘区域 884 | a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region, 885 | invalidationTransform); 886 | parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION; 887 | 888 | // 更新计算有效区域 889 | final int left = mLeft + (int) region.left; 890 | final int top = mTop + (int) region.top; 891 | 892 | // 进行区域更新 893 | parent.invalidate(left, top, left + (int) (region.width() + .5f), 894 | top + (int) (region.height() + .5f)); 895 | } 896 | } 897 | return more; 898 | } 899 | ``` 900 | 901 | `drawAnimation`中主要操作是动画的初始化, 动画操作, 界面刷新. 动画的回调监听`onStart()`会在动画进行初始化的时候调用, 动画的具体实现是通过`Animation#getTransformation()`方法.这个方法主要获取了`缩放系数`和调用`Animation.getTransformation(long, Transformation)`来计算和应用动画效果. 902 | 903 | 904 | ```java 905 | public boolean getTransformation(long currentTime, Transformation outTransformation) { 906 | //... 907 | float normalizedTime; 908 | // 1.计算当前时间的流逝百分比 909 | if (duration != 0) { 910 | normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / 911 | (float) duration; 912 | } else { 913 | // time is a step-change with a zero duration 914 | normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; 915 | } 916 | // 动画是否完成标记 917 | final boolean expired = normalizedTime >= 1.0f; 918 | mMore = !expired; 919 | 920 | if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { 921 | // 2.通过插值器获取动画执行百分比 , 这里获取的方法就是通过策略模式 922 | final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); 923 | // 3.应用动画效果 924 | applyTransformation(interpolatedTime, outTransformation); 925 | } 926 | 927 | // 4. 如果动画执行完毕, 那么触发动画完成的回调或者执行重复动画等操作 928 | // ... 929 | if (!mMore && mOneMoreTime) { 930 | mOneMoreTime = false; 931 | return true; 932 | } 933 | return mMore; 934 | } 935 | ``` 936 | 937 | 这段代码, 先计算已经流逝的的时间百分比, 然后再通过`具体的插值器`重新计算这个百分比, 也就是上面的第二步. 而具体是哪一个插值器是通过之前说的策略模式来实现的. 938 | 939 | 第3步调用了`applyTransformation`, 这个方法在基类`Animation`中是空实现, 可以在子类查看实现如`ScaleAnimation`,`AlphaAnimation`等查看. 当这个方法内部主要通过`矩阵`来实现动画. 当这个方法执行完毕之后, View的属性也就发生了变化, 不断地重复这个过程, 动画就随之产生. 940 | 941 | 942 | #### 实战场景 943 | 944 | 当我们自己组装了一个队列请求, 对于这个队列的处理方式默认可能是`先处理进入队列的`, 但是如果想实现一个可以`先处理后入队的`, 和`随机读取队列元素`. 那么为了以后的扩展不影响源代码, 那么可以通过`策略模式`在代码中通过对`策略抽象`面向接口,抽象编程. 是具体的实现有后续的传入的子类来决定. 945 | 946 | 947 | 948 | 未完待续... 949 | 950 | --------------------------------------------------------------------------------