Updates from 十月, 2012 Toggle Comment Threads | 键盘快捷键

  • Roger 10:30 am on October 13, 2012 固定链接 | 回复
    Tags: android 4.1, rom,   

    Android 4.1 – 将系统浏览器编译成独立应用 

    为了方便在手机上(Galaxy Note with CM10),调试Android4.1 系统浏览器的代码,进行代码研究,我把系统浏览器编译成了一个独立的应用,不会跟ROM原来的系统浏览器产生冲突,可以很方便地在Eclipse自己建立的工程里面对Java部分的代码进行跟踪调试,理论上C++的部分也可以通过GDB进行调试。
    自己编译的库,显示Layer边界和信息
    首先系统浏览器可以认为分为3部分:

    1,Browser.Apk 一个全功能浏览器应用
    2,android.webkit 平台适配层的Java部分代码,对外提供了封装好的WebView
    3,libwebcore.so 包括WebKit的代码和平台适配层C++部分的代码,libchromium_net.so Chrome的网络堆栈
    我们实际只需要后面两部分(2和3),然后加上自己的一个简单的测试用外壳就可以了。

    首先参考官方的文档,建立ROM编译环境,编译ROM(http://source.android.com/source/index.html)。

    开始建立独立的应用(home/roger/a41是我的ROM的目录,需要替换成自己ROM的目录):
    1. 在Eclipse创建一个Android工程,把android.webkit目录下的Java代码拷贝过来;
    2. 将/home/roger/a41/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/webkit下面的EventLogTags.java也拷贝到自己的工程;
    3. 因为android.webkit下的类会使用SDK中非公开的API,我们需要解决编译错误:
      1. 创建一个User Library,并且勾选System Library的选项;
      2. 加入以下Jar包:
        1. /home/roger/a41/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
        2. /home/roger/a41/out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar
        3. /home/roger/a41/out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/classes.jar
      3. 在Java Build Path/Order and Export把创建的库放在最前面;
    4. 因为在我们应用中的android.webkit包跟SDK中的重名,所以我们需要更改包名,可以改成android.webkit2;
    5. 我们需要重新编译libchromium_net.so和libwebcore.so,并且使用另外的名字,并且把其代码中使用的android/webkit/ JNI路径改成android/webkit2/保证JNI的正确性:
      1. 在/home/roger/a41/external/chromium下面,把所有源文件的android/webkit/路径改成android/webkit2/;
      2. 打开/home/roger/a41/external/chromium/Android.mk,修改库名为libchromium_net2,并且加多一行“LOCAL_MODULE_TAGS := optional“,具体内容见后;
      3. 重新编译chromium_net,得到libchromium_net2.so;
      4. 在/home/roger/a41/external/webkit/Source/WebKit/android下面,把所有源文件的android/webkit/路径改成android/webkit2/;
      5. 打开/home/roger/a41/external/webkit/Android.mk,将库名改成libwebcore2.so,并且加多一行“LOCAL_MODULE_TAGS := optional“(需要修改两个地方,静态库编译和动态库编译),另外还需要把导入库libchromium_net改成libchromium_net2,具体内容见后;
      6. 重新编译webcore,得到libwebcore2.so;
    6. 接下来我们可以把修改后的libwebcore2.so和libchromium_net2.so push到手机的rom里面,假设路径是/data/local(如果没有写权限,用Root Explorer修改);
    7. 然后我们需要修改Java的代码,让它去加载我们自己的库,修改的地方位于JniUtil.java和WebViewCore.java,具体内容见后(加载顺序需要改变,先加载libchromium_net2.so再加载libwebcore2.so);
    8. 最后加上我们自己的Test Shell的代码,运行就OK了,如果只修改了C++的代码,重编译后再Push到手机,然后重新运行Test Shell就可以马上生效,Java的代码可以在Eclipse里面很方便的调试,C++的代码理论上也可以通过GDB进行调试;

    LOCAL_MODULE := libchromium_net2
    LOCAL_MODULE_CLASS := SHARED_LIBRARIES
    LOCAL_MODULE_TAGS := optional
    INTERMEDIATES := $(call local-intermediates-dir)


    # Define our module and find the intermediates directory
    LOCAL_MODULE := libwebcore2
    LOCAL_MODULE_CLASS := STATIC_LIBRARIES
    LOCAL_MODULE_TAGS := optional
    base_intermediates := $(call local-intermediates-dir)


    # Do not attempt prelink this library. Needed to keep master-gpl happy, no

    1. effect in master.
    2. TODO: remove this when master-gpl is updated.

    LOCAL_PRELINK_MODULE := false
    LOCAL_MODULE := libwebcore2
    LOCAL_MODULE_TAGS := optional
    LOCAL_LDLIBS := $(WEBKIT_LDLIBS)


    1. Build the list of shared libraries
    2. We have to use the android version of libdl

    LOCAL_SHARED_LIBRARIES := \
    libEGL \
    libGLESv2 \
    libandroid \
    libandroidfw \
    libandroid_runtime \
    libchromium_net2 \
    libcrypto \
    libcutils \
    libdl \
    libgui \
    libicuuc \
    libicui18n \
    libmedia \
    libmedia_native \
    libnativehelper \
    libskia \
    libsqlite \
    libssl \
    libstlport \
    libutils \
    libui \
    libz


    static {
    System.load(“/data/local/libchromium_net2.so”);

             System.load(“/data/local/libwebcore2.so”);
    }
     
    • ddy123 6:47 上午 on 十月 29, 2012 固定链接 | 回复

      不知道楼主对sony给出的webkit,使其支持webgl的代有研究没,想交流下。

      • Roger 9:59 上午 on 十月 29, 2012 固定链接 | 回复

        同事有研究过,貌似是使用离屏的Pixmap进行绘制,效率不怎么样。

    • 匿名 7:26 下午 on 三月 4, 2013 固定链接 | 回复

      博主,这里从第5步开始就不怎么明白了,我在源码external/chromium下面,没找到android/webkit目录啊。

  • Roger 11:56 am on February 4, 2012 固定链接 | 回复
    Tags:   

    Going Native? 

    赖勇浩写了一篇文章,叫做”2012 不宜进入的三个技术点“,其中最后一个就是C++。而陈皓的”Why C++?王者归来“则表达的不同的意见,文中引述了C++界的大牛Herb Sutter的观点,认为随着计算能力再次集中在大公司构建的数据中心里面,而这些公司为了能够节约电力和机器的开销,它们有意愿,也有能力聘请高水平的工程师,使用C/C++这样的本地代码来编写它们服务端的软件堆栈。另外因为移动设备的迅猛发展,考虑电力和性能的因素,本地代码编写的应用在移动设备上仍然是最佳的选择。

     

    总的来说,我个人认为这两种观点有正确和错误的地方。下面是自己了解到的一些现实世界正在发生的真实的例子,最后再总结出自己的观点。

     

    1. NodeJS使用Google公司的V8 JS引擎(使用C++编写),构建了一个可以使用JavaScript编写服务端应用的运行时环境;
    2. 浏览器引擎如WebKit(使用C++编写)的发展,使得使用HTML/CSS/JavaScript编写能够与Native App相媲美的Web App/Game越来越成为可能;
    3. Android虽然使用了大量第三方和自家的C/C++库,平台本身的系统服务层也完全使用C++编写,但是为普通应用的编写提供了一个完整的基于Davik虚拟机,使用Java语言的应用程序框架;
    4. Windows Phone7只允许使用C#编写应用,而更换了内核的Windows Phone8虽然有可能允许使用C/C++语言,但是使用HTML+JavaScript在WinRT上编写Metro风格的应用仍然是官方主推的方式;
    5. Lua是iOS上面最热门的脚本语言,很多游戏都使用它来编写脚本,基于C++编写的游戏引擎;
    6. Qt下一代GUI框架QML,支持使用QML脚本语言构建用户界面,混合JavaScript编写的事件处理逻辑,C++编写的对象可以很容易导入JS虚拟机运行环境中。

    从上面的例子我们可以看到,毋庸置疑,C++在新的10年里面(2010+)仍然会扮演及其重要的作用,它会被用来编写各种游戏引擎,浏览器引擎,虚拟机,运行时环境,系统服务层等等,本地代码所占用的计算时间在总的计算时间中的比重有可能还会增加。

     

    但是,对于普通应用的开发者而言,无论是服务器后端,前端,还是编写本地应用,他们都将会离C/C++越来越远,需要使用的几率也越来越低,而更有可能使用各种动态语言和脚本语言。之前的一篇文章为什么学习JavaScript也阐述了自己关于JavaScript未来在各种应用开发中会越来越重要的观点。

     
    • chen 10:19 上午 on 一月 4, 2013 固定链接 | 回复

      现实世界中只有C++才能工作在CPU上,所有其他语言都在一层VM上。借助VM的确可以做非常广泛的应用,但C++和CPU之间才是最直接的映射,无论如何少不了。

  • Roger 9:16 pm on July 26, 2011 固定链接 | 回复  

    [CSDN旧文]C++中的作用域解析 

    C++到底有多少作用域,它们的作用是什么,又是如何相互影响的。

    上图显示了C++中所有的作用域,也试图图形化它们之间粗略关系,这里当然无法表达的很准确。这是因为它们之间并没有语义上的一致性,通常着眼点于不同的领域,相互间存在复杂的互动关系。
    名字空间域
    名字空间主要用于解决名字冲突的问题,在名字空间出现之前,库的作者通常通过附加给库中的类型,全局变量和函数予特定的前缀来防止名字冲突的问题,例如dbus库的Error类型和Error初始化函数被命名为:
    DBusError
    dbus_init_error
    有了名字空间后,我们就可以通过附加名字空间的名字来构成名字的限定名(Qualified Name)来解决名字冲突的问题。
    当然更主要的是我们可以通过名字空间别名,使用声明(特定的名字)和使用指示(全部名字)来达成即能有效防止冲突,又能在已确定的上下文中更方便的访问名字的作用。
    跟Java的包机制不同,名字空间是纯逻辑上的,它不具备对文件组织上任何的物理约束,一个名字空间可以跨越多个编译单元(常见的方式,一个库一个名字空间),但也可以一个编译单元包含多个名字空间(比较少见,通常是用来通过嵌套的子名字空间来防止一些函数重载上的意外发生)。(Java中的包,编译单元,类型域的包含关系更加明确,容易理解和使用,一个包必然包含一个或多个编译单元,一个编译单元也必然包括一个或多个类型,而且只能有一个是包可见的——公共类)
    有的意见认为,名字空间引起的问题比它解决的要多,比如连接时名字解析的问题,特别是不同编译器编译出来的程序片段(静态库,导入库,Object文件)如何能够正确的连接。名字空间也使得重载的决议规则变的更复杂。所以像有些著名的库仍然坚持使用前缀的方式,比如QT。
    名字空间出现后,以前的全局域就变成了名字空间的一个特例——全局名字空间。没有放置在某个名字空间的类型,具有编译单元外部链接的非成员函数和变量就缺省属于全局名字空间。
    编译单元域
    编译单元域是个比较特殊的域,它通常跟代码的物理组织方式有关。一个编译单元中非成员变量和函数的名字可以有外部链接,从而使得链接器可以用来解决跨编译单元的名字引用的问题(跨编译单元的变量的名字引用通常被视为是邪恶的东西)。但是也可以没有外部链接,从而防止一些无意中产生的副作用(错误的引用了不是预想中的名字)。
    没有外部链接的名字处于编译单元域,这个可以通过附加static修饰符来达成。例如:
    static double static_d = 3.0;
    也可以放置在一个特殊的名字空间里,这个名字空间叫做匿名名字空间,每个编译单元都可以有一个匿名名字空间,不同编译单元之间的匿名名字空间不会相互影响,处于匿名名字空间的名字只能被该编译单元所访问。例如:
    namespace
    {
        double intern_d = 2.0;
    }
    namespace foo
    {
    static double static_d = 3.0;
    Foo::Foo(void)
    {
        intern_d = 3.0;
    }
    Foo::~Foo(void)
    {
    }
    }
    像上面的非成员变量intern_d就是处于编译单元foo(foo.cpp)的匿名名字空间中。如果在头文件中试图导出这个变量,例如:
    extern double intern_d;
    实际上你会得到一个“ambiguous symbol”的编译错误(在VC 2008中)。
    类型域
    用户使用struct和class定义一个自定义类型,也同时构成了一个类型域,处于类型域里面的变量和函数被称为成员变量和成员函数,它们可以是静态的(属于类型),也可以是非静态的(属于实例)。静态的成员变量和成员函数与非成员变量和函数类似,而类型在这里只是起到一个特殊的名字空间的作用,或者说是附加类型成员访问规则的名字空间,公共的静态成员函数如果是可见的,那也是可访问的,也就是具备外部链接。
    函数域
    每个函数都构成了一个函数域,函数域的概念跟变量的存储位置和生命期有关。函数的参数和在函数中声明并定义的变量被称为局部变量或者是自动变量,它们分配在堆栈上,它们随着函数的执行而生成,随着函数的退出而消亡。而静态成员变量和非成员变量则分配在静态存储区中,它们的位置是固定的,生命期从程序启动一直到程序关闭。(在自由存储区中动态分配的对象是由使用者来控制其生命周期的。)
    函数里面还可以有多个局部域,比如说每个if,else,else if,for,while,do,switch,try,catch块,或者是用户自己产生的代码块(使用{}包围)。局部域的作用通常是用来进一步限制局部变量的使用范围,在某个局部域声明的局部变量,在退出该局部域时会被自动销毁。用户自己产生的代码块(局部域)多用于所谓的关键区,用来同步线程对外部状态的访问。如果函数需要写的很长,刻意的区分不同的局部域也有助于代码的可读性和防止不必要的错误。
     
  • Roger 1:18 am on May 12, 2011 固定链接 | 回复
    Tags: RamDisk   

    使用RamDisk加快编译,链接,调试速度 

    做了一下RamDisk的测试。

    做法:

    使用VSuite RamDisk专用版创建了一个1GB的RamDisk,并且选择启用NTFS压缩,然后把编译的输出路径设置到RamDisk,并把需要进行链接的WebKit编译出来的WebCore.lib(950m),Jscore.lib(100m)拷贝到RamDisk,自动压缩后的大小加起来约500m,能够节省一半 ^_^。

    结果:

    编译的速度没有明显改善,但是链接的速度提升非常明显,并且启动调试程序的速度也有提高,总的来说还是能够节约不少时间。

    RamDisk

     
  • Roger 11:12 am on April 30, 2011 固定链接 | 回复
    Tags: ,   

    段子 – Java,C++,C语言的不同 

    Java和C++的最大区别在于,Java程序员再烂,写的代码烂起来还是有底线的,而C++程序员写的代码,烂起来简直毫无底线可言,轻易就把整个系统crash 100遍啊100遍…

    而C和C++程序员的最大差别在于,C++程序员可以把自己的烂隐藏在类与对象的汪洋大海中,难以发现,而C程序员的烂如漆黑中的萤火虫,无处藏身。

     
  • Roger 11:26 am on April 16, 2011 固定链接 | 回复
    Tags: , Forward Declaration, Header   

    CSDN旧文 – 如何使用前置声明取代包括头文件 

    这篇文章很大程度是受到Exceptional C++ (Hurb99)书中第四章 Compiler  Firewalls and the Pimpl Idiom  (编译器防火墙和Pimpl惯用法) 的启发,这一章讲述了减少编译时依赖的意义和一些惯用法,其实最为常用又无任何副作用的是使用前置声明来取代包括头文件。

    Item 26 的Guideline – “Never #include a header when a forward declaration will suffice”

    在这里,我自己总结了可以使用前置声明来取代包括头文件的各种情况和给出一些示例代码。

    首先,我们为什么要包括头文件?问题的回答很简单,通常是我们需要获得某个类型的定义(definition)。那么接下来的问题就是,在什么情况下我们才需要类型的定义,在什么情况下我们只需要声明就足够了?问题的回答是当我们需要知道这个类型的大小或者需要知道它的函数签名的时候,我们就需要获得它的定义。

    假设我们有类型A和类型C,在哪些情况下在A需要C的定义:

    1. A继承至C
    2. A有一个类型为C的成员变量
    3. A有一个类型为C的指针的成员变量
    4. A有一个类型为C的引用的成员变量
    5. A有一个类型为std::list<C>的成员变量
    6. A有一个函数,它的签名中参数和返回值都是类型C
    7. A有一个函数,它的签名中参数和返回值都是类型C,它调用了C的某个函数,代码在头文件中
    8. A有一个函数,它的签名中参数和返回值都是类型C(包括类型C本身,C的引用类型和C的指针类型),并且它会调用另外一个使用C的函数,代码直接写在A的头文件中
    9. C和A在同一个名字空间里面
    10. C和A在不同的名字空间里面

    1,没有任何办法,必须要获得C的定义,因为我们必须要知道C的成员变量,成员函数。

    2,需要C的定义,因为我们要知道C的大小来确定A的大小,但是可以使用Pimpl惯用法来改善这一点,详情请
    看Hurb的Exceptional C++。

    3,4,不需要,前置声明就可以了,其实3和4是一样的,引用在物理上也是一个指针,它的大小根据平台不同,可能是32位也可能是64位,反正我们不需要知道C的定义就可以确定这个成员变量的大小。

    5,不需要,有可能老式的编译器需要。标准库里面的容器像list, vector,map,
    在包括一个list<C>,vector<C>,map<C, C>类型的成员变量的时候,都不需要C的定义。因为它们内部其实也是使用C的指针作为成员变量,它们的大小一开始就是固定的了,不会根据模版参数的不同而改变。

    6,不需要,只要我们没有使用到C。

    7,需要,我们需要知道调用函数的签名。

    8,8的情况比较复杂,直接看代码会比较清楚一些。

    C& doToC(C&);

    C& doToC2(C& c)  {return doToC(c);};

    从上面的代码来看,A的一个成员函数doToC2调用了另外一个成员函数doToC,但是无论是doToC2,还是doToC,它们的的参数和返回类型其实都是C的引用(换成指针,情况也一样),引用的赋值跟指针的赋值都是一样,无非就是整形的赋值,所以这里即不需要知道C的大小也没有调用C的任何函数,实际上这里并不需要C的定义。

    但是,我们随便把其中一个C&换成C,比如像下面的几种示例:

    1.

    C& doToC(C&);

    C& doToC2(C c) {return doToC(c);};
    2.
    C& doToC(C);
    C& doToC2(C& c) {return doToC(c);};
    3.
    C doToC(C&);
    C& doToC2(C& c) {return doToC(c);};

    4.
    C& doToC(C&);
    C doToC2(C& c) {return doToC(c);};

    无论哪一种,其实都隐式包含了一个拷贝构造函数的调用,比如1中参数c由拷贝构造函数生成,3中doToC的返回值是一个由拷贝构造函数生成的匿名对象。因为我们调用了C的拷贝构造函数,所以以上无论那种情形都需要知道C的定义。

    9和10都一样,我们都不需要知道C的定义,只是10的情况下,前置声明的语法会稍微复杂一些。

    最后给出一个完整的例子,我们可以看到在两个不同名字空间的类型A和C,A是如何使用前置声明来取代直接包括C的头文件的:

    A.h
    #pragma once
    #include <list>
    #include <vector>
    #include <map>
    #include <utility>
    //不同名字空间的前置声明方式
    namespace test1
    {
    class C;
    }
    namespace test2
    {
    //用using避免使用完全限定名
    using test1::C;

    class A
    {
    public:
    C   useC(C);
    C& doToC(C&);
    C& doToC2(C& c) {return doToC(c);};

    private:
    std::list<C>    _list;
    std::vector<C>  _vector;
    std::map<C, C>  _map;
    C*              _pc;
    C&              _rc;

    };

    }

    C.h
    #ifndef C_H
     #define C_H
     #include <iostream>
    namespace test1
     {
     class C
     {
     public:
     void print() {std::cout<<"Class C"<<std::endl;}
     };
     }
    #endif // C_H
     
  • Roger 12:01 am on April 12, 2011 固定链接 | 回复
    Tags: , , Operator overload   

    CSDN旧文 – Java 为什么没有操作符重载 

    最近一直在看Herb 的 Exceptional C++ 系列书籍 ,Scott Meyer 在书的序中写到,Exceptional C++是一本常常会”make me surprise” 的书,的确,即使使用了C++ 已经很多年,但看这样的书仍然让自己觉得对C++的理解还十分浅薄。

    但是正如不断有人提出来的这个问题 - “是系统设计的复杂程度本身需要那么多的知识,经验和技巧还是C++ 自己本身太过复杂” (嗯,我个人的理解是二者皆是 ^_^),Bruce 在 Thinking In Java 4 的序中写到,随着自己不断加深的对Java的理解,越来越了解到Java语言的设计目标就是使得描述和构建复杂系统的工作变得更容易,一个即使只有有限的知识和经验的程序员也能够使用Java来进行某种复杂程度的系统建构工作,Java就是一门更容易,能够在更高的抽象层次上描述系统的语言,也许它不那么高效,有时也显得不够灵活,但是它不会经常让你 surprise…

    回到题目的话题,Java为什么没有操作符重载,C++ 的操作符重载是其语言强大与灵活的一个重要特征, 我今年因为工作原因接触过Quatro DSP的模拟器,Quatro DSP是一个用于打印机,扫描仪的数字图象处理器,模拟器本身其实是一个C++库,通过库的支持能够把Quatro DSP的汇编语言转化为合法的C++ 函数调用,从而在C++的集成开发环境(如 VC)中模拟DSP汇编语言的执行,主要工作原理无非是将寄存器定义为某个类类型对象,从而使用宏替换和操作符重载将汇编语言变成函数调用。

    C++不但有操作符重载,还有自定义类型转换 (嗯,大多数的C++书籍都会劝你在没有正当理由的情况下千万不用去使用后者),Exceptional C++书中在讲到异常安全代码的章节中有这么一段话 ——

    “In particular, it helps to develop a habit of eyeing with mild suspicion anything that might turn out to be a function call — including user-defineed operators, user-defined conversions, and silent temporary objects among the more subtle cluprits – because any function call might throw.”

    “实际上,它会帮你养成这样的习惯,对所有语句都带着怀疑的眼光,猜测这有可能会是一个函数调用 - 包括自定义操作符,自定义类型转换,和一些更是难以捉摸的语境中悄悄产生的临时对象 - 因为函数调用就有可能抛出异常。”

    Herb写这段话的目的是为了说明在C++中写异常安全或者异常中立的代码,你必须小心分析那些语句有可能抛出异常,而一个模版代码中的大部分语句都十分有可能是一个函数调用,而函数调用就有可能抛出异常,最后剩下给你的确定不会抛出异常的语句会非常的少… … 如果你没有做好这样的心理预期,想当然的认为大多数语句都不会抛出异常,那么你被 surprise的几率就会非常之高 … …

     
c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
回复
e
编辑
o
Show/Hide comments
t
返回顶部
l
Go to login
h
Show/Hide help
shift + esc
取消