使用iPhoto和微云管理照片

自从上次移动硬盘损坏,丢失了不少珍贵的照片,一直心有余悸,现在保存资料都会进行双备份。近一两年,云存储市场风起云涌,越来越多的企业加入战团。不仅免费,而且容量越来越大。当腾讯宣布微云免费容量10T后,我毫不犹豫的就申请了一个账号,将资料都保存在云端(基本上是一些照片和视频,对于普通老百姓,隐私从来都不是大事),形成本地和云端双保险。喜上加喜,近期新入手了一台iMac,Mac OS自带的iPhoto应用程序让人爱不释手,管理照片起来非常方便,可以直接导入RAW文件,还提供简单的编辑功能。但iPhoto通常是将相机照片导入到自己的库中,这样会出现iPhoto和微云两套照片,有点浪费空间。在琢磨了一段时间后,总结了一套自己的照片管理方法。

阅读全文 →

Chromium网络加载速度研究(2)

本文主要内容翻译自chromium的文档NetLog: Chrome’s network logging system,如果您觉得文章写的不明白,请参看原文。

通过<<Chromium网络加载速度研究(1)>>这篇文章的分析,可以得出结论,云端加速是高富帅的玩法。对于个人开发者和小公司而言,是没有那个实力去部署强大的数据中心的。所以接下来还得继续研究,看看有没有其它的途径提高网页加载速度。在着手优化之前,我们需要一个profiling手段找出瓶颈,有目的的优化。另一方面,也需要考虑如何评估优化的效果。如果没有一个客观的评估,优化方案是否有效心中就没有底。谈到Profiling,大多数人估计都会想到log大法,就是在程序的一些关键位置打上log。log通常包括时间、事件、状态等。在chromium中,开发人员早就预料了此类需求,设计了一套强大的NetLog。下面就分析一下chromium中NetLog的设计、实现及其用法。

阅读全文 →

Chromium网络加载速度研究(1)

本文主要内容翻译自Google的文档Data Compression Proxy,如果您觉得文章写的不明白,请参看原文。

对于一款浏览器而言,速度无疑是非常重要的,其中加载速度更是重中之重。UC浏览器很早就采用了云端加速技术,所以在网页加载速度方面一直很有优势。现在越来越多的浏览器,如Opera、QQ浏览器都采用了服务器端加速技术。Chrome移动版从V33开始正式支持数据压缩代理。下面就分析一下Chrome for Android所采用的数据压缩技术。

阅读全文 →

Android 4.4 WebView实现分析

随着Android4.4的发布,Android WebView改成由Chromium驱动了。浏览Android4.4源代码,可以看到之前版本中的external/WebKit目录被移除掉了,取而代之的是chromium_org。也就是说chromium已经完全取代了之前的WebKit for Android。虽然chromium完全取代了以前的WebKit for Android,但Android WebView的API接口并没有变,与老的版本完全兼容。这样带来的好处是基于WebView构建的APP,无需做任何修改,就能享受chromium内核的高效与强大。下面就Android 4.4中WebView的实现进行分析。

先来看看frameworks/base/core/java/android/webkit下的文件,里面有我们熟悉的WebView、WebSettings、WebViewClient等类,其中最主要的就是WebView了,相关的类关系如下:

从图中可以看出,设计者抽象出了一个WebViewProvider接口,WebView本身并不做事,所有的处理就是交给WebViewProvider。而WebViewProvider只是一个接口,由实现者决定采用哪种引擎。从Android4.1开始,就采用了这种设计架构,开始为chromium核心的WebView做准备。在Android 4.4之前的版本,由WebViewClassic实现WebViewProvider接口。在Android4.4中,则是WebViewChromium实现WebViewProvider接口。WebViewFactory也采用了相似的结构,决定了如何实例化WebViewFactoryProvider,WebViewFactoryProvider有一个关键的接口createWebView,创建具体的WebViewProvider。(注:AOSP master源码基于WebKit的实现仍然保留,但在Android4.4中已经移除,由于采用了灵活的架构,所以在两种核心之间非常容易切换)

阅读全文 →

调试ContentShell启动过程的方法

本文探讨的如何调试ContentShell for Android的启动过程,主要是C++部分代码的流程,这对于理解ContentShell的启动流程、render进程是如何创建并启动的有很大的帮助。

理解Contenthell启动流程的最好方法是使用调试器逐步跟踪,不过chromium在很多地方采用了异步方式,单步跟踪相当困难,比较好的方法是在关键代码处打上断点,查看调用栈。由于ContentShell程序一启动,就会执行启动流程,启动主进程和render进程,并加载网页,这时再用gdb attach到进程,往往我们希望跟踪的代码已经执行过去了。一种比较土的方法就是在程序入口处加上sleep语句,在程序暂停时,执行gdb attach到进程,然后在关键代码处设上断点。这种方法有点不好的地方就是sleep的时间不好定,定短了,gdb还没有attach上去,程序已经执行过了。设长了,每次等的时间就长了。

其实chromium开发者已经考虑到这种需求,所以加入了一个命令行参数:wait-for-java-debugger。查看代码,可以发现在ContentShellActivity.java中就有一个方法waitForDebuggerIfNeeded(),其作用就是等待java调试器attach之后才继续往下执行。所以本文介绍的方法就是jdb/gdb组合来调试ContentShell启动过程。

  1. 修改content/shell/android/shell_apk/AndroidManifest.xml,在application节点加入android:debuggable=“true”,然后编译ContentShell,安装到连接的android设备
  2. 这里下载jdb_content_shell脚本,放到build/android目录下。
  3. 执行如下命令增加命令行参数:

    adb_content_shell_command_line –wait-for-java-debugger

  4. 启动android设备上的ContentShell。应该可以看到界面只是一个空白界面,程序没有接着往下执行。

  5. 执行adb_gdb_content_shell命令启动gdb调试,设置断点。
  6. 执行jdb_content_shell命令,可以观察到程序在继续往后执行。

[译]Android WebView

原文地址:https://crosswalk-project.org/#wiki/android-webview

  • 架构&组件

    • AOSP中的Android WebView将基于AwContents,其位于Content API和Content View之上。
    • 与Content API不同,它还依赖于从最初的chrome browser分离出来的组件,组件位于components/下,包含auto_login_parser,navigation_interception,visitedlink_browser,visitedlink_renderer和web_contents_delegate_android,这些都是用于页面。
    • 无渲染进程(进程内渲染),无沙盒机制(sandbox机制)
    • 图形架构和Content Shell有很大的不同,且正在开发中,将支持软件和硬件输出。
  • API

    • API类似于EFL WebView,不会暴露内部的browser/renderer/gpu进程。
    • 能够加载URL,接收加载过程中的事件和回调。
    • 可通过addJavaScriptInterface扩展JavaScript API,允许JavaScript调用Java代码,反之这不支持。
    • 提供了若干API接收页面状态改变通知和页面回调。
  • 消息循环和事件处理

    • 整合利用Android消息处理
    • native代码设置定时器到Java侧的消息循环
  • 图形

    • 渲染使用chromium合成器,开启了硬件加速
    • 合成器输出支持软件和硬件Surface
    • 硬件加速的架构和content shell不同,和ChromeOS类似。现在还处在早期开发阶段,使用了UberCompositor,支持软件输出
  • WebAPI扩展

    • API addJavaScriptInterface可用于JavaScript API扩展,但支持JavaScript调用Java代码。
    • 相反方向的调用(Java -> JavaScript)不支持
    • addJavaScriptInterface通过NPObject机制(之前用于NPAPI插件)支持进程外访问,虽然在WebView中不再需要。
    • 来自Java侧的事件通知貌似还不支持
  • API细节和涵盖范围

    • WebViewClient用于页面状态改变通知
    • WebChromeClient包含参与渲染逻辑的回调
    • 原生代码/JS交互
    • 页面加载
    • 导航&历史记录
    • 文字搜索
    • 事件处理
    • 轨迹球、触摸、键盘
    • 不支持鼠标事件
    • 设置:编码
    • 页面信息:标题、favicon
    • HTTPS/SSL
    • 页面加载状态、缩放、重定向、URL重载,等等
  • 源码

浏览器插件扩展研究

首先说明一下,本文探讨的并非NPAPI和PPAPI这种插件技术,而是一种扩展浏览器功能的方法,并且范围仅限于Android平台。插件技术在软件系统中得到了广泛的应用,程序员熟悉的Eclipse就是因为有着极好的插件扩展机制,几乎可以作为所有的程序设计语言的IDE。一般人熟悉的媒体播放器,也使用到了插件技术,以支持不同的编码和格式。甚至photoshop中的滤镜,也算的上是插件。

百度百科上对插件的定义为:

插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。

插件和应用程序的最大区别在于插件必须依赖于应用程序才能发挥自身功能,仅靠插件是无法正常运行的。相反地,应用程序并不需要依赖插件就可以运行。一般而言,插件具有以下特征:

  • 独立于应用程序部署,可以被应用程序动态加载。比如Eclipse的插件,可以直接放到plugins目录,就可以被Eclipse识别并动态加载。视频解码器可以单独安装,媒体播放器可以根据视频格式自动加载。
  • 具有自描述元数据,比如插件名称、icon、版本、描述等,能够以统一的方式被应用程序检索到。
  • 遵循应用程序的应用程序接口,插件一般只针对特定的应用程序而开发。

要实现插件机制,需要解决如下问题:

  • 自注册。要求插件能够独立于应用程序记录某些信息,让应用程序能够找到插件,并且加载。比如Windows下可以通过注册表。
  • 动态加载。要求应用程序能够动态的加载插件,而不是静态绑定。比如Java应用程序可以通过反射机制动态。
  • 插件管理。应用程序一般要求能够安装、卸载、禁用插件,显示插件信息。

Android插件系统设计

Android是采用现代设计思想而设计的移动操作系统,其天然的组件架构为设计插件系统提供了极大的便利。我将按照如下思路设计插件:

  • 插件都设计为Service。通过AIDL定义插件和应用程序之间的应用程序接口,插件实现应用程序定义的接口,应用程序通过Android IPC机制调用接口。插件和应用程序位于不同的进程控件,避免由于插件崩溃而影响到应用程序的运行。
  • 插件可以有UI,也可以是纯实现功能的后台服务。插件编译为单独的apk包,可独立于应用程序部署/卸载。
  • 应用程序通过Android Package Manager发现插件。为了区分应用程序所属的插件,可以定义一个特殊的Intent(比如aexp.intent.action.PICK_PLUGIN),并在插件的AndroidManifest.xml中定义Intent filter。
  • 利用Android的元数据(meta-data)定义插件信息,比如名称、描述、版本等等。
  • 应用程序监听 ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED和ACTION_PACKAGE_REPLACED事件来动态的更新插件列表。
  • 利用Android的Receiver机制,实现应用程序和插件之间的事件交互。

插件的例子请看参考文档,无法翻墙的同学看这里

浏览器插件框架设计

Chrome浏览器的extensions技术非常强大,让广大的开发人员使用html/css/js就能扩充Chrome浏览器的功能。Google甚至还打造了chrome web store,上面已经有成千上万个插件,有很多新奇和实用的插件,不得不佩服程序员的创造力。不过Chrome for android尚不支持extensions,在开发Browser,曾考虑过enable chromium的extensions开关,后来有些问题实在无法解决,只能放弃。

为了让插件能够更好的扩充浏览器功能,浏览器也需要开放一些接口和事件给插件,比如:

  • tabs操作,包括新建tab、关闭tab,在tab上加载指定的URL
  • bookmark
  • history

如果想要设计一份完备的浏览器编程API,建议参考Chrome extensions文档,虽然是以js提供的,但非常完备,具有极高的参考价值。

参考文档

  1. MY LIFE WITH ANDROID :-) Plugins

  2. MY LIFE WITH ANDROID :-) plugins with user interface

浏览器中实现阅读模式

这里对阅读模式的定义是:去除网页上与主题内容无关的图片、导航、广告等等,给用户提供一个清爽的网页。在PC浏览器中,chrome/firefox都有这样的扩展/插件。我个人用的最多的是chrome上的印象笔记.悦读插件,和evernote整合,非常好用。从IOS 6开始,safari浏览器也提供了阅读器功能,用户界面如下:

safari_readmode_1 safari_readmode_2

当网页中主题内容比较明确、且文字内容较多时,地址栏上就会出现“阅读器”按钮,点击“阅读器”按钮,就会提供给用户一个清爽的网页。更绝的是,如果网页内容包含多个分页,“阅读器”模式下还可以自动整合,无需费力的点击下一页了,相当贴心,如下图所示:

safari_readmode_3

如果要在浏览器中实现这一个功能,可以有三种方法:

1、服务器端对网页进行处理

其思路和百度转码相同,就是在服务器端对网页进行处理,UC浏览器能够做到省流量,也是采用了这一技术。不过百度转码主要目的是为网页瘦身,所以处理出来的效果惨不忍睹。国外有一家公司,提供了免费的readability API,其口号是:

Transforms any article … into a calm, humane, readable experience

详情请访问http://www.readability.com

使用国外免费的API,实现简单,无需架设服务器,但缺点也是明显的,速度不够理想,容易被墙,说不定哪天就不能使用了。

2、修改webkit,对DOM树进行处理

webkit中,有一个步骤就是解析html,形成DOM树,有了这个DOM树就好办了,有什么节点、节点属性、节点样式都一目了然。所以接下来要做的事情就是筛选主题内容,过滤无关紧要的节点。至于采用什么算法进行筛选,还没有找到现成的C++库,网上有一个C#库:NReadability 可供参考,如果对javascript比较熟的话,直接参考readability提供的js脚本也可以。

通过修改webkit实现阅读模式,速度快,更容易和浏览器界面进行整合(比如,safari中,只有文字较多的网页才会出现阅读器),也比较适合对js前端开发不熟悉的C++程序员,缺点是webkit比较复杂,需要对webkit了解深入,而且之后的维护也是个麻烦事情。

3、注入js,对DOM树进行处理

javascript具有操作DOM的能力,也可以应用样式表来修改节点的样式,所以通过注入JS脚本来实现阅读模式是一个不错的选择。chrome/firefox中扩展插件,或者一种称为bookmarklet的小片代码就是这样实现的。

回到chromium for android,ContentView提供了evaluateJavaScript方法为当前ContentView注入javascript,所以实现起来非常容易。

TODO

在safari中,实现了多页拼接的处理。Readability在一篇文章中也声明实现了多页合并的处理,不过我对javascript研究的不够多,还没有弄清是如何实现的。如果有精通web前端开发的同学一起探讨,非常欢迎。

SublimeText配置

在我的一台开发机上在ubuntu系统下使用eclipse,经常会出现死机,不得不寻求其它的代码编辑器。windows下大名鼎鼎的source insight放到linux下就状况不断。后看到chromium项目推荐了sublime text这款编辑器,用起来感觉还不错。下面记录一下使用过程中的一些设置,网上各种资料比较凌乱,所以就稍微整理一下,供自己今后翻阅。

1. 过滤不相关的文件

可以针对某个特定的工程,进入菜单project | Edit Project,编辑当前工程的sublime-project文件,加入两个过滤器,folder_exclude_patternsfile_exclude_patterns,如下所示:

"folders":
[
    {
        "path":"/home/alex/android/source/android4.2",
        "file_exclude_patterns":
        [
            "*.o"
        ],
        "folder_exclude_patterns":
        [
            ".repo"
        ]
    }
]

2. 模拟eclipse的快捷键

在eclipse中,有几个快捷键用的非常多,分别为:

CTRL+TAB 头文件和c/c++文件之间切换
CTRL+SHIFT+R 对话框中输入文件名(有联想功能),快速打开文件
CTRL+o 输入方法(函数)名,可快速定位到方法(函数)
CTRL+l 输入行号,快速定位到指定行
CTRL+h 打开文件搜索对话框

可以修改sublime text的Key Bindings达到模拟eclipse快捷键的效果,有两个配置项可以修改,一个是Key Bindings-Default,一个是Key Bindings-User,最好修改后一个配置文件。进入菜单Preferences | Key Bindings – User,加入如下行:

[
{ "keys": ["ctrl+shift+r"], "command": "show_overlay", "args": {"overlay": "goto", "show_files": true} },
{ "keys": ["ctrl+o"], "command": "show_overlay", "args": {"overlay": "goto", "text": "@"} },
{ "keys": ["ctrl+l"], "command": "show_overlay", "args": {"overlay": "goto", "text": ":"} },
{ "keys": ["ctrl+t"], "command": "show_overlay", "args": {"overlay": "goto", "text": "#"} },
{ "keys": ["ctrl+tab"], "command": "switch_file", "args": {"extensions": ["cpp", "cxx", "cc", "c", "hpp", "hxx", "h", "ipp", "inl", "m", "mm"]} }
]

3. 针对项目的代码风格设置

chromium的项目的代码缩进为两个空格,而其它项目的代码缩进一般为4个空格,所以我就希望针对特定的项目进行代码风格设置,方法是编辑当前工程的sublime-project文件,加入如下行(和folders项平级):

"settings":
{
"tab_size": 2,
"translate_tabs_to_spaces": true,
"trim_trailing_white_space_on_save": true,
"draw_white_space": "all"
}

 4. 插件集合

sublime_text的插件扩展机制非常强大,已经有很多人出于不同的需求,开发出各种插件。首先说一下插件的安装。

要安装插件,最好是先安装插件包管理器,最简单的方法是在sublime_text控制台(可以通过ctrl+`快捷键或View > Show Console菜单打开),在控制台中粘贴如下python代码:

1
import urllib2,os,hashlib; h = '7183a2d3e96f11eeadd761d777e62404' + 'e330c659d4bb41d3bdf022e94cab3cd0'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler()) ); by = urllib2.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); open( os.path.join( ipp, pf), 'wb' ).write(by) if dh == h else None; print('Error validating download (got %s instead of %s), please try manual install' % (dh, h) if dh != h else 'Please restart Sublime Text to finish installation')

按ctrl+shit+p显示命令列表,在其中输入install,选择Package Control: Install package,接着就会出现插件列表,选择你所需要的插件即可。

4.1 GBK编码支持

  1. 按ctrl+shit+p显示命令列表,在其中输入install,选择Package Control: Install package,待列表获取完成再输入GBK Encoding Support,等安装完毕就可以了。
  2. 按照上述方法安装Codecs26和ConvertToUTF8
  3. 重启sublime_text,打开文件后,选择菜单 File | Reopen with Encoding | UTF-8 即可

WebKit的JavaScript对象扩展

本文的内容主要参考网上收集的资料,不过在Android 4.0 webkit上做扩展时,碰到一些问题,觉得有必要记录下来。

所谓扩展JavaScript对象,就是增加一个JS对象,但它并没有定义在标准的JS对象集合中。如果网页中包含了扩展的JS对象,使用普通的浏览器就会报JS错误。

下面以添加HelloObject对象为例说明具体步骤,该对象具有description属性:

  • 添加HelloObject.h, HelloObject.cpp, HelloObject.idl文件,简单起见,将这三个文件放到Source/WebCore/page目录下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef HelloObject_h   
#define HelloObject_h 

#include <wtf/PassRefPtr.h>  
#include <wtf/RefCounted.h>

#include "PlatformString.h"

namespace WebCore {

class HelloObject : public RefCounted<HelloObject> {
public:
    static PassRefPtr<HelloObject> create() { return adoptRef(new HelloObject()); }

    String description() const;
private:
    HelloObject();
};

} // namespace WebCore

#endif // HelloObject_h

HelloObject.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "config.h"
#include "HelloObject.h"

namespace WebCore {

HelloObject::HelloObject()
{
}

String HelloObject::description() const
{
    return "Hello Object";
}

} // namespace WebCore

HelloObject.cpp

1
2
3
4
5
6
module window {

    interface [OmitConstructor] HelloObject {
        readonly attribute DOMString description;
    };
}

HelloObject.idl

  • 修改Source/WebCore/page/下的DOMWindow.h文件,添加如下声明:
1
2
3
4
5
6
7
class HelloObject;

public:
    HelloObject* helloObject() const;
    HelloObject* optionalHelloObject() const { return m_helloObject.get(); }
private:
    mutable RefPtr<HelloObject> m_helloObject;
  • 修改Source/WebCore/page/下的DOMWindow.cpp文件,添加接口实现:
1
2
3
4
5
6
HelloObject* DOMWindow::helloObject() const
{
    if (!m_helloObject)
        m_helloObject = HelloObject::create();
    return m_helloObject.get();
}

在DOMWindow::clear()函数中添加一行语句:

1
m_helloObject = 0;
  • 修改DOMWindow.idl文件,添加:
1
attribute [Replaceable] HelloObject helloObject;
  • 接下来需要修改编译系统,让android编译系统编译新增的文件:

首先修改Source/WebCore/Android.mk,增加page/HelloObject.cpp到LOCAL_SRC_FILES变量,其次需要修改Source/WebCore/Android.derived.v8bindings.mk,增加 $(intermediates)/bindings/V8HelloObject.h到GEN变量。(注:这个是必须的,否则就不会根据HelloObject.idl生成V8HelloObject.h文件,在编译时会出错,这也 是折腾了半天得出的成果)

至此,工作基本上完成,待webkit重新编译后,可以用如下的网页进行验证:

1
2
3
4
5
6
7
8
<html>
<body>
<script type="text/javascript">
document.write("<pThis is from HelloObject: ");
document.write(helloObject.description + "</p>");
</script>
</body>
</html>

[参考资料]

  1. webkit的js对象扩展(一)——binding方式创建自定义对象(单实例)

  2. android 上 webkit js 本地扩展之全局本地对象实现步骤