无关紧要的XXX

这周请了前端的老罗来讲点心得,他讲了两个钟(早知给他一个钟),一开篇就是:用户体验是无关紧要的。

这个说法早些时间在一些feed上看过,倒不是他的原创。最大的一个证据,就是“支付宝”。几乎没人敢站出来为支付宝的用户体验辩护,但糟糕的用户体验并不影响它占领市场。既然对业务目标没有多少影响,说它“无关紧要”,似乎也顺理成章。

但仔细想想,其实“无关紧要”可以用在任何地方。例如高性能是无关紧要的,水平扩展是无关紧要的,代码规范、团队合作、技术人员了解业务、了解缓存机制、写技术文档、熟悉Linux操作系统、懂优化数据库等等。。。 随便可以举出一堆,都能找到某些案例,即使这些地方做得不好,项目仍然可以成功。所以。。。它们都是无关紧要的。出粮也是无关紧要的,只要上个月的还有剩。

我明白提出这个说法的人所要表达的沮丧甚至愤怒–也许他并不真的认为是无关紧要。

当中的道理是,一样东西的成功,往往不是一个因素决定的。某一方面做得特别出色导致成功的也有,但某些方面做得不足,不见得就会完蛋。这对项目经理是好消息,一个项目组不可能没个方面的人才都完美无缺,总有些短板。不用怕。

Pad Reader

最近把Sony Reader借给了同事,自己尝试在手机上看小说。看的是《The Girl with the Dragon Tatoo》,btw内容真的很精彩,硬盘上的电影版还是忍住等书看完再说。

我买的是Kindle版电子书,在Kindle for Android上看。Nexus One的3.7寸屏幕,和Sony的6寸比当然有很大差距。这点差距,足以让手机的横屏功能起到关键作用。以前在Sony Reader上,我是懒得去横屏的,因为宽带已经和普通书差不多。但在手机上不一样,如果竖着看,几个单词就要换行,非常影响阅读体验。

以前不懂得把180°翻转给关掉,在躺卧的时候,很容易过度倾侧之下,横屏又变成了竖屏。现在这个一禁用就好办了,只要方向握对,过度倾侧也不会变竖屏的。

电子阅读的最佳尺寸,对于小说来说,我觉得在7寸左右。ipad一代的9寸屏,实在有点太大,它过度照顾游戏和上网的需要,这导致重量也太重,成年男子单手拿10分钟会累。听闻第二代是7.7寸,对于阅读为主的我非常有诱惑力。

如果单纯阅读,其实Droidpad也未尝不可,便宜好多。

Android上SQLite的DB Schema版本维护策略

过去10多年的开发生涯中,基本上都是与数据库打交道。从来没遇过这次搞Android上面的SQLite数据库遇到的问题那么奇怪。后来我明白了:因为以前起搞的都是服务器端编程,DB其实都在一个地方,改数据库结构,都是前后两个版本之间的事,更多关注点都是在如何保证改表对正常服务的冲击减到最小。

而在移动开发上,我们需要考虑的是外面可能有无数个旧版本并存,它们都同时需要升级到最新版本。分别要执行的操作都可能不一样,尤其在要保留用户旧数据的情况下。

在我写的这个Android程序里,用到了SQLite数据库来保存数据。第一次写,所以数据访问的代码是抄书上的,用了SQLiteOpenHelper这个辅助类。基本方法是:

  1. 为每个表都创建一个Adapter类;
  2. 每个这样的Adapter类里面再定义一个继承自SQLiteOpenHelper的内部类。这个类主要帮助包含它的Adapter类执行打开数据库,运行sql的工作。它覆盖了两个关键的方法:onCreate和onUpgrade。分别在DB未创建和DB升级时被回调。

详细的解释可以参考网上资料,可以搜到一大堆,例如这个

在应用只有一个表的时候,用起来是没太大问题的。后来多建了几个表,用着发现数据经常神秘地丢失,有时表又不会自动创建。后来经过多番查找资料,才明白了其中的机制。

首先,DB Schema的版本是针对整个数据库的(即针对整个应用,对于不共享数据库的情况来讲),而并非针对一个表的。它其实保存在SQLite数据库的pragma中,变量名为user_version,用命令行进去就可以看到当前的值。

其次,SQLiteOpenHelper对象被构建时,总是要接受一个version参数,这个参数代表最新版本号。SQLiteOpenHelper会将它和数据库中当前的值对比,如果有差异,就会调用onUpgrade方法,并且把这个数据库的user_version设置为最新的值。

问题就出现在这里,当我有多个数据表、多个Adapter的时候,第一个表被访问时,SQLiteOpenHelper已经将数据库的版本号更新为最新值。当第二、第三个表去访问的时候,就会发现数据库版本已经是最新,就不会调用onUpgrade方法。因此如果这个版本同时改了两个表,就会出错。

究其原因,觉得DB Schema的版本控制代码是全局的,根本不应该散落在各个地方,应该有一个地方中央控制。于是我作了一些改动:

  • 把所有Adapter的内部类的两个方法onCreate和onUpgrade都设置为什么都不做:

@Override
public void onCreate(SQLiteDatabase _db) {
System.out.println(“do nothing”);
}

@Override
public void onUpgrade(SQLiteDatabase _db, int _oldVersion,
int _newVersion) {
System.out.println(“Per-table upgrade is disabled”);
}

  • 创建一个新的类DbUtil来专门负责维护DB Schema的版本。

DbUtil负责记录当前最新的版本号,它有一个静态方法checkDbSchemaVersion(Context context),每次主程序的Activity启动的时候,都会在onCreate里面首先调用一下它来检查数据库版本。这个方法里面包含了一个继承自SQLiteOpenHelper的内部类,和以前的那些Adapter的内部类相似,它也是覆盖了onCreate和onUpgrade方法。onCreate方法的实现比较简单,它就是简单地执行了所有表的create语句。而onUpgrade方法中,我让它调用updateDbSchemaVersion(oldVersion, newVersion),这里的oldVersion是程序在当前运行的数据库里面拿到的版本值,可能是几个版本之前的值;newVersion是本程序设置的最新版本值。

upgradeDbSchemaVersion方法的实现也比较简单,它的作用是根据新旧版本的差异,将两版本之间的SQL语句全部按顺序执行一次,源码如下:

private static void updateDbSchemaVersion(int oldVersion, int latestVersion) {
if (oldVersion>=latestVersion) return;
System.out.println(“Upgrading DB schema from “+oldVersion +” to “+(oldVersion+1));
switch (oldVersion) {
case 2:
break;
case 3:
break;
}

oldVersion++;
updateDbSchemaVersion(oldVersion,latestVersion);
}

可以看到上面的程序是一个递归,它是将程序从2开始,将每个版本与前一版本之间的升级操作写在对应的case语句块中。无论当前当前的数据库版本是多少,总是可以在里面找到合适的位置,然后开始逐个版本升级,直到更新到最新版本为止。

需要注意的是,当程序是新安装(不是升级),也就是数据库不存在的情况下,DbUtil中的SQLiteOpenHelper的onCreate会被回调,但onUpgrade不会被回调,数据版本会即时更新为最新的。因此需要保证onCreate里面执行出来的DB Schema是最新的。所以每个版本除了要写updateDbSchemaVersion中的case,还要确保CREATE语句都是对的,建表没有建漏。

当然把程序再优化一下,把onCreate和onUpgrade整合,也是可以的,能做到严格按照版本升级的时间线来走一遍这些DDL。现在表不多,暂且偷一下懒吧。

201008#3

又想了一下php开发的速度问题,觉得这可能和jsp嵌入数据访问代码的时代相似。当java程序员有过一定的专业训练,就很难再容忍 这样做——即使有个好理由。还不如干脆换语言。
四年前我搭建svn时采用的开发模式,适合人数很少的团队。审视对比了一番,还是觉得应该改掉,与业界的规范“接轨”。
google搜索android的资料与java相比真的痛苦许多,不是没有,就是大部分重复。

选择

选择Java还是PHP?Oracle还是MySQL?这种问题90%的情况下讨论是没必要的,讲个笑话让它过去即可。

10%的情况是那种大家都懂行,有很多假设前提条件大家都已经有默契,即使不明确讲出来也互相心照不宣的情况。

90%是那些双方关心的根本不是同一个方面,某一方付出的代价另一方根本不在乎,而这边得到的好处,那边也根本享受不到。

记得有次开一个会,开发的人程序性能写得很差,明明很多改善空间,但提出的方案是使用Oracle RAC。业主也很赞成,对呀,用来试试吧。当然,因为出钱的不是你们,为啥不试试?为啥不用IBM的深蓝试试?

201008#1

今天要和十几个同事去深圳西冲海滩悠闲一天,此刻正在M记吃早餐等出发。yeah

昨晚纵横上见到黄师傅,赫然出现在深圳世界大观附近。今天没准能和他擦身而过…

这周最大的新闻是google宣布放弃wave。这其实是我一直期待的消息。作为google的粉丝,痛苦的不是偶像犯错,而是偶像被犯的错误一直困扰。承认显然是个解脱。eweek十大问题那个文章已经把根源讲得比较透彻。我最大体会是这东西不够简单。

Tasker是我购买的第二个收费手机软件。这礼拜都沉迷它(尽管同事对它普遍比较冷淡)。除了功能,我觉得它的教程非常精巧。类编程的使用方式,通过简短的教程,把学习曲线压得很平缓。不需要看完长篇大论,就可以投入使用。

Android手机输入法

用android到现在用过一大堆输入法,还没有完美的。
触宝最接近完美了,也是现在默认的输入法。但最新版本有几个新旧问题:不能动态英文组词,输入区域变小了,切换中英时必须按退格删掉已输入内容,还有bug导致自动输入字符串。
搜狗没有触宝的下划输入功能,但组词比较强,可惜切换中英时特别卡。
谷歌最差,不值得评论 。
QQ还勉强,但是找符号比较困难,空格键竟然放在了左边。
我希望有个能综合所有优点的:
1 速度不要卡
2 能够中英同时输入,并且中英都可动态组词
3 下划输入
4 能自定义布局,放自己常用的符号到主界面

201007#5

看了一段时间的Android开发资料,其实似懂非懂。还是要做一个应用才叫入门。终于想到了一个不太涉及UI摆布,但有菜单,有配置,有数据库,还有网络通讯(将来)的应用来练练。搞起来之后,发现还是很麻烦的。不断地在电子书,浏览器的API文档、Eclipse开发环境以及模拟器之间切来切去,真是累。做几个屏幕,要摆布xml文件,类,注册到清单中,覆盖事件等等,速度很慢。Eclipse虽然用惯了,但还是觉得它搞UI的东西不是强项。应该有更好的开发工具(或者插件)才对。

断续搞了一个星期,基本搞出了以下几个最基本最基本的东西:图片字符串等等的资源管理、一两种最基本的Activity的开发和配置、读写程序配置、读写SQLite数据库。

开始关注了一下Git。很久很久以前为部门设置SVN服务器的时候,我记得看过一本SCM模式的参考书。那本书其实挺经典的,可惜就是太简洁。那时我还做过一个关于介绍版本、码线、分支等等概念的PPT,仔细找找应该还在。用SVN之前是用CVS,觉得SVN实在太强了,解决了很多CVS原来的不便之处,正好是挠到了痒处。最大的特点当然是版本终于是基于整个文件集的变化而不是单个文件。在很长一段时间,觉得SVN的功能无出其右。听闻有人讲Git,怀疑这是不是只是一个分布了的SVN而已。

其实并非这样的。电子书我看了半本,其实还真没特别强调它的分布,尽管它是分布。最有意思的是它对待文件内容、对待路径文件名和对待目录树的思路,换了一种新的。这就是强大和平庸的技术员的区别了。强大的总是能跳出框框去解决问题。书还没看完,还不好说喜欢还是不喜欢,比SVN强还是各有千秋。但的确是很有意思,我的Android应用,也是用它来做版本管理。

特别值得一提的是msysgit,它附送了一个Bash Shell,就算不玩Git,也是一个很爽的在Windows下执行各种shell命令的命令行工具。比单个找gawk、grep的windows版爽多了。

终于购买了比较贵的在线数据备份服务-Carbonite。用它比较省心,不用老是担心文件在两次备份过程中会否正好丢失,又不用再烧碟,或者找不到备份的打包放哪里去了。这不代表不再需要做备份打包工作,当需要为某个文件集做个快照的时候,还是需要的。

无题

昨天尝试堆砌android一个屏幕,搞到一头烟。很多事情打断是原因,更重要的是不熟那堆组件。做UI要知道手头有哪些元件可用 它们分别有什么特性,我离这个阶段还远。办法,无非就是多动手了。

201007#04

以前试过找90-96的一堆流行曲,一边搜一边下载,下错了还要删掉重新搜。太麻烦后来就放弃了。到上周再尝试做这件事,已经变得非常简单。只要把列表找到,在虾米网里面找,必然能找到。分别定义了播放列表保存下来,随时可以在线听。如果愿意付一点点费用,还可以打包下载。不过我觉得已经没有这个必要。

可以电子化的东西,已经越来越容易找到,相信会冲击收藏这个爱好(抑或工作、艺术?)。甚至一些不能电子化,但可以高精度复制的东西,收藏似乎也因此黯淡一些。例如邮票?

iPad在香港最低只卖3800港币左右,有力地冲击了很多观望的朋友。

网易微博,手机版没得拖黑,网页版拖黑了但垃圾信息还在。郁闷。。。

这周讲Scrum的时候,很担心同事睡着,结果还好,没想象中那么差。

Android的那个App Inventor备受关注。有个讲者说:Apple就不会有这个东西!听起来也蛮有道理,因为Apple的控制欲太强了。目前比较遗憾的是,它并不能生成源代码,只能做成apk。

Page 7 of 13« First...56789...Last »