2016

2015这一年,印象最深的就是去(浪)了很多地方。

  • 3月先是胆战心惊地去了次Tijuana续签签证,胆战心惊主要是被宣传了很多那里的都市传说,索性一切还算顺利。
  • 4月底5月初去了北九州(博多长崎福冈熊本和由布院),来到了拉面的故乡博多,拉面吃得是再也不要吃了,然后在东京玩了几天。
  • 8月初又去了次Tijuana,为什么呢?因为我护照丢了。。。去洛杉矶中国领事馆重新办了新护照(花了3个礼拜,呵呵)。但是因为下个月要去德国西班牙没有有效美国签证stamp是回不来的,也没有补办签证这一说只能重新办,所以就又去了Tijuana办签证,谁叫那是离我最近的美国大使馆呢。还是走正常的流程,跟签证官说了情况后,她叫我不要再弄丢了就通过了。
  • 8月末去了次纽约,主要是因为DroidCon NYC这个Android程序员的会,票价相比别的convention实在是便宜太多就去了。看了本以为很文艺会看不懂其实很十三会笑全场的The Book of Mormon。
  • 9月下半月就去了德国跟西班牙,出发前2天刚刚拿到的签证。一共只待了10天,德国就在法兰克福周边玩玩,科隆大教堂确实比较震撼,租了两天车开了下没有限速的路,去了逼格很高的徕卡总部。尝了很多好吃的,猪肘啦啤酒啦大香肠啦。还有两只超可爱的猫Joy和Lingling!西班牙就去了巴塞罗那,终于看了一场巴萨的球,可惜梅西4分钟就受伤离场,而且那个时候我还在巴萨纪念品店里买买买,就什么也没看到。巴塞罗那印象最深的就是好吃的东西很多,海鲜饭tapas还有Jamón ibérico!
  • 11月回了趟国。中途先去了日本,带爸妈在东京京都大阪走马观花玩了一周,感觉就是时间像飞一样刹那间就到了要回上海的那天。在上海的两周,基本天天都在下雨,空气倒是蛮好的,有几天特别冷,还问我爸借了衣服穿。面基了很多新老朋友,相谈甚欢,关键,吃了很多好吃的。
  • 圣诞节的时候去了三藩,其实15年三藩大概去了三四次了,这次是去Lake Tahoe滑雪。以前在波士顿的时候没有机会,今年趁着加州大雪,尝试了skiing,第一天下来是走不动路的。

工作上呢,公司里发生了很多狗血的事情,目测2016年会继续,我也就静观其变了。这一年算是全职写Java/Android的一年,本来很讨厌这两样东西的,但这一整年下来慢慢地发现太多有意思的东西可以琢磨,现在想叫我换别的方向我还不太愿意了。

这一年有点遗憾的就是我的Gibson摸得越来越少了,希望明年能够重新拾起来,不要到时候连Green Day都不会弹了(。

2016年,想做的事情还有很多,慢慢来了就。

2014年度H1B报税

又到了一年报税的时候,本着早报税早省(拿)心(钱)的原则,一拿到W2开始着手报了。

2014年度,一整年都是H1B,就不再享受那个适用于学生的中美$5000 Treaty,但是已经是税法意义上的居民,所以就可以用居民friendly诸如Turbo Tax/H&R Block的报税软件来节省很多时间。看了the wirecutter上针对报税软件的评测后,决定就用他们认为最好的:Turbo Tax

像我这样就W2跟一些简单的1099-INT, 1099-G的话,直接用Turbo Tax免费版本就可以了。免费版本其实就是帮你填写1040EZ/1040A,如果更复杂些的,就得额外付费买更高级版本了。另外免费版本仅仅包括报联邦税,如果州税要一起报的话,是额外要花钱的。因为我在加州,网上报州税的系统超级简单,就不用额外再花钱了。只需在联邦税填完后跳过州税,如果不小心使用turbo tax报了州税的话,可以在overview的时候去掉。Turbo Tax用起来还是非常简单的,如果你的W2上有control number的话,直接就可以导入了,无需再手动输入各种数字。然后按着向导,把州税的1099-G,利息税的1099-INT依次填上就可以了。信息都填完之后,会直接显示会退多少税(一般是按standard deduction来的)。其实这个数字各大报税软件基本都是一样的。如果觉得不满意数字,就得用高级版本来找是不是有别的项目可以退税。不过个人觉得如果需要用上高级版本的话,还不如找个会计师报了。

今年报税就是这样。我自己非常“不幸”,兴冲冲地去报税,最后不仅没有拿到退税,还要补交税。:(

Disclaimer: I do not provide tax, legal or accounting advice. This material has been prepared for informational purposes only, and is not intended to provide, and should not be relied on for, tax, legal or accounting advice. You should consult your own tax, legal and accounting advisors before engaging in any transaction.

Android JDK 7签名问题

前一阵要发布一个新Android版本,在发布的前一天晚上,QA突然跟我汇报说无法在一些Android设备上安装apk。看了下logcat说是INSTALL_PARSE_FAILED_NO_CERTIFICATES

百思不得其解中,想起前不久刚在Jenkins上把Java 6升级到Java 7(终于!)来编译Android app。Google了一番,发觉原来是用来给apk签名的jarsigner在Java 7中默认使用SHA-256和SHA256withRSA算法,而Android还没有完全支持,需要在用jarsigner的时候指定MD5withRSA为签名算法以及SHA1为摘要算法即可。比如如果是Maven作为build system的,在maven-jarsigner-plugin中的arguments节点里设置就行了。

<arguments>
    <argument>-sigalg</argument><argument>MD5withRSA</argument>
    <argument>-digestalg</argument><argument>SHA1</argument>
</arguments>

2015

2014年就这样过去了。刚刚去机场把lulu送回东京,也没什么心情写总结。心神不宁地网上乱转,都是上海跨年踩踏的消息。

这一年换了个工作,算是一个比较大且开心的事情。出远门玩了几个地方,东京魔都阿拉斯加还有大峡谷什么的。花了5个月看了本Steven Pinker的大部头「The Better Angels of Our Nature」,还是云里雾里的。今年演出明显看得少了,财务问题好像略微改善了些。还有些别的,其实也挺重要的,但还是留给自己好了。

2015年,也没有什么特别想在这里说的,但感觉要做的事情还是很多的,一件一件慢慢地来吧。

在Android项目中启用ProGuard

最近不得不面对Android 65k method limit问题,所以启用ProGuard来除去一些没有被调用到的方法来降低被引用的方法总数(referenced method count)是一个不错的暂时的解决办法。

启用ProGuard
如何启用其实非常简单。如果项目是用Maven作为build system的,在app项目的pom文件里的android maven插件的节点下加入:

<proguard>
    <skip>false<skip>
    <config>proguard-project.txt</config>
</proguard>

proguard-project.txt即是配置文件,待会儿会详细讲。如果是Gradle的话,可以在buildTypes里加入1

runProguard true
proguardFiles file('proguard-project.txt')

启用后,每次build一般都会生成四份文件,dump.txt/mapping.txt/seeds.txt以及usage.txt。

  • dump.txt:包含了最终在.apk文件里的类结构
  • mapping.txt:如果启用了混淆代码的话(默认是开启的),这个文件里会列出原来的类/方法/成员的名字与混淆后的对应
  • seeds.txt:列出了没有混淆的类及其成员
  • usage.txt:列出了那些被去除的代码

配置ProGuard
在Android SDK的tools/proguard文件夹下有两个默认的配置文件,proguard-android-optimize.txt和proguard-android.txt。从文件名中可以看出一个是启用了优化另一个则是普通的配置文件。按需选择一个对一般的app来讲就足够用了。不过如果是因为64k问题而选择proguard的话,肯定也是个不一般的app,所以需要根据自己app的情况添加一些规则。这里有完整的ProGuard用法。那接下来说下我自己添加的几条规则供参考:

  • Warning: can’t find superclass/interface/referenced class:一般来讲,这是启用proguard最先会遇到的警告。这个警告说的是无法找到父类/借口/被引用的类,出现这个有可能是真的所需要的类没有被找到,需要指定包含这些类的jar的所在位置,但对于Android来说,很多情况下只需要使用-dontwarn规则来关闭这些警告就可以,因为在Android的编译过程中会自动指定所引用的jar。
  • -dontobfuscate:混淆代码后会使debug非常困难。即是是发布的版本,如果使用Crashlytics,最后收到的crash报告也是混淆后的,还是会使debug变得非常困难。虽然可以用retrace脚本来转换混淆后的stack trace,但是这样多做一步并不是很值得。不过如果你的app因为安全原因必须要混淆代码,那也只有这条路可以走了。
  • Automated Instrumentation Testing:如果你的项目里有Instrumentation测试,需要加入-keep public class * extends android.app.Application这样一条规则。因为每个测试项目会自动生成一个android.app.Application类,而ProGuard则认为这些Application类没有在任何地方被调用就自动剔除了,所以测试就没法在ci服务器上运行。这点我当时花了一天时间才最后解决的。:-(
  • Play Services:这个庞大的库本身就占用了大概两三万的方法数,虽然这个库在Android开发中算是必须的,但其实大部分都未必用得到,于是就造成了浪费。Google对于这个库有它自己推荐的规则,直接添加进来就可以。好消息是Play Services下一个版本6.5将会模块化。
  • Reflection:用到reflection的地方也是需要添加规则来保留这些类的。

Android 65k
现在我们来吐槽下这个65k问题Davlik是Android的运行时,其指令里有一个method reference index的变量,就是Davlik里最多能调用的方法数量。Android从出生到现在这个变量的大小从来没有改变过,就是16 bit,也就是令人发指的65536,从此,臭名昭著的65k限制由此而生。除了使用ProGuard外,还有一种比较流行的办法是multi-dex。

Ambitious New Android Developer

Multi-dex
随着app体积越来越大,即使用了ProGuard也还是会导致引用的方法数超过65536,于是有人就想到那我就编译多个dex文件,其中一个是主要的dex文件,然后在运行时再载入其他所需的dex。在Android新的运行时ART发布之前,这是非常繁琐低效的。随着ART的发布,multi-dex有了原生的支持,多个dex文件会在app安装的时候被编译成一个.oat文件让ART来执行,所以也不需要在运行时才载入所需的dex。而在ART之前的版本,Google也发布了支持multidex的库。详细的设置可以看这里:Configuring Your App for Multidex with Gradle。但这个归根到底还是一个hack,而且还是有很多限制,比如不怎么支持Android 4.0之前的版本,极大地增加了编译时间等等。

虽然看上去65k问题的实质很简单,但既然Google这么多年都没有能够修复,或许这真的是一个很难搞的bug。但话又说回来,随着移动领域越发地火热以及迅速地发展,不很好地解决这个问题实在很说不过去。


  1. Gradle指定ProGuard配置文件的时候还有类似getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'这样的写法,直接从SDK里找到默认的配置文件再与自己项目特定的配置文件整合在一起,这点是Android Maven插件还无法做到的。

如何在终端下使用Swift

在安装完Xcode 6 Beta之后,首先先把Xcode的Developer tools切换到Xcode 6下。

sudo xcode-select --switch /Applications/Xcode6-Beta.app/Contents/Developer

然后在iTerm/Terminal下输入xcrun swift即可!

Swift初探

在前几天的WWDC上,Apple宣布了Swift这个新的编程语言。花了一点时间读了点文档玩了会儿,总结下一些值得一提的地方。因为也是刚刚接触,如果哪里有错,还请悉心指出。

  • 类型推导:既然Swift作为编译时静态的语言,大部分时候就可以由编译器推断出变量类型,于是比如就可以用var关键字来声明变量以减少拼写,增加代码的可读性。当然,在编译器无法推断出类型的时候,还是需要显示声明对象的类型。

  • 可变字符串:在JAVA/C#里,字符串默认是不可变的,即对于一个字符串对象进行修改,都会创建一个新的字符串对象,而在Swift,如果是声明的变量就可以直接对其内容进行修改。

  • ..来表示一段范围:这个跟Ruby类似,比如循环就可以这样表达:for i in 0..3

  • 多个返回值:跟C#的Tuple类似,不过一般来讲在OOP里,如果需要用到多个返回值的情况,很有可能是设计不当,没有用一个类型来封装这些返回值。但又不得不承认有时候的确这样也会比较方便。

  • 枚举类型:这次Swift的枚举类型功能强大,不仅支持Int和字符串,还支持任意类型的枚举,甚至Swift里的Optional类型也是建立在枚举类型上的。具体可以查看文档的Enumerations章节

  • 闭包:即Clousure, 也就是Lambda表达式/匿名函数/Block,无需多说了。

  • Protocol:或许对于Objective-C程序员来讲并不陌生,但其实也就是OOP里的接口。

  • 结构体和类(Struct and Class):跟C#类似,结构体一般被内存分配在栈上,而类在堆上,结构体值传递,类则引用传递。

  • 属性:跟C#类似,由编译器来生成类型的成员以及getter/setter,不过Swift在属性上还有Observer来反应值的改变。

  • 泛型:是类似C#的真泛型,不像JAVA的泛型,总是需要装箱拆箱,没有很强的类型安全,性能也相比较差。作为一门新的语言,不用考虑向后兼容,这点也是应该的。:p

  • dynamicType:Swift是编译时静态运行时动态的语言,即有些类型在运行时才确定,具体可以看文档的Dynamic Type Expression这一章节。

  • 扩展方法:跟C#类似,可以自由地扩展类型,只是语法上比C#更简洁一些。

  • Optional:就是C#里的Nullable,因为有些类型本身不支持nil的赋值,所以如果在这类类型声明的时候加上?即声明了一个optional value,比如var a: Int? = nil,这样value就是一个初始值为空的Int?类型。有些人会问那不如直接初始值赋为-1好了。但是既然给一个变量赋了一个值,就代表这个值对于程序的上下文是有意义的,而把这个变量设为nil就代表这个变量在这段程序里是暂时没有意义的。另外有了Optional之后,由于每次都需要检查是不是nil会造成if-else语句的层叠影响代码可读性,Swift的Optional Chaining就很好的解决了这个问题。

  • weak和unowned关键字来表达这种情况,以便编译器在合适的时候生成代码回收对象。

总的来说,现代语言该有的Swift都包含了进去,使得写代码效率提高很多,代码也更加易读。这一点上我还是很兴奋的。至于所谓的坑,其实只是语言设计时候的各种取舍。比如JAVA的泛型之所以这么设计就是为了向后兼容,而当C# 2.0推出泛型的时候,就不考虑这一点。再比如Swift的Optional最近就一直让人很困惑受到很多人的吐槽,典型的比如这条推里写的,其实就是没有理解Optional。这条推里的代码声明了一个Optional的变量并且赋了有意义的值,却没有弄明白在if语句里判断这个变量其实是判断这个Optional的变量是不是nil,而不是判断这个变量的值(因为可能这个变量根本没有值)。

至于学习,最好的办法就是照着文档写代码了。目前官方主要有The Swift Programming LanguageSwift Standard Library ReferenceUsing Swift with Cocoa and Objective-C。而这次WWDC里也有很多session是介绍Swift的,还涵盖了文档里没有介绍的一些东西,非常值得一看。全部的视频都已经在上传到了WWDC14的网站。另外有不懂的,也可以去Stackoverflow的swift-language标签下问问题或者看看别人的回答。

以上说的这些,只是基于还在pre-release的文档,所以还会有变化,但初看下来Swift是一门设计得不错的语言。尽管现在看上去只是为了开发iOS和OS X软件服务的,个人觉得不久之后或许就是一门general purpose的语言,因为这门语言本身是有这样的潜力的。另外还希望苹果能开源与Swift相关的工具,比如编译器、REPL等等。C#的编译器都开源了,厨子!

最后引用Swift的设计者Crhis Lattner的一条推: