2015年即将结束,那这一年我到底都干嘛了?

从去年(2014)的12月份就开始暗无天日的加班忙碌,直到今天下半年才好了很多,眼看这就要要2016了,
也该回顾一下2015年的工作了。

AutoLayout

2015年12月,加入了一个新团队-嗨购,仍然是负责iOS项目开发,人员上加上我两人,版本处于0.0.1版本。

当时接收项目后,发现这个项目的界面采用storyboard+autolayout开发,另外那个哥们去AutoLayout的使用也处于学习阶段,所以遇到的主要问题就是修改界面的Bug。由于我之前没有使用过storyboard和autolayout,再加上UI设计师对界面的细节控制要求很高,所以有些应付不了了。在另一个哥们儿的辅助下,将就着开发了0.1.0版本(仍采用storyboard+autolayout),其实这个过程非常苦恼,“是好好学习一下AutoLayout,然后在项目里继续使用AutoLayout呢?” 还是 “采用自己熟悉的纯代码方式来编写?”

最后,综合考虑项目的进度压力、质量的可控性、学习的时间成本后,决定还是暂时放弃Storyboard+AutoLayout,转而使用保守的纯代码方式开发。

在0.1.0版本后,恰逢元旦,另外那个哥们儿休假半个月左右,回家结婚去了。我就逮着这个时机,在项目0.2.0时把项目界面实现方式、网络层、数据库等进行了大重构。

现在相对不忙碌了,计划补补 Todo: AutoLayout以及 Todo: Masonry.

CocoaPods

其实,上面提到AutoLayout被我弃用后,就感觉比较反人类了,然后我仍然还要反人类一次。

嗨购项目在0.2.0及之前,对第三方库的依赖方式是通过CocoaPods来管理的,但是为什么这之后我把CocoaPods去掉了呢?这就不得不提到Jenkins(我会在后面聊聊Jenkins)和CocoaPods的代码管理。

本来采用Jenkins对iOS项目打包发布是一件很NB的事情,但是对通过CocoaPods管理第三库的iOS项目打包是一件非常痛苦的事情。说起痛苦,主要是集中在Jenkins上的配置以及项目本身的一些配置,如:Jenkins里build目录的指定、项目中schema的share等问题。这一系列问题我都有记录,这里就不细说了。虽然痛若,但是经过两三天的努力,可以通过Jenkins打包CocoaPods管理的项目。

感觉这一点还好,但是,没有想到最后让我放弃CocoaPods的原因尽是它的优点:无感知管理项目的第三方库依赖。 因为,

  1. 第三方库难免有Bug,所以我也经常修改这些源码里Bug,修改地方多了后也就忘了修改过哪些地方了。然后有一天我pod update了一下,如果本地的源码修改没有了,我得从项目的SVN上对比着再被回来,当时非常恼怒。。。
  2. 由于天国的各种墙,安装cocoapods时我修改了gem源为https://rubygems.org/ . 但是,没有想到在某几天pod update和pod install根本用不了,导致严重影响了我预期的开发进度。

所以,我又选择了保守的方案,删除cocoapods,转而采用源代码依赖的方式,这样一来Jenkins打包也简化了。

现在回想,关于那段配置Jenkins打包CocoaPods项目的经历非常可贵,那是对意志的磨砺。

Jenkins

刚到嗨购的时候,发布安装包的方式是archive ipa文件并上传到fir.im进行发布,以供其它人员进行安装试用或测试。随着项目的推进,期望把这种Inhouse Release的方式自动化,所以引入之前在搜狐时就一直采用的Jenkins进行自动打包发布。

关于Jenkins的安装我简要提一下,主要还是回顾一下使用过程中遇到的问题

Jenkins环境

  • 下载Jenkins.war,然后启动java -jar jenkins.war
  • Jenkins的相关目录
    • /Applications/Jenkins – which is having the jenkins.war alone.
    • /Users/Shared/Jenkins – with all the plugins, war, jobs, usercontent, etc.
    • /Users/Shared/Jenkins/Home – JENKINS_HOME
    • /Library/LaunchDaemons/org.jenkins-ci.plist – Jenkins自启动文件
    • /Library/Application Support/Jenkins/jenkins-runner.sh – 自启动shell脚本

Jenkins+CocoaPods项目

七牛空间

打包状态邮件通知

  • 在Jenkins里安装Mail Notification Plugin
  • 以126邮件为例,配置SMPT服务器及端口
  • 注意:Jenkins管理员邮箱要与发送邮件的账号一致,不然会发送邮件失败。
  • 在具体项目中,可以配置相应的收件人、trigger等。

Redmine

在2008年刚参加工作的时候,老东家就有搭建Redmine用于项目管理、issue跟踪、Wiki书写。在搜狐的时候,是由服务器同学搭建的trac来写wiki。由于刚到嗨购团队的时候,接口文件都是服务器同学通过word文档来记录并分发给相关开发人员。我觉得这样非常的不方便,不但不方便更改,也不便于分发。所以,我偷摸地在公司内网搭建了Redmine,给相关技术人员分发了账号和权限,从而以后“妈妈再也不用担心我们的接口文档了”

Todo: 也有朋友在git上写项目的wiki,改天一定要试下

到目前为止,我采用过三种搭建Redmine的方法:

  1. 纯手工24K金方式打造Apache+MySQL+ROR
    • 完全手工地安装Apache、MySQL、RubyOnRails环境
    • 此法太虐心,会遇到各种东西没有安装,非常煎熬
  2. 采用BitNami一键式安装Redmine
    • 采用BitNami安装的Redmine,在一个单独的目录下安装了Apache、MySQL等环境。
    • BitNami安装Redmine后,操作系统里安装的Apache、MySQL的环境变量被修改了
    • 为了安装一个Redmine,把操作系统里本身配置的一些环境变量给搞乱,不好不好。
  3. 布署Redmine项目源码
    • Redmine本身是一个ROR项目,再加上09年左右的时候做过一个ROR项目,所以此法可试。
    • 下载Redmine源码: http://www.redmine.org/projects/redmine/wiki/Download
    • 解压Redmine到某个目录
    • 配置数据库连接,并创建或导入Redmine数据库表
    • 在Redmine项目运行sudo ruby bin/rails server webrick -e production来启动项目,从而抛弃Apache
    • 此法最好,对于我来说最简单直接,也符合程序思维(建立数据库、配置数据连接、部署项目源码并运行)

Push Notification Simulator

早在搜狐的时候,就遇到推送调试非常不方便的问题。试用了一些方案,但最终于我还是搭建了一套更好的方案,并运用到了搜狐新闻和嗨购项目中。

最开始的方案是采用PushMeBaby,这是一个Mac版的源代码项目,把项目需要收到推送的Dev或Release的push certification导入项目,并设置好相应的宏就可以运行一个Mac App,然后输入相应手机的Token就可以发推送了。

随着我对PushMeBaby的使用,非常想把推送模拟发送机制进行改进,因为我发现它有几个弊端:

  • PushMeBaby的环境建立不方便,因为它是一个Mac环境,需要在开发人员自己的电脑各位持有一位源码并运行。完全没有必要。
  • PushMeBaby需要配置Push Certificate,这一点估计会难倒不少对iOS开发的PrivateKey、Certificate、Provisioning Profile等授权概念比较混淆的人。门槛有点高。
  • PushMeBaby只能给某一个PushCertificate发推送。非常不灵活。
  • PushMeBaby只能输入Token。非常不灵活。
  • PushMeBaby的推送内容修改只能修改源码。非常不方便。

终于,在开发搜狐新闻的推送打开Navtive页面这一功能时,由于要反复调试推送内容,所以我决定自己搭一个即保留PushMeBaby已有功能,也可以解决以上问题的推送环境,我给它起了个名字-Push Notification Simulator。

Push Notification Simulator

  • 找一台做CI的服务器(肯定是Mac嘛)
  • Mac自带Apache+PHP环境。大环境就这么轻松的满足了。
  • 开发一个简单的H5页面作为首页
    • 具备推送内容的配置功能
    • 具备Push Certificate的选择功能,只需要由一人来上传各Push Certificate即可。
    • 具备Token的选择和手动输入功能
  • 开发一个可以把H5页可编辑内容作为参数来发送推送的PHP文件
  • 运行,测试,好使。

在一个团队里,这是一个非常省心的方案。

Crashlytics & App Log

App crash给人一种非常重大的事件的感觉,所以早期遇到App crash总是有些菊花一紧的感觉。倒不是说害怕App crash,而是因为环境的限制或者低概念crash而查不到App crash的原因,尤其是在线上版本crash的时候。

目前,友盟、TalkingData等第三方统计平台也都可以借助其SDK上报Crash Log了,但毕竟它们的核心是做统计,其实Crash Log分析远远比不上Crashlytics.

Crashlytics

最开始,Crashlytics作为一个独立项目存在,后被Twitter收购作为Fabric下的一个子项目了。

现在,集成Crashlytics挺方便的:

  • 注册 Fabric 账号
  • 安装 Fabric 客户端
  • 按 Fabric 客户端的指引导入 Fabric 和 Crashlitics Framework
  • 在 AppDelegate 中 import Crashlytics,并初始化 SDK
  • 添加 RunScript。我会判断不是 Debug Configuration 时才上报 Crash Log。Debug Configuration 下的crash 一般都好解决,因为这时还处于开发环境;而 Release 或 Release-Inhouse 已处于 Distribution 环境了。

在 Crashlytics 网站后台可以很方便的分App、分版本的查看不同时间段的 Crash Log,并且还可以在 App 内使用Crashlytics 来输出特定的日志或标记,以便在Crashlytics 网站后台查看 Crash Log 时可以更好的跟踪 Crash 原因。

App Log

Crashlytics偏技术,重点在于解决Crash。

但是,App发版以后,可能仍存在一些逻辑漏洞没有被测试出来,比如页面上的数据展现效果很奇怪、页面间某些衔接逻辑有些混乱了,类似的这一类问题属于业务问题。当在App发版后,发生业务Bug且开发人员很难通过Review自己的代码来查找Bug时,就需要借助服务器日志或App日志。

我在嗨购项目里尝试写一个上传代码日志的逻辑,原理很简单:

  • 把App内的NSLog输出的日志重定向输出到一个文件并保存起来
  • 每次App EnterBackground时,启动一个BackgroundTask线程检查日志文件是否大于1M了,如果大于1M则上传到服务器,上传成功则删除日志文件
  • 此日志文件不纳入App缓存管理的范畴

另看,这一小小的机制,在后来辅助修改业务Bug起到了大作用。

Crashlytics & App Log已作为我经手的项目的常规架构支持。

嗨购项目架构演进概要

技术架构,这一词给我的印象一直是既清晰又模糊,并不断地刷新它在我心目中的概念。

何谓清晰

从08年工作至今,同事经常会聊天技术架构、网络上经常看到技术架构各种概念,有些人或网站能说得头头是道,有些却说个大概,总之,耳濡目染地能清晰地感受到技术架构是一件很NB的事儿。

何谓模糊

从刚工作几年时,在内心就萌芽了架构师的梦想,但是真正在向这个方向努力的时候我却在问自己一个问题:“什么是架构?什么是架构师?好的架构师如何来做好的架构?”,所以,架构一词就像隐形的空气里,明明能感觉到离你很近,但是却摸不着看不见,有一种无从下手的感觉。

学习之路

这几年工作下来,通过自我的学习,与同事、朋友的交流,工作的实践,以及阶段性的总结,有了自己一个循序渐进地对架构的理角:

重构 - 从在搜狐工作开始,我开始培养自己的程序设计能力,以及对程序结构的理解。那时,我对架构的理解是从代码重构过渡过去的,即,好的架构就是代码整洁、逻辑清晰。所以,那时我必须让自己按这个要求去做,以致于现在都有了代码洁癖之类的强迫证。

抽像模块 - 随着开发工作的进展,我发现很多功能、逻辑模块是可以复用地,但是好像很多人愿意Copy & Paste代码,因为这样写代码很快。其实,到最后得不偿失,你会发现最后代码失控了、到处打补丁、一个很小的Bug都要费九牛二虎之力才能Fix。所以,我必须多花一两个小时或一两天的时间把我觉得应该重构、抽像、解耦的模块进行修改,然后再在此基础上做相应的业务。

分层、职责专一 - 后来,打算从搜狐离职了,去腾讯面试,我现在都还清楚地记得人家问了我一个问题:“搜狐新闻App iOS版里有没有你们自己设计的一些架构?”,我当然说有啊,但是经过我描述我所理解地以及在搜狐新闻App中看到的所谓的架构后,人家说他想要的不是这个。

我当时大致是这样描述搜狐新闻App的架构的:UIViewController作为控制器发起网络请求,网络请求由Service负责,Service另还负责网络请求获取到的JSON数据解析,最后把解析好的数据返回给上层。其实,现在再回过头去Review之前的代码,还有诸多设计问题。

我例举此经历其实是为了举出这个反例。因为后来我对架构有了新的理解-分层设计以及专一职责这两大概念。经过,后来我的反思、反问自己:“这个哥们儿期望的架构应该是怎样的?什么叫自己设计过的架构?”,我有了对架构新的理解:App架构(以及别的领域)不是把诸多纯粹的代码模块、第三方库、数据等像堆积木一样简单直接地拼凑而成(尽管可能它们之间是独立的、解耦的、易用的),也不是简单地套用被大家都说烂了的MVC(就如上面我在腾讯面试时陈述的那样)。后来,我又review了之前搜狐新闻App的代码,确实发现了诸多问题,如:(只列举一二,开始自己扇自己脸了)

  • 过渡依赖TT框架
  • View里居然发起网络请求
  • 过渡依赖数据库,以致数据库迁移方案同样不简单
  • 数据库操作需要写SQL(对于之前不会SQL的同事来说是灭顶之灾,哈哈哈)
  • 等等,我狠狠的扇了自己几下,算了不说了。
  • BTW:我这是过于自惭形秽了,其搜狐新闻客户端App里很多我现在在用以及非常受益的思考和代码。

在我加入嗨购后,我仍然在反思那哥们儿的问题,同时更激进地去查阅别人对App架构的观点或理解,慢慢地我有一了一套我自己设计的基于Layered Architecture的iOS App架构:(简单描述如下,而后我会另起一文来阐述我的理解-待续

  • View Layer
  • URLPath Based Controller Layer
  • DataCenter Layer
  • APIManager Layer
  • PersistenceManager Layer

同时,从跨业务、跨App的角度,我把这套架构设计又划分为:

  • Business Layer -开发人员主要关心的
  • Business Support -开发人员大部分不用关心的
  • Tech Support -开发人员完全不用关心的

以上两种分层的方式都涉及到里面非常多的技术点和坑,我还是另起一文来阐述我的理解-待续

跨业务/跨App

目前,在嗨购团队开发出来三个App基本都采用了上面我设计的这种架构,而且对于某些类我已配置了Xcode代码模板,用起来非常方便。这套架构设计最大的益处就是开发人员可以把主要的精力集中在业务层面,对于技术以及可复用的业务层面代码基本上都已经被抽离出来了。当然,它仍然还在改进中、细化中,比如:View层的事件驱动,持久层的泛化等等。

可约定地、可传承地、固定且逐渐演进的开发模式

从在搜狐期间工作到现在来到创业团队奋斗,我一直在不断的摸索、学习、反问自己什么是架构,什么是好的架构,没有人来手把手的教你、告诉你从哪儿学习、该怎么做,不过没有关系,我想说的是即使别人在你的头上开个孔,然后把他知道的一切灌进去也是无用的,因为很多细节需要积累、需要自己反思和总结。只有你经过摸索、学习、总结后,尽管自己的设计、观点是错的,但是在与别人切磋、讨论时很容易理解别人的观点,以及辩证地看待优劣、好坏。

所以,如果我觉得我的程序或架构设计成果还行,那么我会第一时间把它分享给我的队友,首先是阐述我的思想、然后说服他们接受我的架构,如果说服了那么在思想上就达成一致了,那么在以后的执行层面就不是问题了,队友们就可以帮我传递这些设计并且会即时反馈缺点以便我修正。

现在,我们队员间相互Review代码时,只需关注在业务层面以及一些新增的有意义的技术点,而不是纠结在队员对技术观点、代码风格等等无休止的争论上。

最后,我建议:作为一名软件技术人员,我们要不断的探索、学习、实践,最后一定不要忘了总结,把自己的切身体会和理解抽象成一套属于自己原创的有体系结构的技术方法论。

所以,越学习越发现自己的知识很贫瘠,我仍需要大量的学习,也正在学习中…..Fighting!