月度归档:2012年07月

WP7开发初体验 — WP7版“百度.ting”开发记录


项目地址: lb.ting(github)
如果你有WP7手机,可以下载XAP文件体验一下:lb.ting.xap

感觉已经有段时间没有接触过什么新技术了,或许也是想看看自己接触一项全新的技术需要多少时间来熟悉。近来想看看WP7开发。
上周在MSDN简单的看过一些WP7开发文章后决定动手做自己的地一个WP7应用。因为只是想简单的体验一下WP7开发,因此跳过系统学习的阶段直接动手。
这次想做的是百度.ting的WP7客户端。虽然虾米电台、豆瓣电台等电台网站/软件已经一大堆了,但百度.ting还是有自己的优势。百度电台的的音乐码率普遍为128k,比其他电台要好出不少。大多音乐电台支持直接手机访问,直接浏览器访问无法实现音乐的后台播放。
C#刚出时大家评价C#是Delphi和Java的杂交产物。我虽没什么C#的开发经验,但相信凭自己的Java与Delphi经验应付一般的C#代码还是没多少问题的。电台软件的功能以及技术点都不多,我很乐观的想周末在家宅个两天,折腾出地一个基础版本。显然,这个计划已经失败。接触一项新技术的遇到的问题远比我想象的要多,中间也走了不少弯路。
目前第一个初步可用的版本已经完成。我会将代码放到github上。我希望在接下来一段时间里对这个应用再进行一些优化,然后上线到WP7的商店。
下面的文章里会我记录下一些我遇到的问题。

准备工作,知识储备

微软在帮助文档方面做的非常的好,你可以很容易找到自己需要的资料。

开发过程中遇到的相关问题以及解决方案

百度.ting API

直接用FireBug对百度播放器的请求进行分析,可以很轻松的得到大部分API。这些API中唯独缺少电台列表的API。该问题最简单的解决方案是直接分析网页获取到电台列表,然后在程序里写死。另外还可以让程序对电台首页的html代码进行分析,从中提取电台列表。方案一,感觉有些傻。方案二实现起来又过麻烦。于是有了方案三。
百度.ting有官方的Android客户端,而Android程序是比较容易反编译的。使用 Dedexer 反编译后,得到相关API。Android客户端和web版本电台使用的并不是同一套API,而且从接口的参数类型上看服务端似乎用的是Java。

//主要接口
//获取频道列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getCategoryList&format=json
//获取频道歌曲列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getChannelSong&format=json
//获取专辑的歌曲列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getArtistChannelSong&format=json
//获取歌曲的详细信息
http://ting.baidu.com/data/music/links?songIds=

JSON解析

最初我希望JSON能被自动解析成LIST/MAP类型的结构,查了一轮并没有找到比较好的解决方案。在C#中JSON都需要和对象进行绑定。如果手动为所有JSON数据创建相关的数据类,无疑是意见很麻烦的时。我相信我并不是遇到该困扰的第一人。果然网上已经有不少C#的JSON工具。
json2csharp 只要你输入json,改网站会自动帮你生成相关的数据类。

配置文件的存储

大多应用程序都需要保存一些自定义的配置数据。在WP7.1后,本地数据的使用变得非常的简单。本地数据库的使用可以参考 如何使用 MVVM 为 Windows Phone 创建本地数据库应用程序
lb.ting最终方案中并没有使用本地数据库。我采用了直接将JSON数据持久化的做法进行数据存储。直接持久化JSON的方案在数据的可靠性比较差,且配置文件格式的修改会成问题。JSON持久化的方案胜在够简单。

后台音频播放

WP7不支持后台进程,为了保证音频的后台播放需要为应用创建对应的后台音频播放代理。按照通常的理解,动态更新播放列表是一个必须的功能。WP7的后台播放代理还就没有更新播放列表的API。在MSDN的例子中播放列表直接写死在程序里。
去网上转了一圈也并没有找到比较好的解决方案。网上的建议是将播放列表保存在数据库或本地文件中,通过创建一个“中间层”的方式同前台UI进行交互。lb.ting最终采用了将播放列表持久化到本地存储区的方案。

baidu图片获取

专辑封面在浏览器可以正常显示,客户端则无法显示专辑图片。怀疑是代码的问题,于是将图片源换为豆瓣,图片正常显示。一通折腾,发现是百度的防盗链引起。将图片的加载换为WebClient实现。

public static void SetImageUrl(Image img, Uri uri, Uri defaultUri)
{
    WebClient wc = new WebClient();
    wc.Headers["Referer"] = "http://www.baidu.com";
    BitmapImage bi = new BitmapImage();
    img.Source = bi;
    wc.OpenReadCompleted += (s, e) =>
    {
        try
        {
            bi.SetSource(e.Result);
        }
        catch (Exception)
        {
            //bi.UriSource = defaultUri;
        }
    };
    wc.OpenReadAsync(uri);
}

CSRF(Cross-site request forgery)以及django中的处理方法

CSRF(Cross-site request forgery) 中文名是跨站请求伪造。
简单的说是用户访问了安全的网站A(比如支付宝)并登录,之后用户又访问了问题网站B。网站B包含恶意代码,在访问网站B的时候,网站B的网页偷偷的向网站A发起post(get)请求进行转账操作。由于你已经登录过网站A,网站A误以为请求是用户自己发起的安全请求。在操作成功后你的钱啥的也不见了。
当前Django的处理方法:
在用户访问网站时为用户生成一个CSRF防范用的token并将token保存到cookie中。每次用户发起请求时需要将token的值作为表单字段一同提交。服务端对用户提交的token值同cookie中的token进行比较,如果相同则认为安全。
由于浏览器对cookie的访问有严格的限制,问题网站B无法访问到网站A的cookie无法正确的设置token。

谈“降级论”

降级论 最近这篇文章转的到处都是。因为太火了,虽然赞同文中的大部分观点,还是免不了有些反感。
文中“降级”更准确的说是换个角度,但“降级”这个说法很好的满足了很多“it”人士的虚荣心。或者说很多“it”人一直自以为是的认为自己高级、高智商、如果混的不好是怀才不遇。
相比其他行业,圈内人的喜欢图个嘴快问题更为明显(我也有这个问题,不然就不写这么多了)。在cnbeta、网易、微薄发表各类为图一时之快的言论。大意都是XX公司做的XX很烂、XX很烂、黑幕XX,总之这世界上几乎没什么好的。在费了很多口水后,要说行动则为零。如果你的脚步都不曾迈出又谈何改变。
作者在文中谈及了自己此前“失败”的经验,但我认为这并算不上失败。尽管商业上是失败的,但将一个网站、一个APP做到他文中的程度,从一定程度上说已经非常成功了。能成功的做出了一个受欢迎的产品,并不是一件容易的事。作者的成功并不是一夜暴富的结果,作者的成功的背后是他前期的积累。如果作者继续一心做“纯互联网”产品,也未必不会成功。
互联网渗透入传统行业,这已经成了必然。现阶段是电商的活跃,接下来必将更深度的渗透下去。互联网人做传统行业,和传统行业的人做互联网其实都是各有利弊的。并不能说互联网人做传统行业就“降级”就容易。互联网有互联网的门槛有互联网的问题,传统行业也是。
就如丁磊的养猪,养了好几年的猪,猪到现在都没有出栏。在线出版也算是传统行业迈向互联网的一个尝试。“印客网”有段时间看上去发展的还不错,也关门了。
做什么都有难度,并不是做互联网就高级了。
有想法就行动,用行动去将想法变成现实。

替换博客的代码高亮插件为“Crayon Syntax Highlighter”

此前一直对博客的代码高亮插件不是很满意。以前用的插件,默认配色方案不是太美观,而且配色方案的修改还不是一件容易的事。
将代码高亮插件换到 Crayon Syntax Highlighter 。现在的这个插件还是让我比较满意的。

插件演示

# -*- coding: UTF-8 -*-
import os
from contextlib import contextmanager as __ctxmgr
from fabric.api import *
from fabric.decorators import runs_once
import fabsettings as cfg
#host settings
env.hosts = ['vicalloy@jstwind.com']
env.password = cfg.PASSWORD
#env.passwords = fabsettings.PASSWORDS
#custom settings
env.deploy_dir = '/home/vicalloy/webapps/jstwind/timeline-site'
env.activate = 'source %s' % os.path.join(env.deploy_dir, 'env/bin/activate')
env.mg = 'python %s' % os.path.join(env.deploy_dir, 'sites', 'manage.py ')
@__ctxmgr
def __virtualenv():
    with cd(env.deploy_dir):
        with prefix(env.activate):
            yield
def reload():
    wsgi = os.path.join(env.deploy_dir, 'deploy', 'dj_scaffold.wsgi')
    run("touch %s" % wsgi)
def update():
    with __virtualenv():
        run("git pull")
        run('%s syncdb' % env.mg)
        run('%s migrate' % env.mg)
        run('%s compress --force' % env.mg)
        reload()

sentry,异常记录的云托管平台

最近打算为公司的应用增加异常跟踪功能,所以又去看了看 sentry
sentry 最早是 disqus 发布的一个开源的django异常跟踪APP。一段时间没见发现sentry已经发展成了一个支持 Python、PHP、Node.js、Java 的异常跟踪中心,而且还推出了对于的云服务 getsentry.com
getsentry.com 按月收费,不同方案的区别在于日志的保存时间以及每日的记录事件数的配额。和大多云服务一样 getsentry.com 也提供免费方案,免费情况下每日有100个事件配额。 由于 getsentry.com 本身是一个开源产品,部署成本也不高。 getsentry.com 的服务是否真能让广大用户接受还很难说,但这个做法让人不得不佩服disqus的想象力,连一个异常跟踪平台也能做到这个程度。
sentry 作为一个开源项目有着不错的代码质量,sentry代码中有不少可以借鉴的东西。

  • tox 似乎越来越多的python项目有在使用tox。tox是一个python虚拟环境管理工具,主要用于为测试用例创建虚拟环境。tox可以为不同的python版本单独创建虚拟环境,以测试在不同python版本下的工作情况。
  • 实现了一个自己的BaseManager,增加get_from_cache方法。用缓存来加快查询速度。
  • twitter-bootstrap sentry也是用bootstrap作为前端框架。因为有对bootstrap做过一些调整,所以UI不至于太过bootstrap。
  • celery、nose、gunicorn……