diff --git a/README.md b/README.md index b7ebfc9..d62c697 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,61 @@ # awesome-coder -- 专注于产品设计与研发 -- 微信小程序开发资源汇总 -- Android ,JavaScript ,Python ,Growth Hacking 实践等。 -To Be Excellent ! !! - - -## 产品与设计 -- [创造师导航](http://chuangzaoshi.com/) -- [Material Design中文版](https://www.mdui.org/design/) -## 学习笔记 -- [做自己的产品经理](http://www.jianshu.com/p/26228262e1ae) -- [市场分析](http://www.jianshu.com/p/7f95f6c68533) -- [竞品分析](http://www.jianshu.com/p/187f5e361f07) -- [需求那些事儿](http://www.jianshu.com/p/05d9018f2d27) -- [产品运营](http://www.jianshu.com/p/351d5999fcce) +![失意不灰心,得意莫忘形](https://github.com/MrLeion/awesome-coder/blob/master/fallinLover.png) -## 小程序开发 +- 专注于 Android,Python 开发实践。 +- 产品设计与研发 +- 微信小程序开发资源汇总 -### 官方教程 +To Be Excellent !!! +------------------- -- [微信小程序官方教程](https://mp.weixin.qq.com/debug/wxadoc/dev/index.html -) +## Android 笔记 -### 社区 -- [微信小程序联盟](http://www.wxapp-union.com/) +### 项目实践 -### 轮子 +- [快速开发(一)](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/%5BAndroidDev-%E7%B3%BB%E5%88%97%5D-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%8F%91%EF%BC%88%E4%B8%80%EF%BC%89.md) -- [wepy](https://github.com/wepyjs/wepy) +- [音视频会员实现](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/%E9%9F%B3%E8%A7%86%E9%A2%91%E4%BC%9A%E5%91%98%E5%AE%9E%E7%8E%B0.md) -### Github 上的一些项目 -- [微信小程序开发资源汇总](https://github.com/justjavac/awesome-wechat-weapp) -- [效果汇总](http://javascript.ctolib.com/categories/javascript-wechat-weapp.html) +- [开源协议](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE.md) -#### 娱乐 +- [Android屏幕适配](/journey/android/Android屏幕适配.md) +- [5.x CoordinarLayout](https://blog.csdn.net/gdutxiaoxu/article/details/52858598) -- [豆瓣](https://github.com/zce/weapp-demo) -- [2048](https://github.com/natee/wxapp-2048) -- [电影](https://github.com/wangmingjob/weapp-weipiao) +### Android 进阶 -#### 工具 +- [动画(一):坐标系](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/%5BAndroid%5D%E8%87%AA%E5%AE%9A%E4%B9%89View(%E4%B8%80)%EF%BC%9A%E5%9D%90%E6%A0%87%E7%B3%BB.md) -- [地图](https://github.com/giscafer/wechat-weapp-mapdemo) -- [番茄闹钟](https://github.com/kraaas/timer) -- [音乐播放器](https://github.com/eyasliu/wechat-app-music) -- [知乎Live](https://github.com/dongweiming/weapp-zhihulive) -- [企业宣传小程序](https://github.com/yaoshanliang/weapp-ssha) -- [高仿One](https://github.com/ahonn/weapp-one) -- [简易计算器](https://github.com/dunizb/wxapp-sCalc) +- [Andorid 安全攻防战-反编译及加固[Mac版]](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/-Andorid-%E5%AE%89%E5%85%A8%E6%94%BB%E9%98%B2%E6%88%98-%E5%8F%8D%E7%BC%96%E8%AF%91%E5%8F%8A%E5%8A%A0%E5%9B%BA%5BMac%E7%89%88%5D.md) -#### 资讯 +- [Android 开发问题记录](/journey/android/Androidmemo.md) -- [掘金](https://github.com/hilongjw/weapp-gold) -- [V2EX](https://github.com/jectychen/wechat-v2ex) -- [知乎日报](https://github.com/myronliu347/wechat-app-zhihudaily) -- [公众号热门文章](https://github.com/hijiangtao/weapp-newsapp) -- [Gank](https://github.com/lypeer/wechat-weapp-gank) -#### 商城 +### 源码解析 -- [巴爷商城](https://github.com/bayetech/wechat_mall_applet) +### Java -## Android -- [Gank](http://gank.io/) -- [Android Arsenal](https://android-arsenal.com/) -- [CodeKK](http://p.codekk.com/) -## 学习笔记 -- [动画(一):坐标系](http://www.jianshu.com/p/5ccbc2528f53) -- [Android studio使用入门](http://www.jianshu.com/p/4e5cfc1a1cfc) -- [快速开发(一)](http://www.jianshu.com/p/716b54c4a9c3) -- [Andorid 安全攻防战-反编译及加固[Mac版]](http://www.jianshu.com/p/a26fa25a93ea) -- [Markdown 使用指南](http://www.jianshu.com/p/a4934a23111b) -- [Github 使用指南](http://www.jianshu.com/p/0ccfce10cdf9) -- [音视频会员实现](http://www.jianshu.com/p/557f61fdb8db) -## JavaScript - -[高仿项目-饿了么Vue.js版](https://github.com/bailicangdu/vue2-elm) @@ -106,18 +64,65 @@ To Be Excellent ! !! - [python 爬虫学习](https://github.com/lining0806/PythonSpiderNotes) +------------------- -## 增长工程师实践 -- [Hello-Github](https://github.com/521xueweihan/HelloGitHub) +## 好玩儿 + +### AR -## AR - [ARkit](https://developer.apple.com/arkit/) - [ARCore](https://github.com/google-ar/arcore-android-sdk) +- [Hello-Github](https://github.com/521xueweihan/HelloGitHub) + + +### 开发者常用社区 +- [V2EX](https://www.v2ex.com) +- [TesterHome](https://testerhome.com/) +- [pmcaff](https://www.pmcaff.com/) +- [segmentfault](https://segmentfault.com/) +- [Gank](http://gank.io/) +- [Android Arsenal](https://android-arsenal.com/) +- [CodeKK](http://p.codekk.com/) +- [wanAndroid](http://wanandroid.com/index/) + + +### 产品与设计 + +- [创造师导航](http://chuangzaoshi.com/) +- [Material Design中文版](https://www.mdui.org/design/) + + + +### 产品学习笔记 + +- [做自己的产品经理](https://github.com/MrLeion/awesome-coder/blob/master/%E7%94%9F%E6%B4%BB%E5%90%AF%E7%A4%BA%E5%BD%95/%E4%BA%A7%E5%93%81/%5B%E4%BA%A7%E5%93%81%E5%AD%A6%E4%B9%A0%5D%E5%81%9A%E8%87%AA%E5%B7%B1%E7%9A%84%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86.md) +- [市场分析](https://github.com/MrLeion/awesome-coder/blob/master/%E7%94%9F%E6%B4%BB%E5%90%AF%E7%A4%BA%E5%BD%95/%E4%BA%A7%E5%93%81/%5B%E4%BA%A7%E5%93%81%E5%AD%A6%E4%B9%A0%5D%E5%B8%82%E5%9C%BA%E5%88%86%E6%9E%90.md) +- [竞品分析](https://github.com/MrLeion/awesome-coder/blob/master/%E7%94%9F%E6%B4%BB%E5%90%AF%E7%A4%BA%E5%BD%95/%E4%BA%A7%E5%93%81/%5B%E4%BA%A7%E5%93%81%E5%AD%A6%E4%B9%A0%5D%E7%AB%9E%E5%93%81%E5%88%86%E6%9E%90.md) +- [需求那些事儿](https://github.com/MrLeion/awesome-coder/blob/master/%E7%94%9F%E6%B4%BB%E5%90%AF%E7%A4%BA%E5%BD%95/%E4%BA%A7%E5%93%81/%5B%E4%BA%A7%E5%93%81%E5%AD%A6%E4%B9%A0%5D%E9%9C%80%E6%B1%82%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF.md) +- [产品运营](https://github.com/MrLeion/awesome-coder/blob/master/%E7%94%9F%E6%B4%BB%E5%90%AF%E7%A4%BA%E5%BD%95/%E4%BA%A7%E5%93%81/%E4%BA%A7%E5%93%81%E8%BF%90%E8%90%A5.md) + +## 你可能需要的 Tools + + +- [Git 简易教程](http://www.bootcss.com/p/git-guide/) +- [Github 使用指南](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/Github-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97.md) +- [Markdown 使用指南](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/Markdown-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97.md) +- [Android studio使用入门](https://github.com/MrLeion/awesome-coder/blob/master/journey/android/Android-studio%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8.md) + + + + +## 联系我 +个人精力有限,如果对记录中有任何疑问或者觉得不足之处欢迎微博私信我,或者给我发 email: +- [Twitter](https://twitter.com/MrLeion) +- [微博](http://weibo.com/john1211) +- shtangzenglei@163.com +- shtangzenglei@gmail.com diff --git a/fallinLover.png b/fallinLover.png new file mode 100644 index 0000000..b6b412b Binary files /dev/null and b/fallinLover.png differ diff --git "a/journey/Business/\346\200\235\350\200\203/imgs/\351\243\216\346\212\225\345\210\260\351\207\221\350\236\215\345\270\202\345\234\272\345\217\221\345\261\225\346\265\201\347\250\213.png" "b/journey/Business/\346\200\235\350\200\203/imgs/\351\243\216\346\212\225\345\210\260\351\207\221\350\236\215\345\270\202\345\234\272\345\217\221\345\261\225\346\265\201\347\250\213.png" new file mode 100644 index 0000000..1f65938 Binary files /dev/null and "b/journey/Business/\346\200\235\350\200\203/imgs/\351\243\216\346\212\225\345\210\260\351\207\221\350\236\215\345\270\202\345\234\272\345\217\221\345\261\225\346\265\201\347\250\213.png" differ diff --git "a/journey/Business/\346\200\235\350\200\203/\351\243\216\351\231\251\346\212\225\350\265\204\344\270\216\350\265\204\346\234\254\346\234\254\345\270\202\345\234\272.md" "b/journey/Business/\346\200\235\350\200\203/\351\243\216\351\231\251\346\212\225\350\265\204\344\270\216\350\265\204\346\234\254\346\234\254\345\270\202\345\234\272.md" new file mode 100644 index 0000000..bcbf5c2 --- /dev/null +++ "b/journey/Business/\346\200\235\350\200\203/\351\243\216\351\231\251\346\212\225\350\265\204\344\270\216\350\265\204\346\234\254\346\234\254\345\270\202\345\234\272.md" @@ -0,0 +1,25 @@ + +![风险投资与资本本市场](imgs/风投到金融市场发展流程.png) + + +> 一级市场和二级市场的区别: + +> vc(venture captial)、pe和战略投资: + + +> (ipo 到上市后的定价机制) +审批制、核准制和注册制: + + +> 股权和债券的区别 + + + +> A、B、C、D......轮融资: + + +> 估值到底怎么个估法?: + +学习资料: + +- [可汗学院公开课:风险投资与金融市场](http://open.163.com/special/Khan/venturecapitalandcapitamarket.html) \ No newline at end of file diff --git a/journey/JavaScipt/Fe.md b/journey/JavaScipt/Fe.md new file mode 100644 index 0000000..f158109 --- /dev/null +++ b/journey/JavaScipt/Fe.md @@ -0,0 +1,69 @@ +------------------- +## JavaScript + +[高仿项目-饿了么Vue.js版](https://github.com/bailicangdu/vue2-elm) + + +------------------- + + + + +## 小程序开发 + +### 官方教程 + + +- [微信小程序官方教程](https://mp.weixin.qq.com/debug/wxadoc/dev/index.html +) + +### 社区 + +- [微信小程序联盟](http://www.wxapp-union.com/) +- [](http://wxopen.club/) + +### 轮子 + +- [wepy](https://github.com/wepyjs/wepy) +- [wxopen](http://wxopen.club/) + +### Github 上的一些项目 + +- [微信小程序开发资源汇总](https://github.com/justjavac/awesome-wechat-weapp) +- [效果汇总](http://javascript.ctolib.com/categories/javascript-wechat-weapp.html) + + +#### 娱乐 + + +- [豆瓣](https://github.com/zce/weapp-demo) +- [2048](https://github.com/natee/wxapp-2048) +- [电影](https://github.com/wangmingjob/weapp-weipiao) + + +#### 工具 + +- [地图](https://github.com/giscafer/wechat-weapp-mapdemo) +- [番茄闹钟](https://github.com/kraaas/timer) +- [音乐播放器](https://github.com/eyasliu/wechat-app-music) +- [知乎Live](https://github.com/dongweiming/weapp-zhihulive) +- [企业宣传小程序](https://github.com/yaoshanliang/weapp-ssha) +- [高仿One](https://github.com/ahonn/weapp-one) +- [简易计算器](https://github.com/dunizb/wxapp-sCalc) + +#### 资讯 + +- [掘金](https://github.com/hilongjw/weapp-gold) +- [V2EX](https://github.com/jectychen/wechat-v2ex) +- [知乎日报](https://github.com/myronliu347/wechat-app-zhihudaily) +- [公众号热门文章](https://github.com/hijiangtao/weapp-newsapp) +- [Gank](https://github.com/lypeer/wechat-weapp-gank) + +#### 商城 + +- [巴爷商城](https://github.com/bayetech/wechat_mall_applet) + +#### 学习笔记 +- [我从小程序学到了什么(一)](https://mp.weixin.qq.com/s?__biz=MzAwODcwODYwMw==&mid=2247484292&idx=1&sn=437ef07bfc4fdedaeefe90a5bf3b644a&chksm=9b6b8b10ac1c020665d95893a1294f98c19a7ced18a44b8001044205e6693f8cf25d65923961&scene=21#wechat_redirect) + +------------------- \ No newline at end of file diff --git "a/journey/TODO/Android-\345\200\222\350\256\241\346\227\266\347\232\204\344\272\224\347\247\215\345\256\236\347\216\260\346\226\271\345\274\217.md" "b/journey/TODO/Android-\345\200\222\350\256\241\346\227\266\347\232\204\344\272\224\347\247\215\345\256\236\347\216\260\346\226\271\345\274\217.md" new file mode 100644 index 0000000..fb3b319 --- /dev/null +++ "b/journey/TODO/Android-\345\200\222\350\256\241\346\227\266\347\232\204\344\272\224\347\247\215\345\256\236\347\216\260\346\226\271\345\274\217.md" @@ -0,0 +1 @@ +https://juejin.im/post/58bead54ac502e006b2fe6e0 diff --git "a/journey/TODO/Android-\351\200\232\344\277\241.md" "b/journey/TODO/Android-\351\200\232\344\277\241.md" new file mode 100644 index 0000000..e6202ab --- /dev/null +++ "b/journey/TODO/Android-\351\200\232\344\277\241.md" @@ -0,0 +1 @@ +http://www.jianshu.com/p/442a9e32ab52 diff --git a/journey/TODO/FloatingView.md b/journey/TODO/FloatingView.md new file mode 100644 index 0000000..9b4a510 --- /dev/null +++ b/journey/TODO/FloatingView.md @@ -0,0 +1,11 @@ + + +判断当前运行程序 +http://www.jb51.net/article/81933.htm + +https://github.com/waylife/DemoCollections/tree/master/ViewDebugHelper + +https://github.com/jaredrummler/AndroidProcesses + +权限适配 +http://blog.csdn.net/self_study/article/details/52859790 diff --git "a/journey/TODO/Listen1-\347\211\210\346\234\2541-0-0-\345\212\250\345\221\230.md" "b/journey/TODO/Listen1-\347\211\210\346\234\2541-0-0-\345\212\250\345\221\230.md" new file mode 100644 index 0000000..0ea90ca --- /dev/null +++ "b/journey/TODO/Listen1-\347\211\210\346\234\2541-0-0-\345\212\250\345\221\230.md" @@ -0,0 +1,22 @@ +同志们,晚上好~~ +明天又要开始新的一周的工作啦! +本周双休和飞飞讨论下咋们项目这个版本的功能模块具体工作报告如下: +- 版本的迭代计划意见上传至tower.im,大家有空可以看下有啥意见可以提提意见。 +- [ 交互原型](https://github.com/listenOne/Document/tree/master/listen1_1.0.0)飞飞已经完成大致框架,预计在下周三可以完成。在这之前大家有什么比较好的交互的点都可以提给飞飞,具体的建议提交方式因为tower.im不方便支持,所以暂时以[github issue](https://github.com/listenOne/Document/issues) 的形式提给飞飞,这个项目希望飞飞可以花时间维护下。 + +- 由于我们使用到的数据比较敏感,我对市面上常见的开源协议进行了调研,具体的理解在[Listen1](https://www.jianshu.com/c/90b9ffcef4e9)专栏中有,这里大家如果疑问欢迎沟通~~ + +本周任务建议: + +- 交互和UI设计 +- 后端/数据端:接口文档 +- Android前端框架(网路,图片,音频播放器) + +[推荐一个接口文档书写平台,有什么更好的平台欢迎大家推荐](https://www.showdoc.cc/) + + +另:本次开源项目本着大家一起成长的目的开启,体验全栈开发模式。希望大家可以通过博客记录咋们开发过程中的点点滴滴,这里在简书开了一个专栏:[Listen1](https://www.jianshu.com/c/90b9ffcef4e9),希望大家可以通过总结和分享的方式获得更多的成长。 + + +最后祝大家平安夜快乐~~ + diff --git "a/journey/TODO/\345\210\251\347\224\250Jenkins\347\216\251\350\275\254Android\350\207\252\345\212\250\346\211\223\345\214\205\345\217\221\345\214\205.md" "b/journey/TODO/\345\210\251\347\224\250Jenkins\347\216\251\350\275\254Android\350\207\252\345\212\250\346\211\223\345\214\205\345\217\221\345\214\205.md" new file mode 100644 index 0000000..2c8abf3 --- /dev/null +++ "b/journey/TODO/\345\210\251\347\224\250Jenkins\347\216\251\350\275\254Android\350\207\252\345\212\250\346\211\223\345\214\205\345\217\221\345\214\205.md" @@ -0,0 +1 @@ +http://blog.csdn.net/mabeijianxi/article/details/52680283 diff --git "a/journey/TODO/\345\215\216\344\270\272-Mate-8---UnsatisfiedLinkError---\"-so-is-is-64-bit-instead-of-32-bit.md" "b/journey/TODO/\345\215\216\344\270\272-Mate-8---UnsatisfiedLinkError---\"-so-is-is-64-bit-instead-of-32-bit.md" new file mode 100644 index 0000000..b6e44e4 --- /dev/null +++ "b/journey/TODO/\345\215\216\344\270\272-Mate-8---UnsatisfiedLinkError---\"-so-is-is-64-bit-instead-of-32-bit.md" @@ -0,0 +1,3 @@ +http://blog.csdn.net/ouyang_peng/article/details/51168072 + +https://corbt.com/posts/2015/09/18/mixing-32-and-64bit-dependencies-in-android.html diff --git "a/journey/TODO/\347\247\273\345\212\250\346\212\200\346\234\257\344\274\232\350\256\256\346\200\273\347\273\223.md" "b/journey/TODO/\347\247\273\345\212\250\346\212\200\346\234\257\344\274\232\350\256\256\346\200\273\347\273\223.md" new file mode 100644 index 0000000..d9e990c --- /dev/null +++ "b/journey/TODO/\347\247\273\345\212\250\346\212\200\346\234\257\344\274\232\350\256\256\346\200\273\347\273\223.md" @@ -0,0 +1,18 @@ +![](http://upload-images.jianshu.io/upload_images/2539684-453ab6b3a8331773.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +一年一度的Google IO大会于2017年5月19号在Google总部山景城举行,会议期间宣布期间有101项规定产生。 +这里分别对会议的内容摘要进行了手机: + +* [中文会议总结](https://axiang.cc/archives/30587) + +* [英文会议总结](https://blog.google/topics/developers/all-io17-announcements/) + + +毕竟作为移动端开发人员,对于Google爸爸那是要绝对的忠诚嘛~~根据会议的内容简要地做了一个总结并绘制了思维导图,谨以此图做个归纳: + + ![](http://upload-images.jianshu.io/upload_images/2539684-8a423853cb13e332.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +此外,GMTC于2017年06月09-2017年06月10日在北京召开,虽然是个学术讨论会议,但是让我们深省的是Android开发人员除了做好native app,更要对新出的pwa,小程序,RN以及Weex有个关注和学习,这样才不会被技术趋势所淘汰!此外,AR/VR和微服务架构的企业端服务市场还是需要跟进的。 + +![](http://upload-images.jianshu.io/upload_images/2539684-72dde5ff9af2ca3c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) diff --git "a/journey/android/-Andorid-\345\256\211\345\205\250\346\224\273\351\230\262\346\210\230-\345\217\215\347\274\226\350\257\221\345\217\212\345\212\240\345\233\272[Mac\347\211\210].md" "b/journey/android/-Andorid-\345\256\211\345\205\250\346\224\273\351\230\262\346\210\230-\345\217\215\347\274\226\350\257\221\345\217\212\345\212\240\345\233\272[Mac\347\211\210].md" new file mode 100644 index 0000000..34a49e6 --- /dev/null +++ "b/journey/android/-Andorid-\345\256\211\345\205\250\346\224\273\351\230\262\346\210\230-\345\217\215\347\274\226\350\257\221\345\217\212\345\212\240\345\233\272[Mac\347\211\210].md" @@ -0,0 +1,159 @@ + +![](http://upload-images.jianshu.io/upload_images/2539684-b7085d0a8f8487a4.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +首先必须得承认如果通过反编译直接窃取别人的劳动成果是有那么点不道德!所以我们一定一定一定要学好反编译,这样我们就可以尽情地窃取别人的劳动成果,并且防止我们自己的技术不被反编译啦(开个玩笑啦,别当真)~~ + +好吧,下面单纯从技术的角度一起谈谈我们的android安全攻防大战吧: + +![](http://upload-images.jianshu.io/upload_images/2539684-58f9f0249bb6df74.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +如上图,迎面向我们走来的是本文的两大重要环节攻与防,也就是我们所说的反编译和混淆加固啦;反编译(攻)一般分为两种情况: + +- 1.资源反编译,针对喜欢高仿但是不会P图的广大程序员屌丝们; +- 2.源代码反编译,可以看看别人的代码和实现手段; + +混淆加固(防)也分为两种手法: + +- 1.混淆,这种情况比较麻烦,引入第三方还要避免混淆别人的代码,并且混淆后的代码还是可以看得出大致使用的技术和逻辑,所以不推荐使用; +- 2.加固,360加固全部自动化打包,但是现在应用商店似乎对加固软件进行了限制,比如之前工作上传百度应用市场的时候居然提示我们要用百度加固~~,baidu,坑爹! + +### 赛前准备 + +工欲善其事,必先利其器。选择一个好的工具可以使我们的工作: + +- [dex2jar](https://sourceforge.net/projects/dex2jar/files/?source=navbar) + +- [JD-GUI](http://jd.benow.ca/) + +- [apktool](https://ibotpeaches.github.io/Apktool/install/) + +- [iTerm命令行工具](https://www.iterm2.com/) + +- [360加固工具](http://jiagu.360.cn/qcmshtml/details.html#helper) + +- [Apk编辑器](http://android.myapp.com/myapp/detail.htm?apkName=com.gmail.heagoo.apkeditor.pro) + + + +> 问题一:apktool安装配置,作为mac盲的我展示下零基础怎么配置以及遇到的一些问题吧: + +- 1.右键保存链接为 apktool 所有格式文本[wrapper script](https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/osx/apktool) +- 2.apktool.jar 下载地址打开可以看到历史版本列表,可以选择最新版本的,此教程使用的版本为:2.2.3 下载成功重命名为apktool.jar. + +- 3.将 apktool.jar 和 apktool 拷贝到 /usr/local/bin(需要 root 权限) + +> sudo cp apktool apktool.jar /usr/local/bin [此目录是 mac 中管理员可执行命令] + +- 4.修改这两个文件的权限: sudo chmod + x file名 + +- 5.现在就可以在终端运行 apktool 命令了 + +> 验证安装 apktool 是否安装成功的时候发现错误: +Error: Invalid or corrupt jarfile /usr/local/bin/apktool.jar + +![](http://upload-images.jianshu.io/upload_images/2539684-1ca922efdbc2dad7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +Google了一下,检测jar包是否正常的命令: + +> java -jar jar包名 + +报出同样的错误,删除原本 /usr/local/bin/ 目录下的文件,重新配置,获得成功: + +![](http://upload-images.jianshu.io/upload_images/2539684-d82bc617b744483c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +> 问题二:dex2jar 需要授权: + +切换到 dex2jar 目录下,执行命令:chmod a+x *.sh + +注:否则直接执行会提示: + +d2j-dex2jar.sh: line 36: ./d2j_invoke.sh: Permission denied + + + +###反编译 + +为了能够更好地串联整个过程也本着尊重版权的原则,我们写了一个简易的demo: + + ![](http://upload-images.jianshu.io/upload_images/2539684-20b45148983d10c7.gif?imageMogr2/auto-orient/strip) + + 好的,下面就围绕这个demo进行展开吧~~ + + 出于文件整理的考虑,我们将apk文件和授权完成的dex2jar放入同一目录下: + + ![](http://upload-images.jianshu.io/upload_images/2539684-989b8bd7199b3ada.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +####源代码反编译 + +思路:我们知道 android 的源代码是以 .dex 文件的形式存在的,也就是说我们要查看源码,我们需要将.dex -> java 代码;但是目前还没有找到比较好的直接转换工具,所以我们套用原有套路:.dex -> .jar->java源码; + +说干就干,执行命令: + +> sh dex2jar-2.0/d2j-dex2jar.sh app-release.apk + +通过dex2jar我们获得以下文件: + +![](http://upload-images.jianshu.io/upload_images/2539684-069d38e3715e4a15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +右击app-release-dex2jar.jar文件选择用JD-GUI打开查看源码即可,让我们来看看我们的成果吧: +![](http://upload-images.jianshu.io/upload_images/2539684-0e2f86aeaeb07a43.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +做到这里大家不知道有没有好奇,没毛病啊~~图片什么的都有啦,源代码也有啦,打开 .xml 文件一看,目瞪口呆: + +![](http://upload-images.jianshu.io/upload_images/2539684-38dadf20b73c4f5f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +所以咋们都资源反编译也就应运而生啦~~ + +####资源反编译 + +正如上面提到的,资源反编译针对的主要是 xml 资源文件,我们使用的工具也就是 apktool ,切换到上图目录下废话不多说来行命令压压惊: + +> apktool d app-release.apk + + +xml完美呈现: + +![](http://upload-images.jianshu.io/upload_images/2539684-c8c9950826fb6f4a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +这个时候我们可以打开smali文件夹改些文本什么的,重新拿个证书打个包,就是一个添加了我们 tag 的 app 啦~~ + +切换目录到demo下,执行命令 + +> apktool b app-release + +兴冲冲装到自己手机上,结果发现安装不了,原来是没有证书,没有条件就去创造下条件从AS中生成一个自己的证书,然后用该证书给咋们的app打个包就OK啦。 + +执行命令: + + > jarsigner -verbose -keystore android.keystore -signedjar app-release-1.apk app-release.apk android.keystore + + 效果如下: + + ![](http://upload-images.jianshu.io/upload_images/2539684-02abcb9375674028.gif?imageMogr2/auto-orient/strip) + + + +### 加固 + +好啦,我们成功破解所有套路之后,我们来看看如何反套路吧,目前市面上对代码进行混淆或者加固。本文选择运用加固技术对我们的app进行保护。如果需要加固助手的同学可以360官网下载到加固助手,另外它还需要注册账号,个人配置密钥证书和下载渠道,参考帮助手册进行操作即可。 + +![](http://upload-images.jianshu.io/upload_images/2539684-717f4f0484a01130.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +如果你看到这个界面那么咋们就可以开始加固打我们的渠道包啦,暂时我们只添加了腾讯应用宝、百度手机助手和360市场打包成功后我们可以获取每个渠道的app。 + +下面我们还是用上面的方法对我们的渠道包进行反编译,我们会发现很安全因为整个逻辑根本就看不清楚啦~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-eea3b77b9f17dec2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +好啦,Android 攻防战到此结束,如果有朋友有更好玩的欢迎微博或者简书私信我偶~~ + + +参考文章: + +- [Android安全-反编译-郭霖](http://blog.csdn.net/guolin_blog/article/details/49738023) + +- [Android安全-混淆技术-郭霖](http://blog.csdn.net/guolin_blog/article/details/50451259) + +- [反编译Mac实操参考](http://www.jianshu.com/p/b3bb4da64dc7) diff --git a/journey/android/APT/Annotation Processor Tool.md b/journey/android/APT/Annotation Processor Tool.md new file mode 100644 index 0000000..598a8c7 --- /dev/null +++ b/journey/android/APT/Annotation Processor Tool.md @@ -0,0 +1,43 @@ +# Annotation Processor Tool + + +## Hello World + + +## 基础部分 + +### 注解 + +用来标记或者注释的一段 @*** 形式的 java 代码,我们要特别注意 编译时注解 和 运行时注解以及源码级注解的区别 + +### JavaPoet + +- TypeSpec +- MethodSpec +- FieldSpec +- JarFile + + +### APT + + + + +### SimpleButterKnife + + +## 题外话(调试小技巧) + + + +## 常用库的源码分析和优缺点 + + +## Gradle Plugin + JavaAssist + + +## 参考文章 + +- [APT 指南](https://xsfelvis.github.io/categories/%E6%B3%A8%E8%A7%A3/) +- [Android APT(编译时代码生成)最佳实践](https://joyrun.github.io/2016/07/19/AptHelloWorld/) +- [APT tool Api](https://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/package-summary.html) \ No newline at end of file diff --git a/journey/android/APT/JavaPoet.md b/journey/android/APT/JavaPoet.md new file mode 100644 index 0000000..8f92a5b --- /dev/null +++ b/journey/android/APT/JavaPoet.md @@ -0,0 +1,671 @@ +# JavaPoet + + +注解系列 + +* [注解基础](https://xsfelvis.github.io/2017/01/06/%E6%B3%A8%E8%A7%A3%E5%9F%BA%E7%A1%80/) +* [JavaPoet](https://xsfelvis.github.io/2017/03/12/%E7%BC%96%E8%AF%91%E6%9C%9F%E6%B3%A8%E8%A7%A3%E4%B9%8BJavaPoet/) +* [编译期注解处理之APT](https://xsfelvis.github.io/2017/04/07/%E7%BC%96%E8%AF%91%E6%9C%9F%E6%B3%A8%E8%A7%A3%E4%B9%8BAPT/) + +### 0x00 概述 +----------------------------- + +上一篇限于篇幅只介绍了APT,这篇来继续介绍[javapoet](https://github.com/square/javapoet),是square公司的开源库。正如其名,java诗人,通过注解来生成java源文件,通常要使用javapoet这个库与Filer配合使用。主要和注解配合用来干掉那些重复的模板代码(如butterknife +和databinding所做的事情),当然你也可以使用这个技术让你的代码更加的炫酷。 + +### 0x01 简单使用 +----------------------------------- + +使用之前要先引入这个库 + + +``` +compile 'com.squareup:javapoet:1.7.0' +``` + + +javapoet是用来生成代码的,需要借助 + +### 常用类 + +使用javapoet前需要了解4个常用类 + +#### [](#MethodSpec-代表一个构造函数或方法声明。 "MethodSpec 代表一个构造函数或方法声明。")MethodSpec 代表一个构造函数或方法声明。 + +#### [](#TypeSpec-代表一个类,接口,或者枚举声明。 "TypeSpec 代表一个类,接口,或者枚举声明。")TypeSpec 代表一个类,接口,或者枚举声明。 + +#### [](#FieldSpec-代表一个成员变量,一个字段声明。 "FieldSpec 代表一个成员变量,一个字段声明。")FieldSpec 代表一个成员变量,一个字段声明。 + +#### [](#JavaFile包含一个顶级类的Java文件。 "JavaFile包含一个顶级类的Java文件。")JavaFile包含一个顶级类的Java文件。 + +国际惯例先自动生成一个helloWorld类 +定义一个编译期注解 + +``` +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface clazz_hello { + String value(); +} +``` + +然后看下helloworld的注解处理器 + +``` +@AutoService(Processor.class) +public class HelloWorldProcess extends AbstractProcessor { + + private Filer filer; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + filer = processingEnv.getFiler(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (TypeElement element : annotations) { + if (element.getQualifiedName().toString().equals(clazz_hello.class.getCanonicalName())) { + MethodSpec main = MethodSpec.methodBuilder("main") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(void.class) + .addParameter(String\[\].class, "args") + .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") + .build(); + TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(main) + .build(); + + try { + JavaFile javaFile = JavaFile.builder("com.xsf", helloWorld) + .addFileComment(" This codes are generated automatically. Do not modify!") + .build(); + javaFile.writeTo(filer); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return true; + } + + @Override + public Set getSupportedAnnotationTypes() { + Set annotations = new LinkedHashSet<>(); + annotations.add(clazz_hello.class.getCanonicalName()); + return annotations; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } +} + +``` + +这样就会在app-build-source-apt-debug-com.xsf文件夹下生成这个文件 + +### 0x02 使用进阶 +----------------------------------- + +### [](#方法-amp-控制流: "方法&控制流:")方法&控制流: + +#### [](#添加方法 "添加方法")添加方法 + +`addcode` 和 `addstatement`,对与无需类引入的极简代码可以直接使用`addCode` + +``` +MethodSpec main = MethodSpec.methodBuilder("main") + .addCode("" + \+ "int total = 0;\\n" + \+ "for (int i = 0; i < 10; i++) {\\n" + \+ " total += i;\\n" + \+ "}\\n") + .build(); +``` + +生成的是 + +``` +void main() { + int total = 0; + for (int i = 0; i < 10; i++) { + total += i; + } +} +``` + +要是需要import的方法,如上面的`.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")` 就需要使用`.addStatement`来声明 + +##### [](#更优雅的流控制 "更优雅的流控制")更优雅的流控制 + +`beginControlFlow` 流开启 +`addStatement` 处理语句 +`endControlFlow()`流结束 + +如上面的用流改写就是 + +``` +MethodSpec main = MethodSpec.methodBuilder("main") + .addStatement("int total = 0") + .beginControlFlow("for (int i = 0; i < 10; i++)") + .addStatement("total += i") + .endControlFlow() + .build(); +``` + +### [](#占位符 "占位符")占位符 + +javapoet里面提供了占位符来帮助我们更好地生成代码 + +#### [](#L-字面常量(Literals) "$L 字面常量(Literals)")$L 字面常量(Literals) + +``` +private MethodSpec computeRange(String name, int from, int to, String op) { + return MethodSpec.methodBuilder(name) + .returns(int.class) + .addStatement("int result = 0") + .beginControlFlow("for (int i = $L; i < $L; i++)", from, to) + .addStatement("result = result $L i", op) + .endControlFlow() + .addStatement("return result") + .build(); +} + +``` + + + +这个就是一个for循环,op负责加减乘除等符号 + +#### [](#S-字符串常量(String) "$S 字符串常量(String)")$S 字符串常量(String) + +#### [](#T-类型-Types "$T 类型(Types)")$T 类型(Types) + +最大的特点是自动导入包, + + + +``` +MethodSpec today = MethodSpec.methodBuilder("today") + .returns(Date.class) + .addStatement("return new $T()", Date.class) + .build(); + +TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(today) + .build(); + +JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) + .build(); + +javaFile.writeTo(System.out); + +``` + + +生成的代码如下,而且会自动导包 + +``` +package com.example.helloworld; + +import java.util.Date; + +public final class HelloWorld { + Date today() { + return new Date(); + } +} +``` + +如果我们想要导入自己写的类怎么办?上面的例子是传入系统的class,这里也提供一种方式,通过ClassName.get(”类的路径”,”类名“),结合$T可以生成 + +``` +ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); +ClassName list = ClassName.get("java.util", "List"); +ClassName arrayList = ClassName.get("java.util", "ArrayList"); +TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard); + +MethodSpec beyond = MethodSpec.methodBuilder("beyond") + .returns(listOfHoverboards) + .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList) + .addStatement("result.add(new $T())", hoverboard) + .addStatement("result.add(new $T())", hoverboard) + .addStatement("result.add(new $T())", hoverboard) + .addStatement("return result") + .build(); +``` +然后生成 + +``` +package com.example.helloworld; + +import com.mattel.Hoverboard; +import java.util.ArrayList; +import java.util.List; + +public final class HelloWorld { + List beyond() { + List result = new ArrayList<>(); + result.add(new Hoverboard()); + result.add(new Hoverboard()); + result.add(new Hoverboard()); + return result; + } +} +``` +在导入包这里,javapoet 同样支持import static,通过`addStaticImport` + +``` +ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); + +ClassName namedBoards = ClassName.get("com.mattel", "Hoverboard", "Boards"); + +MethodSpec beyond = MethodSpec.methodBuilder("beyond") + .returns(listOfHoverboards) + .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList) + .addStatement("result.add($T.createNimbus(2000))", hoverboard) + .addStatement("result.add($T.createNimbus(\\"2001\\"))", hoverboard) + .addStatement("result.add($T.createNimbus($T.THUNDERBOLT))", hoverboard, namedBoards) + .addStatement("$T.sort(result)", Collections.class) + .addStatement("return result.isEmpty() $T.emptyList() : result", Collections.class) + .build(); + +TypeSpec hello = TypeSpec.classBuilder("HelloWorld") + .addMethod(beyond) + .build(); + +JavaFile.builder("com.example.helloworld", hello) + .addStaticImport(hoverboard, "createNimbus") + .addStaticImport(namedBoards, "*") + .addStaticImport(Collections.class, "*") + .build(); +``` + + +#### [](#N-命名-Names "$N 命名(Names)")$N 命名(Names) + +通常指我们自己生成的方法名或者变量名等等比如这样的代码块 + +``` +public String byteToHex(int b) { + char\[\] result = new char\[2\]; + result\[0\] = hexDigit((b >>> 4) & 0xf); + result\[1\] = hexDigit(b & 0xf); + return new String(result); +} + +public char hexDigit(int i) { + return (char) (i < 10 ? i + '0' : i - 10 \+ 'a'); +} +``` +我们可以传递`hexDigit()`来代替。 + +``` +MethodSpec hexDigit = MethodSpec.methodBuilder("hexDigit") + .addParameter(int.class, "i") + .returns(char.class) + .addStatement("return (char) (i < 10 ? i + '0' : i - 10 + 'a')") + .build(); + +MethodSpec byteToHex = MethodSpec.methodBuilder("byteToHex") + .addParameter(int.class, "b") + .returns(String.class) + .addStatement("char\[\] result = new char\[2\]") + .addStatement("result\[0\] = $N((b >>> 4) & 0xf)", hexDigit) + .addStatement("result\[1\] = $N(b & 0xf)", hexDigit) + .addStatement("return new String(result)") + .build(); +``` + + +### [](#构建类的元素 "构建类的元素")构建类的元素 + +#### [](#Methods "Methods")Methods + +方法的修饰,如`Modifiers.ABSTRACT` + +``` +MethodSpec flux = MethodSpec.methodBuilder("flux") + .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED) + .build(); + +TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addMethod(flux) + .build(); +``` + +这将会生成如下代码 +``` +public abstract class HelloWorld { + protected abstract void flux(); +} +``` +当然Methods需要和`MethodSpec.Builder`配置来增加方法参数、异常、javadoc、注解等。 + +#### [](#构造器 "构造器")构造器 + +这个其实也是个函数方法而已,因此可以使用MethodSpec来生成构造器方法。比如: + +``` +MethodSpec flux = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(String.class, "greeting") + .addStatement("this.$N = $N", "greeting", "greeting") + .build(); + +TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") + .addModifiers(Modifier.PUBLIC) + .addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL) + .addMethod(flux) + .build(); +``` +将会生成 + +``` +public class HelloWorld { + private final String greeting; + + public HelloWorld(String greeting) { + this.greeting = greeting; + } +} +``` +#### [](#参数 "参数")参数 + +之前我们是通过`addstatement`直接设置参数,其实参数也有自己的一个专用类`ParameterSpec`,我们可以使用`ParameterSpec.builder()`来生成参数,然后MethodSpec的addParameter去使用,这样更加优雅。 + +``` +ParameterSpec android = ParameterSpec.builder(String.class, "android") + .addModifiers(Modifier.FINAL) + .build(); + +MethodSpec welcomeOverlords = MethodSpec.methodBuilder("welcomeOverlords") + .addParameter(android) + .addParameter(String.class, "robot", Modifier.FINAL) + .build(); +``` +生成的代码 + + +``` +void welcomeOverlords(final String android, final String robot) { +} +``` + +#### [](#字段 "字段")字段 + +可以使用FieldSpec去声明字段,然后加到Method中处理 + +``` +FieldSpec android = FieldSpec.builder(String.class, "android") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL) + .build(); + +TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") + .addModifiers(Modifier.PUBLIC) + .addField(android) + .addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL) + .build(); +``` + +然后生成代码 +``` +public class HelloWorld { + private final String android; + + private final String robot; +} +``` + +通常Builder可以更加详细的创建字段的内容,比如javadoc、annotations或者初始化字段参数等,如: + +``` +FieldSpec android = FieldSpec.builder(String.class, "android") + .addModifiers(Modifier.PRIVATE, Modifier.FINAL) + .initializer("$S + $L", "Lollipop v.", 5.0d) + .build(); +``` +对应生成的代码 + +``` + +private final String android = "Lollipop v." \+ 5.0; +``` + + +#### [](#接口 "接口")接口 + +接口方法必须是PUBLIC ABSTRACT并且接口字段必须是PUBLIC STATIC FINAL ,使用`TypeSpec.interfaceBuilder` + +如下 + +``` +TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld") + .addModifiers(Modifier.PUBLIC) + .addField(FieldSpec.builder(String.class, "ONLY\_THING\_THAT\_IS\_CONSTANT") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$S", "change") + .build()) + .addMethod(MethodSpec.methodBuilder("beep") + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .build()) + .build(); +``` + +生成的代码如下 + + +``` +public interface HelloWorld { + String ONLY\_THING\_THAT\_IS\_CONSTANT = "change"; + + void beep(); +} +``` + + +* 枚举类型 + +使用`TypeSpec.enumBuilder`来创建,使用`addEnumConstant`来添加 + +``` + +TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") + .addModifiers(Modifier.PUBLIC) + .addEnumConstant("ROCK") + .addEnumConstant("SCISSORS") + .addEnumConstant("PAPER") + .build(); + +``` + +生成的代码 + +``` + +public enum Roshambo { + ROCK, + + SCISSORS, + + PAPER +} +``` + +更复杂的类型也可以支持,如重写、注解等 + +``` +TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") + .addModifiers(Modifier.PUBLIC) + .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("$S", "fist") + .addMethod(MethodSpec.methodBuilder("toString") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .addStatement("return $S", "avalanche!") + .build()) + .build()) + .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace") + .build()) + .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat") + .build()) + .addField(String.class, "handsign", Modifier.PRIVATE, Modifier.FINAL) + .addMethod(MethodSpec.constructorBuilder() + .addParameter(String.class, "handsign") + .addStatement("this.$N = $N", "handsign", "handsign") + .build()) + .build(); +``` +生成代码 + +``` +public enum Roshambo { + ROCK("fist") { + @Override + public void toString() { + return "avalanche!"; + } + }, + + SCISSORS("peace"), + + PAPER("flat"); + + private final String handsign; + + Roshambo(String handsign) { + this.handsign = handsign; + } +} +``` + +#### [](#匿名内部类 "匿名内部类")匿名内部类 + +需要使用`Type.anonymousInnerClass("")`,通常可以使用$L占位符来指代 + +``` +TypeSpec comparator = TypeSpec.anonymousClassBuilder("") + .addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class)) + .addMethod(MethodSpec.methodBuilder("compare") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .addParameter(String.class, "a") + .addParameter(String.class, "b") + .returns(int.class) + .addStatement("return $N.length() - $N.length()", "a", "b") + .build()) + .build(); + +TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") + .addMethod(MethodSpec.methodBuilder("sortByLength") + .addParameter(ParameterizedTypeName.get(List.class, String.class), "strings") + .addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator) + .build()) + .build(); +``` + + +生成代码 + +``` +void sortByLength(List strings) { + Collections.sort(strings, new Comparator() { + @Override + public int compare(String a, String b) { + return a.length() - b.length(); + } + }); +} +``` + +定义匿名内部类的一个特别棘手的问题是参数的构造。在上面的代码中我们传递了不带参数的空字符串。TypeSpec.anonymousClassBuilder(“”)。 + +#### [](#注解 "注解")注解 + +注解使用起来比较简单 + +``` + +MethodSpec toString = MethodSpec.methodBuilder("toString") + .addAnnotation(Override.class) + .returns(String.class) + .addModifiers(Modifier.PUBLIC) + .addStatement("return $S", "Hoverboard") + .build(); +``` + +生成代码 + +``` +@Override +public String toString() { + return "Hoverboard"; +} +``` +通过`AnnotationSpec.builder()` 可以对注解设置属性: + +``` + +MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent") + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addAnnotation(AnnotationSpec.builder(Headers.class) + .addMember("accept", "$S", "application/json; charset=utf-8") + .addMember("userAgent", "$S", "Square Cash") + .build()) + .addParameter(LogRecord.class, "logRecord") + .returns(LogReceipt.class) + .build(); +``` +代码生成如下 + +``` +@Headers( + accept = "application/json; charset=utf-8", + userAgent = "Square Cash" +) +LogReceipt recordEvent(LogRecord logRecord); +``` +注解同样可以注解其他注解,通过$L引用如 + +``` +MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent") + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addAnnotation(AnnotationSpec.builder(HeaderList.class) + .addMember("value", "$L", AnnotationSpec.builder(Header.class) + .addMember("name", "$S", "Accept") + .addMember("value", "$S", "application/json; charset=utf-8") + .build()) + .addMember("value", "$L", AnnotationSpec.builder(Header.class) + .addMember("name", "$S", "User-Agent") + .addMember("value", "$S", "Square Cash") + .build()) + .build()) + .addParameter(LogRecord.class, "logRecord") + .returns(LogReceipt.class) + .build(); +``` +生成代码 + +``` +@HeaderList({ + @Header(name = "Accept", value = "application/json; charset=utf-8"), + @Header(name = "User-Agent", value = "Square Cash") +}) +LogReceipt recordEvent(LogRecord logRecord); +``` +### 0x03 后续 +----------------------------- + +在javapoet之前有javawriter,但javapoet有着更强大的代码模型,并且对类的理解更加到位,因此推荐使用javapoet + + + + + + +* 注:本文转载自:[云来博客 -- JavaPoet]( https://xsfelvis.github.io/2017/03/12/%E7%BC%96%E8%AF%91%E6%9C%9F%E6%B3%A8%E8%A7%A3%E4%B9%8BJavaPoet/)* \ No newline at end of file diff --git "a/journey/android/APT/\346\263\250\350\247\243\345\237\272\347\241\200.md" "b/journey/android/APT/\346\263\250\350\247\243\345\237\272\347\241\200.md" new file mode 100644 index 0000000..76e86fe --- /dev/null +++ "b/journey/android/APT/\346\263\250\350\247\243\345\237\272\347\241\200.md" @@ -0,0 +1,202 @@ +# 注解基础 + + + +本文主要是介绍注解的基础知识,为后面的APT和JavaPoet打下基础 + +### 0x01 什么是注解 + +注解(Annotation)是Java5的一个新特性,是插入在代码中的一种注释或者说是一种元数据(meta data),这些注释信息可以在编译期使用预编译工具进行获取处理,也可以在运行期使用Java反射机制来获取,这取决于你的注解类型。 + +### 0x02 注解的语法及其定义 + +在Android中注解经常存在我们代码中: + +``` +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main4); + getIntentData(); +} + +``` + +上面的`@Override`就是系统的注解,表明这是个重写方法,点击源码可以看到长成下面的样子 + + +``` +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Override { +} +``` + + +实际中会带有一些参数 + + +``` +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface run_methodInfo { + String name() default "long"; + String data(); + int id() default 365; +} +``` + +@Retention @Target @Document @Inherited四种 是元注解即 “注解的注解” + + +> @Target 表示该注解目标,可能的 ElemenetType 参数包括: + +* ElemenetType.CONSTRUCTOR 构造器声明 +* ElemenetType.FIELD 域声明(包括 enum 实例) +* ElemenetType.LOCAL_VARIABLE 局部变量声明 +* ElemenetType.METHOD 方法声明 +* ElemenetType.PACKAGE 包声明 +* ElemenetType.PARAMETER 参数声明 +* ElemenetType.TYPE 类,接口(包括注解类型)或enum声明 + + +> @Retention 表示该注解的生命周期,可选的 RetentionPolicy 参数包括 + +* RetentionPolicy.SOURCE **注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃注解将被编译器丢弃** +* RetentionPolicy.CLASS **注解被保留到class文件,但jvm加载class文件时候被遗弃** +* RetentionPolicy.RUNTIME **注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;,因此可以通过反射机制读取注解的信息** + +> @Documented 指示将此注解包含在 javadoc 中 +> @Inherited 指示允许子类继承父类中的注解 + +使用注解需要遵守它的规则: + +* Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口. +* 参数成员只能用public或默认(default)这两个访问权修饰 +* 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组. +* 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法(分为编译期还是运行期) + + + +### 0x03 使用 + + +(这里仅介绍最常见的运行期的注解,编译期注解涉及到apt、javapoet会单独再开一篇介绍) + +> Step One 定义注解 + +先看三个Runtime注解,包括类、方法、字段, + +``` +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface run_classInfo { + String value(); +} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface run_methodInfo { + String name() default "long"; + String data(); + int id() default 365; +} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface run_fieldInfo { + int\[\] value(); +} +``` + +> Step two 使用这些注解 + + +``` +@run_classInfo("类注解RunTime Class") +public class RunTimeTest { + @run_fieldInfo(value = {77, 88}) + public String fieldInfo = "filedInfo"; + + @run_fieldInfo(value = {163}) + public int id = 55; + + @run_methodInfo(name = "彩笔学长", data = "finance") + public static String getMethod() { + return RunTimeTest.class.getSimpleName(); + } +} +``` + + + + +> 使用反射解析注解 + + +``` + +/** + * 解析运行时注解 + */ +private void showRunTimeInfo() { + StringBuffer sb = new StringBuffer(); + //获取Class 注解 + Class clazz = RunTimeTest.class; + Constructor\[\] constructors = clazz.getConstructors(); + //获取包含的注解类信息 + run\_classInfo runClassInfo = clazz.getAnnotation(run\_classInfo.class); + if (runClassInfo != null) { + //获取class注解 + sb.append("Class注解: ").append("\\n"); + sb.append(Modifier.toString(clazz.getModifiers())).append(" ") + .append(clazz.getSimpleName()).append("\\n"); + + sb.append("注解值:").append("\\n") + .append(runClassInfo.value()).append("\\n\\n"); + } + + //获取Field注解 + sb.append("Field注解:").append("\\n"); + Field\[\] fields = clazz.getDeclaredFields(); //获取自身的不包括继承类 + for (Field field : fields) { + //获取field注解类信息 + run\_fieldInfo fieldInfo = field.getAnnotation(run\_fieldInfo.class); + if (fieldInfo != null) { + sb.append(Modifier.toString(field.getModifiers())).append(" ") + .append(field.getType().getSimpleName()).append(" ") + .append(field.getName()).append("\\n"); + sb.append("注解值: ").append("\\n") + .append(Arrays.toString(fieldInfo.value())).append("\\n\\n"); + } + } + + //获取Method 注解 + sb.append("Method注解: ").append("\\n"); + Method\[\] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + run\_methodInfo methodInfo = method.getAnnotation(run\_methodInfo.class); + if (methodInfo != null) { + sb.append(Modifier.toString(method.getModifiers())).append(" ") + .append(method.getReturnType().getSimpleName()).append(" ") + .append(method.getName()).append("\\n"); + sb.append("注解值:").append("\\n"); + sb.append("name: ").append(methodInfo.name()).append("\\n"); + sb.append("data: ").append(methodInfo.data()).append("\\n"); + sb.append("id: ").append(methodInfo.id()).append("\\n"); + } + + } + tvDes.setText(sb.toString()); +} +``` + + +##0x04 参考文献 + +* [http://www.cnblogs.com/lbangel/p/3523741.html](http://www.cnblogs.com/lbangel/p/3523741.html) +* [http://blog.csdn.net/github_35180164/article/details/52107204](http://blog.csdn.net/github_35180164/article/details/52107204) + + + +* 注:本文转载自:[云来博客 -- 注解基础]( https://xsfelvis.github.io/2017/01/06/注解基础/) \ No newline at end of file diff --git "a/journey/android/APT/\347\274\226\350\257\221\346\234\237\346\263\250\350\247\243\345\244\204\347\220\206\344\271\213APT.md" "b/journey/android/APT/\347\274\226\350\257\221\346\234\237\346\263\250\350\247\243\345\244\204\347\220\206\344\271\213APT.md" new file mode 100644 index 0000000..cd99218 --- /dev/null +++ "b/journey/android/APT/\347\274\226\350\257\221\346\234\237\346\263\250\350\247\243\345\244\204\347\220\206\344\271\213APT.md" @@ -0,0 +1,317 @@ +# 编译期注解处理之APT + + +### 0x00 概述 +----------------------------- + +前一篇介绍了注解的基本知识以及常见用法,由于运行期(RunTime)利用反射去获取信息还是比较损耗性能的,本篇将介绍一种使用注解更加优雅的方式,编译期(Compile time)注解,以及处理编译期注解的手段APT和Javapoet,限于篇幅,本篇着重介绍APT +首先你的注解需要声明为CLASS +@Retention(RetentionPolicy.CLASS) + +编译期解析注解基本原理: +在某些代码元素上(如类型、函数、字段等)添加注解,在编译时编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,然后将添加了注解的所有元素都传递到process函数中,使得开发人员可以在编译器进行相应的处理,例如,根据注解生成新的Java类,这也就是ButterKnife等开源库的基本原理。 + +### 0x01 APT +-------------------------------- + +在处理编译器注解的第一个手段就是APT(Annotation Processor Tool),即注解处理器。在java5的时候已经存在,但是java6开始的时候才有可用的API,最近才随着butterknife这些库流行起来。本章将阐述什么是注解处理器,以及如何使用这个强大的工具。 + +> 什么是APT + +APT是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解,一个注解的注解处理器,以java代码(或者编译过的字节码)作为输入,生成.java文件作为输出,核心是交给自己定义的处理器去处理, + +> 如何使用 + +每个自定义的处理器都要继承虚处理器,实现其关键的几个方法 + +* 继承虚处理器 AbstractProcessor + +``` +public class MyProcessor extends AbstractProcessor { + @Override + public synchronized void init(ProcessingEnvironment env){ } + + @Override + public boolean process(Set annoations, RoundEnvironment env) { } + + @Override + public Set getSupportedAnnotationTypes() { } + + @Override + public SourceVersion getSupportedSourceVersion() { } +} +``` + +下面重点介绍下这几个函数: + +1. `init(ProcessingEnvironment env)`: 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer +2. `process(Set annotations, RoundEnvironment env)`: 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。这是一个布尔值,表明注解是否已经被处理器处理完成,官方原文`whether or not the set of annotations are claimed by this processor`,通常在处理出现异常直接返回false、处理完成返回true。 +3. `getSupportedAnnotationTypes()`: 必须要实现;用来表示这个注解处理器是注册给哪个注解的。返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。 +4. `getSupportedSourceVersion()`: 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported(),你也可以使用SourceVersion\_RELEASE\_6、7、8 + +* 注册 处理器 + +由于处理器是javac的工具,因此我们必须将我们自己的处理器注册到javac中,在以前我们需要提供一个.jar文件,打包你的注解处理器到此文件中,并在在你的jar中,需要打包一个特定的文件 `javax.annotation.processing.Processor到META-INF/services路径下` +把MyProcessor.jar放到你的builpath中,javac会自动检查和读取javax.annotation.processing.Processor中的内容,并且注册MyProcessor作为注解处理器。 + +超级麻烦有木有,不过不要慌,谷歌baba给我们开发了AutoService注解,你只需要引入这个依赖,然后在你的解释器第一行加上 + +1 +``` +@AutoService(Processor.class) +``` +然后就可以自动生成META-INF/services/javax.annotation.processing.Processor文件的。省去了打jar包这些繁琐的步骤。 + +> APT中的Elements和TypeMirrors + +在前面的init()中我们可以获取如下引用 + +* Elements:一个用来处理Element的工具类 +* Types:一个用来处理TypeMirror的工具类 +* Filer:正如这个名字所示,使用Filer你可以创建文件(通常与javapoet结合) + +在注解处理过程中,我们扫面所有的Java源文件。源文件的每一个部分都是一个特定类型的Element + +先来看一下Element + +对于编译器来说 代码中的元素结构是基本不变的,如,组成代码的基本元素包括包、类、函数、字段、变量的等,JDK为这些元素定义了一个基类也就是`Element`类 + +Element有五个直接子类,分别代表一种特定类型 + +PackageElement + +表示一个包程序元素,可以获取到包名等 + +TypeElement + +表示一个类或接口程序元素 + +VariableElement + +表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数 + +ExecutableElement + +表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注解类型元素 + +TypeParameterElement + +表示一般类、接口、方法或构造方法元素的泛型参数 + +开发中Element可根据实际情况强转为以上5种中的一种,它们都带有各自独有的方法,如下所示 +``` +package com.example; // PackageElement + +public class Test { // TypeElement + + private int a; // VariableElement + private Test other; // VariableElement + + public Test () {} // ExecuteableElement + public void setA ( // ExecuteableElement + int newA // TypeElement + ) {} +} +``` +再举个栗子🌰: + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.CLASS) +public @interface Test { + + String value(); +} +``` +这个注解因为只能作用于函数类型,因此,它对应的元素类型就是ExecutableElement当我们想通过APT处理这个注解的时候就可以获取目标对象上的Test注解,并且将所有这些元素转换为ExecutableElement元素,以便获取到他们对应的信息。 + +查看其代码定义 + +定义如下: + +``` +\*\* + \* 表示一个程序元素,比如包、类或者方法,有如下几种子接口: + \* ExecutableElement:表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注解类型元素 ; + \* PackageElement:表示一个包程序元素; + \* TypeElement:表示一个类或接口程序元素; + \* TypeParameterElement:表示一般类、接口、方法或构造方法元素的形式类型参数; + \* VariableElement:表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数 + */ +public interface Element extends AnnotatedConstruct { + /\*\* + \* 返回此元素定义的类型 + \* 例如,对于一般类元素 C,返回参数化类型 C + */ + TypeMirror asType(); + + /\*\* + \* 返回此元素的种类:包、类、接口、方法、字段...,如下枚举值 + \* PACKAGE, ENUM, CLASS, ANNOTATION\_TYPE, INTERFACE, ENUM\_CONSTANT, FIELD, PARAMETER, LOCAL\_VARIABLE, EXCEPTION\_PARAMETER, + \* METHOD, CONSTRUCTOR, STATIC\_INIT, INSTANCE\_INIT, TYPE\_PARAMETER, OTHER, RESOURCE\_VARIABLE; + */ + ElementKind getKind(); + + /\*\* + \* 返回此元素的修饰符,如下枚举值 + \* PUBLIC, PROTECTED, PRIVATE, ABSTRACT, DEFAULT, STATIC, FINAL, + \* TRANSIENT, VOLATILE, SYNCHRONIZED, NATIVE, STRICTFP; + */ + Set getModifiers(); + + /\*\* + \* 返回此元素的简单名称,例如 + \* 类型元素 java.util.Set 的简单名称是 "Set"; + \* 如果此元素表示一个未指定的包,则返回一个空名称; + \* 如果它表示一个构造方法,则返回名称 ""; + \* 如果它表示一个静态初始化程序,则返回名称 ""; + \* 如果它表示一个匿名类或者实例初始化程序,则返回一个空名称 + */ + Name getSimpleName(); + + /\*\* + \* 返回封装此元素的最里层元素。 + \* 如果此元素的声明在词法上直接封装在另一个元素的声明中,则返回那个封装元素; + \* 如果此元素是顶层类型,则返回它的包; + \* 如果此元素是一个包,则返回 null; + \* 如果此元素是一个泛型参数,则返回 null. + */ + Element getEnclosingElement(); + + /\*\* + \* 返回此元素直接封装的子元素 + */ + List getEnclosedElements(); + + boolean equals(Object var1); + + int hashCode(); + + /\*\* + \* 返回直接存在于此元素上的注解 + \* 要获得继承的注解,可使用 getAllAnnotationMirrors + */ + List getAnnotationMirrors(); + + + /\* + \* 返回此元素针对指定类型的注解(如果存在这样的注解),否则返回 null。注解可以是继承的,也可以是直接存在于此元素上的 + */ + A getAnnotation(Class annotationType); + + //接受访问者的访问 (??) + R accept(ElementVisitor var1, P var2); +} + +``` +最后一个,并没有使用到,感觉不太好理解,查了资料这个函数接受一个ElementVisitor和类型为P的参数。 + +``` + +public interface ElementVisitor { + //访问元素 + R visit(Element e, P p); + + R visit(Element e); + + //访问包元素 + R visitPackage(PackageElement e, P p); + + //访问类型元素 + R visitType(TypeElement e, P p); + + //访问变量元素 + R visitVariable(VariableElement e, P p); + + //访问克而执行元素 + R visitExecutable(ExecutableElement e, P p); + + //访问参数元素 + R visitTypeParameter(TypeParameterElement e, P p); + + //处理位置的元素类型,这是为了应对后续Java语言的扩折而预留的接口,例如后续元素类型添加了,那么通过这个接口就可以处理上述没有声明的类型 + R visitUnknown(Element e, P p); +} +``` +在ElementgVisitor中定义了多个visit接口,每个接口处理一种元素类型,这就是典型的访问者模式。我们制定,一个类元素和函数元素是完全不一样的,他们的结构不一样,因此,在编译器对他们的操作肯定是不一样,通过访问者模式正好可以解决数据结构与数据操作分离的问题,避免某些操作污染数据对象类。 + +因此,代码在APT眼中只是一个结构化的文本而已。Element代表的是源代码。TypeElement代表的是源代码中的类型元素,例如类。然而,TypeElement并不包含类本身的信息。你可以从TypeElement中获取类的名字,但是你获取不到类的信息,例如它的父类。这种信息需要通过TypeMirror获取。你可以通过调用elements.asType()获取元素的TypeMirror。 + +### 0x02 辅助接口 +----------------------------------- + +在自定义注解器的初始化时候,可以获取以下4个辅助接口 + + +``` +public class MyProcessor extends AbstractProcessor { + + private Types typeUtils; + private Elements elementUtils; + private Filer filer; + private Messager messager; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + typeUtils = processingEnv.getTypeUtils(); + elementUtils = processingEnv.getElementUtils(); + filer = processingEnv.getFiler(); + messager = processingEnv.getMessager(); + } + } + +``` +* Filer + +一般配合JavaPoet来生成需要的java文件(下一篇将详细介绍javaPoet) + +* Messager + +Messager提供给注解处理器一个报告错误、警告以及提示信息的途径。它不是注解处理器开发者的日志工具,而是用来写一些信息给使用此注解器的第三方开发者的。在官方文档中描述了消息的不同级别中非常重要的是Kind.ERROR,因为这种类型的信息用来表示我们的注解处理器处理失败了。很有可能是第三方开发者错误的使用了注解。这个概念和传统的Java应用有点不一样,在传统Java应用中我们可能就抛出一个异常Exception。如果你在process()中抛出一个异常,那么运行注解处理器的JVM将会崩溃(就像其他Java应用一样),使用我们注解处理器第三方开发者将会从javac中得到非常难懂的出错信息,因为它包含注解处理器的堆栈跟踪(Stacktace)信息。因此,注解处理器就有一个Messager类,它能够打印非常优美的错误信息。除此之外,你还可以连接到出错的元素。在像现在的IDE(集成开发环境)中,第三方开发者可以直接点击错误信息,IDE将会直接跳转到第三方开发者项目的出错的源文件的相应的行。 + +* Types + +Types是一个用来处理TypeMirror的工具 + +* Elements + +Elements是一个用来处理Element的工具 + +[](#0x03-优缺点 "0x03 优缺点")0x03 优缺点 +-------------------------------- + +> 优点(结合javapoet) + +* 对代码进行标记、在编译时收集信息并做处理 +* 生成一套独立代码,辅助代码运行 + +> 缺点 + +* 可以自动生成代码,但在运行时需要主动调用 +* 如果要生成代码需要编写模板函数 + +[](#0x04-其他 "0x04 其他")0x04 其他 +----------------------------- + +1. 通常我们需要分离处理器和注解 + 这样做的原因是,在发布程序时注解及生成的代码会被打包到用户程序中,而注解处理器则不会(注解处理器是在编译期在JVM上运行跟运行时无关)。要是不分离的话,假如注解处理器中使用到了其他第三方库,那就会占用系统资源,特别是方法数, + +2. 该技术可以让我们在设计自己框架时候多了一种技术选择,更加的优雅 + +3. 反射优化 + + +运行时注解的使用可以减少很多代码的编写,但是谁都知道这是有性能损耗的,不过权衡利弊,我们选择了妥协,这个技术手段可以处理这个问题 + +[](#0x05-参考文献 "0x05 参考文献")0x05 参考文献 +----------------------------------- + +* [http://hannesdorfmann.com/annotation-processing/annotationprocessing101](http://hannesdorfmann.com/annotation-processing/annotationprocessing101) +* [http://blog.csdn.net/github_35180164/article/details/52121038](http://blog.csdn.net/github_35180164/article/details/52121038) +* [https://www.zhangningning.com.cn/blog/Android/android_rentention.html](https://www.zhangningning.com.cn/blog/Android/android_rentention.html) +* [https://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/AbstractProcessor.html](https://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/AbstractProcessor.html) + + + +* 注:本文转载自:[云来博客 -- 编译期注解处理之APT]( https://xsfelvis.github.io/2017/04/07/%E7%BC%96%E8%AF%91%E6%9C%9F%E6%B3%A8%E8%A7%A3%E4%B9%8BAPT/)* \ No newline at end of file diff --git "a/journey/android/Android-studio\344\275\277\347\224\250\345\205\245\351\227\250.md" "b/journey/android/Android-studio\344\275\277\347\224\250\345\205\245\351\227\250.md" new file mode 100644 index 0000000..d808dec --- /dev/null +++ "b/journey/android/Android-studio\344\275\277\347\224\250\345\205\245\351\227\250.md" @@ -0,0 +1,286 @@ +##基础设置 +1 `cmd+alt+s`进入设置界面,输入 encoding 设置默认字符集为`UTF-8`(千万别忘了,否则就坑队友啦~~) + +![](http://upload-images.jianshu.io/upload_images/2539684-514e6cacf8c39c60.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +2 设置主题,窗口大小,动画效果 + +- 修改主题 + +- 修改全局窗口字体,字号 + +- 窗口动画 + +![](http://upload-images.jianshu.io/upload_images/2539684-2fcbc20f84f7311e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + +3.修改menu或者toolBar + +> 这里我是添加了创建class,fragment,activity的ToolBar + +![](http://upload-images.jianshu.io/upload_images/2539684-809a509e6fe71671.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +4.释放空包折叠 + +![](http://upload-images.jianshu.io/upload_images/2539684-939640c737612f4e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +5.打开工程设置 + +- 禁用自动打开上次关闭工程 ,禁用退出提示 + +- 打开新项目提示方式 + +![](http://upload-images.jianshu.io/upload_images/2539684-8f1be1792c239e38.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +6.禁用自动检查更新 + +- 取消as自动更新 + +- 消sdk自动更新 + + +![](http://upload-images.jianshu.io/upload_images/2539684-92951e0fbde14538.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +7.配置快捷键 + +- 自定义快捷键 + +- 根据内容搜索快捷键 + +- 根据按键搜索快捷键 + +- 删除快捷键 + +![](http://upload-images.jianshu.io/upload_images/2539684-24cea3286d2cb2d0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +快捷键查询: + +[Android Studio常用快捷键汇总(mac的小伙伴们看过来)](https://segmentfault.com/a/1190000006898515) + +[Mac官方快捷键](https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf) + + +8.编辑器 + +- 鼠标悬停显示文档 + +- 格式化&导包提示 + + +![](http://upload-images.jianshu.io/upload_images/2539684-b9fc23f8f4edd632.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +9.显示行号,显示方法分隔线 + +- 显示行号 + +- 显示方法分隔符 + +![](http://upload-images.jianshu.io/upload_images/2539684-cd4f14a56e197394.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +10.代码折叠 + +- 取消方法自动折叠 + + +![](http://upload-images.jianshu.io/upload_images/2539684-e5b5dcc075b38c11.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +11.代码智能提示 + +- 敲什么都提示 + +- 提示时间设置 + +![](http://upload-images.jianshu.io/upload_images/2539684-e99af54a18a1c35d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +> 敲什么字符会提示,All(大小写全部符合),None(不管大小写,符合就提示),(First letter)(第一个字符符合就OK,其他随意)。我这种脑残没记性的当然选择None。时间设置根据自己电脑性能. + + +12.自动导包 + +![](http://upload-images.jianshu.io/upload_images/2539684-98014f12c91999aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +Optimize imports on the fly:优化导包,格式化代码时会删掉多余的导包。Add unambiguous imports on the fly:敲代码时,敲简单类名就帮你把包导了。 + +##代码风格设置 + +1.创建个人代码样式配置 + +> 估计是为了保护默认的代码样式配置,让用户把配置搞坏了也能一键还原,IDEA不允许修改默认的配置,需要用户创建配置才能进行修改。选择基于哪个主题的,然后Save As一份即可。 + +![](http://upload-images.jianshu.io/upload_images/2539684-4d5add49c9260456.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +2.修改代码字体 + +> 强烈建议用Consolas字体,好看!!! + +![](http://upload-images.jianshu.io/upload_images/2539684-28b9ea6b4fd5694f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +3.修改控制台字体 + +> 要改的话,得先把1那个地方的勾取消掉,默认android Logcat 每个级别的颜色都是一样的.建议修改 + +![](http://upload-images.jianshu.io/upload_images/2539684-2e474c54614ad404.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +4.Logcat字体 + +> 要改的话,得先把1那个地方的勾取消掉,默认android Logcat 每个级别的颜色都是一样的.建议修改 + +![](http://upload-images.jianshu.io/upload_images/2539684-a90e4a09afda7e95.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +5.修改注释位置 + +> 禁用“语句堆一行” + +![](http://upload-images.jianshu.io/upload_images/2539684-173633b90bcb16dc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +> Comment at frist column:启用的话,注释符号就会在行首,否则就按照缩进来注释。 +> Control statement in one line:格式化代码的时候,会把些很短的语句合并成一行。这样影响代码可读性. + + +6.对齐变量名 + +![](http://upload-images.jianshu.io/upload_images/2539684-e2ad1c34f2760f3e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +7.修改变量前后缀 + +> 静态成员是s,普通成员是m,转换成成员变量的时候自动加上m,生成setter,getter的时候会忽视m,很好很强大. + +![](http://upload-images.jianshu.io/upload_images/2539684-5022955f3baa1dca.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +8.取消Android Lint 检查 + +> 一定程度加快速度吧,不过打开Android Lint会有一些android相关提示 + +![](http://upload-images.jianshu.io/upload_images/2539684-e9d1ab236e832dac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +9.新建文件模板 + +` /** +* author: ${USER} + * created on: ${DATE} ${TIME} +* description: + */ +` +![](http://upload-images.jianshu.io/upload_images/2539684-d0ba47f594116806.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +10.修改新建文件文件头 + +> 每次建新类,会加上这样的头信息 +![](http://upload-images.jianshu.io/upload_images/2539684-1f2f761440d59c90.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +> 上图就是通用的文件头,框住的地方是你系统的用户名,想个性化的话,可以改这里,至于哪里引用这个文件头的呢,就在隔壁 +![](http://upload-images.jianshu.io/upload_images/2539684-8edfcf37355b600e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + + +11.自己定义Live Templates + +> 模板定义,方便开发,减少重复代码 + +![](http://upload-images.jianshu.io/upload_images/2539684-e74b1bfe801ef3cd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +##工具篇 + + +1.添加管理插件 + +![](http://upload-images.jianshu.io/upload_images/2539684-4eaa71f4178d2463.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +1) 从远程仓库获取插件 +2) 从本地仓库获取插件 + + + +2.Svn添加移除项目 + +![](http://upload-images.jianshu.io/upload_images/2539684-9130aeb1e1306796.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +3.配置Svn安装路径 + +a)可以自己是自己下载的Subversion SVN的svn.exe + +b)可以是Visual SVN里面的svn.exe + +c)可以是TortoiseSVN里面的svn.exe + +![](http://upload-images.jianshu.io/upload_images/2539684-67b351500260d808.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +4.配置git安装路径 + +![](http://upload-images.jianshu.io/upload_images/2539684-7851f25865c018d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +5.配置Gradle安装路径,离线模式,本地Gradle仓库 + +![](http://upload-images.jianshu.io/upload_images/2539684-aae44ab67245f49b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +6.配置maven仓库路径 + +![](http://upload-images.jianshu.io/upload_images/2539684-c1e1714b9e46ac01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +7.项目自动编译 + +![](http://upload-images.jianshu.io/upload_images/2539684-e2b7b6039b6c84aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +8.Gradle Task超时时间 + +![](http://upload-images.jianshu.io/upload_images/2539684-6f0e3831185b5cd6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +9.优化-取消同步 + +![](http://upload-images.jianshu.io/upload_images/2539684-c1c21adb98301b1e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +##AndroidStudio奇淫异巧 + + +- 上面的这些设置我已经帮各位设置好,放在这个目录下啦,各位只要`import settings`,下载地址:[setting.jar](https://github.com/MrLeion/MacTools/blob/master/AndroidStudio/settings.jar) + +- mac多行编辑 + +> ctrl+G + +> shift+ 选中单个字母 + +> shift+command+ 选中整行 + +> shift+alt+ 选中单个单词 + + +- mac插件集合 + + - ButterKnife + + - GsonFormat + + - Selector + + +- 资源下载站 + + [Google中国开发官网](https://developer.android.google.cn/index.html) + + [Android Studio中文社区]( http://tools.android-studio.org/index.php) + + [Android Studio问答社区](http://ask.android-studio.org/?/explore/) + + [Android Studio开发者工具下载](http://www.androiddevtools.cn/) + + + + +- 参考资料 + +[Androidstudio快速入门](http://stormzhang.com/devtools/2015/06/17/android-studio-all/) + +[Android studio官方权威指南](https://developer.android.google.cn/studio/index.html) diff --git "a/journey/android/Android-\346\225\260\346\215\256\344\270\216\345\255\230\345\202\250.md" "b/journey/android/Android-\346\225\260\346\215\256\344\270\216\345\255\230\345\202\250.md" new file mode 100644 index 0000000..f12dd15 --- /dev/null +++ "b/journey/android/Android-\346\225\260\346\215\256\344\270\216\345\255\230\345\202\250.md" @@ -0,0 +1,61 @@ + +![注意这些目录欧](http://upload-images.jianshu.io/upload_images/2539684-65cc3b43a39094e8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +[xmind源文件下载](http://ox1aqdtfp.bkt.clouddn.com/Android%E6%95%B0%E6%8D%AE%E4%B8%8E%E5%AD%98%E5%82%A8.xmind) + + +相信部分做 Android 的朋友经常会对内存、内部存储、外部存储这些概念有点含糊不清,也经常对下面这些操作: + +- 一键清理 +- 清除数据 +- 清除缓存 + +不太清楚从开发角度上代表的真正的意义。今天工作上遇到了需要一些缓存方面的问题,发现对这些概念理解并不是那么清晰,做下小结段友勿喷~~ + + +从英文上去理解 + +- 内存 memory + + 类似于电脑的内存条,是设备进行逻辑和算术预算的重要部件; + +- 内部存储 Internal Storage + + 如下图所示,手机里需要 root 才能够查看的部分 :/data/data/包名/...一般用来保存应用的一些配置和登录信息。在 apk 卸载之后包名下的文件会跟着清掉; + + +![内部存储](http://upload-images.jianshu.io/upload_images/2539684-dcb869bc12fe9695.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + +- 外部存储 External Storage + +需要关注的常用的私有目录 + +- /Android/data/data/caches :getExternalCacheDir() +- /Android/data/data/files:getExternalStorageDir() + +一般要将缓存的数据放在这两个目录下,在 apk 卸载之后这两个目录中的文件会跟着清掉; + +![外部存储](http://upload-images.jianshu.io/upload_images/2539684-aa8d7eeccd49e531.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +这里提供一个文件操作的工具类库 [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) + +下面解析下三种操作: + +一键清理:清除 memory,杀死进程 + +清除缓存:清除 app 缓存的页面数据 + +清除数据:清除内部存储和外部存储中 files 和 caches 下的文件。 + + + + + + + + + + diff --git a/journey/android/Androidmemo.md b/journey/android/Androidmemo.md new file mode 100644 index 0000000..3b1ad83 --- /dev/null +++ b/journey/android/Androidmemo.md @@ -0,0 +1,12 @@ +# Android Memo + + +- viewpager获取当前view +解决方案:每个 Item setTag后,通过 findViewByTag 获取view. + + + +- android:clipToPadding=“boolean” + - true:绘制区域会往里面锁 + - flase: 滑动时忽略padding的值 + 系统默认是true。 \ No newline at end of file diff --git "a/journey/android/Android\345\261\217\345\271\225\351\200\202\351\205\215.md" "b/journey/android/Android\345\261\217\345\271\225\351\200\202\351\205\215.md" new file mode 100644 index 0000000..8cb15b6 --- /dev/null +++ "b/journey/android/Android\345\261\217\345\271\225\351\200\202\351\205\215.md" @@ -0,0 +1,33 @@ +# Android 屏幕适配 + + +数据显示[Android Fragmentation](https://opensignal.com/reports/2014/android-fragmentation/):Android 碎片化得到业界持续不断的关注和攻克。 + + +正常情况下,我们明确目标需要适配哪些屏幕:[Android 主流分辨率查询](http://screensiz.es/),我们在写 UI 的时候一般会拿到一份 iOS 750*1334 的设计稿,下面对常见的各种适配方案优缺点进行下分析: + +- Google 官方提出dp ,sp,match_parent,wrap_content,weight +的方法虽然可以解决屏幕大小和像素之间的对应关系。但是如果如果对于控件在屏幕中的位置却不能很好的解决 + +``` +注: + +- px = density * dp; + +- density = dpi / 160; + +- px = dp * (dpi / 160); +``` + + +- 张鸿洋的基于设计稿生成对应比例的思想和前端适配百分比的思路是一脉想成的,即分别拿屏幕当前宽度和高度除以设计稿的宽度和高度从而相当于将屏幕划分成一个网格坐标。这样一来既保证了不同屏幕下像素对应关系,也可以控制控件的位置。但是在实际操作过程中会发生如资源文件不匹配当前屏幕,或者屏幕的比例与设计稿的比例不一致,从而导致控件变形。 + +- 针对张鸿洋提出的解决方案在实际中遇到的问题,我们是这样解决的: + + - 将比列关系全部映射成一个维度,保证这一个维度,也就保证资源不会发生变形的情况 + + - 做一套默认适配的情况 + + + +- 今日头条等一系列方案实际是在代码中将宽高映射到一个维度,然后通过宽高限定符的方式匹配屏幕尺寸。 diff --git "a/journey/android/Charles\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/journey/android/Charles\344\275\277\347\224\250\346\214\207\345\215\227.md" new file mode 100644 index 0000000..6c9df74 --- /dev/null +++ "b/journey/android/Charles\344\275\277\347\224\250\346\214\207\345\215\227.md" @@ -0,0 +1,253 @@ +* 2013 年 12 月,第一版。 +* 2015 年 11 月,增加 Rewrite 相关介绍。 +* 2016 年 8 月,增加 Charles 4 的介绍,反向代理功能和设置外部代理,并且介绍了如何解决与翻墙软件的冲突。 + +本文的内容主要包括: + +* Charles 的简介 +* 如何安装 Charles +* 将 Charles 设置成系统代理 +* Charles 主界面介绍 +* 过滤网络请求 +* 截取 iPhone 上的网络封包 +* 截取 Https 通讯信息 +* 模拟慢速网络 +* 修改网络请求内容 +* 给服务器做压力测试 +* 修改服务器返回内容 +* 反向代理 +* 设置外部代理,解决与翻墙软件的冲突 +* 总结 + +## [](#简介 "简介")简介 + +![](/images/charles-logo.png) + +[Charles](http://www.charlesproxy.com/) 是在 Mac 下常用的网络封包截取工具,在做 +移动开发时,我们为了调试与服务器端的网络通讯协议,常常需要截取网络封包来分析。 + +Charles 通过将自己设置成系统的网络访问代理服务器,使得所有的网络访问请求都通过它来完成,从而实现了网络封包的截取和分析。 + +除了在做移动开发中调试端口外,Charles 也可以用于分析第三方应用的通讯协议。配合 Charles 的 SSL 功能,Charles 还可以分析 Https 协议。 + +Charles 是收费软件,可以免费试用 30 天。试用期过后,未付费的用户仍然可以继续使用,但是每次使用时间不能超过 30 分钟,并且启动时将会有 10 秒种的延时。因此,该付费方案对广大用户还是相当友好的,即使你长期不付费,也能使用完整的软件功能。只是当你需要长时间进行封包调试时,会因为 Charles 强制关闭而遇到影响。 + +Charles 主要的功能包括: + +1. 截取 Http 和 Https 网络封包。 +2. 支持重发网络请求,方便后端调试。 +3. 支持修改网络请求参数。 +4. 支持网络请求的截获并动态修改。 +5. 支持模拟慢速网络。 + +Charles 4 新增的主要功能包括: + +1. 支持 Http 2。 +2. 支持 IPv6。 + +## [](#安装-Charles "安装 Charles")安装 Charles + +去 Charles 的官方网站([http://www.charlesproxy.com](http://www.charlesproxy.com))下载最新版的 Charles 安装包,是一个 dmg 后缀的文件。打开后将 Charles 拖到 Application 目录下即完成安装。 + +## [](#将-Charles-设置成系统代理 "将 Charles 设置成系统代理")将 Charles 设置成系统代理 + +之前提到,Charles 是通过将自己设置成代理服务器来完成封包截取的,所以使用 Charles 的第一步是将其设置成系统的代理服务器。 + +启动 Charles 后,第一次 Charles 会请求你给它设置系统代理的权限。你可以输入登录密码授予 Charles 该权限。你也可以忽略该请求,然后在需要将 Charles 设置成系统代理时,选择菜单中的 “Proxy” -> “Mac OS X Proxy” 来将 Charles 设置成系统代理。如下所示: + +![](/images/charles-pro-3.png) + +之后,你就可以看到源源不断的网络请求出现在 Charles 的界面中。 + +需要注意的是,Chrome 和 Firefox 浏览器默认并不使用系统的代理服务器设置,而 Charles 是通过将自己设置成代理服务器来完成封包截取的,所以在默认情况下无法截取 Chrome 和 Firefox 浏览器的网络通讯内容。如果你需要截取的话,在 Chrome 中设置成使用系统的代理服务器设置即可,或者直接将代理服务器设置成 `127.0.0.1:8888` 也可达到相同效果。 + +## [](#Charles-主界面介绍 "Charles 主界面介绍")Charles 主界面介绍 + +![](/images/charles-pro-4.png) + +Charles 主要提供两种查看封包的视图,分别名为 “Structure” 和 “Sequence”。 + +1. Structure 视图将网络请求按访问的域名分类。 +2. Sequence 视图将网络请求按访问的时间排序。 + +大家可以根据具体的需要在这两种视图之前来回切换。请求多了有些时候会看不过来,Charles 提供了一个简单的 Filter 功能,可以输入关键字来快速筛选出 URL 中带指定关键字的网络请求。 + +对于某一个具体的网络请求,你可以查看其详细的请求内容和响应内容。如果请求内容是 POST 的表单,Charles 会自动帮你将表单进行分项显示。如果响应内容是 JSON 格式的,那么 Charles 可以自动帮你将 JSON 内容格式化,方便你查看。如果响应内容是图片,那么 Charles 可以显示出图片的预览。 + +## [](#过滤网络请求 "过滤网络请求")过滤网络请求 + +通常情况下,我们需要对网络请求进行过滤,只监控向指定目录服务器上发送的请求。对于这种需求,以下几种办法: + +方法一:在主界面的中部的 Filter 栏中填入需要过滤出来的关键字。例如我们的服务器的地址是:`http://yuantiku.com` , 那么只需要在 Filter 栏中填入 yuantiku 即可。 + +方法二:在 Charles 的菜单栏选择 “Proxy”->”Recording Settings”,然后选择 Include 栏,选择添加一个项目,然后填入需要监控的协议,主机地址,端口号。这样就可以只截取目标网站的封包了。如下图所示: + +![](/images/charles-filter-setting.jpg) + +通常情况下,我们使用方法一做一些临时性的封包过滤,使用方法二做一些经常性的封包过滤。 + +方法三:在想过滤的网络请求上右击,选择 “Focus”,之后在 Filter 一栏勾选上 Focussed 一项,如下图所示: + +![](/images/charles-focus.png) + +这种方式可以临时性的,快速地过滤出一些没有通过关键字的一类网络请求。 + +## [](#截取-iPhone-上的网络封包 "截取 iPhone 上的网络封包")截取 iPhone 上的网络封包 + +Charles 通常用来截取本地上的网络封包,但是当我们需要时,我们也可以用来截取其它设备上的网络请求。下面我就以 iPhone 为例,讲解如何进行相应操作。 + +### [](#Charles-上的设置 "Charles 上的设置")Charles 上的设置 + +要截取 iPhone 上的网络请求,我们首先需要将 Charles 的代理功能打开。在 Charles 的菜单栏上选择 “Proxy”->”Proxy Settings”,填入代理端口 8888,并且勾上 “Enable transparent HTTP proxying” 就完成了在 Charles 上的设置。如下图所示: + +![](/images/charles-proxy-setting.jpg) + +### [](#iPhone-上的设置 "iPhone 上的设置")iPhone 上的设置 + +首先我们需要获取 Charles 运行所在电脑的 IP 地址,Charles 的顶部菜单的 “Help”->”Local IP Address”,即可在弹出的对话框中看到 IP 地址,如下图所示: + +![](/images/charles-local-ip.png) + +在 iPhone 的 “ 设置 “->” 无线局域网 “ 中,可以看到当前连接的 wifi 名,通过点击右边的详情键,可以看到当前连接上的 wifi 的详细信息,包括 IP 地址,子网掩码等信息。在其最底部有「HTTP 代理」一项,我们将其切换成手动,然后填上 Charles 运行所在的电脑的 IP,以及端口号 8888,如下图所示: + +![](/images/charles-iphone-setting.jpg) + +设置好之后,我们打开 iPhone 上的任意需要网络通讯的程序,就可以看到 Charles 弹出 iPhone 请求连接的确认菜单(如下图所示),点击 “Allow” 即可完成设置。 + +![](/images/charles-proxy-confirm.jpg) + +## [](#截取-Https-通讯信息 "截取 Https 通讯信息")截取 Https 通讯信息 + +### [](#安装证书 "安装证书")安装证书 + +如果你需要截取分析 Https 协议相关的内容。那么需要安装 Charles 的 CA 证书。具体步骤如下。 + +首先我们需要在 Mac 电脑上安装证书。点击 Charles 的顶部菜单,选择 “Help” -> “SSL Proxying” -> “Install Charles Root Certificate”,然后输入系统的帐号密码,即可在 KeyChain 看到添加好的证书。如下图所示: + +![](/images/charles-pro-1.png) + +需要注意的是,即使是安装完证书之后,Charles 默认也并不截取 Https 网络通讯的信息,如果你想对截取某个网站上的所有 Https 网络请求,可以在该请求上右击,选择 SSL proxy,如下图所示: + +![](/images/charles-ssl-add-host.jpg) + +这样,对于该 Host 的所有 SSL 请求可以被截取到了。 + +### [](#截取移动设备中的-Https-通讯信息 "截取移动设备中的 Https 通讯信息")截取移动设备中的 Https 通讯信息 + +如果我们需要在 iOS 或 Android 机器上截取 Https 协议的通讯内容,还需要在手机上安装相应的证书。点击 Charles 的顶部菜单,选择 “Help” -> “SSL Proxying” -> “Install Charles Root Certificate on a Mobile Device or Remote Browser”,然后就可以看到 Charles 弹出的简单的安装教程。如下图所示: + +![](/images/charles-pro-2.png) + +按照我们之前说的教程,在设备上设置好 Charles 为代理后,在手机浏览器中访问地址:[http://charlesproxy.com/getssl](http://charlesproxy.com/getssl),即可打开证书安装的界面,安装完证书后,就可以截取手机上的 Https 通讯内容了。不过同样需要注意,默认情况下 Charles 并不做截取,你还需要在要截取的网络请求上右击,选择 SSL proxy 菜单项。 + +## [](#模拟慢速网络 "模拟慢速网络")模拟慢速网络 + +在做移动开发的时候,我们常常需要模拟慢速网络或者高延迟的网络,以测试在移动网络下,应用的表现是否正常。Charles 对此需求提供了很好的支持。 + +在 Charles 的菜单上,选择 “Proxy”->”Throttle Setting” 项,在之后弹出的对话框中,我们可以勾选上 “Enable Throttling”,并且可以设置 Throttle Preset 的类型。如下图所示: + +![](/images/charles-throttle-setting.jpg) + +如果我们只想模拟指定网站的慢速网络,可以再勾选上图中的 “Only for selected hosts” 项,然后在对话框的下半部分设置中增加指定的 hosts 项即可。 + +## [](#修改网络请求内容 "修改网络请求内容")修改网络请求内容 + +有些时候为了调试服务器的接口,我们需要反复尝试不同参数的网络请求。Charles 可以方便地提供网络请求的修改和重发功能。只需要在以往的网络请求上点击右键,选择 “Edit”,即可创建一个可编辑的网络请求。如下所示: + +![](/images/charles-edit.png) + +我们可以修改该请求的任何信息,包括 URL 地址、端口、参数等,之后点击 “Execute” 即可发送该修改后的网络请求(如下图所示)。Charles 支持我们多次修改和发送该请求,这对于我们和服务器端调试接口非常方便,如下图所示: + +![](/images/charles-execute.png) + +## [](#给服务器做压力测试 "给服务器做压力测试")给服务器做压力测试 + +我们可以使用 Charles 的 Repeat 功能来简单地测试服务器的并发处理能力,方法如下。 + +我们在想打压的网络请求上(POST 或 GET 请求均可)右击,然后选择 「Repeat Advanced」菜单项,如下所示: + +![](/images/charles-repeat-1.png) + +接着我们就可以在弹出的对话框中,选择打压的并发线程数以及打压次数,确定之后,即可开始打压。 + +![](/images/charles-repeat-2.png) + +悄悄说一句,一些写得很弱的投票网站,也可以用这个办法来快速投票。当然,我也拿 Charles 的 Repeat 功能给一些诈骗的钓鱼网站喂了不少垃圾数据,上次不小心还把一个钓鱼网站的数据库打挂了,嗯,请叫我雷锋。 + +## [](#修改服务器返回内容 "修改服务器返回内容")修改服务器返回内容 + +有些时候我们想让服务器返回一些指定的内容,方便我们调试一些特殊情况。例如列表页面为空的情况,数据异常的情况,部分耗时的网络请求超时的情况等。如果没有 Charles,要服务器配合构造相应的数据显得会比较麻烦。这个时候,使用 Charles 相关的功能就可以满足我们的需求。 + +根据具体的需求,Charles 提供了 Map 功能、 Rewrite 功能以及 Breakpoints 功能,都可以达到修改服务器返回内容的目的。这三者在功能上的差异是: + +1. Map 功能适合长期地将某一些请求重定向到另一个网络地址或本地文件。 +2. Rewrite 功能适合对网络请求进行一些正则替换。 +3. Breakpoints 功能适合做一些临时性的修改。 + +### [](#Map-功能 "Map 功能")Map 功能 + +Charles 的 Map 功能分 Map Remote 和 Map Local 两种,顾名思义,Map Remote 是将指定的网络请求重定向到另一个网址请求地址,Map Local 是将指定的网络请求重定向到本地文件。 + +在 Charles 的菜单中,选择 “Tools”->”Map Remote” 或 “Map Local” 即可进入到相应功能的设置页面。 + +![](/images/charles-map.png) + +对于 Map Remote 功能,我们需要分别填写网络重定向的源地址和目的地址,对于不需要限制的条件,可以留空。下图是一个示例,我将所有 `ytk1.yuanku.ws`(测试服务器)的请求重定向到了 `www.yuantiku.com`(线上服务器)。 + +![](/images/charles-map-remote.png) + +对于 Map Local 功能,我们需要填写的重定向的源地址和本地的目标文件。对于有一些复杂的网络请求结果,我们可以先使用 Charles 提供的 “Save Response…” 功能,将请求结果保存到本地(如下图所示),然后稍加修改,成为我们的目标映射文件。 + +![](/images/charles-save-response.png) + +下图是一个示例,我将一个指定的网络请求通过 Map Local 功能映射到了本地的一个经过修改的文件中。 + +![](/images/charles-map-local.png) + +Map Local 在使用的时候,有一个潜在的问题,就是其返回的 Http Response Header 与正常的请求并不一样。这个时候如果客户端校验了 Http Response Header 中的部分内容,就会使得该功能失效。解决办法是同时使用 Map Local 以下面提到的 Rewrite 功能,将相关的 Http 头 Rewrite 成我们希望的内容。 + +### [](#Rewrite-功能 "Rewrite 功能")Rewrite 功能 + +Rewrite 功能功能适合对某一类网络请求进行一些正则替换,以达到修改结果的目的。 + +例如,我们的客户端有一个 API 请求是获得用户昵称,而我当前的昵称是 “tangqiaoboy”,如下所示: + +![](/images/charles-rewrite-1.jpeg) + +我们想试着直接修改网络返回值,将 tangqiaoboy 换成成 iosboy。于是我们启用 Rewrite 功能,然后设置如下的规则: + +![](/images/charles-rewrite-2.png) + +完成设置之后,我们就可以从 Charles 中看到,之后的 API 获得的昵称被自动 Rewrite 成了 iosboy,如下图所示: + +![](/images/charles-rewrite-3.png) + +### [](#Breakpoints-功能 "Breakpoints 功能")Breakpoints 功能 + +上面提供的 Rewrite 功能最适合做批量和长期的替换,但是很多时候,我们只是想临时修改一次网络请求结果,这个时候,使用 Rewrite 功能虽然也可以达到目的,但是过于麻烦,对于临时性的修改,我们最好使用 Breakpoints 功能。 + +Breakpoints 功能类似我们在 Xcode 中设置的断点一样,当指定的网络请求发生时,Charles 会截获该请求,这个时候,我们可以在 Charles 中临时修改网络请求的返回内容。 + +下图是我们临时修改获取用户信息的 API,将用户的昵称进行了更改,修改完成后点击 “Execute” 则可以让网络请求继续进行。 + +![](/images/charles-breakpoint.png) + +需要注意的是,使用 Breakpoints 功能将网络请求截获并修改过程中,整个网络请求的计时并不会暂停,所以长时间的暂停可能导致客户端的请求超时。 + +## [](#反向代理 "反向代理")反向代理 + +Charles 的反向代理功能允许我们将本地的端口映射到远程的另一个端口上。例如,在下图中,我将本机的 61234 端口映射到了远程(www.yuantiku.com)的80端口上了。这样,当我访问本地的 61234 端口时,实际返回的内容会由 www.yuantiku.com 的 80 端口提供。 + +![](/images/charles-reverse-proxy.jpg) + +## [](#设置外部代理,解决与翻墙软件的冲突 "设置外部代理,解决与翻墙软件的冲突")设置外部代理,解决与翻墙软件的冲突 + +Charles 的原理是把自己设置成系统的代理服务器,但是在中国,由于工作需要,我们常常需要使用 Google 搜索,所以大部分程序员都有自己的翻墙软件,而这些软件的基本原理,也是把自己设置成系统的代理服务器,来做到透明的翻墙。 + +为了使得两者能够和平共处,我们可以在 Charles 的 `External Proxy Settings` 中,设置翻墙的代理端口以及相关信息。同时,我们也要关闭相关翻墙软件的自动设置,使其不主动修改系统代理,避免 Charles 失效。 + +## [](#总结 "总结")总结 + +通过 Charles 软件,我们可以很方便地在日常开发中,截取和调试网络请求内容,分析封包协议以及模拟慢速网络。用好 Charles 可以极大的方便我们对于带有网络请求的 App 的开发和调试。 + +愿本文帮助大家成为 Charles 的专家,祝大家玩得开心~ diff --git "a/journey/android/Github-\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/journey/android/Github-\344\275\277\347\224\250\346\214\207\345\215\227.md" new file mode 100644 index 0000000..f861acb --- /dev/null +++ "b/journey/android/Github-\344\275\277\347\224\250\346\214\207\345\215\227.md" @@ -0,0 +1,308 @@ +![](http://upload-images.jianshu.io/upload_images/2539684-1a53495ad76841c8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +### Github和Git的区别 + +写这篇文章的目的一来是总结下自己对于 Github 这个社区的使用经验总结;另一方面希望可以帮助到那些刚刚接触到编程的筒子们,希望你们能够在编程的道路上找到属于自己的乐趣! +首先需要更正的一个常识:Github 和 Git 不是一个玩意儿;在实际工作中,我们的代码可能会被存储在当前的项目工程目录下、本地仓库(电脑某个固定的文件下)、远程服务器上。Github 可以理解为我们储存代码的服务器,当然它远非服务器这么简单啦,Git 就是协助我们在上个目录下提交代码的一款工具,就和 SVN 是一个道理; +Github 作为全球最大的基友交流群,有着很多的公司驻扎在其中: + +- [Google 爸爸](https://github.com/google) +- [Facebook](https://github.com/facebook) +- [Square](https://github.com/square) +- [Alibaba](https://github.com/alibaba) +- 。。。。。。 + +在这里你还可以认识各界大神,比如 Android 界的大神: + +- [Jake Wharton](https://github.com/JakeWharton) +- [Triena](https://github.com/Trinea) +- [代码家](https://github.com/daimajia) +- 。。。。。。 + +作为一个程序员,GitHub 是我们提升个人影响力的很好的一种方式,所以你还在犹豫什么,赶紧注册一个号一起投入到编程的行业中来吧:[传送门](http://www.github.com/) + +另外,宣传下[我个人的账号](https://github.com/MrLeion)欢迎大家一起来交流技术~~ + +### 基本概念 + +![](http://upload-images.jianshu.io/upload_images/2539684-fe196467bcf62c75.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +当你注册完账号,进入 Your Profile 界面时,你就会看到上图的界面,当然如果你是第一次进入可能还没有什么项目。 + +这里我们要对Github上一些操作有一个概念性的理解(英语不好的筒子们不用担心): + +- Repository + 仓库的意思,即你的项目,你想在 GitHub 上开源一个项目,那就必须要新建一个 Repository。 + +- Issue + 别人发现你的项目中有bug,或者哪些地方做的不够好,他就可以给你提个 Issue 。 + +- Star + 就是给项目点赞。 + +- Fork + + 你开源了一个项目,别人想在你这个项目的基础上做些改进,然后应用到自己的项目中,这个时候他就可以 Fork 你的项目,这个时候他的 GitHub 主页上就多了一个项目,只不过这个项目是基于你的项目基础。 + + +- Pull Request(PR) + +发起请求,这个其实是基于 Fork 的,还是上面那个例子,如果别人在你基础上做了改进,后来觉得改进的很不错,应该要把这些改进让更多的人收益,于是就想把自己的改进合并到原有项目里,这个时候他就可以发起一个 Pull Request(简称PR) ,原有项目创建人就可以收到这个请求,这个时候他会仔细review你的代码,并且测试觉得OK了,就会接受你的PR,这个时候你做的改进原有项目就会拥有了。 + + +- Watch + +这个也好理解就是观察,如果你 Watch 了某个项目,那么以后只要这个项目有任何更新,你都会第一时间收到关于这个项目的通知提醒。 + + +- Gist + +如果你只是单纯的想分享一些代码片段,那这个时候 Gist 就派上用场了! + + +好吧,下面就一起开启 Github 浪漫之旅吧~~ + + + +### 第一次使用需要的配置 + +工具安装:[Git 传送门](https://sourceforge.net/projects/git-osx-installer/) + +第一次使用嘛,肯定要告诉人 Github 你的身份啦,有必要配置你的用户名和密码加上你的 SSH 密钥; + +``` +$ git config --global user.name "用户名" +$ git config --global user.email "电子邮箱" + +``` + + +在使用 Github 的过程中,我讲大部分操作归结为两大类,即从 Github 上 clone 项目;或者将我们自己的项目分享到 Github 上和志同道合的人士进行交流,下面来看看具体是如何操作的吧~~ + +### Project To GitHub + + +- case 1:空的代码库 +``` +# 在当前目录新建一个Git代码库 +$ git init + +# 新建一个目录,将其初始化为Git代码库 +$ git init [project-name] +``` +- case 2:已存在代码库 +# 切换到项目目录下 +$ git init + +# 添加到本地暂存区 +$ git add . + +# 提交到本地暂存 +$ git commit -m "hello world" + + +# 添加到远程仓库,将本地仓库与远程仓库简历链接 +$ git remote add origin [github 仓库地址] + +# 更新 +$ git pull origin master --allow-unrelated-histories + +# 提交 +$ git push origin master + +** 注: ** 按步骤操作偶~~ + + + + +### Project From Github + +``` +下载一个项目和它的整个代码历史 +$ git clone [url] + +``` + +### 常用命令 + +![图片来源于阮一峰博客](http://upload-images.jianshu.io/upload_images/2539684-82b89315231e2286.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +如上图所示,我们需要清楚了解下面几个概念: + +- Workspace:工作区 +- Index :暂存区 +- Repository:本地仓库 +- Remote:远程仓库 + +我们的项目工程在从本地提交中央远程仓库的过程中, + + + +1. 工作区->暂存区 + +``` +# 添加指定文件到暂存区 +$ git add [file1] [file2] ... + +# 添加指定目录到暂存区,包括子目录 +$ git add [dir] + +# 添加当前目录的所有文件到暂存区 +$ git add . + +# 添加每个变化前,都会要求确认 +# 对于同一个文件的多处变化,可以实现分次提交 +$ git add -p + +# 删除工作区文件,并且将这次删除放入暂存区 +$ git rm [file1] [file2] ... + +# 停止追踪指定文件,但该文件会保留在工作区 +$ git rm --cached [file] + +# 改名文件,并且将这个改名放入暂存区 +$ git mv [file-original] [file-renamed] +``` + +2. 暂存区->本地仓库 + +``` +# 提交暂存区到仓库区 +$ git commit -m [message] + +# 提交暂存区的指定文件到仓库区 +$ git commit [file1] [file2] ... -m [message] + +# 提交工作区自上次commit之后的变化,直接到仓库区 +$ git commit -a + +# 提交时显示所有diff信息 +$ git commit -v + +# 使用一次新的commit,替代上一次提交 +# 如果代码没有任何新变化,则用来改写上一次commit的提交信息 +$ git commit --amend -m [message] + +# 重做上一次commit,并包括指定文件的新变化 +$ git commit --amend [file1] [file2] ... +``` + + + +3. 本地仓库->远程仓库 + +``` + +# 删除远程tag +$ git push origin :refs/tags/[tagName] + +# 查看tag信息 +$ git show [tag] + +# 提交指定tag +$ git push [remote] [tag] + +# 提交所有tag +$ git push [remote] --tags + +# 新建一个分支,指向某个tag +$ git checkout -b [branch] [tag] +``` + + + +### 分支管理 + +``` +# 提交暂存区到仓库区 +$ git commit -m [message] + +# 提交暂存区的指定文件到仓库区 +$ git commit [file1] [file2] ... -m [message] + +# 提交工作区自上次commit之后的变化,直接到仓库区 +$ git commit -a + +# 提交时显示所有diff信息 +$ git commit -v + +# 使用一次新的commit,替代上一次提交 +# 如果代码没有任何新变化,则用来改写上一次commit的提交信息 +$ git commit --amend -m [message] + +# 重做上一次commit,并包括指定文件的新变化 +$ git commit --amend [file1] [file2] ... +五、分支 + +# 列出所有本地分支 +$ git branch + +# 列出所有远程分支 +$ git branch -r + +# 列出所有本地分支和远程分支 +$ git branch -a + +# 新建一个分支,但依然停留在当前分支 +$ git branch [branch-name] + +# 新建一个分支,并切换到该分支 +$ git checkout -b [branch] + +# 新建一个分支,指向指定commit +$ git branch [branch] [commit] + +# 新建一个分支,与指定的远程分支建立追踪关系 +$ git branch --track [branch] [remote-branch] + +# 切换到指定分支,并更新工作区 +$ git checkout [branch-name] + +# 切换到上一个分支 +$ git checkout - + +# 建立追踪关系,在现有分支与指定的远程分支之间 +$ git branch --set-upstream [branch] [remote-branch] + +# 合并指定分支到当前分支 +$ git merge [branch] + +# 选择一个commit,合并进当前分支 +$ git cherry-pick [commit] + +# 删除分支 +$ git branch -d [branch-name] + +# 删除远程分支 +$ git push origin --delete [branch-name] +$ git branch -dr [remote/branch] +``` + + +### 通过 Github 提升自己 + +上面的操作让我们将github这个「网盘」的功能发挥到了淋漓尽致;但是如果需要真正通过Github提升自己的能力,就要回归到根本。作为有名的「基友交友社区」,我们可以看到通过别人的代码拓广自己解决问题的思路,而且可以通过自己的积累发现问题并完善别人的代码! +这里我们就经常要 Fork 下别人的代码,然后给别人(项目主程开发)提交 PR (Pull Request)。给大家推荐一份电子书,作者 Phodal 可以说是 TDD 的死忠,大家有兴趣可以去看看他的文章也算体验下大神的日常生活吧: [GitHub 漫游指南](http://github.phodal.com) + + +- TDD +- CI +- 重构 +- 自动部署 + + +最后给大家推荐几款用着还不错的 Github 插件和网站吧(PS:网上有很多很好的,大家搜索关键词就好了,有不错的记得推荐给我欧~~): + +- [Github Ranking](http://git-awards.com/) +- [Githuber](https://githuber.cn/) +- [Github 开放接口](https://developer.github.com/) + +最后如果希望搭建个人网站但是苦于没有钱买服务器的筒子们可以使用 Github Pages ,个人就是用 Hexo 在上面建了个自己的站,还可以。 + +推荐文章: + +[常用 Git 命令清单 —— 阮一峰](http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html) + +[GitHub 漫游指南](http://github.phodal.com) + +[如何通过github提升自己](https://www.phodal.com/blog/use-github-grow-self/) diff --git "a/journey/android/Markdown-\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/journey/android/Markdown-\344\275\277\347\224\250\346\214\207\345\215\227.md" new file mode 100644 index 0000000..1aeddc8 --- /dev/null +++ "b/journey/android/Markdown-\344\275\277\347\224\250\346\214\207\345\215\227.md" @@ -0,0 +1,330 @@ +**** + +简书的 MarkDown 编辑器居然不支持音视频解析~~ + +[相声:郭德纲、于谦《我要穿越》](http://ofc6yl8uw.bkt.clouddn.com/%E9%83%AD%E5%BE%B7%E7%BA%B2%20%E4%BA%8E%E8%B0%A6%20%E6%88%91%E8%A6%81%E7%A9%BF%E8%B6%8A.mp3) + +**** + + ![](http://upload-images.jianshu.io/upload_images/2539684-73a19d2abeb550e2.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +喜欢上「简书」与生俱来简约的风格,不用考虑任何排版,只要了解一些基本的语法就可以写出简洁的文章,妈妈再也不用担心我写作排版难看了。选择 Markdown 这几点值得拥有: + +- 专注于内容创造 +- 可以导出为 html、pdf + + +###工具推荐 + +因为本人使用的是Mac电脑,所以推荐的工具只能局限于这一平台的啦,Windows上工具多多大家去百度下就可以了; + +- MacDown:[传送门](https://macdown.uranusjr.com/) + +- sublime Text 3 安装插件,并安装插件:[传送门](https://www.sublimetext.com/3) + +废话不多说,自行 Google 或者尝试着用下,包君满意~~ + +###语法 + +####标题 + +在 Markdown 中,你只需要在文本前面加上 # 即可,同理、你还可以增加二级标题、三级标题、四级标题、五级标题和六级标题,总共六级,只需要增加 # 即可,标题字号相应降低: + + +---- +代码: + +``` +# 一级标题 +## 二级标题 +### 三级标题 +#### 四级标题 +##### 五级标题 +###### 六级标题 +``` +---- +效果: +# 一级标题 +## 二级标题 +### 三级标题 +#### 四级标题 +##### 五级标题 +###### 六级标题 + + +---- + + +#### 分割线 + +在 Markdown 中,你只需要使用连续的四个 * 就可以了 + +---- +代码: + +``` +**** + +``` + +---- +效果: + +**** + + + +---- + + + +#### 列表 + +在 Markdown 中,列表分为有序和无序两种: + +##### 无序列表 + +在 Markdown 中,你只需要在文字前面加上 - 就可以了。 + +---- +代码: + +``` +- 无序 +- 无序 +- 无序 +``` +---- +效果: + +- 无序 +- 无序 +- 无序 + +--- + +##### 有序列表 + +在 Markdown 中,你只需要在文字前面加上 1. 2. 3. 就可以了。 + + +---- +代码: + +``` +1. 有序 +2. 有序 +3. 有序 +``` +---- +效果: + +1. 有序 +2. 有序 +3. 有序 + +--- + + +####多媒体超链接 + +#### 链接 + +---- +代码: + + ``` + [百度](http://www.baidu.com) + ``` + +---- +效果: + +[百度](http://www.baidu.com) + +---- + + + + +##### 图片 + + +---- +代码: + + +``` + ![图片描述](http://upload-images.jianshu.io/upload_images/2539684-73a19d2abeb550e2.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +``` + + +---- +效果: + + ![图片描述](http://upload-images.jianshu.io/upload_images/2539684-73a19d2abeb550e2.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +---- + +#### 音频 + + +---- +代码: + +``` + + +``` + +---- +效果: + + + +---- + + +#### 视频 + +---- +代码: + +``` + +``` + +---- +效果: + + + +---- + + +#### 引用 + +我们写作的时候经常需要引用他人的文字,这个时候引用这个格式就很有必要了,在 Markdown 中,你只需要在你希望引用的文字前面加上 > 就好了: + +---- +代码: + +``` +> 失意莫灰心,得意莫忘形 + +``` + +---- +效果: + +> 失意莫灰心,得意莫忘形 + +---- + + +#### 代码引用 + +---- +代码: + +``` + + ``` + + @Override + protected void initViews() { + initListener(); + } + + ``` + +``` + +---- +效果: + + + + + ``` + @Override + protected void initViews() { + initListener(); + } + + ``` + +---- + + + + +#### 表格 + +---- +代码: + +``` +dog | bird | cat +----|------|---- +foo | foo | foo +bar | bar | bar +baz | baz | baz + +``` + +---- +效果: + + +dog | bird | cat +----|------|---- +foo | foo | foo +bar | bar | bar +baz | baz | baz + + +---- + +考虑到大部分读者对于代码语法这个东西基本无感,这里给大家介绍一个在线的练习网站,希望可以帮助大家更快的了解 Markdown: + +[在线练习网站](https://github.com/gjtorikian/markdowntutorial.com) + +学习了简单的使用后相信大家在以后的使用中还会有需要查证的地方: + + [Markdown中文参考](http://wowubuntu.com/markdown/#list) + + +###多媒体资源处理 + +5G 时代都快到来了,相信大家在使用 Markdown 书写过程中一定会有给自己的文章配图或者偶尔附加几个音视频等需求吧。这里给出自己作为IT男的一些玩法吧,如果大家有更好玩的欢迎给我私信欧~~ + +首先图片资源搜集问题相信大家应该都有自己的套路(百度谷歌随便搞搞,当然如果是专业点的图片还需要去到图片网站上找的,后期会给大家分享这方面的技巧)。平常的时候我喜欢听听相声什么的,然后通过自己的文章分享出来。一般喜欢去 youtube 上去找一些视频资源,然后借助在线的转换工具,这里给大家推荐一款吧,类似的可以去 Google 上找找: + +[youtube-mp3](https://www.youtubeto.com/zh/) + +这个网站会将在线的 youtube 视频链接直接转成 mp3 并且下载到本地来视频也是一样的,好啦有了获取自己想要的多媒体资源的能力,那么下面我们就一起看看怎么去管理好自己的资源吧。 + + +通过上一节的 Markdown 语法的学习相信大家对它已经不陌生了吧,可以看到我们在向 Markdown 文档中添加自己需要的这些多媒体资料的时候需要一系列的超链接,如果这些资源我们不能很好的管理的话,那么很有可能在咋们文章上传之后那些超链所对应的资源会被删除。所以为了避免这样的问题,咋们需要自己的一款多媒体资源的服务器来寄存咋们的资源。 +干编程的同志们应该听过这两个网站: + +- [七牛云服务器](https://www.qiniu.com) +- [Github](https://github.com/) + +这两款产品可以作为我们多媒体资源的存储服务器,并且获取对应的超链接这样就完美解决这个问题啦~~( PS:随着我们持续不断地使用相信可能会收一点点费用,但是我相信对于长期打算写作锻炼自己的我应该是不打紧的,毕竟半年多了我没有被收一毛钱!) + + +###中文写作规范 + +既然选择用文字记录下自己思绪的点滴,那么就让我们打造出最完美的格式,让别人在获取分享的同时更能理解我们对于细节的追求和打磨(PS:实际上就是扯扯淡,也就是让自己的文章看起来没有那么辣眼睛而已),归纳一下总结为以下几点: +1. 中、英、数字、链接之间要有空格; +2. 不要有重复标点符号; +3. 专有名词需要规范大小写; + + +详情请点击: [中文排版规范](https://github.com/sparanoid/chinese-copywriting-guidelines) diff --git "a/journey/android/[AndroidDev-\347\263\273\345\210\227]-\345\277\253\351\200\237\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211.md" "b/journey/android/[AndroidDev-\347\263\273\345\210\227]-\345\277\253\351\200\237\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211.md" new file mode 100644 index 0000000..2a57c5e --- /dev/null +++ "b/journey/android/[AndroidDev-\347\263\273\345\210\227]-\345\277\253\351\200\237\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211.md" @@ -0,0 +1,57 @@ +因为受到github上江清清的[FastDev4Android](https://github.com/jiangqqlmj/FastDev4Android)项目的启发,正巧又要负责公司新项目的启动,所以准备写个类似的库,争取做到write once,run AnyWhere(哈哈,开玩笑,方便以后快速开发啦~~) + + +随着`Android`项目业务量的迅速增长,原本的项目结构已经远远不能满足我们的需求来。因为直线上涨的代码量让我们花在编译项目上的时间由原来的一分钟左右到现在的五分钟甚至更长的时间。虽然谷歌官方提出`instant run`的解决方案,但是由于种种问题,还是没能很好的解决这个问题。 + +> 题外话:这里在网上看到有一种比较好玩的方案,通过编写`gradle`来计算每个`task`的执行时间,从而对整个项目的编译时间进行优化。有兴趣的朋友可以试下~~ + +[Gradle插件编写](http://blog.csdn.net/sbsujjbcy/article/details/50782830) + +好吧,回到我们的主题:由于组件化采取的是在开发过程中将每一个业务模块都作为一个`application`,并伴随着一个壳工程,这样就大大缩减了我们开发时的编译时间;当项目上线的时候,只要将这些`application`变成第三方`Lib`,然后让壳工程依赖于这些`Libs`,进行打包,这样就完美的结局了编译时间上的问题。由于之前已经有大神总结了比较好的实践过程,这里就不赘述啦,直接看大神的文章吧~~ + +[Android组件化开发实践](http://www.jianshu.com/p/186fa07fc48a) + + +相信朋友们一定听过插件化这个概念,这里可以通过下面这篇文章稍稍科普下: + +[Android 组件化和插件化开发](https://juejin.im/entry/577bae93d342d30057970e05) + + +下面来介绍下接下来我们要完成的项目的结构吧: + + +![](http://upload-images.jianshu.io/upload_images/2539684-9181f495f635ebdc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +通过图中我们可以看到,对我们来说比较重要的突破点就是: + +- 路由工程 + + +- BaseLibrary + +其中,路由工程我们可以直接引用下[ActivityRouter](https://github.com/mzule/ActivityRouter)这个工程; + +接下来,就是根据我们的业务需要去完成最重要的baseLibrary啦~~ +从图中我们可以看到,我们需要在接下来的日子里面完成: + +- 网络请求的封装 + +- 图片加载的封装 + +- 加密库的封装 + +- UI的封装 + +....... + +不知道这里你是不是和我有着一样的疑问:github上有那么多现成的第三方框架,例如Square全家桶,facebook等(PS:移动端技术更新太过频繁啦,向大家推荐一个站可以看看最近出来哪些比较稳定的技术吧[Android 流行框架查速表](http://www.ctolib.com/cheatsheets-Android-ch.html)),为什么还要封装自己的框架呢? + +实际上我们的封装也是建立在这些第三方库的基础上的,只是随着业务量的不断扩大,我们可能会切换项目所引用的第三方库,这个时候我们只需要更改自己的封装类就可以啦,是不是很方便。其实我也是借鉴stormzhang的观点的啦,有兴趣深入了解这样做的原因可以看看这篇文章,[如何正确使用开源项目?](http://stormzhang.com/android/2016/05/08/how-to-choose-open-source-project/) + +好啦,接下来就让我们一起开启Android快速开发之旅吧,一起加油吧~~ + +待会儿,在没开始之前,咋们还是要确立注意点规范,还有就是工具啦,工欲善其事,必先利其器嘛~~ + +* [安卓开发规范](https://github.com/Blankj/AndroidStandardDevelop#2-as%E8%A7%84%E8%8C%83) +* TODO:android开发工具篇 diff --git "a/journey/android/[Android]\350\207\252\345\256\232\344\271\211View(\344\270\200)\357\274\232\345\235\220\346\240\207\347\263\273.md" "b/journey/android/[Android]\350\207\252\345\256\232\344\271\211View(\344\270\200)\357\274\232\345\235\220\346\240\207\347\263\273.md" new file mode 100644 index 0000000..f1ae33c --- /dev/null +++ "b/journey/android/[Android]\350\207\252\345\256\232\344\271\211View(\344\270\200)\357\274\232\345\235\220\346\240\207\347\263\273.md" @@ -0,0 +1,103 @@ +Android中view的绘制和动画效果的制作是常见的两大难点,前段时间正好在优化项目的时候做了一些动画。现在对于它们进行个归纳总结,如果有其他看法的同学欢迎给我留言,或者微博私信我~~ + +以敝人粗浅的理解,Android中交互是通过手势触摸屏幕中的view而产生的一系列动作,那么在view过程中我们可以理解为在一个数学坐标系上去判断触碰点击的位置和view点关系;动画我们可以理解为让view在坐标系做坐标运算从而产生动画效果。所以,了解坐标系也就成了我们把握view点绘制,动画制作等一系列动作等基础。 + +下面我们就一起探讨下Android中的哪些坐标系: + +在讨论这个问题之前我们需要明确两点: + +1. 一般情况下,我们只关心我们应用开发人员可以操纵的区域,下面让我们一起看看android屏幕的大致划分吧: + + - 状态栏 + - [ActionBar] + - 应用布局区域 + +在这样一个前提下,我们应用开发人员可以操纵的区域分为两种情况: + +> 有ActionBar +在这种情况下,程序员可操纵区域的范围则是以ActionBar左下角为原点,水平向右为X轴正方向,垂直向下为Y轴正方向。如下图所示: + +![](http://upload-images.jianshu.io/upload_images/2539684-e32a77b1848a5283.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +> 无ActionBar,那么我们的标题栏也会包含在应用布局中,一般的做法是会用一个公用的布局来作为标题。程序员可操纵区域的范围则是以ActionBar左下角为原点,水平向右为X轴正方向,垂直向下为Y轴正方向。如下图所示: + +![](http://upload-images.jianshu.io/upload_images/2539684-217f1e59f198aa07.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + +具体原因可以通过查看源码得知,这里就不再赘述啦,感兴趣的朋友可以看下两篇博客: + +- [ Android应用setContentView与LayoutInflater加载解析机制源码分析](http://blog.csdn.net/yanbober/article/details/45970721) +- [Android View 理论基础之坐标系](https://juejin.im/entry/577cedb1a3413100619a7dba) + + +2. View在屏幕中都是以矩形的形式存在的。 + + +好啦,在这两个前提的基础上,我们一起给坐标分分类吧: + +1. View的坐标系 + + - 屏幕坐标 + + 以可操纵区域的左上角为原点,水平向右为X轴正方向,垂直向下为Y轴正方向。 + + - 视图坐标 + + ![](http://upload-images.jianshu.io/upload_images/2539684-07000603ee80334d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + 以可操纵区域的左上角为原点,水平向右为X轴正方向,垂直向下为Y轴正方向。在这个时候View被视为一个矩形,通过API可以获得View的坐标: + + 对应API: + + | View中的方法 | 含义 | + | ------------- |-------------| + | getTop() | 获取View自身顶边到其父布局顶边的距离 | + | getLeft() | 获取View自身左边到其父布局左边的距离| + | getRight() | 获取View自身右边到其父布局左边的距离| + | getBottom() | 获取View自身底边到其父布局顶边的距离| + + + | View中的方法 | 含义 | + | ------------- |-------------| + | getX() | 在当前父View坐标系中对应的坐标X的值 | + | getY() | 在当前父View坐标系中对应的坐标Y的值| + 从Android3.0以后,引入transitionX和transitionY + + - X = transitionX + getLeft() + - Y = transitionY + getTop() + + **注:这里的getLeft()和getTop()是不变的,当view在父View中移动是改变的是transitionX和transitionY的值,因此X和Y的值也会跟着改变** + +2. 触摸点的坐标系 + + +- 绝对坐标 + + 触摸点在屏幕坐标的坐标值;具体如图所示: + + ![](http://upload-images.jianshu.io/upload_images/2539684-edf396eb49f1d2bb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + 对应API: + + | View中的方法 | 含义 | + | ------------- |:-------------:| + | getRawX() | 在屏幕坐标系中对应的坐标X的值 | + | getRawY() | 在屏幕坐标系中对应的坐标Y的值 | + + +- 相对坐标 + + 以当前View的左上角为坐标原点建立的坐标系,触摸点在此坐标系中对应的坐标值;具体如图所示: + + ![](http://upload-images.jianshu.io/upload_images/2539684-239debbef5d5c7cb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + 对应API: + + | View中的方法 | 含义 | + | ------------- |:-------------:| + | getX() | 在当前View坐标系中对应的坐标X的值 | + | getY() | 在当前View坐标系中对应的坐标Y的值| diff --git "a/journey/android/gradlePlugin/Gradle Plugin \345\274\200\345\217\221.md" "b/journey/android/gradlePlugin/Gradle Plugin \345\274\200\345\217\221.md" new file mode 100644 index 0000000..c94e6a6 --- /dev/null +++ "b/journey/android/gradlePlugin/Gradle Plugin \345\274\200\345\217\221.md" @@ -0,0 +1,502 @@ +============================ + +Gradle Plugin 开发 + +================================ + +注: +本文转自:[Embrace Android Studio](http://kvh.io/cn/embrace-android-studio-gradle-plugin.html) + + +目录 + +1. [1. 需求](#需求) +2. [2. 原理简述](#原理简述) + 1. [2.1. 插件之于 Gradle](#插件之于-Gradle) + 2. [2.2. 插件打包方式](#插件打包方式) + 1. [2.2.1. Build script](#Build-script) + 2. [2.2.2. buildSrc 项目](#buildSrc-项目) + 3. [2.2.3. 独立项目](#独立项目) +3. [3. Build script 插件](#Build-script-插件) + 1. [3.1. 接受外部参数](#接受外部参数) + 1. [3.1.1. 声明参数类](#声明参数类) + 2. [3.1.2. 接受参数](#接受参数) + 3. [3.1.3. 获取和使用参数](#获取和使用参数) + 4. [3.1.4. 进化版本一:参数](#进化版本一:参数) +4. [4. 独立项目插件](#独立项目插件) + 1. [4.1. 创建项目](#创建项目) + 2. [4.2. 修改 build.gradle 文件](#修改-build-gradle-文件) + 3. [4.3. 修改项目文件夹](#修改项目文件夹) + 4. [4.4. 建立对应文件](#建立对应文件) + 5. [4.5. com.asgradle.apkdist.properties 文件](#com-asgradle-apkdist-properties-文件) + 6. [4.6. 将 plugin module 传到本地 maven 仓库](#将-plugin-module-传到本地-maven-仓库) + 1. [4.6.1. 添加 gradle.properties](#添加-gradle-properties) + 2. [4.6.2. 在 build.gradle 添加上传功能](#在-build-gradle-添加上传功能) + 7. [4.7. 在 app module 中使用插件](#在-app-module-中使用插件) + 1. [4.7.1. 在项目的 buildscript 添加插件作为 classpath](#在项目的-buildscript-添加插件作为-classpath) + 2. [4.7.2. 在 app module 中使用插件:](#在-app-module-中使用插件:) + 3. [4.7.3. 可能会遇到问题](#可能会遇到问题) +5. [5. 真正的实现插件需求](#真正的实现插件需求) +6. [6. 后记](#后记) +7. [7. 参考文献](#参考文献) +8. [8. 系列导读](#系列导读) +9. [9. 番外](#番外) + +[官方文档](https://docs.gradle.org/current/userguide/custom_plugins.html)给出了比较详细的实现步骤,本文的脉络会跟官方文档差不了太多,额外增补实际例子和一些实践经验。文中的代码已经托管到了 [github 项目](https://github.com/kevinho/Embrace-Android-Studio-Demo)中。 + +[](#需求 "需求")需求 +============== + +默认的 Android 打包插件会把 apk 命名成 `module-productFlavor-buildType.apk`,例如 `app-official-debug.apk`,并且会把包文件发布到固定的位置: `module/build/outputs/apk` 有的时候,这个命名风格并不是你所要的,你也想讲 apk 输出到别的目录。咱们通过 gradle 插件来实现自定义。这个插件的需求是: + +* 输入一个名为 nameMap 的 Closure,用来修改 apk 名字 +* 输入一个名为 destDir 的 String,用于输出位置 + +[](#原理简述 "原理简述")原理简述 + +==================== + +[](#插件之于-Gradle "插件之于 Gradle")插件之于 Gradle + +----------------------------------------- + +根据官方文档定义,插件打包了可重用的构建逻辑,可以适用于不同的项目和构建过程。 + +Gradle 提供了很多官方插件,用于支持 Java、Groovy 等工程的构建和打包。同时也提供了自定义插件的机制,让每个人都可以通过插件来实现特定的构建逻辑,并可以把这些逻辑打包起来,分享给其他人。 + +插件的源码可以使用 Groovy、Scala、Java 三种语言,笔者不会 Scala,所以平时只是使用 Groovy 和 Java。前者用于实现与 Gradle 构建生命周期(如 task 的依赖)有关的逻辑,后者用于核心逻辑,表现为 Groovy 调用 Java 的代码。 + +另外,还有很多项目使用 Eclipse 或者 Maven 进行开发构建,用 Java 实现核心业务代码,将有利于实现快速迁移。 + +[](#插件打包方式 "插件打包方式")插件打包方式 + +-------------------------- + +Gradle 的插件有三种打包方式,主要是按照复杂程度和可见性来划分: + +### [](#Build-script "Build script")Build script + +把插件写在 build.gradle 文件中,一般用于简单的逻辑,只在该 build.gradle 文件中可见,笔者常用来做原型调试,本文将简要介绍此类。 + +### [](#buildSrc-项目 "buildSrc 项目")buildSrc 项目 + +将插件源代码放在 `rootProjectDir/buildSrc/src/main/groovy` 中,只对该项目中可见,适用于逻辑较为复杂,但又不需要外部可见的插件,本文不介绍,有兴趣可以参考[此处](https://docs.gradle.org/current/userguide/organizing_build_logic.html#sec:build_sources)。 + +### [](#独立项目 "独立项目")独立项目 + +一个独立的 Groovy 和 Java 项目,可以把这个项目打包成 Jar 文件包,一个 Jar 文件包还可以包含多个插件入口,将文件包发布到托管平台上,供其他人使用。本文将着重介绍此类。 + +[](#Build-script-插件 "Build script 插件")Build script 插件 + +===================================================== + +首先来直接在 build.gradle 中写一个 plugin: +``` + +class ApkDistPlugin implements Plugin { + + @Override + void apply(Project project) { + project.task('apkdist') << { + println 'hello, world!' + } + } +} + +apply plugin: ApkDistPlugin +``` +命令行运行 + +``` +$ ./gradlew -p app/ apkdist +:app:apkdist +hello, world! +``` +这个插件创建了一个名为 `apkdist` 的 task,并在 task 中打印。 + +插件是一个类,继承自 `org.gradle.api.Plugin` 接口,重写 `void apply(Project project)` 方法,这个方法将会传入使用这个插件的 project 的实例,这是一个重要的 context。 + +[](#接受外部参数 "接受外部参数")接受外部参数 + +-------------------------- + +通常情况下,插件使用方需要传入一些配置参数,如 bugtags 的 SDK 的插件需要接受两个参数: +``` +bugtags { + appKey "APP_KEY" //这里是你的 appKey + appSecret "APP_SECRET" //这里是你的 appSecret,管理员在设置页可以查看 +} +``` +同样,ApkDistPlugin 这个 plugin 也希望接受两个参数: + +``` + +apkdistconf { + nameMap { name -> + println 'hello,' + name + return name + } + destDir 'your-distribution-dir' +} + +``` + +参数的内容后面继续完善。那这两个参数怎么传到插件内呢? + +`org.gradle.api.Project` 有一个 `ExtensionContainer getExtensions()` 方法,可以用来实现这个传递。 + +### [](#声明参数类 "声明参数类")声明参数类 + +声明一个 Groovy 类,有两个默认值为 null 的成员变量: + +``` + +class ApkDistExtension { + Closure nameMap = null; + String destDir = null; +} +``` +### [](#接受参数 "接受参数")接受参数 + + +``` +project.extensions.create('apkdistconf', ApkDistExtension); +``` +要注意,`create` 方法的第一个参数就是你在 build.gradle 文件中的进行参数配置的 dsl 的名字,必须一致;第二个参数,就是参数类的名字。 + +### [](#获取和使用参数 "获取和使用参数")获取和使用参数 + +在 create 了 extension 之后,如果传入了参数,则会携带在 project 实例中, + +``` + +def closure = project\['apkdistconf'\].nameMap; +closure('wow!'); + +println project\['apkdistconf'\].destDir +``` +### [](#进化版本一:参数 "进化版本一:参数")进化版本一:参数 + +``` +class ApkDistExtension { + Closure nameMap = null; + String destDir = null; +} + +class ApkDistPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.extensions.create('apkdistconf', ApkDistExtension); + + project.task('apkdist') << { + println 'hello, world!' + + def closure = project\['apkdistconf'\].nameMap; + closure('wow!'); + + println project\['apkdistconf'\].destDir + } + } +} + +apply plugin: ApkDistPlugin + +apkdistconf { + nameMap { name -> + println 'hello, ' + name + return name + } + destDir 'your-distribution-directory' +} +``` +运行结果: + +``` +$ ./gradlew -p app/ apkdist +:app:apkdist +hello, world! +hello, wow! +your-distribution-directory +``` + +[](#独立项目插件 "独立项目插件")独立项目插件 + +========================== + +代码写到现在,已经不适合再放在一个 build.gradle 文件里面了,那也不是我们的目的。建立一个独立项目,把代码搬到对应的地方。 + +理论上,IntelliJ IDEA 开发插件要比 Android Studio 要方便一点点,因为有对应 Groovy module 的模板。但其实如果我们了解 IDEA 的项目文件结构,就不会受到这个局限,无非就是一个 build.gradle 构建文件加 src 源码文件夹。 + +最终项目的文件夹结构是这样: + +![Java-Library](http://img.kvh.io/16-3-4/6224454.jpg) + +下面我们来一步步讲解。 + +[](#创建项目 "创建项目")创建项目 + + +在 Android Studio 中新建 `Java Library` module `“plugin”`。 + +[](#修改-build-gradle-文件 "修改 build.gradle 文件")修改 build.gradle 文件 + +-------------------------------------------------------------- + +添加 Groovy 插件和对应的两个依赖。 +``` +//removed java plugin +apply plugin: 'groovy' + +dependencies { + compile gradleApi()//gradle sdk + compile localGroovy()//groovy sdk + compile fileTree(dir: 'libs', include: \['*.jar'\]) +} +``` +[](#修改项目文件夹 "修改项目文件夹")修改项目文件夹 + +----------------------------- + +src/main 项目文件下: + +* 移除 java 文件夹,因为在这个项目中用不到 java 代码 +* 添加 groovy 文件夹,主要的代码文件放在这里 +* 添加 resources 文件夹,存放用于标识 gradle 插件的 meta-data + +[](#建立对应文件 "建立对应文件")建立对应文件 +-------------------------- + +``` +. +├── build.gradle +├── libs +├── plugin.iml +└── src + └── main + ├── groovy + │ └── com + │ └── asgradle + │ └── plugin + │ ├── ApkDistExtension.groovy + │ └── ApkDistPlugin.groovy + └── resources + └── META-INF + └── gradle-plugins + └── com.asgradle.apkdist.properties +``` +注意: + +* groovy 文件夹中的类,一定要修改成 `.groovy` 后缀,IDE 才会正常识别。 +* resources/META-INF/gradle-plugins 这个文件夹结构是强制要求的,否则不能识别成插件。 + +[](#com-asgradle-apkdist-properties-文件 "com.asgradle.apkdist.properties 文件")com.asgradle.apkdist.properties 文件 + +-------------------------------------------------------------------------------------------------------------- + +如果写过 Java 的同学会知道,这是一个 Java 的 properties 文件,是 `key=value` 的格式。这个文件内容如下: + +1 + +implementation-class=com.asgradle.plugin.ApkDistPlugin + +按其语义推断,是指定这个插件的入口类。 + +* 英文敏感的同学可能会问了,为什么这个文件的承载文件夹是叫做 `gradle-plugins`,使用复数?没错,这里可以指定多个 properties 文件,定义多个插件,扩展性一流,可以参考 [linkedin](https://github.com/linkedin/gradle-plugins/tree/master/buildSrc/src/main/resources/META-INF/gradle-plugins) 的插件的组织方式。 +* 使用这个插件的时候,将会是这样: + + 1 + + apply plugin:'com.asgradle.apkdist' + + +因此,`com.asgradle.apkdist` 这个字符串在这里,又称为这个插件的 id,不允许跟别的插件重复,取你拥有的域名的反向就不会错。 + +[](#将-plugin-module-传到本地-maven-仓库 "将 plugin module 传到本地 maven 仓库")将 plugin module 传到本地 maven 仓库 + +----------------------------------------------------------------------------------------------- + +参考上一篇:[拥抱 Android Studio 之四:Maven 仓库使用与私有仓库搭建](http://kvh.io/2016/01/20/embrace-android-studio-maven-deploy/),和对应的 [demo 项目](https://github.com/kevinho/Embrace-Android-Studio-Demo/tree/master/S4-MavenDeploy),将包传到本地仓库中进行测试。 + +### [](#添加-gradle-properties "添加 gradle.properties")添加 gradle.properties + +``` +PROJ_NAME=gradleplugin +PROJ_ARTIFACTID=gradleplugin +PROJ\_POM\_NAME=Local Repository + +LOCAL\_REPO\_URL=file:///Users/changbinhe/Documents/Android/repo/ + +PROJ_GROUP=com.as-gradle.demo + +PROJ_VERSION=1.0.0 +PROJ\_VERSION\_CODE=1 + +PROJ_WEBSITEURL=http://kvh.io +PROJ_ISSUETRACKERURL=https://github.com/kevinho/Embrace-Android-Studio-Demo/issues +PROJ_VCSURL=https://github.com/kevinho/Embrace-Android-Studio-Demo.git +PROJ_DESCRIPTION=demo apps for embracing android studio + +PROJ\_LICENCE\_NAME=The Apache Software License, Version 2.0 +PROJ\_LICENCE\_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +PROJ\_LICENCE\_DEST=repo + +DEVELOPER_ID=your-dev-id +DEVELOPER_NAME=your-dev-name +DEVELOPER_EMAIL=your-email@your-mailbox.com +``` +### [](#在-build-gradle-添加上传功能 "在 build.gradle 添加上传功能")在 build.gradle 添加上传功能 + +``` + +apply plugin: 'maven' + +uploadArchives { + repositories.mavenDeployer { + repository(url: LOCAL\_REPO\_URL) + pom.groupId = PROJ_GROUP + pom.artifactId = PROJ_ARTIFACTID + pom.version = PROJ_VERSION + } +} +``` +上传可以通过运行: + +``` +$ ./gradlew -p plugin/ clean build uploadArchives +``` + +[](#在-app-module-中使用插件 "在 app module 中使用插件")在 app module 中使用插件 + +-------------------------------------------------------------- + +### [](#在项目的-buildscript-添加插件作为-classpath "在项目的 buildscript 添加插件作为 classpath")在项目的 buildscript 添加插件作为 classpath + +``` + +buildscript { + repositories { + maven{ + url 'file:///Users/your-user-name/Documents/Android/repo/' + } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.0-alpha3' + classpath 'com.as-gradle.demo:gradleplugin:1.0.0' + } +} +``` + +### [](#在-app-module-中使用插件: "在 app module 中使用插件:")在 app module 中使用插件: + +``` +apply plugin: 'com.asgradle.apkdist' +``` +命令行运行: + +``` +$ ./gradlew -p app apkdist +:app:apkdist +hello, world! +hello, wow! +your-distribution-directory +``` +### [](#可能会遇到问题 "可能会遇到问题")可能会遇到问题 + +``` +Error:(46, 0) Cause: com/asgradle/plugin/ApkDistPlugin : Unsupported major.minor version 52.0 +Open File +``` +应该是本机的 JDK 版本是1.8,默认将 plugin module 的 groovy 源码编译成了1.8版本的 class 文件,放在 Android 项目中,无法兼容。需要对 plugin module 的 build.gradle 文件添加两个参数: + +``` +sourceCompatibility = 1.6 +targetCompatibility = 1.6 +``` +[](#真正的实现插件需求 "真正的实现插件需求")真正的实现插件需求 + +=================================== + +读者可能会观察到,到目前为止,插件只是跑通了流程,并没有实现本文提出的两个需求, + +那接下来就具体实现一下。 + +``` + +class ApkDistPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.extensions.create('apkdistconf', ApkDistExtension); + + project.afterEvaluate { + + //只可以在 android application 或者 android lib 项目中使用 + if (!project.android) { + throw new IllegalStateException('Must apply \\'com.android.application\\' or \\'com.android.library\\' first!') + } + + //配置不能为空 + if (project.apkdistconf.nameMap == null || project.apkdistconf.destDir == null) { + project.logger.info('Apkdist conf should be set!') + return + } + + Closure nameMap = project\['apkdistconf'\].nameMap + String destDir = project\['apkdistconf'\].destDir + + //枚举每一个 build variant + project.android.applicationVariants.all { variant -> + variant.outputs.each { output -> + File file = output.outputFile + output.outputFile = new File(destDir, nameMap(file.getName())) + } + } + } + } +} +``` +必须指出,本文插件实现的需求,其实可以直接在 app module 的 build.gradle 中写脚本就可以实现。这里做成插件,只是为了做示范。 + +上传到 bintray 的过程,就不再赘述了,可以参考[拥抱 Android Studio 之四:Maven 仓库使用与私有仓库搭建](http://kvh.io/2016/01/20/embrace-android-studio-maven-deploy/)。 + +[](#后记 "后记")后记 + +============== + +至此,这系列开篇的时候挖下的坑,终于填完了。很多人借助这系列的讲解,真正理解了 Android Studio 和它背后的 Gradle、Groovy,笔者十分高兴。笔者也得到了很多读者的鼓励和支持,心中十分感激。 + +写博客真的是一个很讲究执行力和耐力的事情,但既然挖下了坑,就得填上,对吧? + +这半年来,个人在 Android 和 Java 平台上也做了更多的事情,也有了更多的体会。 + +AS 系列,打算扩充几个主题: + +* Proguard 混淆 +* Java & Android Testing +* Maven 私有仓库深入 +* 持续集成 +* ……待发掘 + +记得有人说,只懂 Android 不懂 Java,是很可怕的。在这半年以来,笔者在工作中使用 Java 实现了一些后端服务,也认真学习了 JVM 字节码相关的知识并把它使用到了工作中。在这个过程中,真的很为 Java 平台的活力、丰富的库资源、几乎无止境的可能性所折服。接下来,会写一些跟有关的学习体会,例如: + +* Java 多线程与锁 +* JVM 部分原理 +* 字节码操作 +* Java 8部分特性 +* ……待学习 + +随着笔者工作的进展,我也有机会学习使用了别的语言,例如 Node.js,并实现了一些后端服务。这个语言的活力很强,一些比 Java 现代的地方,很吸引人。有精力会写一写。 + +因为业务所需,笔者所经历的系统,正在处于像面向服务的演化过程中,我们期望建立统一的通讯平台和规范,抽象系统的资源,拆分业务,容器化。这是一个很有趣的过程,也是对我们的挑战。笔者也希望有机会与读者分享。 + +一不小心又挖下了好多明坑和无数暗坑,只是为了激励自己不断往前。在探索事物本质的旅途中,必然十分艰险,又十分有趣,沿途一定风光绚丽,让我们共勉。 + +[](#参考文献 "参考文献")参考文献 + +==================== + +[官方文档](https://docs.gradle.org/current/userguide/custom_plugins.html) \ No newline at end of file diff --git "a/journey/android/gradlePlugin/Gradle for Android \347\263\273\345\210\227(\344\270\200)\357\274\232\345\210\235\350\257\206Gradle \346\226\207\344\273\266.md" "b/journey/android/gradlePlugin/Gradle for Android \347\263\273\345\210\227(\344\270\200)\357\274\232\345\210\235\350\257\206Gradle \346\226\207\344\273\266.md" new file mode 100644 index 0000000..40eead6 --- /dev/null +++ "b/journey/android/gradlePlugin/Gradle for Android \347\263\273\345\210\227(\344\270\200)\357\274\232\345\210\235\350\257\206Gradle \346\226\207\344\273\266.md" @@ -0,0 +1,137 @@ +Gradle for Android 系列:初识 Gradle 文件 + + +================================== + + +转自[张拭心的博客]( https://blog.csdn.net/u011240877/article/details/53798052) + +读完本文你将了解到: + + + * [setting.gradle](#1-settinggradle) + * [主目录下的 buildgradle](#2主目录下的-buildgradle) + * [模块下的 buildgradle](#3模块下的-buildgradle) + * [备注](#备注) + * * [注意 applicationId 和 package name 其实不是一个东西](#注意-applicationid-和-package-name-其实不是一个东西) + * [总结](#总结) + +![shixinzhang](https://img-blog.csdn.net/20161222004730773?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI0MDg3Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) + +我们用 Android Studio 新创建一个项目时,会自动生成 3 个 Gradle 文件: + +![shixinzhang](https://img-blog.csdn.net/20161222004618663?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI0MDg3Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) + +接下来介绍这三个文件的作用。 + +### 1\. setting.gradle + +[上篇文章:为什么 Gradle 这么火](http://blog.csdn.net/u011240877/article/details/53572264) 中介绍了, + +> 一个 Gradle 构建通常包括三个阶段:初始化,配置,和执行。 + +setting.gradle 文件在 **初始化过程中**被执行,构建器通过 setting.gradle 文件中的内容了解哪些模块将被 build,下面的内容表明当前项目中除了 app 模块还有另外一个叫做 “shixinlibrary” 的依赖模块: + +include ‘:app’, ‘:shixinlibrary’ + +注意:单模块项目不一定需要有 setting 文件,但一旦有多个模块,必须要有 setting 文件,同时也要写明所有要构建的模块,否则 gradle 不会 build 不包括的模块。 + +### 2.主目录下的 build.gradle + +看 gradle 文件中的注释: + +> Top-level build file where you can configuration options common to all sub-projects/modules. + +主目录下的 build.gradle 文件是最顶层的构建文件,这里配置所有模块通用的配置信息。 + +默认的顶层 build.gradle 文件中包括两个代码块 (buildscript 和 allprojects): + +![shixinzhang](https://img-blog.csdn.net/20161221225701377?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI0MDg3Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) + +**buildscript** +从名字就可以看出来,buildscript 是所有项目的构建脚本配置,主要包括依赖的仓库和依赖的 gradle 版本。 + +上图中 repositories 代码块将 jcenter 配置为一个仓库,JCenter 是一个很有名的 Maven 仓库。确定了依赖的仓库后,我们就可以在 dependencies 代码块中添加依赖的、在 jcenter 仓库中的包了。 + +dependencies 代码块用于配置构建过程中的依赖包,注意,这里是用于**构建过程**,因此你不能讲你的应用模块中需要依赖的库添加到这里。 + +默认情况下唯一被用于构建过程中的依赖包是 Gradle for Android 的插件。我们还可以添加一些其他用于构建的插件,比如 retrolambda, apt, freeline 等等。 + +**allprojects** +allprojects 代码块用来声明将被用于所有模块的属性,注意是**所有模块**。常见的就是配置仓库地址(jcenter, 自定义 maven 仓库等),你还可以在 allprojects 中创建 tasks,这些 tasks 最终会运用到所有模块中, + +> 官方建议尽量少添加用于所有模块的属性,因为这意味着强耦合,一旦没有构建主项目,你的子模块很有可能因为缺少所有模块的属性导致构建失败。 + +### 3.模块下的 build.gradle + +模块下的 build.gradle 文件只应用于当前模块,你可以覆盖主目录下的 build.gradle 的内容。 + +以我的练习项目为例介绍: +![shixinzhang](https://img-blog.csdn.net/20161221231912046?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI0MDg3Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) + +上图中主要分三个模块:apply plugin , android, dependencies。 + +**apply plugin** +apply plugin 声明了接下来要用到哪些插件的内容,上图表明使用了 androd 插件,这里之所以能用 android 插件,是因为主目录中声明了 Gradle for Android 的依赖,这里才能使用。 + +因此当我们需要使用其他插件,比如 retrolambda 时,首先需要在主目录 build.gradle 文件中添加依赖,然后在模块 build.gradle 中声明使用 retrolambda 插件。 + +备注:默认的 android 插件是由 Google 官方维护的,为我们提供了构建、测试、打包 Android 应用的能力。除此之外我们还可以自定义插件。在逐渐加深对 Gradle 的了解后,我们将尝试自己写个 Gradle 插件。 + +**android** +在声明了 android 插件后,我们就可以使用 android 插件提供的内容进行构建配置。 + +android 构建配置中必须要有的是两个版本: + +* compileSdkVersion : 编译应用的 Android API 版本 +* buildToolsVersion : 构建工具版本 + * 构建工具包括 aapt, zipalign, renderscript 等 + * 用于在打包时生成各种中间产物,可以从 SDK Manager 中下载构建工具 + +defaultConfig 代码块用于配置应用的默认属性,可以覆盖 AndroidManifest.xml 中的属性,比如: + +* applicationId : 覆盖了 AndroidManifest 中的 package name +* minSdkVersion : 覆盖了 AndroidManifest 中的属性,配置运行应用的最小 API +* targetSdkVersion : 一样,用于通知系统当前应用已经被这个版本测试过,和之前的 compileSdkVersion 没有关系 +* versionCode : 一样,应用的版本号 +* versionName : 版本名称 + +defaultConfig 还可以添加签名,占位符等等,这里只列这些。 + +buildTypes 用来定义如何构建和打包不同类型的应用,常见的就是测试和生产。具体内容后序介绍。 + +android 中还可以配置其他信息,比如 签名、渠道等,你可以在 Project Structure 面板中直观的查看,添加,也可以使用代码添加,这些内容我们后续详细介绍: +![shixinzhang](https://img-blog.csdn.net/20161222003411168?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI0MDg3Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) + +**dependencies** +上图中可以看到 依赖配置 在 android 代码块的外边,事实上依赖配置是 Gradle 配置的基础功能,也就是说除了 Android,其他类型的项目(比如 JavaEE )也可以这么用。 + +我们可以在依赖配置中,添加要使用的库,当然也可以添加本地的 jar 包。具体依赖配置内容我们后续深入介绍。 + +备注 +-- + +#### 注意: **applicationId 和 package name 其实不是一个东西。** + +在使用 Gradle 构建以前,package name 其实有两个作用: + +* 在 R 文件中用作报名 +* 应用的唯一标示 + +我们知道,一个安卓手机上相同包名的 app 只能有一个。但是当我们想要同时安装一个应用的不同的版本,比如一种测试一种生产,这时,就需要修改 package name 了,但是资源代码和 R 文件要求使用的包名不能改变,否则你的所有源文件都会随着构建版本而改变。怎么办呢? + +Gradle 出现后,Android 工具团队解耦了 package name 的两种不同用法,提出了 applicationId 的概念: + +* 定义在 Manifest 文件中的 package,继续用于源代码和 R 文件的标示 +* 而 applicationId 则用作设备和 Google Play 的唯一标识 + +也就是说 applicationId 覆盖了 package name 的一部分职责。 + +总结 +-- + +这篇文章概览了一个 Android 项目中的 Gradle 文件作用及内容,引申出许多细节,比如 自定义构建、依赖管理、多种类型构建的配置等等。接下来我们将深入学习这些内容。 + +相关阅读: + +[Gradle for Android 系列:为什么 Gradle 这么火](http://blog.csdn.net/u011240877/article/details/53572264) \ No newline at end of file diff --git "a/journey/android/gradlePlugin/Gradle\346\246\202\350\247\210.md" "b/journey/android/gradlePlugin/Gradle\346\246\202\350\247\210.md" new file mode 100644 index 0000000..42e6075 --- /dev/null +++ "b/journey/android/gradlePlugin/Gradle\346\246\202\350\247\210.md" @@ -0,0 +1,202 @@ +# Gradle 概览 + + + + +![Android 打包流程](imgs/Android打包流程.jpeg) + + + +### Gradle 初体验 + + + +## 什么是 Gradle + +Gradle 是一个强大的构建工具,它集成了 Ant 基于任务和构建的特性,和 ApacheIvy 强大的依赖管理功能。最重要的是开发者可以通过 Gradle 标准自定义构建插件,这一点远远优于 maven. + +### 执行时序 + + +先从我们最熟悉的开始,当我们新建一个 Android 项目的时候,系统会给我们自动生成: + + - setting.gradle + - Project build.gradle + - module build.gradle + + + 我们称整个项目为 Project,单个项目 为 module,每个项目都有自己的build.gradle 文件。 + + +好的,那么接下来我们看看 这几个文件中都写了啥,参考:[Android DSL](http://google.github.io/android-gradle-dsl/current/index.html) + + + + + + + + + + + + + +### 自定义 Gradle Plugin + + + +[](https://docs.gradle.org/current/userguide/custom_plugins.html) + + + + +一般有三种方式: + +- 直接在 Gradle 脚本中; +- buildSrc +- maven + + + + + + + + +### 自定义 Gradle Task + +好的,接下来通过自定义 Task 来了解系统的原理和完成一些实例。常见的 Gradle Plugin 作为自定义 Task 的一种载体存在。 + + + +![Android 打包流程](imgs/gradlepluginHelloworld.png) + + + + + +R2 + + + + + + + +### Gradle + + + + +[Project](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html) + +[Task](https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html) + +transfromApi + + +### Groovy + + +Groovy 作为 一种 JVM 语言,比 Java 拥有更好的动态性和便捷性。 + + +- [Groovy 官方文档](http://docs.groovy-lang.org/) +- [Groovy 上手指南](https://www.w3cschool.cn/groovy/groovy_methods.html) + + +#### 配置环境 + + +[下载安装路径](http://groovy-lang.org/download.html#osinstall) + +> Mac 上直接使用: +``` +brew install groovy +``` + + +验证: + + +``` +groovy --version +groovy -e "println 'Hello World!'" +``` + + +#### 语法学习 + +回顾我们学习 Java 的历程,我们从: + + +- 成员变量 +- 运算 +- 方法 +- 构造器 +- 类 +- 集合 io 相关操作 +- 新增特性(语言糖 syntactic sugar):闭包[函数传递] + + +慢慢了解 Java 这门语言的大致使用。下面我们也从这几个方面熟悉 Groovy 的语法。 + + + +#### Debug 技巧 + + + + + + + + + + + + + + + + + + + +### ASM/Javassist/ReDex 代码插桩 + +![Android 打包流程](imgs/Android transform 参与流程.png) + + + + + + + +> 感兴趣的童鞋可以研究下: + + - [Gradle 学习概览](https://blog.csdn.net/singwhatiwanna/article/details/78797506) + +- [ASM 实战--饿了么巴掌](http://www.wangyuwei.me/archives/) + +- [Gradle 自定义插件](https://blog.csdn.net/eclipsexys/article/details/50973205) + +- [Gradle Plugin 练手项目](https://github.com/JeasonWong/R2Assistant) + +- [Gradle Plugin 练手项目二](https://github.com/android-notes/TinyPngPlugin) + + +> **感兴趣的同学可以看下 Gradle 源码** + +- [groovy-android-gradle-plugin](https://github.com/groovy/groovy-android-gradle-plugin) + +- [Gradle 源码](https://android.googlesource.com/platform/tools/base/+/gradle_3.1.2) + + + + + + + + diff --git "a/journey/android/gradlePlugin/imgs-classLoader/Class \346\226\207\344\273\266\347\273\223\346\236\204.jpg" "b/journey/android/gradlePlugin/imgs-classLoader/Class \346\226\207\344\273\266\347\273\223\346\236\204.jpg" new file mode 100644 index 0000000..0c926b3 Binary files /dev/null and "b/journey/android/gradlePlugin/imgs-classLoader/Class \346\226\207\344\273\266\347\273\223\346\236\204.jpg" differ diff --git "a/journey/android/gradlePlugin/imgs/Android transform \345\217\202\344\270\216\346\265\201\347\250\213.png" "b/journey/android/gradlePlugin/imgs/Android transform \345\217\202\344\270\216\346\265\201\347\250\213.png" new file mode 100644 index 0000000..e11ed19 Binary files /dev/null and "b/journey/android/gradlePlugin/imgs/Android transform \345\217\202\344\270\216\346\265\201\347\250\213.png" differ diff --git "a/journey/android/gradlePlugin/imgs/Android\346\211\223\345\214\205\346\265\201\347\250\213.jpeg" "b/journey/android/gradlePlugin/imgs/Android\346\211\223\345\214\205\346\265\201\347\250\213.jpeg" new file mode 100644 index 0000000..fb4532f Binary files /dev/null and "b/journey/android/gradlePlugin/imgs/Android\346\211\223\345\214\205\346\265\201\347\250\213.jpeg" differ diff --git "a/journey/android/gradlePlugin/imgs/GradlePlugin\345\257\271\345\272\224\345\205\263\347\263\273.png" "b/journey/android/gradlePlugin/imgs/GradlePlugin\345\257\271\345\272\224\345\205\263\347\263\273.png" new file mode 100644 index 0000000..894d81f Binary files /dev/null and "b/journey/android/gradlePlugin/imgs/GradlePlugin\345\257\271\345\272\224\345\205\263\347\263\273.png" differ diff --git "a/journey/android/gradlePlugin/imgs/gradle \345\221\275\344\273\244\347\255\211\347\272\247.png" "b/journey/android/gradlePlugin/imgs/gradle \345\221\275\344\273\244\347\255\211\347\272\247.png" new file mode 100644 index 0000000..0c2b216 Binary files /dev/null and "b/journey/android/gradlePlugin/imgs/gradle \345\221\275\344\273\244\347\255\211\347\272\247.png" differ diff --git a/journey/android/gradlePlugin/imgs/gradlepluginHelloworld.png b/journey/android/gradlePlugin/imgs/gradlepluginHelloworld.png new file mode 100644 index 0000000..59ec0a9 Binary files /dev/null and b/journey/android/gradlePlugin/imgs/gradlepluginHelloworld.png differ diff --git "a/journey/android/\345\274\200\346\272\220\345\215\217\350\256\256.md" "b/journey/android/\345\274\200\346\272\220\345\215\217\350\256\256.md" new file mode 100644 index 0000000..73fa0b1 --- /dev/null +++ "b/journey/android/\345\274\200\346\272\220\345\215\217\350\256\256.md" @@ -0,0 +1,43 @@ +最近和几个朋友约着一起写一个开源项目,欢迎大家来给我们的 + [项目](https://github.com/listenOne) 点赞;同时由于项目自身数据的敏感性,我们考虑到需要尊重原作者的劳动结果; +这里对 [开源协议](https://opensource.org/licenses/category) 做了一些调查。希望大家在未来的几十天里可以共同进步,也希望开源世界里我们更尊重彼此的劳动成果,这里感谢[Listen1](https://github.com/listen1)为我们提供了灵感~~ + + + +![五大开源协议](http://upload-images.jianshu.io/upload_images/2539684-7eda2f833d3ba896.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +上图为我们开源项目常用到的协议: +从上图中,我们可以看到 BSD、MIT、Apache 都是允许闭源的,顾名思义也就是可以用于商业用途,但是注意啦司机们: + + +- BSD、MIT以及Apache协议都需要原作者的版权说明,Apache 需要按照作者的要求来进行说明,相比之下BSD 和 MIT 就显得人性化点啦,随便说只要有我就好。但是BSD作者害怕我等程序员不懂这些,所以协议要求不能使用它的名字宣传; + +- LGPL、GPL还得需要说明下:GPL 的声明作者为了强调它以及基于它的项目都是开源的,因此这个协议要求在它分支上必须都开源~~,服不服!!! +LGPL 觉得自己开源算了,还得要求别人开源,这种做法是不是不太地道。所以机智的它修改了相关内容说明下就可以了,随便用~~ + +综上所述,果断选择 MIT。 + + + + +欢迎大家持续关注我们团队成员的进度: + +姓名 | Github账号 |角色| +|----|------|--------| +|冷晶晶 |[我](https://github.com/lengmx)|后端开发| +|卜飞飞 |[们](https://github.com/bufeifei)|产品设计| +|王夜寒 |[等](https://github.com/itCatface)|数据爬虫| +|操乐 |[你](https://github.com/lexiaochang)|数据爬虫| +|袁明明 |[偶](https://github.com/yuanmingming)|Android移动端| +|杨光召|[!](https://github.com/StephenYgz)|Android移动端| +|闵亮|[!](https://github.com/minliang1219)|Android移动端| +|汤增雷|[!](https://github.com/MrLeion)|后端开发| + + + +参考文章: + +[五种常用的开源协议](https://github.com/5-say/laravel-4.1-note/blob/master/04.%E7%9F%A5%E8%AF%86%E6%8B%93%E5%B1%95/%E4%BA%94%E7%A7%8D%E5%B8%B8%E8%A7%81%E7%9A%84%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE.md) + + diff --git "a/journey/android/\347\275\221\347\273\234\346\241\206\346\236\266\345\260\201\350\243\205.md" "b/journey/android/\347\275\221\347\273\234\346\241\206\346\236\266\345\260\201\350\243\205.md" new file mode 100644 index 0000000..a20bd85 --- /dev/null +++ "b/journey/android/\347\275\221\347\273\234\346\241\206\346\236\266\345\260\201\350\243\205.md" @@ -0,0 +1,55 @@ +Preparation: + +1.数据来源: +> case 1: [豆瓣开发者文档](https://developers.douban.com/wiki/?title=guide) + +> case 2: [网络爬虫,亲测可试](http://www.10tiao.com/html/169/201702/2650822053/1.html) + + +2.工具 + +网络请求中必须要会用抓包工具,便于后续工作的有效进行。这里给mac开发者们推荐一款抓包工具,下载链接:[paros抓包工具](https://github.com/MrLeion/MacTools/blob/master/paros/iphone%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7.zip)(PS:iOS团队同事付费购买的,被我蹭来啦,给点个star哈~~) + +3.依赖库: + +> [OKHttp](https://github.com/square/okhttp) + +> [OkHttpUtils](https://github.com/hongyangAndroid/okhttputils) + +> [OKGO](https://github.com/jeasonlzy/okhttp-OkGo) + +这里因为一直做项目没用过RxJava,正好撑着这个机会可以学习下,所以选择了OkGO,看看有没有什么更好的值得借鉴的地方。 + +> **好吧,下面正式进入主题** + +思路介绍: + +因为受到github上江清清的[FastDev4Android](https://github.com/jiangqqlmj/FastDev4Android)项目的启发,正巧又要负责公司新项目的启动,所以准备写个类似的库,做到write once,run AnyWhere(哈哈,开玩笑,方便以后快速开发是真的~~) + +so,没错,你现在看到的是我的目标项目的网络框架的请求部分;这个模块实际也没什么重要的工作,就是对引入的网络请求库进行一些包装,因为考虑到前端技术更迭速度比较快,后期如果切换网络请求库的时候我们只需要更换这个类里面的网络请求部分就好了。 + +废话扯了这么多,我们来看看接下来的具体工作吧~~ + +- Http post请求 + +- Https post请求 + +- 文件上传 + +- 文件下载 + + + + + + + + + + + + + + + + diff --git "a/journey/android/\350\256\272\346\212\200\346\234\257\351\235\242\350\257\225.md" "b/journey/android/\350\256\272\346\212\200\346\234\257\351\235\242\350\257\225.md" new file mode 100644 index 0000000..1aa9b18 --- /dev/null +++ "b/journey/android/\350\256\272\346\212\200\346\234\257\351\235\242\350\257\225.md" @@ -0,0 +1,21 @@ + +------ + +![出来混,总是要还的~~](http://upload-images.jianshu.io/upload_images/2539684-734e6bb53e68cb68.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +------ + +魔都的夏天刚刚过去,即将迎来「金九银十」的招聘季节了,公司也是早早下手从人才市场吸纳人才一起建设更加敏捷的团队。正巧有机会趁着这波面试和几个做了四五年 Android 的大牛面试者们一起探讨一下技术,这里写下此文也是从面试者的角度去了解怎样去招纳技术人员。 + +闲暇之余和同事讨论过面试别人的技巧,最后我们得出结论: + +- 首先基本的兴趣爱好了解下; +- 然后看看他的作品并且让他解释一到两个功能模块重点去看看面试者的技术功底和语言表达能力以及逻辑思维能力; +- 再问问他曾经爬过的最深的坑以及最后是怎么排查解决这些问题的; +- 最后也是最重要的我们需要了解下面试者是否从事过现在公司业务的相关功能开发,如果有的话让他去谈谈对行业的理解和技术趋势的看法,因为创业公司对于人员的需求可能是你可以很快上手做事并且把事情做好(如果是大公司可能更加注重基本功扎实以及视野的广阔性,这样便于你能够在切换开发团队能够以最快地速度融入到大环境中)。 + +以上算是对技术面试的一个小小的总结吧,如果时间充足的话可以了解下他的离职原因,个人爱好,对朋友、加班、薪资的看法;这样也可以方便寻找到更快融入到团队的同志嘛~~ + + + + diff --git "a/journey/android/\351\237\263\350\247\206\351\242\221\344\274\232\345\221\230\345\256\236\347\216\260.md" "b/journey/android/\351\237\263\350\247\206\351\242\221\344\274\232\345\221\230\345\256\236\347\216\260.md" new file mode 100644 index 0000000..81ee522 --- /dev/null +++ "b/journey/android/\351\237\263\350\247\206\351\242\221\344\274\232\345\221\230\345\256\236\347\216\260.md" @@ -0,0 +1,54 @@ +**** + +全中国赚钱最容易的男歌手,舞台表演跑下去吃个夜宵都没人会发现,yoyo yoyo,这就是凤凰传奇组合里的玲花和曾毅~~重温的吐槽经典,看一遍笑一遍的大会: + +[《吐槽大会》凤凰传奇](https://v.qq.com/x/cover/hu0fqzmt0mgjsms/i0023mdnv8v.html) + +**** + +好吧,今天偶然看到了一幅好玩的搞笑组图,于是把视频找了出来有空的朋友可以看看欧~~开始今天的主题吧:最近在调研一些关于音视频会员方面的内容,相信你一定很好奇天天都被使用到的播放软件的会员是怎么实现的吧~~下面就让我们一起来探索这个问题吧! + +为了在软件中实现付费观看的音视频播放,团队成员们都在各自收集自己的方法以在会议上进行进一步的讨论。通过一番Google和调研和实现,对音视频的设计也有了些粗浅的看法,在这里用本文进行记录: + +1. 录制过程中对音视频文件内容进行加密,播放过程中对加密文件进行解密; +2. 控制访问权限: + - 对链接加密; + - 防盗链技术; + - 设置专有资源访问接口,通过用户id判断是否进行视频播放授权; + +方案一可以对安全性进行比较好的控制,但是开发的成本相对来说要大一些;方案二中对视频访问链接进行加密,增加防盗链以及设置专有资源接口可以控制用户直接获取资源,但是可以通过获取播放器访问资源时对请求从而获取资源的访问地址,并且无法杜绝后期做下载功能时用户有可能窃取视频资源。 + +注:防盗链技术中所需的字段:「Referer」在 Android 系统 4.0~6.0 中已被移除 [参考链接](https://stackoverflow.com/questions/30491018/referer-header-is-missing-when-user-clicks-on-a-link-from-inside-an-iframe-tha),也就是不能使用系统原生播放器,可替代使用 ijkPlayer 但是由于此库中 .so 库所占空间较大可能使 app 体积大幅增加所以要斟酌使用。网上也有种说法用 User-Agent 字段代替 Referer 实现防盗链,如果希望采取此技术的筒子们可以详细尝试下~~ + +实际上从技术的角度很难去杜绝资源被窃取,我们只能去争取尽可能妥当的方案;还有一点需要考虑的是可以从法律的角度去保护隐秘资源的权益,这里就要使用我们的 DRM 技术啦,常用的就是给咋们的视频打上水印,打个水印还得防止别人给认为去除喽,使用 Google 爸爸的软件「Watermark Master」可以解决打水印的问题~~水印效果如下: +![](http://ofc6yl8uw.bkt.clouddn.com/visible%20watermarking.webp) + + +好啦,说了这么多我们来从技术的角度谈谈实现吧: + +1. 防盗链技术实现 + +实际就是控制 MediaPlayer 在访问过程中添加请求头,详情请参见:[传送门](http://blog.csdn.net/enlangs/article/details/39546785?winzoom=1) +此外,上面也说了要使用 [ijkPlayer](https://github.com/Bilibili/ijkplayer) + + +2. 文件和字符串的加密解密技术 + + 本文的重点来啦~~先来看一张思维导图吧: + ![java加密解密](http://upload-images.jianshu.io/upload_images/2539684-bb179a83d22a9026.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + 图中可以看到: + + + - 从严格意义上来讲 Base64 和凯撒加密只能算作为一种编码的方式; + + - MD5,SHA1,HMAC 作为单向加密算法,不能被解密,一般也只能用于信息发送方的确认,比如高德、百度地图需要添加 SHA1 值作为验证开发者信息的手段; + + - 复杂加密算法可分为对称加密和非对称加密,对称加密算法由于其公钥的普遍性所以并不是特别安全。非对称加密则必须要公私要配合使用,并且私钥作为验证用户身份的方式存在; + + - 数字签名和数字证书的认证思路是一致的:发送方通过 MD5 加密明文获取信息摘要,然后通过私钥对明文+MD5 进行签名表明自己的身份。接受方必须通过 公钥进行验证签名; + + + 具体代码实现请参见:[BlankJ Encrypt](https://github.com/Blankj/AndroidUtilCode/blob/master/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java) + + diff --git "a/journey/android/\351\253\230\344\273\277\345\276\227\345\210\260-\345\272\225\351\203\250\346\222\255\346\224\276\345\231\250.md" "b/journey/android/\351\253\230\344\273\277\345\276\227\345\210\260-\345\272\225\351\203\250\346\222\255\346\224\276\345\231\250.md" new file mode 100644 index 0000000..640784f --- /dev/null +++ "b/journey/android/\351\253\230\344\273\277\345\276\227\345\210\260-\345\272\225\351\203\250\346\222\255\346\224\276\345\231\250.md" @@ -0,0 +1,52 @@ +------------------ + + 《2017感动中国十大事件》 + + 网贷公司一客户是个孤儿,今年在平台上借了几千块钱买了部iphone X,因为最近手头太紧钱一时还不上来。于是平台找到了他的父母将欠平台的几千块追了回来,而客户也找到了自己的父母,这是个感人的故事~~ + +------------------ + +好啦,按照惯例,先来看下效果图: + +![效果图](http://upload-images.jianshu.io/upload_images/2539684-3145150e3b955277.gif?imageMogr2/auto-orient/strip) + +看到这个效果我的第一反应是用 WindowMananger 去做,但是实现起来发现以下不足: + + - WindowManager 会对底部内容进行遮挡; + - 部分手机不支持小空间的显示; + +其实最普通的做法当然是在每个 activity 底部放一个这样的布局,但是这种做法显得比较低级。于是果断调查了下其他 app 的实现思路,下面是对自己 copy 思路的记录,期间发现一些比较不错的工具,大家可以使用下; + + +这里有朋友可能会考虑反编译看下别人的代码,但是现在很多应用都进行了加固,源代码几乎无法查看([想知道具体原因欢迎查看我的另一篇文章](http://www.jianshu.com/p/a26fa25a93ea))因此我们考虑采用其他方式: +[思路](https://www.zhihu.com/question/36448929) + +虽然通过 adb 命令也可以很快查看Android系统当前运行Activity + +’‘’ + + adb shell dumpsys activity |grep "mFocusedActivity" + + ‘’‘ + + + 命令替代工具: + + +[工具github](https://github.com/waylife/DemoCollections/tree/master/ViewDebugHelper) + + + + 效果图: + +![网易调研](http://upload-images.jianshu.io/upload_images/2539684-b71049d4ec45305e.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +通过 DDMS 我们可以看到页面布局的结构: + +![工具使用](http://upload-images.jianshu.io/upload_images/2539684-3b395a57c1272929.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +再通过此方法查看得到验证下自己的想法:每个 Activity 中实现。 + + +到这里,我们只需要在每一页的 ScrollView 或者 ListView上加上收拾的坚挺判断,那么就可以实现上面的效果了~~ diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\345\201\232\350\207\252\345\267\261\347\232\204\344\272\247\345\223\201\347\273\217\347\220\206.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\345\201\232\350\207\252\345\267\261\347\232\204\344\272\247\345\223\201\347\273\217\347\220\206.md" new file mode 100644 index 0000000..7f6a910 --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\345\201\232\350\207\252\345\267\261\347\232\204\344\272\247\345\223\201\347\273\217\347\220\206.md" @@ -0,0 +1,44 @@ +从事产品研发接近两个年头啦,回过头来看一看,一个互联网产品从项目立项->需求的确立->产品的设计->开发团队的实现->测试人员的反复测试->最后的运营上线,这里的每个环节都需要通过伟大的PM去协调和决策并且付诸行动。今天,小小程序员带着一颗渴望感受真正产品的心拜读了[苏杰老师](http://iamsujie.com/)的《人人都是产品经理》这本书,也是为了对产品经理这个职位的认知上有了一个提升,现在趁着还有这股兴头,好好整理下自己的思路吧,体味一下如何打造出一款优质的有价值的产品。 + +让我们思考几个问题: + +1. 什么是产品? +2. 什么是好的产品? +3. 什么是好的产品经理? + +我想每个人的心中都会有不一样的答案吧,谈谈我个人的感受吧: + +1. 我觉得生活中的任意一个物件,只要能够解决生活中客观存在的问题,那么它就可以是一款产品;当然啦,有时候我会发现自己也是一款产品,因为我的功能就是给身边的人带来正能量和温暖的感觉~~(自恋地说) +2. 更深层次一点,一款好的产品我觉得 + * 首先她要有着自己的基本价值(解决问题,满足日常的需求,还必须能创造出商业价值); + * 其次她是设计者价值观的体现和灵魂的寄托,必须是寄托了设计者信仰有灵魂的; + * 然后在时间允许的情况下,她必须有着近乎完美的设计,而我对于设计这个东西的完美性的定义就是在满足设计规范的基础下做到 **Don‘t make me think**; + * 最后如果我们的产品能够从马斯洛需求层次上尽可能多的满足用户的需求那就再好不过啦。 +3. 产品经理,或者叫做PM,我认为一个好的产品经理必须具备**帮助团队为用户打造有价值的产品的能力**。 + +- 团队:他们生活在老板,客户,财务,销售,设计,开发,测试和运营等各个团队的核心地带,就像计算机的总线型结构一般,要保证计算机的高速运转,充当核心的他们必须充当团队的润滑剂,他们需要懂得每个领域的专业知识,使得沟通的成本可以大大降低。他们需要协调身边的一切资源,对任务作出取舍和判断,所以中心地段的他们要求真的比较高。 + +- 用户:要知道用户有时候根本不太清楚自己需要的是什么,他们对于很多东西的判断可能没有进行过深入的思考,所以我觉得一款产品的最初的难点就在于在**特定的产品使用场景下**,通过真实的数据或者对于人性的理解去感知真实存在的需求,同时这种需求要满足我们的价值观和商业发展战略,的确是最难的一块; + +- 打造:我觉得这里是我作为一个开发人员能够做到的,要关注**任何一个细节**,最起码要保证我们的产品没毛病,在此基础上有自己的个性;还要根据自己采集的数据和真实用户的反馈来不断地优化我们的产品,毕竟一次性设计还是会有很多问题的; + + +- 有价值的产品:价值的体现可以从两个角度去考量: + + * 用户的角度:我们的产品要能解决客户固定场景下的客观需求,解决用户实际存在的问题,那我觉得我们的产品就是有价值的; + + * 企业自身的角度:创造利润 + +谈了自己对于几个产品概念的理解,我们知道要打造一款真正好的产品背后需要付诸很多心血,下面想和大家谈谈咋们研发人员应该做出哪些思考吧。 + +很喜欢苏杰老师的一个比喻,刚刚进入公司的时候我们总是在给老板做问答题,总会抛出各种各样的问题,却不知道如何下手;渐渐的有了点经验后我们开始自己想出一部分问题的答案,这个阶段抛出的问题大多是以填空题的形式抛出去;再后来,我们浅浅可以独立地完成很多模块,这个时候我们需要积累自己解决问题的方案,而工作过程遇到的问题也就变成了选择题的形式,在这个基础上我们渐渐尝试着给出自己的判断,如果某一天自己的判断完全可以符合公司的发展战略的时候,我们也就可以独挡一面啦;或许有一天,自己也会变成那个帮别人批改试卷的人。 + +有时候真的觉得我们做事的方式应该像一个产品经理打造一款产品一样,一个个演变的过程会让我们渴求的产品(自己)变得愈加完美。作为开发人员的你们处于哪个阶段呢?你渴求达到什么样一个境界呢?你想好了怎么办到了吗? + +我觉得这两年通过自己的积累自己勉强达到选择题的地步吧,希望自己可以在某一天自己的判断受到PM的认可吧。所以接下来的日子里采取的措施:去感受市场和公司的战略,模拟用户场景,体会用户的真实需求,在工作中提前一步,精心设计和打造自己的产品的每一个细节。 + +> Tips:做一个对产品有感知的人吧,多看看appStore的排行榜的产品,一起加油! + +题外话:我觉得抛开技术谈人生,我们也是自己人生的产品经理,我们需要不断去经营个人品牌,提升自身的魅力,扩大个人影响力,我想这可能是苏老师真正想传达给我们的吧;在这个纸醉金迷的时代,很多东西都略显浮躁,人情味儿似乎没那么强,所以我希望将自己打造成一款**有温度的人**这样的一款产品,希望通过写作的方式来和对面的你一起并肩作战,希望看到我的文章的时候会觉得这个世界还有我的同伴,加油,做自己人生的产品经理吧~~ + +> [markdown笔记下载地址,欢迎star](https://github.com/MrLeion/PMXmind/blob/master/pmFeelings/%E4%BA%BA%E4%BA%BA%E9%83%BD%E6%98%AF%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86.md) diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\345\270\202\345\234\272\345\210\206\346\236\220.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\345\270\202\345\234\272\345\210\206\346\236\220.md" new file mode 100644 index 0000000..2a12182 --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\345\270\202\345\234\272\345\210\206\346\236\220.md" @@ -0,0 +1,8 @@ +![](https://raw.githubusercontent.com/MrLeion/MarkdownPics/master/marketAnalysis/%E5%B8%82%E5%9C%BA%E5%88%86%E6%9E%90.png) + +无论是互联网产品还是传统行业的产品,我们都需要盈利,或者要尽快地走上盈利的道路上。所以我们需要了解产品的市场和背后的逻辑,让自己的产品更加富有竞争力显得尤其的重要。 +微观上,分析所在行业的竞争,我们要由“点”,“线”,“面”的角度出发,借鉴或创新商业盈利模式;宏观上,从公司自身自身的角度出发,判断行业状况以及整个一个大的经济条件下去决定自己产品的市场,从而解决我们在哪儿的问题; +此外,我们需要找到行业的痛点,并提出自己的解决方案。要知道我们要铸造什么样的商业壁垒使得自己屹立不倒,从而解决我们要到哪儿去的问题; +最后就是怎么去的问题啦,好好看看思维导图的传授的方法论吧~~ + + [markdown笔记下载地址,欢迎star](https://github.com/MrLeion/PMXmind/blob/master/marketXmind/%E5%B8%82%E5%9C%BA%E5%88%86%E6%9E%90.xmind) diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\347\253\236\345\223\201\345\210\206\346\236\220.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\347\253\236\345\223\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..f5194ef --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\347\253\236\345\223\201\345\210\206\346\236\220.md" @@ -0,0 +1,69 @@ +![](http://upload-images.jianshu.io/upload_images/2539684-3e3fb59dc82a2f6e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +> 知己知彼,方能百战不怠;商场如战场,对于对手以及产品尽可能全方位的认知应该是不断深入的,那么竞品分析就显得十分的重要,他能让我们时刻从一些比较常规的角度去了解竞品的基本情况。只有在不断地修正对竞品地认知,才能吸取别人的长处,才能让自己紧跟或者超越潮流,所以竞品分析在PM的课程中显得特别重要 + +下面就让我们来思考几个我想到的问题吧: + +* (how?)如何做一个竞品分析? +* (when?)什么时候应该做竞品分析? + + +A1:一款产品有她所在的行业,她可以是从0到1地创造,也可以是伴随着竞争而产生。这里我们只讨论后者,因为毕竟前者需要我们在大量的积淀后才有可能会有的发挥; +假设我们所处的行业有着比较多的竞争对手,那么现在问题来了:这个行业这么大,竞争者这么多,如何寻找自己的对手呢? +我的看法:假想自己是行业中档水平,那么这个行业中的对手只有可能存在三种情况: + +1.远远超过我们的; + +2.和我们持平的; + +3.稍微低于我们水平的; + +建立在这个目标的基础上,我们分别找出三类对手 + +* 行业标杆 +* 同水平竞争者 +* 潜在对手 + +> 注:对手的水平判断依据我认为应该是她所在的公司经济和团队水平和产品的目标定位无关;这里我们推荐给大家几个网站用来收集竞争产品以及调查相应公司的背景吧: + +//TODO: + +> APP相关数据 + * []() + * []() + * []() + * []() + +> 公司背景分析 + * []() + * []() + * []() + * []() + +> 行业数据分析 + * []() + * []() + * []() + * []() + +> 科技咨询网站 + * []() + * []() + * []() + * []() + +在经历过讨论筛选后,我相信大家都会有自己中意的几款产品,接下来我们要做的就是全方位体验对手的产品,线上从《用户体验要素》的角度去分析产品;线下当然实际去感受产品啦(如果有的话,就像摩拜单车~~) + +下面的重点来了,我们前面做的所有准备工作,都是为了让我们分析总结 +对手的产品,那么我们应该从哪些方面总结呢,这里课程中的老师给出了一些角度,让我们做下参考: + +* 公司背景分析 +* 策略分析 + * 产品策略 + * 商业模式 + * 运营策略 +* 定位分析 +* 产品分析 +* 用户分析 + + diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\351\234\200\346\261\202\351\202\243\344\272\233\344\272\213\345\204\277.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\351\234\200\346\261\202\351\202\243\344\272\233\344\272\213\345\204\277.md" new file mode 100644 index 0000000..c895fcf --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/[\344\272\247\345\223\201\345\255\246\344\271\240]\351\234\200\346\261\202\351\202\243\344\272\233\344\272\213\345\204\277.md" @@ -0,0 +1,44 @@ +在前面的学习中,我们了解到了产品作为一种能够高效解决某一类特征群体的问题的工具,它寄托着产品设计师的灵魂。 + +产品的构建是一项项需求完美结合起来的产物,那么如果需要设计一款产品,我们需要考虑以下几个方面的问题: + +- 需求来源何处? + +- 如何正确的表达需求,让沟通更有效率,让自己的思路更加完善? + +- 怎样高效地执行我们的需求? + +- 如何不断优化我们的需求,让我们在竞品之中立于不败之地? + + +A1:我们知道需求可能的来源: + +* 用户或者市场陈述与反馈 +* 公司内部 + + +![](http://upload-images.jianshu.io/upload_images/2539684-34d9dca0ac93a8b6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +> 如上脑图显示,我们需要明确地根据马斯洛需求理论去置身于具体场景中去体会产品的需求。然后将我们的需求通过`Feature List`表格详细的记录思考,纳入我们产品的备选项中;再进行过仔细探讨和推演后,我们就可以将其制作成具体的prd从而让开发去实现我们的需求。 + +**说到这里,我认为对于一个产品经理很实在的能力:** + +1. 对于竞品深入的分析; +2. 对于市场深入地理解和战略的制定; +3. 通过feautrueList尽可能详尽地描述产品需求; +4. 近乎于变态版详尽的prd,让自己的思路蔓延到产品的方方面面; +5. 具体有效的用户反馈手机手段,并且从中提取中产品中的不足; +6. 通过数据分析产品的迭代方向; + +A2:为了提升整个团队的沟通效率,我们需要通过将自己的需求转换成文档,让自己的逻辑更加清晰,也让团队中的人员可以更加清晰的了解我们的思路; + +![](http://upload-images.jianshu.io/upload_images/2539684-9e690fdc6639e714.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +当我们能够用一句换很清晰地表述出我们的需求模块要干什么的时候,可能就已经对我们的产品定位有了个纲领型的认识。那么借助于文档工具可以让我们的想法更加具体化; + + +A3: + + ![](http://upload-images.jianshu.io/upload_images/2539684-6e04d5a211aa17e2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/\344\272\247\345\223\201\350\277\220\350\220\245.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/\344\272\247\345\223\201\350\277\220\350\220\245.md" new file mode 100644 index 0000000..d4f8d33 --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\344\272\247\345\223\201/\344\272\247\345\223\201\350\277\220\350\220\245.md" @@ -0,0 +1,25 @@ +![](http://upload-images.jianshu.io/upload_images/2539684-b4e83ec2a6a459ae.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +> 对于从事 Coder 工作两年的我来说,愈发感觉到自身专业知识的匮乏!学习起点学院的《90天产品经理》课程快三个月了,面临课程即将结束,然而我知道路还很漫长~~最近拜读了黄有璨先生的《运营之光》,品味十年运营路,深深地体会到一位专业大师在慢慢运营路上由原本的懵懵懂懂到后来强有效的执行力,再到最后从格局和行业上把握运营的这么一个过程。结合课程中的内容和《运营之光》以及日常工作中的所见所闻谈谈自己的感受吧,也算是对运营课程的一种总结。 + +> 在我的认知中,运营一直以来是一群擅长软文编辑的群体,他们偶尔会提提关于活动或者产品改善的需求,工作相对轻松时常侃大山。但是黄就自己在互联网运营行业的数十年光阴里对于每个事情的处理细节和自己的深入思考值得学习!常说,产品就是生孩子,运营就是养孩子,在陪伴着一个“孩子”由生到死的过程中我们需要做的就是珍惜每个阶段,做最好的“养父母”,细细体味产品运营和培养孩子真的是一个道理,我们要熟知自己的“孩子”处于一个什么样的状态,才能因材施教。这样的情况有很多种,我们还是从某些方面先把握一些宏观上的理解吧: + +产品的生命周期从运营的角度来看可以分为四大阶段,下面的思维导图中已经做了些许整理,还是可以凑合着看看的~~ +![](http://upload-images.jianshu.io/upload_images/2539684-0e77aaff2fe183b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +下面对这张图做些简要的说明吧: + + * 产品探索阶段:这个时期的产品的未来充满着无限种可能,运营的侧重点需要通过相对可控的方式去获取目标客户,正确地反映出产品体验上的一些问题和不足,并反馈给产品经理去改良产品; + * 产品快速发展阶段:处在这个阶段的产品侧重点应该是,快速抢占市场,数据埋点的方式去获取用户的反馈; + * 产品成熟稳定阶段:产品的发展已经相对平稳,这个时期应该尽量活跃用户,了解用户的心理设计出合理的盈利模式,从而实现盈利; + * 产品衰退阶段:产品的生命即将宣告终结,这个阶段可以做的是尽量减缓老用户的流逝,并且寻找替代产品; + + +其实除了这些宏观的规律之外,我们更要从意识上去更正自己对于运营工作的看法,如果我们需要去完美地完成一项对产品有利的工作,那么我们应该明确地知道自己的每一个对于产品生态有利的目标,还要善于分解这些目标,知道自己每一步应该怎么做,有没有更优良的做法。这样我们才能获得真正的成长。 + + +好啦,我就不瞎扯啦,对运营感兴趣的朋友可以去看看黄有璨先生的《运营之光》吧,当然也可以关注公众号“三节课”,懂点运营知识对于一个互联网人来说是非常有必要的~~ + + +> [思维导图下载](https://github.com/MrLeion/MarkdownPics/tree/master/productopreation) diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\345\271\264\345\272\246\346\200\273\347\273\223/2018 \345\271\264\347\273\210\346\200\273\347\273\223\342\200\224\342\200\224\347\273\231\346\227\266\351\227\264\347\233\226\344\270\252\346\210\263.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\345\271\264\345\272\246\346\200\273\347\273\223/2018 \345\271\264\347\273\210\346\200\273\347\273\223\342\200\224\342\200\224\347\273\231\346\227\266\351\227\264\347\233\226\344\270\252\346\210\263.md" new file mode 100644 index 0000000..9d5ec9c --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\345\271\264\345\272\246\346\200\273\347\273\223/2018 \345\271\264\347\273\210\346\200\273\347\273\223\342\200\224\342\200\224\347\273\231\346\227\266\351\227\264\347\233\226\344\270\252\346\210\263.md" @@ -0,0 +1,83 @@ +# 2018 年终总结——给时间盖个戳 + +2018 年从年初正月初四到四季如春的大理旅游。从三月初回魔都经历了短租,旅店一直到五月感觉自己一直奔走在魔都和杭州之间。2018年06月08日正式入职 杭州***公司。 + + +也开始了人生新的篇章~~ + + +工作上下半年的重点放在了巩固基础上: + +- java 语言 +- gradle plugin +- 数据结构和算法 +- Android Framework +- Python3.7 数据分析 +- 玩 Android 项目 + +业余爱好: + +- 运动 +- 阅读 +- 段子 +- 旅游 + + +新的爱好: + +- 看纪录片写总结 +- 科技产品,软件硬件,开源的好玩的,提升自己的产品意识 +- 写文章 数据分析分析具体问题,技术总结 + + + +TODO: + +- 做视频,分享知识,顺便练练口才 + + +时间管理上的思考: + +- 1.每天早晨过来先思考要干哪些事情,由简单到难 +- 2.每周做下总结,时间定在每周五下午,总结本周,安排下一周[本周双休+下一周的工作日] +- 3.上班时间禁止玩社交媒体,focus on ~~。把关注咨询的时间尽量分散化,或者晚间~~ + +关于学习方面的时间管理上面的思考: + +- 1.要有 deadline 意识 +- 2.要有 deadline 意识 +- 3.要有 deadline 意识 + + +关于技术方向的选择上: + +- 1,列出你有兴趣的30个活动; +- 2,从最有兴趣到最没兴趣排序; +- 3,对前六名的活动,每个活动花一周时间到网上找资料集中学习; +- 4,每周结束后打分; +- 5,最后取前三名各花两周继续学习。 +很多事情是三分钟热度的,根本坚持不了一周。 + + + + 2019 年的 Flag 还在酝酿思考中,已经记在自己的事务清单啦~~ + + + 祝大家新年快乐,猪事顺利~~ + + + + + + + + + + + + + + + + + diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/6W1H\345\216\237\345\210\231.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/6W1H\345\216\237\345\210\231.md" new file mode 100644 index 0000000..11f629b --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/6W1H\345\216\237\345\210\231.md" @@ -0,0 +1,40 @@ +日常生活中我们会接受很多任务,完成各种各样的case,但是由于对事物不熟悉,我们往往不知该从何下手。个人认为6W1H原则可以很好的帮助我们浅层次地分析了解和完成事物,让我们的思维更加缜密,今天就和大家谈一谈对这一规则的理解吧。 + +职场中存在着执行者和管理者这两大角色,那么6W1H原则在这两种人的世界里这一规则实际上是有着一些顺序上的差别的。 + +执行者的角度: + +* what? +* who? +* when? +* where? +* For whom? +* why? +* how? + +从一个执行者的角度来看,我们在执行某一项任务的时候要了解任务是什么?(what),如何了解调查一个事物(我们可以了解事物的历史:人物(who)为了(for whom)谁在什么时间点(when)在哪儿做了什么?(where)) + +建立在这些问题的基础上我们对事物有了个大概的了解,根据任务的具体情况,我们会有着自己的思考,为了便于计划的执行,我们可能需要抛出一些具体的问题(why),最后我们会不断地在why和how之间徘徊,直到事物能够顺利的完成。 + +我觉得快速有效完成任务的基础上,我们可以重新思考下如何做的更加有效以及为什么要从策略上这么做,这样我们才能使得自己的能力有着进一步的提升。这也是我一贯分析和学习事物采取的措施,以及书写文章做采取的思路。 + + +管理着的角度: + +* what? +* why? +* who? +* when? +* where? +* For whom? +* how? + +从一个管理者的角度,我们在快速了解事物的构成(what)之后,要根据自己所在团队的情况分析利弊,找到任务推进的原因(why)。并且要根据对任务和下属的了解,要定夺谁(who)为了什么(for whom)什么时候(when)在哪儿(where)做什么(what)。这些执行环节大概要怎么做(how)?会不会有什么不良后果,或者怎么样才能达到最优? + +这一切需要我们对自身和任务有着清晰的认识,快速的决策力,以及根据自己经验的判断规避很多风险的能力都是必备的。 + + +好啦,这就是我对6W1H原则的理解,真没想到有时候一些思考顺序上的差别还能带来这么大的变化,期待着你有着不一样的思考! + + + diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/MrLeion\347\232\204\346\227\266\345\205\211\346\227\205\350\241\214.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/MrLeion\347\232\204\346\227\266\345\205\211\346\227\205\350\241\214.md" new file mode 100644 index 0000000..3a6db39 --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/MrLeion\347\232\204\346\227\266\345\205\211\346\227\205\350\241\214.md" @@ -0,0 +1,36 @@ +![](http://upload-images.jianshu.io/upload_images/2539684-af773f796a559d3d.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +**** + +今天偶然间打开QQ空间觉得挺好玩的,在这里分享给大家。顺便吐槽下上海的天气,那么多年没有过的高温!这次一股劲儿居然连续一周接近四十度,感觉自己快成了一块奔跑的烤肉~~快点下雨吧,老天爷! + +**** + +源于年初给自己的任务,微博上发了半年的搞笑视频和段子,一直把它作为记录自己跑步的一种方式(怎么样,是不是方式有点儿奇葩)总计130篇,自己今年已经跑了650公里啦,距离自己的一千公里的约定很接近了。这半年的时间也断断续续写了几篇文章,当然也有很多文章的雏形或者说是想法以草稿的形式存储在我的电脑端。但是由于前段时间目标相对比较涣散,每天既想着写段子,又想着写代码并写文章作总结。每天仿佛自己在很努力,但是始终以一个学生的心态去了解和接受事物的方式的确存在着很多的问题,但是也在这半年的时间里渐渐找回了奋斗的状态,很多地方自己需要调整: + + - 工作日除去每天锻炼(1小时20分钟)和上班的时间(PS:平均水平8:30-19:30)以及睡觉7个小时。还要扣除早饭和晚饭的40分钟,也就是说我每个工作日充其量也就4个小时供自己支配,剔除掉可能会存在的杂七杂八的事情需要办花去的一个小时。每个工作日只有7:00-8:30和22:00-23:30这三个小时的时间块; + +- 非工作日,自定义:加班?出去玩?敲代码?看书?一般情况下至少得腾出一下午加一晚上的时间出去high。少数情况下,两天都出去玩儿,或者两天都在工作~~so,双休日大部分情况下自己有着至少一天的时间可供自己支配; + +- 前半年的时间自己的时间管理模式似乎一直都是按部就班的学习计划,这种计划真的是看不到头,而且实用性不是很大,主要是因为觉得研究问题不够深入和具体。一次又一次的工作实践让自己深刻的意识到这种模式的弊端(时间不是连贯的,而是断片儿的,感觉难受极了~~);就比如说上半年的时间里自己也大概买了有三十多本书,其中大部分关于财经专业的书籍和一些软实力思维增长方面的书籍,半年的时间自己读下来感觉没有很好的被自己用起来,所以大部分也就遗忘了,而且阅读的速度和理解也就逐渐变慢,就更没有时间写读书笔记了。 + +- 目标涣散,阶段式的始终处于持续学习的状态并且有着太强的时间约束可能不是自己喜欢的模式; + +当然,其他方面的原因自己就不说啦。通过上半年的执行,我渐渐的意识到只有每天坚持做自己想去做的事情才是最适合我的模式~~因此,我将它修改成强制自己在每个工作日去做的事情,这样觉得自己更加能够接受。 + +根据对自己行程的分析,如果想获得更多的自由时间,每天中唯一可以考虑压缩的就是工作时间上花费的那十一个小时。如果能够以最快的速度高质量地完成自己的工作,那么自己就会有大批的时间去干自己想做的事情啦,所以下半年自己打算将段子,文章合体并作为记录自己650公里开外的记录,我想这可能是我们这辈子需要坚持的习惯,对的! + +好,那么下面谈一谈现在面临的主要的两个问题吧: +- 程序员**高效高质**地完成工作; +- 零散时间的衔接,让自己更加牛逼; + + +Q2:给自己一个清晰明确的阶段目标: +完成记账产品(7.24-8.24) +本周完成产品原型并评估时间 + +每个工作日要做的事情: +- 跑步,写文章 + + +2017年还有五个月的时间,加油吧,让简书见证自己的足迹~~ diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\344\272\224\344\270\200\344\272\224\344\270\200\357\274\214\345\215\227\344\272\254\345\215\227\344\272\254.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\344\272\224\344\270\200\344\272\224\344\270\200\357\274\214\345\215\227\344\272\254\345\215\227\344\272\254.md" new file mode 100644 index 0000000..214305b --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\344\272\224\344\270\200\344\272\224\344\270\200\357\274\214\345\215\227\344\272\254\345\215\227\344\272\254.md" @@ -0,0 +1,203 @@ +##五一五一,南京南京 +> 这个五一不一般啦,Leion先生要去南京和曾经考研的战友们来个大会师,两年没见他们了,说起来还真是有点想念啊,期待来张合影~~ + +**行程前的准备** + +> 关于天气 + + +![](http://upload-images.jianshu.io/upload_images/2539684-a7beed7b539d87d4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +哈哈,人品太好,没办法~~ + +> 关于行程和住宿 + +* Day1:4月30号07:44-09:42 上海——南京南 +* Day2:5月1号17:38-20:19 南京南——上海 + +> 因为其中一个朋友 (小明)离南京南站比较近,因此住宿什么的也就安排在这边;不过如果从外地过去南京南玩的朋友建议去南京站,据说那边属于市中心,交通方便,距离景点也会近很多。然后住宿的话,建议在夫子庙那边,因为你懂的,晚上那边的夜景还是不错的,不说了,想去吃我狮子桥店的鸭血粉丝汤~~ + + +> 关于人文 + +南京,又称金陵,长三角副中心城市,又称“六朝古都,十朝都会” + +书籍推荐: +《明朝那些事儿》 +《一本南京》 + + + +> 行政区域划分 + +* 玄武区 +* 秦淮区 +* 鼓楼区 +* 建邺区 +* 雨花台区 +* 栖霞区 +* 浦口区 +* 六合区 +* 江宁区 +* 溧水区 +* 高淳区 + +配图: + + +![](http://upload-images.jianshu.io/upload_images/2539684-2493143831133f57.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +> 这里强行装个X,毕竟来到六朝古都,连哪儿跟哪儿都分不清有点不太像话。 + + + +**开始旅行喽** + +> 旅游路线 + +Day01: + +* 中山陵(明孝陵) +* 夫子庙(秦淮八绝,老鸭粉丝汤) + +Day02 + +* 河海大学江宁校区 +* 玄武湖公园 +* 狮子桥步行美食一条街 + +第一天,我的心情上下不安(PS:忐忑不安啦~~),清晨五点四十,我激动的从床上跳下来准备出发。一路上的微信互动,终于和我的小伙伴们在十点钟会面于星巴克。因为接近午饭时间,所以过去河海大学把行李早早的放下,吃了个午饭准备下午的行程~~ + +这里吐槽下,南京为什么没有那种单日旅游交通卡呢,害得我们每次进地铁站都要排队买票~~是我太笨没找到?反正在网上只看到了记名卡和不记名卡!!! + +好啦,废话不多说,开始记录旅行喽~~ + + +**Day01** + +先上张合影吧~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-c44f4373b06ff1f6.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +太阳真大,大家都睁不开眼睛~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-de2761f179bfe73c.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +到中山陵山脚啦~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-ce07b463eb17328d.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +登顶啦,朕的天下~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-0e0eab7432a113c1.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +王总~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-fc3d4d57c8baefab.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +王总和尤总~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-97fa5797fd130fc1.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +王总的自助火锅烧烤,棒棒哒~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-76b3f0ebee818cb7.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +夜游夫子庙~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-ec95c7ed9c042ff1.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + + +**Day02** + +玄武湖公园~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-5d33fb52e5835e1c.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +![](http://upload-images.jianshu.io/upload_images/2539684-6a2f7d8adbd2da99.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +泛舟玄武湖~~ + + +哇咔咔,我们的女司机~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-4c555d705e13ae9e.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +![](http://upload-images.jianshu.io/upload_images/2539684-550da4b2305b135a.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +许愿树 + +![](http://upload-images.jianshu.io/upload_images/2539684-82ee423d3f5c4341.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +他们为了找我的许愿牌也是蛮拼的~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-f7e745d938b7c27d.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +出发去狮子坊扫街喽~~ + +骑着单车去狮子坊喽~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-8bf2067fd2597599.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +我们到狮子坊啦~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-f2016867c181c767.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +传说中的小吃一条街~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-bbba1c3f66be059c.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +想了好久的老鸭粉丝汤 + +老鸭粉丝汤的基地~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-b42fd798d5a1bf78.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +阿姨辛苦啦 + +![](http://upload-images.jianshu.io/upload_images/2539684-d2a303c754cf3b59.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +出锅喽 + +![](http://upload-images.jianshu.io/upload_images/2539684-23525699a29e95f1.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +开动啦~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-a2d2b3dc05ef6ef7.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +漫长的等待,我的臭豆腐啊~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-1e2668beb2abaa27.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + +好吃的臭豆腐~~ + + +![](http://upload-images.jianshu.io/upload_images/2539684-f0001f7addad7aa9.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +最是离别忆君时 + + +![](http://upload-images.jianshu.io/upload_images/2539684-9bd655d82c00af6b.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + +参考路线: + +* [上海——南京两日游](http://www.mafengwo.cn/i/5392103.html) +* [南京三日游](http://nanjing.cncn.com/article/147890/) +* [外地人来南京必玩进店全攻略](http://nj.bendibao.com/tour/2014818/ly45543.shtm) +* [南京二日游配地图](http://www.mafengwo.cn/mdd/cityroute/10684_241.html) diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\345\221\274\344\274\246\350\264\235\345\260\224\345\244\247\350\215\211\345\216\237.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\345\221\274\344\274\246\350\264\235\345\260\224\345\244\247\350\215\211\345\216\237.md" new file mode 100644 index 0000000..2b47b7f --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\345\221\274\344\274\246\350\264\235\345\260\224\345\244\247\350\215\211\345\216\237.md" @@ -0,0 +1,199 @@ +如果有人问我,去过的最远的地方是哪?那无疑就是今年过去的内蒙古临江屯啦,位于咋们中国这只大公鸡的鸡冠之上,与俄罗斯接壤,差点儿就到了最北边的漠河啦~~草原,牧马,牛羊群和夜里的星空(由于设备问题不能记录下来),这么多元素构成一幅伊犁牧场的画面。此外,呼伦贝尔还有很多不为人知的美丽,就让我用笔尖记录下这个美丽的草原端午节吧。 + +行程安排: + +![](http://upload-images.jianshu.io/upload_images/2539684-48592d1c01004fcb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +###关于行程: + +* 2017年05月30日07:35——13:00;上海浦东国际机场T2——海拉尔东山机场(呼伦贝尔机场) + +* 2017年06月03日16:40——23:50;海拉尔东山机场(呼伦贝尔机场)——哈尔滨机场——上海浦东国际机场T2 + +**注:** + +* 携程提前购买机票 +* 一般飞机航班提前一天下午选座,提前两小时到达机场,需要检查下有没有什么物品不能携带; +* 由于大家住的比较分散,所以我们选择了在机场附近定了个旅馆在那儿休息一晚,一来便于人员的集中,二来旅馆每天早上有班车送过去,十分钟就到啦; +* 回来的时候已经是魔都的十二点啦,由于浦东机场地理位置的特殊性,机场人员安排了公共巴士,大家回来的时候可以留意下:浦东守夜班车,到上海人民广场等站; +* 选座的话,因为自己第一次做飞机,我们乘坐的是吉祥航空的空中客车A320,所以早早我们根据自己的喜好选择了几个心仪的位置做备选; + + + +### 关于衣食住行 + +#### 衣服 + +呼伦贝尔是个昼夜温差比较大的城市,所以建议大家可以带个厚点的外套;另外,可能会去怕山啊什么的,晚上山上寒气重,建议带一件冲锋衣;虽然现在呼市的蚊虫没有那么多了,但是建议还是穿长裤保险点;另外,带上一些夏季常穿的短袖和薄开衫就OK啦。 + +#### 其他方面 + +可以关注我个人的公众号,或者简书微博私信我,我会把呼市最靠谱的导游介绍给各位~~ + +### 关于路线 + +细心的朋友可能会规划出自己每日的行程,我建议出来玩嘛,还是可以随性一点,所以我只想了下大致的路线,这个呼伦贝尔之行可以有南北环线两种游玩方式。北环线一般适合年轻的同志们,因为一路从草原到森林到湿地最后再回到草原到市区,沿途的风景还是很美的;南环线没有时间玩,不过估计自然景色要单一一点,有个阿尔山,可以看看山,游游湖什么的。具体行程安排请参考我思维导图里的内容吧 + +好啦,准备工作开始啦,让我们带着梦想和地图一起旅行吧,开始爆照~~ + +**Day00** + +我们飞到海拉尔的那个晚上由于担心旅途会累,所以下午也就没有安排行程,找了个酒店休息整顿下,导游全哥很给力,带着我们熟悉被三山一河拥抱着的海拉尔,给我们讲当地的文化,最终要的是带我们一起去吃锅茶还有手扒肉,俯瞰海拉尔的夜景。 +飞到海拉尔东山机场那一刻我们的内心是无比激动的,希望以最快的速度冲到室外,和这里的蓝天白云来个邂逅。见到导游全哥之后,我们径直走向我们的`星城酒店`。 + +海拉尔东山机场的路口铸造着一尊雕塑:成吉思汗的母亲和大妃(蒙古皇后的意思),看来蒙古人民对于成吉思汗的崇拜是发自内心的,真是个伟大的人物啊! +![](http://upload-images.jianshu.io/upload_images/2539684-fafe8932afbb2ee0.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +终于我们到了酒店啦,100元的标间,真的大啊! + +![](http://upload-images.jianshu.io/upload_images/2539684-2e4f42c2a62e7fbb.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +在酒店休整了会儿,晚上我们跟随全哥去了当地有名的一家内蒙古特色饭店,价格相当实惠,五个人不到两百。 + +![](http://upload-images.jianshu.io/upload_images/2539684-6321cd5ee19833b9.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +锅茶上啦~~,中间那锅偶,当然还有手把肉啦~~ +![](http://upload-images.jianshu.io/upload_images/2539684-5e48c72bff0512f8.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +美丽的海拉尔,美丽的夜~~ +![](http://upload-images.jianshu.io/upload_images/2539684-9a7deb9c0afac49c.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +**Day01** + +第一天感受到自然草原那种美,蓝天白云大草原。 + +一路向北,出发喽, +![](http://upload-images.jianshu.io/upload_images/2539684-29156ae24ccd08f6.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + + +像极了windows桌面的草原风光 +![](http://upload-images.jianshu.io/upload_images/2539684-43d7a852cd7d8564.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +草原上的冬瓜 +![](http://upload-images.jianshu.io/upload_images/2539684-eff640923a3235e9.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +我的两个小徒弟~~ + +shocky + +![](http://upload-images.jianshu.io/upload_images/2539684-d2dc307ba10b3ecf.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +JJ + +![](http://upload-images.jianshu.io/upload_images/2539684-d26bc7ee3cfbf15a.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +小黄花,可惜没到芍药盛开的时候,期待着未来带我家那位一起来看芍药~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-41d28cc03f0c81d8.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +配上全哥的哈达,感觉这个世界都是我的啦,哈哈~~ +![](http://upload-images.jianshu.io/upload_images/2539684-ab286c081e5e2b7f.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +草原虽美,但是还是身临其境来得更真切欧,再重温最后一张吧~~ +![](http://upload-images.jianshu.io/upload_images/2539684-aed65b9e7798bdb6.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +**Day02** + +据说电影《断片儿》会在今年的暑期档上映,挺别致一大房子,说丢就丢喽~~ +![](http://upload-images.jianshu.io/upload_images/2539684-37135252248690a0.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +小黄牛 +![](http://upload-images.jianshu.io/upload_images/2539684-07c81af6565d126f.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +拦路的羊群,好想下去抱只上车~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-76a10cfd49f71ee1.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +终于到达边境啦,第一个地方,七卡(第七道边防关卡),看到一方草地,一时激动,于是就有了后来的风筝事件~~,对的,就是它飞到了俄罗斯那边! +![](http://upload-images.jianshu.io/upload_images/2539684-19eaf66bd164b42f.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +被带到边防战士的训练场地,进行深度教育,边防无小事,一定记住啦~~ + +![](http://upload-images.jianshu.io/upload_images/2539684-adfa2defaf7ade13.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +被教导了接近四小时,下午两点多我们的边防战士终于给我们放行啦,然后就开始他们每天的训练,你们辛苦了,最可爱的人~~ + + +由于突发事件,我们不得不为了看日出直奔晚上的终点临江界碑那边去看日出 +两小徒弟望俄 +![](http://upload-images.jianshu.io/upload_images/2539684-e5bcee9e0bab8d91.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +帅气的冬瓜摄影~~ +![](http://upload-images.jianshu.io/upload_images/2539684-aa514e065f5c708d.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +由于天气的原因,很可惜没有看到日落,但是有火烧云也是挺美的 +![](http://upload-images.jianshu.io/upload_images/2539684-fd67440d01165db5.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +草原上的端午节,虽然迟了一天,但是撞蛋这个习俗我喜欢,毕竟我是最后一个碎的~~ +![](http://upload-images.jianshu.io/upload_images/2539684-15ad44d3db87b760.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +**Day03** + +前两天的一路向北,现在我们开始开往了南边的森林之旅~~ + +每年的3月15日~6月15日是草原上的防火期,所以进入林区都要拿到防火林和做好防火措施。 +![](http://upload-images.jianshu.io/upload_images/2539684-db5d04ebdec5964f.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +草原上的月牙弯,这在平原地区是比较少看到的,然后那块岩石据说就是传说中的鹰嘴岩,你们看出来了吗?反正凭借着我有限的想象力是看不出来~~ +![](http://upload-images.jianshu.io/upload_images/2539684-f533cc7605e3b69b.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +一直憧憬的白桦林,虽然没有去成,但是你会发现大兴安邻的白桦林区沿路还是蛮多的,找了块地儿拍了一张,还不错~~(据说白桦树代表着纯情欧) +![](http://upload-images.jianshu.io/upload_images/2539684-dbad74773a8810c5.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +路遇一个小河,一时激动,脱了鞋子就是玩了会,下游的观众们,你们坐稳了 +![](http://upload-images.jianshu.io/upload_images/2539684-80263f3d3c92d54e.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +东北铁锅炖,这味儿,还不错,不愧是个完美的六一~~ +![](http://upload-images.jianshu.io/upload_images/2539684-54c8b4ada5da1222.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +下午我们抵达了得耳布尔,去看驯鹿喽~~ +霸气的驯鹿王 +![](http://upload-images.jianshu.io/upload_images/2539684-c5416cd8e9908e49.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +美美的Shocky喂养小鹿 +![](http://upload-images.jianshu.io/upload_images/2539684-bec053d113dfc5e9.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +草原上用来收草的拖拉机,车上的那个冬瓜自动忽略吧 +![](http://upload-images.jianshu.io/upload_images/2539684-4ec027559ae00ba4.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +顺便提一句,一路“马蹄坑”,“搓板道”还是挺好玩的!!!贵在逗比青年欢乐多,一路上唱着小歌时间过得特别的快~~晚上到达黑山头,晚饭后还看到了篝火晚会,但是场面一度非常high,只录了个视频,这里就不上传啦,强烈建议可以去现场感受欧~~ + +**Day04** + +弘吉剌部草原,成吉思汗的妈妈和大妃过来的出生地。骑马走起~~ + +晚上就睡的这个 +![](http://upload-images.jianshu.io/upload_images/2539684-aec3caaf807b76e2.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +一家三口~~ +![](http://upload-images.jianshu.io/upload_images/2539684-a2aa3083dce0b6e4.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +准备出发喽 +![](http://upload-images.jianshu.io/upload_images/2539684-0d43ac897a6e7b93.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +遛马活着回来啦 +![](http://upload-images.jianshu.io/upload_images/2539684-fbb9e3caacda5c38.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +**Day05** + +在满洲里呆了一晚,我们路过呼伦湖直奔海拉尔,归途......下次再来一定要去那达慕大会 + + + +**Day05** + +呼伦湖(达来湖):海一样的湖水 + +返回海拉尔,给家人买点小礼品,回家喽 diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\345\270\246\344\275\240\345\216\273\346\227\205\350\241\214\347\254\254\344\270\200\351\233\206\357\274\232\346\274\253\346\255\245\344\272\221\345\215\227.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\345\270\246\344\275\240\345\216\273\346\227\205\350\241\214\347\254\254\344\270\200\351\233\206\357\274\232\346\274\253\346\255\245\344\272\221\345\215\227.md" new file mode 100644 index 0000000..be8c6f9 --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\345\270\246\344\275\240\345\216\273\346\227\205\350\241\214\347\254\254\344\270\200\351\233\206\357\274\232\346\274\253\346\255\245\344\272\221\345\215\227.md" @@ -0,0 +1,142 @@ +好久没好好写写博客了,这半年多完成了人生重要的几件大事,后面会慢慢回忆记录的。2018 年都到下半年了,时间过得真的快啊,感慨光阴如梭的日子里越发珍惜用笔尖记录生活的点点滴滴,如果去云南玩的朋友可以微我偶~~ + + +## 行程规划 + +规划时间:2018年02月19日-2018年02月26日 + + +[图片上传失败...(image-843dd4-1531733288478)] + +真实的 TimeLine: + +- 19号晚出发 +- 20 抵达大理古城 +- 21 洱海 +- 22 茶马古道 +- 23 丽江躺尸 +- 24 闲逛,下午去昆明,发热啦,SOS !!! +- 25 送回帝都喽~~ +- 26 我也回去喽~~ + + +这里叮嘱一下,早点订机票,早点订机票,早点订机票~~ + +### Day 1:在路上 + +我们计划是 2 月 19 日晚(大年初四)从老家出发,晚上八点多到的机场,硬是活生生地等到了两点多才到昆明,嗯,*8L9862* 了解一下,千万别坐这趟飞机: + +[图片上传失败...(image-84c88f-1531733288478)] + + +昨天晚上折腾到了四点多才休息,于是第二天一觉蒙头睡到了十点多,十一点半去大理的火车啊!!!一路那个赶(媳妇儿还没忘买份早饭,哈哈)在媳妇和我完美的配合下,我们行云流水般通过了一道道的检查,坐上位子没一会儿就启动了前往大理的里程。旅行开始啦~~ + + +在路上我们遇到了一对去往丽江的夫妇,看样子应该五十有余吧。机缘巧合正好分到了一个车厢,于是大家伙便聊了起来:阿姨和叔叔是第 N 次来的云南,但是他们聊一起去哪儿玩的时候总是十分打趣地说着对方,好不和谐。此行他们的主要目的是要去爬玉龙雪山,还要喂喂小海鸥,听着我们也是分外起劲啦。当然,也聊到了他们的人生经历,出于对他人隐私的保护,这里就不透露啦。当我再回过头来看这件事的时候觉得旅行的意义不仅仅是那些风景,还有路上形形色色的人们和他们的故事吧。就像这对老夫妇这般吧:有两个人的二人时光,在各自的专业领域也是棒棒的,最重要的是夫妇二人互相欣赏和性格上一快一慢,一个深沉一个活泼,感觉一路上多了很多快乐,也对旅途有了很多憧憬! + + +晚上七点多才赶到住处,就在大理古城边朵家客栈(洱海门春牛寺 3 号)那边,正好也可以逛逛古城的夜景,一进客栈很有那么点云南 style: + + +[图片上传失败...(image-91c2d6-1531733288478)] + +[图片上传失败...(image-c1bd58-1531733288478)] + +[图片上传失败...(image-7187ec-1531733288478)] + + +[图片上传失败...(image-25ad65-1531733288478)] + + +[图片上传失败...(image-b7e406-1531733288478)] + + +[图片上传失败...(image-f34c7d-1531733288478)] + +最后在来张媳妇儿的美照吧,手动捂眼: + + [图片上传失败...(image-900c84-1531733288478)] + + 大理古城的城墙还是很有气势的: + + [图片上传失败...(image-98665c-1531733288478)] + + 去转悠转悠喽,你们以为我是来玩的,其实我是来吃的~~ + + [图片上传失败...(image-45077f-1531733288478)] + + + [图片上传失败...(image-42bbaa-1531733288478)] + + + +当然也有喝的~~ + [图片上传失败...(image-de591d-1531733288478)] + +嗝,嗝,嗝......饱了。今天收工,明日再战吧。 + [图片上传失败...(image-22830d-1531733288478)] + + + +## Day2 洱海 + + + 来到大理的人都听过:上关风,下关花。苍山雪,洱海月。一大早,六点多,媳妇儿就早早起来画了个简妆,观众朋友们坐稳了,媳妇儿要摆拍了!!!早上我们跑路边很容易就租下了当地一位师傅的车,环洱海行正式开始喽~~ + + [图片上传失败...(image-31ba7b-1531733288478)] + + + [图片上传失败...(image-5aa758-1531733288478)] + + [图片上传失败...(image-c062eb-1531733288478)] + + [图片上传失败...(image-d86591-1531733288478)] + + [图片上传失败...(image-c62c4a-1531733288478)] + + + [图片上传失败...(image-7ec10b-1531733288478)] + + 车往前开是一个伸出去的浅滩: + [图片上传失败...(image-90bfba-1531733288478)] + [图片上传失败...(image-220b71-1531733288478)] + [图片上传失败...(image-aec8e0-1531733288478)] + + [图片上传失败...(image-3b6089-1531733288478)] + + [图片上传失败...(image-9040f2-1531733288478)] + + + [图片上传失败...(image-98ef1b-1531733288478)] + + 原来打算是去 罗平 看油菜花的,正好沿途大片的油菜花,然我们过了把瘾: + + + + +## Day3 茶马古道+四方街 + + + + +[图片上传失败...(image-82cf61-1531733288478)] + + +## Day4 丽江躺尸 + +## Day5 闲逛,下午去昆明,发热啦,SOS !!! + + + +## Day6 送回帝都喽~~ + + + + +## Day7 我也回去喽~~ + + + + + + diff --git "a/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\347\216\251\350\275\254\344\277\241\347\224\250\345\215\241.md" "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\347\216\251\350\275\254\344\277\241\347\224\250\345\215\241.md" new file mode 100644 index 0000000..493883d --- /dev/null +++ "b/\347\224\237\346\264\273\345\220\257\347\244\272\345\275\225/\350\241\214\350\265\260\345\220\203\350\264\247\347\232\204\346\227\245\345\270\270/\347\216\251\350\275\254\344\277\241\347\224\250\345\215\241.md" @@ -0,0 +1,94 @@ +> 前一段时间在神州租车平台上租了辆小汽车自驾游,当时用的是朋友的信用卡缴纳的预授权押金。来魔都都快两年了,一直也没使用过信用卡,自认为是个消费欲望不那么强的人,但是为了掌握更多的金融工具,所以决定还是办张卡体验体验吧,毕竟租车还可以用到嘛(PS:支付宝芝麻信用积分超过650也是可以的欧)。 + +> 下面一起来思考几个问题吧: + +* Q1 :信用卡是什么? +* Q2 :有哪些类型信用卡,我们应该如何选择? +* Q3 : 有哪些途径可以获得实用进阶技巧? +* Q4 :使用信用卡过程中应注意哪些问题? + +> A1 :实际上关于信用卡的定义,我的理解是个人获取短期无息贷款的一种金融工具。 + +* 短期: 一般为50天,50天指的是还款日和账单日之间的时间差。这里我们需要理解两个概念:账单日和还款日 + * 账单日:生成上个月消费账单的日期 + * 还款日:无息的最后期限 + +> eg: 假如每个月的1号是你的账单日,20号是你的还款日。那么也就是说,如果你2.1号拿到的账单是1.1~2.1对应的消费记录,这笔金额你要在2.20还上就不会产生利息,这里需要注意的是,如果我们在2.1~2.20生成的消费记录是可以等到3.20还上的,没必要在2.20号还上。 + +* 无息: 这里的无息是刷卡消费无利息,如果提现那可是要按天计息的欧。 + +> eg: 常用的做法是能使用信用卡的地方绝对不适用现金。假如我们一年消费了五万元,如果全部使用信用卡消费,并且未产生任何逾期还款的情况,那么这五万元我们可以利用50天的免息时间窗口去投资一些货币基金。 + +> 看完信用卡的定义,相信大家渐渐也就知道什么样的人适合使用信用卡: + +* 有着明确的消费目标,可以控制自己的消费欲望的淫; +* 对信用卡的免息期有兴趣的人,不然信用卡和借记卡也没多大区别啦 +* 体验一些信用卡给你的生活的便利; + +>A2 : 信用卡品牌大比拼 + +>如果你是和我一样自认为符合上述我说的几个条件,那咋们就一起来看看如何做才能使得利益最大化吧。到这里,不知道有没有童鞋会有一个疑问:银行的人傻嘛?平白无故提供这些好处给我们?(PS:并不是本人心机婊,只是任何一项商业活动如果没有利益的支持,鄙人认为都是长久不了的) + +>要想明白这个问题,那我们就要看看咋们的银行从中可以获取哪些好处: +> + +* 显性收益:银行通过信用卡获得的直接盈利 + + * 利息收入:即信用卡透支(PS:还款超期或者提现)发卡行会根据透支金额收取的利息,招商银行信用卡的就有日息万分之五欧,大家可要注意啦,具体可以参考各大发卡行的官方信息; + + * 年费收入:不过现在激烈的信用卡比拼,普卡和金卡(后面会有解释)如果每年刷若干次即可免除年费; + + * 刷卡回佣收入:即客户如果使用信用卡在商家消费,那么商家也要向银行缴纳交易额一定比例的费用,这种费率一般在亚洲国家大概是1.5%~2%,发达国家一般在2%左右; + + +* 隐性收益: + + * 提升开户概率:信用卡结算账户必须是发卡行账户,信用卡的产生极大提升了客户在发卡行开户办理借记卡的意愿; + + * 资金归集:如果你是个精明的商家,我想在购买商品的时候肯定能刷信用卡就刷信用卡,这么一来,发卡行也就避免了资金流入其他银行的风险。银行是如何监管商家防止提现这个问题值得我们思考; + + * 提供用户粘性,更加全面的服务会让用户对信用卡产生依赖,也就会对发卡行的业务有更大的支持; + + +> 怎么样,有没有被震惊到!咋们的银行家们还是很机智的吧~~ + +> 好了,继续我们的问题,这么多信用卡产品我们该何去何从,先来看看我们都有哪些选择吧: +> +>信用卡分为单币种和多币种信用卡,单币种即为银联标志信用卡;双币种即为VISA/MaterCard+银联标志; + +* 按照发卡组织分类 + * 国外: + * 美国VISA,AE(America Express) + * 欧洲MasterCard + * 日本JCB(Japan Credit Bureau) + * 国内: + * 招商银行信用卡:吃喝玩乐; + * 中信银行信用卡:线上购物; + * 交通银行信用卡:沃尔玛购物; + * 四大行(中、农、工、建)信用卡:爱用不用; +* 按照卡片的等级 + * 普通卡(金卡):信用额度一般在5k~2w,一般不需要年费; + * 白金卡:信用额度一般在1w~5w,一般需要缴纳年费; + * 百夫长卡:信用额度无上限; + +>建议:国内消费用招行或者中信信用卡,优惠多多;如果偶尔海淘或者出国旅游可以考虑办理双币或者多币信用卡,如果对币种转换比较感兴趣的童鞋可以参考下: + +A3 :授人以渔 + +> 本人目前对于信用卡的使用也处于探索阶段,在网上了解到几个据说还不错的论坛和公众号,这里分享给大家一下: + +* 飞客论坛,积分换航空里程,酒店优惠 +* 何乐不为(公众号) +* 招商掌上银行 + +如果大家发现有什么比较好的刷卡小窍门,欢迎分享哈~~ + +A4 :注意事项 + +* 安全刷卡,防止盗刷 + * 确保pos机的安全性,防止被盗刷 + * 一般信用卡分为条形卡和芯片卡,芯片卡相对来说较为安全,具体原理请参见: + + * 密码支付或者无密码签字确认 + * 如果发生了盗刷,请参考: +* 按期还款,避免缴纳利息或产生信用污点(PS:影响后期个人信贷)