[Toy] 获取土豆FLV下载地址的小工具
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
一个获取土豆FLV下载地址的小工具,使用方法也比较简单:
前提条件,你必须安装了JAVA JRE 1.5以上的运行环境
1. 访问欲下载的土豆视频连接
2. 点击视频下方的转帖到博客或BBS
3. 拷贝FLASH代码的连接(例如:http://www.tudou.com/l/9Uby2NgANWI)
4. 运行下面的命令
java -jar FLVParser.jar -t http://www.tudou.com/l/9Uby2NgANWI
得到如下下载连接:
http://124.232.157.18/f4v/65/65364165.h264_1.f4v?80000&key=a7111b24f706743495ab584cff1934fe94f5d5&playtype=1&tk=476898256&brt=2&id=tudou&itemid=38111542&fi=65364165&sz=243731218
http://180.137.254.12/f4v/65/65364165.h264_1.f4v?80000&key=a7111b24f706743495ab584cff1934fe94f5d5&playtype=1&tk=476898256&brt=2&id=tudou&itemid=38111542&fi=65364165&sz=243731218
5. 使用你喜爱的下载工具下载FLV视频即可
From Jelly's Blog, post [Toy] 获取土豆FLV下载地址的小工具
Post Footer automatically generated by wp-posturl plugin for wordpress.
[Toy] 一个将指定URL的页面数据保存成图片的小工具
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
这个小工具功能比较简单,将指定的URL的页面的DOM元素保存成图片文件。 From Jelly's Blog, post [Toy] 一个将指定URL的页面数据保存成图片的小工具 Post Footer automatically generated by wp-posturl plugin for wordpress.
—————————————————————————–
Usage: WebPageCapture –url=http://www.example.org/ –out=localfile.png
—————————————————————————–
–help Print this help page and exit
–url=
–out=
–html=
–class=
–bg=
–id=
—————————————————————————–
http://www.jjos.org – (c) 2010 Jiang Jiang –
构建XCode免证书开发环境
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
写在最前面
如果你和我一样,是一名爱好iOS开发的开发者,并且又舍不得花费99美金去购买一个合法的开发License,这篇博客则非常适合你。如果,您是一位已经获得Apple开发者证书的开发者,则可以完全无视该篇Blog。
开篇
在没有证书的情况下,开发iOS应用程序只能在功能有限的模拟器环境中运行你的应用程序(x86环境)。对于那些与硬件平台关系不大的iOS应用程序开发来说,模拟器环境可以模拟大多数功能,但是一旦你的应用需要涉及实际的硬件设备(比如:摄像头,感应器等),则模拟器就无法满足您的开发需求。拜股沟大神所赐,网路上已经早已有开发者用自签名证书的方式,绕开XCode的证书检查,可以将XCode编译的应用程序部署到实际的iOS设备上进行测试和调试。
生成自签名开发证书
1. 启动证书链管理工具(应用程序->实用工具->钥匙串访问)创建证书

- 设置证书名称设置为iPhone Developer(该名称需要和XCode编译签名时使用的证书名称一致,下文会提到),身份类型选择自签名根证书,证书类型选择S/MIME (电子邮件),并且在覆盖这些默认值选项上打勾。

选择继续制作自签名证书

- 设置证书有效期365天(时长不要超过365天)

- 输入用户信息

- 选择密钥大小和算法(2048位和采用RSA算法)

- 设置密钥扩展功能(选择签名功能和代码签名功能)


- 禁止基本约束扩展

- 设置主题设备用名称扩展,RFC822输入邮箱即可

- 设置钥匙串类型为登陆类型

至此,自签名证书创建完毕
Hacking XCode
1. 绕开XCode证书检测(以下代码在终端执行)
#!/bin/bash
cd /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Plug-ins/iPhoneOS\ Build\ System\ Support.xcplugin/Contents/MacOS/
dd if=iPhoneOS\ Build\ System\ Support of=working bs=500 count=255
printf “\x8f\x2a\x00\x00″ >> working
dd if=iPhoneOS\ Build\ System\ Support of=working bs=1 skip=127504 seek=127504
/bin/mv -n iPhoneOS\ Build\ System\ Support iPhoneOS\ Build\ System\ Support.original
/bin/mv working iPhoneOS\ Build\ System\ Support
chmod a+x iPhoneOS\ Build\ System\ Support
mkdir /Developer/iphoneentitlements30
cd /Developer/iphoneentitlements30
curl -O http://www.alexwhittemore.com/iphone/gen_entitlements.txt
mv gen_entitlements.txt gen_entitlements.py
chmod 777 gen_entitlements.py
- 设置PROVISIONING为禁止证书
在/Developer/Platforms/iPhoneOS.platform/Info.plist中增加如下两个项目
PROVISIONING_PROFILE_ALLOWED 设置成 NO
PROVISIONING_PROFILE_REQUIRED 设置成 NO
并且,将该文件中的XCiPhoneOSCodeSignContext字符串替换成XCCodeSignContext
- 使用XCode打开你的项目文件,在Project->Edit Project Setting中选择Build页面,找到Code Signing Identifty选项,将签名设置成iPhone Developer
- 修改项目的xxx-Info.plist文件,添加SignerIdentity,将其设置为Apple iPhone OS Application Signing
- 在Project->New Build Phase->New Run Script Build Phase的script部分,输入下面的代码
export CODESIGN_ALLOCATE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate
if [ "${PLATFORM_NAME}" == "iphoneos" ]; then
/Developer/iphoneentitlements30/gen_entitlements.py “my.company.${PROJECT_NAME}” “${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/${PROJECT_NAME}.xcent”;
codesign -f -s “iPhone Developer” –resource-rules “${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/ResourceRules.plist” \
–entitlements “${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/${PROJECT_NAME}.xcent” “${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/”
fi
- 选择项目部署的目标为Device
- 打开Window->Organizer,选择实际需要测试的设备平台(我这里是iPad)
- 点击XCode的Build and Debug按钮,在设备上部署和运行应用程序
From Jelly's Blog, post 构建XCode免证书开发环境
Post Footer automatically generated by wp-posturl plugin for wordpress.
青春如同奔流的江河,而生活像一把无情刻刀
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
不想多说什么,一切都在短片中。。。
那是我日夜思念深深爱着的人呐
到底我该如何表达
她会接受我吗
也许永远都不会跟他说出那句话
注定我要浪迹天涯
怎么能有牵挂
梦想总是遥不可及
是不是应该放弃
花开花落又是雨季
春天啊你在哪里
青春如同奔流的江河
一去不回来不及道别
只剩下麻木的我没有了当年的热血
看那漫天飘零的花朵
在最美丽的时刻凋谢
有谁会记得
这世界她来过
转眼过去多年时间多少离合悲欢
曾经志在四方少年羡慕南飞的燕
各自奔前程的身影匆匆渐行渐远
未来在哪里平凡啊谁给我答案
那时陪伴我的人啊
你们如今在何方
我曾经爱过的人啊
现在是什么模样
当初的愿望实现了吗
事到如今只好祭奠吗
任岁月风干理想
再也找不回真的我
抬头仰望着满天星河
那时候陪伴我的那颗
这里的故事
你是否还记得
生活像一把无情刻刀
改变了我们模样
未曾绽放就要枯萎吗
我有过梦想
青春如同奔流的江河
一去不回来不及道别
只剩下麻木的我没有了当年的热血
看那漫天飘零的花朵
在最美丽的时刻凋谢
有谁会记得这世界她曾经来过
当初的愿望实现了吗
事到如今只好祭奠吗
任岁月风干理想 再也找不回真的我
抬头仰望着满天星河
那时候陪伴我的那颗
这里的故事你是否还记得
如果有明天 祝福你亲爱的
From Jelly's Blog, post 青春如同奔流的江河,而生活像一把无情刻刀
Post Footer automatically generated by wp-posturl plugin for wordpress.
Tomorrow is just another day
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
刚刷新苹果的官方主页,发现下面一张页面,难道明日苹果ios 4.2即将发布么?期待中…
From Jelly's Blog, post Tomorrow is just another day
Post Footer automatically generated by wp-posturl plugin for wordpress.
iPad把玩-初体验
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
从老乔手握iPad出现在Apple新品发布大会的那一刻起,iPad华丽的外观、强大的功能以及App store上数以千计的高质量应用就深深的吸引了我。在我毫无意识的情况下,又一次被拉入到老乔的“陷阱“中。经过半年时间的关注,最终还是决定入手一只iPad。
从iPad发布之初公布的尺寸大小(242.8×189.7×13.4mm)以及相关的视频上看,iPad的尺寸是相当薄的。但是一直只是一个概念性的认识,直到第一次拿到iPad真机后,才惊叹Apple竟然可以在不影响功能、效率的基础上将iPad作的如此只薄,真的不得不佩服Apple的工艺水平。下面从拿到iPad后把玩的过程,来介绍一下这个新增神器。
iPad配备了9.7英寸大小的电阻式多点触摸显示屏,由于其运行的系统和iPhone/iTouch一样,并且其外观类似,因此之前很多网上评论文章都将其比为打好iPhone/iTouch。但是,在实际的把玩过程中发现,iPad的大屏幕的用户体验感受是iPhone/iTouch的小屏幕无法比拟的。比如,在iPhone/iTouch上打开一张页面,要想导航到目标区域可能需要做多次的放大,移动等滑动操作。而在iPad上的体验确大为不同,由于其9.7寸屏的优势,可以让整个网页内容尽收眼底,并且不需要做很很多(甚至压根不需要)滑动操作即可浏览到想要的信息。处理器方面,iPad采用了自家设计研发的A4 SoC处理器,主频是1GHz。电池采用25Whr的俚电,按照老乔的介绍,该电池可让iPad续航10个小时,但是按照我实际的使用时间来计算,一共使用了16.5小时后,系统剩余电量还有25%。在这个过程中主要是进行iTunes同步,网页浏览,QQ,邮件,RSS,越狱等操作,这让我对iPad的续航能力非常满意。Flash容量有16G,32G和64G三种规格。内存大小是256MB。由于我没有做实际的称量,按照前人的评测说,整体重量0.73公斤。虽然感觉很轻便,但是在实际的使用过程中发现,长时间的手握iPad会感觉 1. 安装iTunes和注册iTunes美国免费帐号
拿到iPad后的第一件事情是安装iTunes,然后注册一个美国的iTunes帐号。关于如何注册美国免信用卡帐号的方法网络上有很多介绍,这里不做详细说明,可以参考weiphone网的这篇注册介绍文章:
http://bbs.weiphone.com/read-htm-tid-205881.html
2. 同步和备份iPad
当iTunes安装完毕后,使用刚才注册的美国帐号登录App Store,后将数据线插入iPad,这时iTunes会自动弹出检测到iPad的对话框,并且提示用户输入该iPad的名称。点击确认后,就可以在iTunes中看到iPad的当前名称,序列号以及系统的版本号。这时,右键点击iTunes左边栏的iPad图标,选择备份,即可将iPad中的数据备份到PC端。
3. 获取和保存SHSH
获取SHSH的工具,当然是传说中的umbrella,备份的过程全图形化操作,很简单。可以参看下面这篇文章:
http://ifan.178.com/thread-690979-1-1.html
4. 越狱
并不是说不越狱iPad不能使用,App Store上为iPhone/iPad/iTouch提供了很多免费的应用供下载。但是一些主流、优秀、功能强劲的Apple应用往往是需要出血的。而且,在没有越狱的情况下,一些辅助性的神器(backgrounder,activator等)都是无法使用的。因而,对于我个人来说越狱是必须的,可以让我更好的体验iPad上强大的应用程序以及功能。
我的iPad运行的是iOS 3.2.2版本,通过疯狂的Google后发现,网络上提供两个越狱软件-绿蛙、绿雨,以及大量的越狱教程(在这里,不得不感谢哪些无私奉献的DXJM们)。通读完大量的越狱文章,了解了越狱的大致步骤、可能出现的问题后。先是下载了绿蛙的最新版本,然后开始了我的第一次越狱。按照网站上的教程说明,启动绿蛙后软件再等待2秒以后,会提示用户按住POWER键,关闭iPad(大概持续3秒),然后提示用户按住POWER键的同时,按下HOME键,进入到恢复模式(整个过程大概持续10秒)。最后,软件会提示用户按住HOME键不放,松开POWER键(过程大概持续15秒)。当软件的越狱按钮变为可用时,松开HOME键,然后用鼠标点击软件上的越狱按钮进行越狱。此时,iPad会显示让用户插入iTunes连接线的图标。等待大概10-15秒以后,iPad会出现白色屏幕,然后出现Console界面。最后,iPad会关机,手工按住POWER开机后,按照教程上所说,应该在第2屏中出现Cydia的图标。在我疯狂的用手翻动iPad面板后,我失望了。iPad屏幕中只有默认屏和search屏,压根没有传说中的第2屏,更别说见到神奇的Cyndia图标了。继续关机重启数回以后,现象依旧,难道是越狱失败?
没招,这次转换工具用传说中的绿毒进行第二次越狱。不得不说,绿毒的用户操作要比绿蛙简便。不说别的,由于不熟悉越狱的按键过程,在使用绿蛙的时,几次都由于没有及时按照提示按键被要求重新尝试,而这点绿毒要作的好很多。打开绿毒界面后,按照提示一路走下去,最后会弹出一个对话框说明,表示越狱成功。此时,iPad屏幕上显示一个硕大的绿色水滴图标,并且风火轮开始神转。紧接着,iPad重启了并且一直停留在要求用户插入iTunes连接线的界面。按照界面提示,插入数据线后,iTunes提示,系统进入恢复模式,要求下载3.2.2版本进行恢复。神马?难道给刷挂了?不管了,点击了下载按钮后。将iPad关机了,然后再次使用绿蛙进行越狱。整个操作和上面过程一样,但是这次不同的是,越狱之后竟然iTunes找到iPad设备了(不再提示要求下载固件并且恢复了)。虽然,是可以进入系统了,但是依旧是没有看到Cydia图标。
就这样来回用绿蛙和绿毒越狱多次后,一次关机重启后发现神奇的第2屏出现了,而且屏幕上出现了绿毒和绿蛙两个图标,并且神奇的Cydia图标也出现了。后来,我在想,之前也许也越狱成功了,但是由于自己越狱心切,越狱在进行中时,由于没有提示,误认为是关机或者其他操作了,然后强制重启,导致越狱失败。
越狱教程可以参看:
http://ifan.178.com/thread-690450-1-1.html
5. 设置Cydia源,安装补丁
同样参看上面的越狱教程,里面有很详细的说明
最后,越狱完毕后就是疯狂的下载软件。至此,神器可以真正开始释放威力了。
~~~ END ~~~
From Jelly's Blog, post iPad把玩-初体验
Post Footer automatically generated by wp-posturl plugin for wordpress.
CSS 兼容一览表
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
From Jelly's Blog, post CSS 兼容一览表
Post Footer automatically generated by wp-posturl plugin for wordpress.
Introduce to WebKit2
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
WebKit2是为WebKit引擎而设计的支持单独进程模型的API层。将Web的内容(Javascript,HTML,布局等)处理与应用程序UI分离,分别运行在两个不同的进程之中。这种分离的进程模型和Google的Chrome浏览器有几分类似,不同的是WebKit2将从引擎内部原生的支持这种多进程机制。通过将内容和展现相分离的方式,可以更加有效的提高浏览器的运行效率。
WebKit2提供了一套基于C语言的非阻塞API接口,为了实现WebKit2引擎中对这些API接口的非阻塞调用,WebKit2在设计的时候采用了以下几个方面的技术:
- 以通知的方式回调客户端函数(比如,didFinishLoadForFrame),通过这种机制可以告知client发生了什么事情(目前WebKit版本中也大量的使用了该技术,用来通知client事件的发生)
- 以策略方式回调客户端函数(比如,decidePolicyForNavigationAction),当引擎执行某一项操作时,通过该机制由实现具体功能的client来决定是否该操作被执行。
- 策略设置(例如WKContextSEtCacheModel, WKContextSetPopupPolicy),该机制允许client重新定义策略,而不需要再通过回调的方式访问UIProcess。
- 代码注入(例如,WebBundle),当所有其他的方法都无法完成时,可以通过代码注入的方式将代码载入到WebProcess中。不过按照WebKit官方文档所说,这个功能目前只是在计划中,还没有真正的在代码级实现。
WebKit的多进程架构
下图是之前webkit的进程架构图
从上图可以看到,之前的WebKit的ui进程和webkit内核进程都是运行在相同的进程空间。其中API Boundary表示和系统相关的api层,应用层使用webkit api使用webkit引擎,并且引擎使用该api访问系统资源。
接着,再看看新版的webkit2引擎的架构
从上图可以看到,webkit2将webkit引擎分成了2个部分,一个部分是和ui相关的操作,运行在一个进程中。而另外的webkit部分运行在独立的进程中。通过多进程机制的支持,增加了ui响应和引擎布的效率,同时也增强了系统的健壮性和安全性。
之前也提到,webkit2的多进程模型有点类似chrome的多进程模型,但是有一些不同之处,下图是chrome的进程模型:
从上图可以看到api bondary是在render进程的下方,也就是说ui绘制进程是在webkit框架之上扩展支持的。该部分的扩展是非webkit原生支持的,属于chromium代码的一部分,这导致无法在其他平台或者框架中支持多进程功能。
为了支持多进程的机制,在webkit中增加了两个新的子系统:
- CoreIPC:为消息传递和事件处理提供支持。
- DrawingArea:为交叉绘制区域提供了抽象。通过共享内存位图信息的方式,为多个进程绘制提供了支持。
目前,我只在Mac OS 10.6.3平台上编译并且体验了Webkit2,可以使用下面的方法加入多进程支持:
WebKitTools/Scripts/build-webkit –webkit2
~~~ END ~~~
From Jelly's Blog, post Introduce to WebKit2
Post Footer automatically generated by wp-posturl plugin for wordpress.
Titanium 架构分析
如需转载,请注明出处!
WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283
一、分析的目标
- 了解Titanium产品的基本框架结构和特点
- 了解Titanium产品如何扩展本地API以及访问方式
- 了解Titanium产品中的动态语言之间如何相互调用
二、Titanium概述
2. 1 Titanium介绍
Titanium是一个Web应用程序运行环境,它支持不同的系统平台(Windows、Linux、Mac),并且支持Web应用程序对本地APIs的访问。在基于Titanium平台上,用户可以快速开发和方便的部署应用程序,并且这些应用程序可以使用本地APIs实现许多普通Web应用程序无法完成的功能和特性。
2.2 Titanium特点
Titanium框架具有如下几个方面的特点:
- 支持多平台(Linux、Mac、Windows、移动设备)
- 使用Web技术加快软件开发速度
- 支持Web中内嵌多种编程语言
- 支持对本地APIs的访问
- 通过Appcelerator网络云服务,基于Titanium的应用可以更容易的打包、测试和部署
- 本地功能的模块化,可动态加载指定的功能模块
- 强大灵活的语言扩展,用户在Titanium框架中可以很方便的扩展多种动态语言
2.3 Titanium 框架结构
上图来自于Appcelerator官网,该图以iPhone和Android两个移动平台为例,描述了Titanium的总体框架结构。在Titanium框架中,Web应用程序可以很方便的访问设备UI组件。比如,可以在页面中使用Titanium提供的API控制导航条、工具栏、菜单,以及可以动态的向用户弹出对话框、警告框等。除此,之外Titanium API还支持本地功能模块的访问,即用户可以使用Titanium提供的APIs接口访问数据库、定位功能、文件系统功能、网络功能、媒体功能等。
不过该框架图,并没有将Titanium中对多种脚本语言的相互访问机制很好的表现出来。但是,这一机制却又是Titanium框架的一个比较重要的功能特性。
三、Titanium构建
Titanium的构建过程使用scons管理(http://www.scons.org/)。scons是一个开源的软件构建工具,使用Python语言来描述软件构建规则。通过Titanium的源码级构建和Titanium的构建规则两个方面,可以了解Titanium运行环境由那些部分组成、这些模块和模块之间的关系是什么。
[注]以下所有的测试和分析内容均是以Linux平台上Desktop版本的Titanium代码为基础。
- 构建Titanium所依赖的库和环境
- Ruby 1.8.x 开发包
- Python 2.5.x开发包
- scons构建工具
- git 版本管理工具
- Ubuntu 9.04上构建Titanium所需的支持包
sudo apt-get install build-essential ruby rubygems libzip-ruby \ scons libxml2-dev libgtk2.0-dev python-dev ruby-dev \ libdbus-glib-1-dev libnotify-dev libgstreamer0.10-dev \ libxss-dev libcurl4-openssl-dev sudo apt-get install git-core
- 获取Titanium源码
git clone git://github.com/marshall/titanium cd titanium
- 获取Kroll源码
git submodule init git submodule update cd kroll git checkout master
- 构建Titanium测试程序
cd .. scons debug=1
- 运行
scons testapp debug=1 run=1
有关Titanium构建相关的信息,可以访问以下页面获得:
http://wiki.github.com/marshall/titanium/build-instructions
3.2 Titanium构建规则分析
3.2.1 版本需求
| 构建过程所需的库/程序版本 | |
| Python | 2.5 |
| Ruby | 1.8 |
| Scons | 1.2 |
| kroll 源码版本 | 12/30/99 |
| titanium_desktop 源码版本 | 12/30/99 |
| WebKit版本 | libwebkittitanium-1.0.so.2.7.0 |
3.2.2 默认配置项
| 默认配置项 | ||
| 配置 | 值 | 备注 |
| PRODUCT_VERSION | 0.7.0 | |
| INSTALL_PREFIX | /usr/local | |
| PRODUCT_NAME | Titanium | |
| CONFIG_FILENAME | tiapp.xml | |
| BUILD_DIR | build | |
| THIRD_PARTY_DIR | kroll/thirdparty | |
| DISTRIBUTION_URL | api.appcelerator.net | |
| CRASH_REPORT_URL | api.appcelerator.net/p/v1/app-crash-report | |
| GLOBAL_NS_VARNAME | Titanium | 定义了全局Titanium对象名称 |
3.2.3 scons编译参数
| Scons编译参数 | |
| debug | 0表示release版本,1表示debug版本 |
| clean | 清除构建的工程 |
| qclean | 清除构建的工程 |
| run | 运行TestApp |
| run_with | 带参数运行TestApp,好像Linux平台上没用 |
3.2.4 构建规则文件
| 构建规则文件 | |
| kroll/SConscript.thirdparty | Titanium所需的第三方支持文件规则 |
| installation/SConscript | Titanium安装器构建规则 |
| kroll/SConscript | 构建kroll库规则 |
| modules/SConscript | 构建语言支持模块规则 |
| apps/SConscript | 构建TestApp规则 |
| SConscript.dist | 构建SDK规则 |
| SConscript.docs | 构建APIs文档规则 |
| SConscript.test | 构建测试程序规则 |
3.2.5 核心库和程序构建规则
| 库/程序 | 规则 |
| build/linux/runtime/template/kboot | kroll/boot/breakpad/common/*.c
kroll/boot/breakpad/common/*.cc kroll/boot/breakpad/client/*.cc kroll/boot/breakpad/processor/*.cc kroll/boot/breakpad/client/linux/handler/*.cc kroll/boot/breakpad/common/linux/*.cc |
| build/linux/runtime/libkroll.so | kroll/api/*.cpp
kroll/api/config/*.cpp kroll/api/binding/*.cpp kroll/api/utils/*.cpp kroll/api/utils/poco/*.cpp kroll/api/utils/linux/*.cpp kroll/api/net/proxy_config.cpp kroll/api/net/*_linux.cpp |
| build/linux/runtime/libkhost.so | kroll/host/linux/host.cpp
kroll/host/linux/linux_job.cpp |
| /linux/modules/api/libapimodule.so | poco third library(http://pocoproject.org/)
kroll/modules/api/*.cpp |
| build/linux/modules/javascript/libjavascriptmodule.so | poco third library(http://pocoproject.org/)
webkittitanium-1.0 third library kroll/modules/javascript/*.cpp |
| build/linux/modules/ruby/librubymodule.so | poco third library(http://pocoproject.org/)
libruby third library kroll/modules/ruby/*.cpp |
| build/linux/modules/php/libphpmodule.so | poco third library(http://pocoproject.org/)
kroll/modules/php/*.cpp |
四、Titanium静态分析
该部分主要是说明整个Titanium的阅读工作量、弄清楚Titanium中定义的核心对象的功能作用,以及各个模块之间的关系是什么。
4.1 代码统计
这里,将Titanium项目代码分成kroll和功能模块扩展两部分代码来统计,数据如下两表所示:
| Kroll模块代码量统计 | ||||||
| Language | Files | Blank | Comment | Code | Scale | Equiv |
| C/C++ Header | 1168 | 35490 |
63506 |
111461 |
1.00 |
111461 |
| HTML | 386 | 1252 | 16112 | 51375 | 1.9 | 97612.5 |
| C++ | 162 | 6401 | 7046 | 33133 | 1.51 | 50030.83 |
| Javascript | 47 | 3273 | 1598 | 13214 | 1.48 | 19556.72 |
| CSS | 3 | 554 | 41 | 2720 | 1 | 2720 |
| Object C | 6 | 359 | 312 | 1400 | 2.96 | 4144 |
| Python | 10 | 260 | 185 | 1206 | 4.2 | 5065.2 |
| Shell | 11 | 56 | 157 | 234 | 3.81 | 891.54 |
| Make | 3 | 30 | 29 | 93 | 2.5 | 232.5 |
| Assembly | 1 | 15 | 39 | 57 | 0.25 | 14.25 |
| Ruby | 1 | 10 | 0 | 54 | 4.2 | 226.8 |
| Yaml | 1 | 0 | 0 | 12 | 0.9 | 10.8 |
| SUM | 1802 | 47938 | 89263 | 217012 | 1.35 | 293546.95 |
| titanium_desktop模块(排除Kroll模块) | ||||||
| Language | Files | Blank | Comment | Code | Scale | Equiv |
| Javascript | 118 | 5801 | 3276 | 28678 | 1.48 | 42443.44 |
| C++ | 125 | 4690 | 5169 | 27320 | 1.51 | 41253.2 |
| C/C++ Header | 159 | 1647 | 3443 | 7682 | 1 | 7682 |
| HTML | 49 | 347 | 39 | 3715 | 1.8 | 7058.5 |
| Ruby | 29 | 673 | 643 | 3227 | 4.2 | 13553.4 |
| CSS | 5 | 542 | 41 | 2655 | 1 | 2655 |
| Python | 45 | 601 | 664 | 2632 | 4.2 | 11054.4 |
| C | 1 | 167 | 237 | 1925 | 0.77 | 1482.25 |
| Shell | 13 | 60 | 158 | 251 | 3.81 | 956.31 |
| PHP | 5 | 37 | 1 | 179 | 3.5 | 626.5 |
| XML | 5 | 0 | 8 | 151 | 1.9 | 286.9 |
| Object C | 2 | 31 | 15 | 119 | 2.96 | 352.24 |
| SUM | 556 | 14596 | 13694 | 78534 | 1.65 | 129404.14 |
4.2 核心对象的介绍
| 对象 | 基类 | 说明 |
| AccessorBoundObject | StaticBoundObject | 对setter和getter的封装,当用户访问想访问XXX属性时,该对象会调用setXXX方法或者getXXX方法。目前Titanium中主要是JS的Titanium对象使用AccessortBoundObject封装 |
| AccessorBoundMethod | StaticBoundMethod | 用于通过属性的方式访问方法,由该对象封装的方法,会自动的导出setter和getter方法 |
| AccessorBoundList | StaticBoundList | 用于以属性的方式访问list对象,由该对象封装的list,会自动导出setter和getter |
| ArgList | 对参数列表对象的封装 | |
| Blob | 对数据封装,可以描述任何数据 | |
| Tuplex | 对元组对象的封装 | |
| DelegateStaticBoundObject | KObject | 用于对全局访问对象的封装,目前Titanium中只有UI和Titanium JS对象使用该对象封装 |
| KList | KObject | 封装List对象 |
| KMethod | KObject | 对方法的封装,所有扩展语言的函数,都需要用该对象封装 |
| KEventObject | AccessorBoundObject | 描述事件对象,JS中可以通过该对象,向主线程发送事件。比如重新载入页面、弹出对话框。 |
| KEventMethod | KEventObject | 对事件方法的封装,目前只有ti.Process模块使用该对象 |
| KObject | ReferenceCounted | 所有的其他类型语言对象和方法都是继承该类,这样可以按照相同的方法处理不同语言对象和方法 |
| StaticBoundList | KList | 静态列表,使用内部map绑定属性 |
| StaticBoundMethod | KMethod | 静态方法 |
| StaticBoundObject | KObject | 静态对象,继承该对象可以很方便的设置对象的属性、方法。
每个StaticBoundObject内部,都保存着一个String到ShareValue的map成员属性。 |
| Value | ReferenceCounted | 描述对象类型 |
4.3 模块之间的关系
从整体框架结构上来看,可以将Titanium分成三个部分,最上层是WebKit以及针对WebKit的扩展(修改很少),中间层是kroll可以将其看成是一个中间件,最下层是个个模块的扩展。模块之间的关系如图所示:
以下从WebKit、Kroll和模块扩展三个部分来说明
WebKit: 当WebKit引擎解析页面数据发现<script>标签,或者当用户触发了页面中某个与脚本函数相关的控件时,WebCore会将相应的脚本代码片段传递给JavascriptCore解析执行。如果对比Tinanium修改的WebKit代码和原始的WebKit代码(http://www.webkit.org)会发现,tinanium对WebKit的修改是及小的。主要是作了两个方面的工作:首先,tinanium扩展了KURL的处理,增加了ti://, app://等私有协议的支持。再者,在WebKit/gtk/webkit/目录中,添加了几个接口函数(主要是用来处理扩展的协议和注册解析器),其中最重要的是webkit_titanium_add_script_evaluator,该接口在Kroll模块的script类中会被调用,用来向WebKit引擎注册一个Evaluator Proxy。
Kroll和Base Module:这部分主要的职责是负责Javascript的方法、对象和Python、Ruby、PHP等语言之间相互转换、事件处理,以及模块动态加载。Kroll模块中,定义了一个host对象,这个对象是整个TestApp的主线程,UI初始化、WebKit初始化和事件处理都是在host中完成的。host对象中保存了一个全局对象表,该表会在WebKit引擎、Python引擎、Ruby引擎之间以KObject中间对象形式相互传递,最终达到不同语言之间的相互调用。
API Extension:这里扩展了大量的与系统平台功能相关的API共Web应用使用。其中最重要的一个对象是ti.UI,该模块负责UI相关的资源、事件处理、GTK主界面的创建、Tininum JS对象的创建。
五、Titanium动态分析
下面从6个方面以TestApp为例,来分析Titanium的主要特性和功能。
5.1 TestApp初始化
TestApp的启动过程有个自启动过程。首先,TestApp启动后会创建一个Application对象,该对象会从Mainifest文件中获取App相关的资源,并且保存在一个全局变量中。然后,TestApp会设置几个系统环境变量:
KR_BOOTSTRAPPED: 描述是否已经初始化环境变量以及构建Application对象
KR_HOME:描述运行程序的HOME路径
KR_RUNTIME:描述运行时资源路径
KR_MODULES:描述需加载模块信息
LD_LIBRARY_PATH:描述模块所在的文件夹路径
最后,TestApp会使用exec系统调用将自己自启,然后通过之前设置 KR_BOOTSTRAPPED环境变量判断是否进入下一阶段的初始化过程。如果 KR_BOOTSTRAPPED设置为YES,则会首先将其unset,然后启动LinuxHost。
在titanium框架中,使用动态库的方式将模块之间的关系解耦合。在TestApp启动的第二阶段中,StartHost(kroll/boot/boot_linux.cpp)会根据之前设置的 KR_RUNTIME路径信息,找到libkhost.so动态库,然后从libkhost.so中获取Execute函数指针,并且调用(这里有个问题,如果多个实列同时运行,有可能KR_RUNTIME尚未unset就启动第二个应用,则会出现TestApp异常)。
libkhost.so动态库中的Execute方法,首先创建一个Host实例,在这里是LinuxHost对象,然后调用该对象的Run方法进入一个循环。这个循环是整个TestApp的主循环,主要负责模块的动态发现和加载,事件处理。在LinuxHost的实现中,会维护一个job队列,通过定时器的方式,每隔250ms的时间会去检测该job队列中是否有job存在(LinuxJob描述)。如果,事件存在则会一次性将所有的事件取出,并且清空事件队列,然后一个个的执行job对象的Execute方法。
TestApp的两次初始化过程如下图所示:
5.2 模块初始化
TestApp程序创建LinuxHost对象,并且执行Run方法之后,会首先扫描KR_MODULES环境变量中指定的模块,并且从 LD_LIBRARY_PATH定义的路径信息中寻找到这些动态库模块,并且加载(调用相应模块的Initialize方法)。LinuxHost首先加载的是基本模块(API,PythonModule、RubyModule、PHPModule和JavascriptModule)。
以pythonModule为例,描述一个完整的加载过程:
- Host::LoadModules从环境变量中获取到python动态库的路径信息
- 调用Host::FindBasicModules方法,将libpythonmodule.so文件加载进来,然后调用PythonModule的Initialize方法
- PythonModule::Initialize首先会向全局属性表中创建一个Python和PythonEaluator对象的关联。前面也说到,Host对象会保存一个全局属性表,这个表中使用KObject中间对象形式,将JAVASCRIPT、PYTHON、RUBY、PHP等语言定义的对象封装,并且保存在该表中。运行时,可以使用Host对象的GetGlobalObject方法获取。
- 调用Script::AddScriptEvaluator静态方法将PythonEvaluator对象放入Script对象中维护的一个Ealuator列表中。当JavascriptCore引擎发现<script>标签会遍历这个evaluator链表,通过MIME类型找到相应的解析器实例,然后将代码片段传递给相应的解析器处理(这样就可以支持HTML代码中内嵌多种语言)。
这里还有一个模块比较特殊需要仔细说明,即ti.UI模块。该模块负责WebKit引擎初始化,GTK窗口创建以及UI事件的处理。加载过程类似PythonModule,首先Host对象找到libtiuimodule.so动态库,然后调用Initialize方法初始化,ti.UI模块中的Initialize方法只做了两件事情,创建了一个APIBinding对象,然后将“API”属性和APIBinding关联起来,保存在全局属性表中。接下来,当Host::LoadModules方法加载完毕所有的动态库后,会调用Host::StartModules来启动模块(在整个TestApp运行中,只有ti.UI模块的Start方法被重载了,而其他模块在StartModules方法被执行时,什么事情都没有做)。UIModule::Start方法做了三个非常重要的操作:1、创建GtkUIBinding对象,并且将“UI”和该对象绑定,存放在全局属性表中。2、调用ScriptEvaluator::Initialzie()使用WebKit扩展中导出的webkit_titanium_add_script_evaluator函数,将自己注册到JavascriptCore中。3、创建初始化WebView。
至此,WebKit引擎已经初始化完毕、UI界面已经初始化完毕、相应语言的解析器以及JAVACRIPT API扩展对象已经添加到全局属性表中。但是至此,页面中是无法正常访问Titanium对象的。
整个过程如下图所示:
5.3 Titanium对象的注册
Javascript中的Titanium并没有通过硬编码的方式定义该名称,而是在构建的过程中通过变量的方式指定的。在构建规则中有GLOBAL_NS_VARNAME变量,该变量名称会作为编译参数传递,代码通过改变量的定义,来确定Javascript可见的Titanium主对象的名称是什么。
当webView构建完毕后,Titanium Js主对象并不存在,只有当第一次WebKit遇到脚本代码时,这个对象才被创建。当WebKit引擎碰到脚本对象时,会调用JavascriptCore的initScipt方法,初始化Javascript引擎。在此JavascriptCore会将我们之前调用webkit_titanium_add_script_evaluator增加的Evaluator代理和JavascriptCore解析关联起来。当引擎和资源都初始化完毕,会向FrameClient发送object avaliable通知,会调用FrameLoader::dispatchWindowObjectAvaliable()方法。这个方法会导致UserWindow::RegisterJSContext()方法的调用。然后UserWindow对象会创建一个DelegateStaticBoundObject对象来描述Titanium对象,并且将之前初始化完毕的Titanium API对象(KObject)与Titanium对象关联起来,然后将其放入到全局属性表中。这样,之后Web应用程序就可以从全局属性表中访问到Titanium对象了。但是Titanium对象中有哪些子属性是不知道的,这是运行时才去确定。
这个初始化过程如下图所示:
5.4 事件系统
Titanium的事件系统分成两个部分来说明,一部分是如何获取事件,另一部分是如何向事件系统中增加新事件。
当linuxHost::RunLoop循环被调用后,立即会注册一个定时器,每隔250ms调用一次main_thread_job_handler函数。该函数首先通过GetJobs方法获取当前系统中未处理的事件,并且依次的调用事件对象(LinuxJob)的Execute方法执行。如果系统没有未决的事件存在,则立即返回。
事件的添加和触发均通过LinuxHost::InvokeMethodOnMainThread()方法完成。该函数会创建一个LinuxJob对象,并且插入到事件队列尾中。事件的触发有多种可能性,可以是由Javascript代码触发,也可以是内部事件触发,同样也有可能是UI事件触发。
由于Titanium扩展的JS API在C层上都会与相应的C方法对应,当Web应用程序调用相应的JS方法,对应的C方法会被调用,该方法则会使用LinuxHost::InvokeMethodOnMainThread()方法,向主线程发送事件处理请求。
对于UI来说,在Linux平台上所有的UI相关的事件首先是被GTK的应用框架截获,当有UI事件到来时,ti.UI模块会创建对应事件的KEvent对象(在event.h中定义),然后调用KEvent对象的Fire方法,触发事件。该方法会间接的使用LinuxHost::InvokeMethodOnMainThread方法向LinuxHost事件队列注册事件。
这个过程如下图所示:
5.5 访问Titanium对象属性和方法
比如,有一段Javascript脚本中调用Titanium.API获取Titanium对象的API属性,当JavascriptCore解析到这部分代码时,并不知道这个对象是什么类型的,完全当作一个抽象的JSValue对象来对待,因为对于Javascript引擎来说,并不需要时刻知道对象是什么,有那些属性和方法,只有到运行时才会去用查询该对象中是否存在指定的属性或者方法(JavascriptCore内部维护了一张属性表,通过查询方式获取相应属性或者函数的处理函数指针。这点V8做的就比较高明,用类的方式描述,动态将对应JS的方法和属性转换成C++的成员函数和成员变量,大量的减少了访问时间)。由于API这个对象是由Kroll创建的,是一个KObject对象,在挂载到Javascript 全局属性表之前,Titanium会调用KObjectToJSValue方法,将KObject对象转换成一个JS的Object对象,并且设置了几个比较重要的回调函数:
- HasPropertyCallback: 当查询属性时候被调用
- GetPropertyCallback: 当获取指定属性时被调用
- SetPropertyCallback: 当设置指定属性时被调用
当javascript访问用Titanium对象的API属性时,通过JSValue.Get方法会调用到GetPropertyCallback函数,该函数会查询KObject对象中(这里是说的Titanium)是否有API这个属性,如果有则转换成JSValue对象,并且返回给Javascript引擎。
如果这里访问的是一个属性的方法,过程和访问对象是一样的,只不过最后创建的是一个Function Js对象,而非Object对象。当Javascript访问这个返回的Function对象时,Javascript引擎会调用callAsFunction方法,而该方法会引发CallAsFunctionCallback回调函数被调用(该函数是静态函数在KMethodToJSValue函数中注册)。这样通过CallAsFunctionCallback这个回调接口,调用实际的KObject的Call方法,执行实际的处理函数。
属性访问过程如下图所示:
5.6 Javascript、Python、Ruby动态语言间的相互调用
Titanium框架中引入了一个比较有意思的特性,即支持多种语言之间的相互调用。从实现技术角度来说,Titanium的多语言支持的设计思想,是在学习了WebKit的Binding机制而发展过来的。主要用到了JavascriptCore引擎可以动态注册Evaluator的机制。HTML语言中定义了<script>标签,用于内嵌脚本语言,该标签有个子属性type,通过该属性可以让浏览器引擎区分是什么类型的脚本。加入我们有如下的脚本代码:
<script type=”text/python” src=”xxx.py”></script>
首先,WebCore引擎会解析HTML页面数据,当发现有<script>标签出现,则会创建HTMLScriptElement,对于script有两种处理情况,一种是如上代码通过src包含一个脚本路径,还有一种情况是定义一段代码,通过控件或者超连接的方式以事件方式触发。如果是第一种情况,则会在创建HTMLScriptElement的时引发ScriptElementData::requestScript方法的调用。如果是第二种情况,则会在触发相应事件时候调用FrameLoader::executeScript方法执行脚本。最终都会调用JavascriptCore中的EvaluatorAdapter::evaluate()。由于在初始化ti.UI模块时,我们已经注册了自己的evaluator(ScriptEvaluator),因此会将获取的脚本信息传递给ScriptEvaluator,在该对象中,会通过Script::Evaluate()方法,根据传递下来脚本的MIME类型(也就是script中text字段定义的类型)派发给注册的不同解析器去执行。
这里以Javascript调用Python代码,并且Python代码中又调用了ruby代码为例子说明其调用过程。
Python代码:
def abc():
ruby_fun()
Ruby代码:
def ruby_func()
…
end
当pythonEvaluator::Evaluate()被调用后,首先将保存的全局JS对象表转换成Python可识别的对象字典(KMethodToPyObject完成,转换成PyKMethodType的Python对象)。这样在之后编译的Python代码中就可以访问到这些对象。然后将Python代码使用Python编译器编译,并且将编译后的函数对象转换成KObject对象,插入到全局的JS对象表中(abc)。这样,Javscript,和其他语言都可以识别该对象。同样,对Ruby函数的处理,也会首先将全局JS对象表中的KObject对象转换成Ruby的对象,然后对Ruby函数进行编译,将新生成的Ruby函数对象(ruby_fun)转换成KObject对象,然后从新更新JS全局对象转换表。至此,全局JS对象表中就新增了两个KObject对象:ruby_fun, abc。
当javascript访问该abc函数对象时,按照通常方式首先调用CaAsFunctionCallback,该函数会调用KObject的Call方法,由于该KObject实际上就是一个KPythonMethod对象,因此KPythonMethod对象的Call方法会被调用。之前我们注册的Python方法是一个PyKMethodType,该类型中定义了一个方法回调函数,当Python方法被调用时,该回调函数(PyKMethod_call)会被调用。这个例子中,在Python代码里调用了ruby_fun,因此当PyKMethod_call被调用时,首先将调用的KObject对象(实际上是一个对Ruby函数对象的封装)转换成KMethod对象,然后调用Call方法。这样就通过间接调用,调用到KRubyMethod的Call方法,使得Ruby函数得到执行。
整个过程如下图所示:
六、参考资源
http://www.appcelerator.com/
From Jelly's Blog, post Titanium 架构分析
Post Footer automatically generated by wp-posturl plugin for wordpress.










