jiyu's profile人生路漫漫,快乐先行PhotosBlogLists Tools Help

Blog


    May 01

    今天才知道,一个磁盘的扇区大小为512B

    软盘、硬盘都是这样,那么来说,处理起来是一样的。

    不过又看到一则新闻,稍微老一些,2006-3-26。

    德国IT新闻网站Golem.de报道,“国际磁盘驱动器、器材及原料协会”(IDEMA)已经同意将硬盘扇区大小由目前的512Byte增加到4096Byte,这将导致硬盘存储密度和容量的进一步提升。

    该报道称更大的扇区可以实现更健全的错误修正机制。采用4KB扇区的硬盘将在今年晚些时候上市,但目前我们还没有哪些厂商将推出这种硬盘及其容量大小的消息。

    世界变化就是快啊。

    April 29

    Petri网简介(转载)

        Petri网是对离散并行系统的数学表示。Petri网是1960年代由卡尔·A·佩特里发明的,适合于描述异步的、并发的计算机系统模型。Petri网既有严格的数学表述方式,也有直观的图形表达方式,既有丰富的系统描述手段和系统行为分析技术,又为计算机科学提供坚实的概念基础。
        由于Petri网能够表达并发的事件,被认为是自动化理论的一种。研究领域趋向认为Petri网是所有流程定义语言之母。

    • 经典Petri网
      经典的Petri网是简单的过程模型,由两种节点:库所和变迁,有向弧,以及令牌等元素组成的。

      • Petri网的结构
        (1) Petri网的元素:
        • 库所(Place)圆形节点
        • 变迁(Transition)方形节点
        • 有向弧(Connection)是库所和变迁之间的有向弧
        • 令牌(Token)是库所中的动态对象,可以从一个库所移动到另一个库所。
          (2) Petri网的规则是:
        • 有向弧是有方向的
        • 两个库所或变迁之间不允许有弧
        • 库所可以拥有任意数量的令牌
      • 行为
        如果一个变迁的每个输入库所(input place)都拥有令牌,该变迁即为被允许(enable)。一个变迁被允许时,变迁将发生(fire),输入库所(input place)的令牌被消耗,同时为输出库所(output place)产生令牌。
        • 变迁的发生是原子的
        • 有两个变迁都被允许的可能,但是一次只能发生一个变迁
        • 如果出现一个变迁,其输入库所的个数与输出库所的个数不相等,令牌的个数将发生变化
        • Petri网络是静态的
        • Petri网的状态由令牌在库所的分布决定
      • Petri网的形式化定义
        一个经典的Petri网由四元组(库所,变迁,输入函数,输出函数)组成。任何图都可以映射到这样一个四元组上,反之亦然。
      • Petri网流程建模
        一个流程的状态是由在场所中的令牌建模的,状态的变迁是由变迁建模的。令牌表示事物(人,货物,机器),信息,条件,或对象的状态; 库所代表库所,通道或地理位置;变迁代表事件,转化或传输。
        一个流程有当前状态,可达状态,不可达状态。
      • 经典Petri网的局限性
        • 没有测试库所中零令牌的能力
        • 模型容易变得很庞大
        • 模型不能反映时间方面的内容
        • 不支持构造大规模模型,如自顶向下或自底向上

    • 高级Petri网
      为了解决经典Petri网中的问题,研究出了高级Petri网,在以下方面进行了扩展:

      • 令牌着色
        一个令牌通常代表具有各种属性的对象,因此令牌拥有值(颜色)代表由令牌建模的对象的具体特征,如一个令牌代表一个工人(张三,28岁,经验3级)。
      • 时间
        为了进行分析,我们需要建模期间,延迟等,因此每一个令牌拥有一个时间戳,变迁决定生产出的令牌的延迟。
      • 层次化
        构造一个复杂性与数据流图相当的Petri网的机制。子网是由库所,变迁和子网构成的网络。
      • 时序
        增加时序逻辑的定义,更好的描述行为过程。

    • Petri网的应用
      Petri网的应用非常广泛,以下是Petri网比较常用的几种应用:

      • 软件设计
      • 工作流管理
      • 工作流模式
      • 数据分析
      • 并行程序设计
      • 协议验证

    彩信之我见

    -- 2006.4.28 By A.TNG


    1、废话。
    搞IT总是让人感觉没有前途,来公司实习,越来越觉得前途渺茫,直到突然有一天,醒悟过来,我原来不是属于IT行业的,而是属于通信行业。看看sina上科技版首页,俨然的把IT业界与电信分开来,于是觉得很欣慰,自己原来也不是那么“挨踢”。

    爱立信、诺基亚、摩托罗拉、西门子、阿尔卡特、朗讯、北电、华为和中兴,哪家不是世界巨头,哪家不令各大学高材生趋之若鹜。她们都是属于电信行业。因此,就算是进了排名最后的中兴,“挨起踢”来,都让人觉得舒服,或者说,人家放个屁都是香的。

    完了,跑题了。最后跑一句:珍爱生命,远离··。

    2、关于彩信。
    其实跟彩信结缘也是在去了上海之后,本科毕业以前,不了解什么是彩信,就听说过SMS/MMS,知道肯定是两个不同的东西,听说过发彩信巨爽,图片声音样样都有,还有就是发一条,要花1RMB,掐指头算了一下,合10条短信呢,算了,还是省省吧。直到二月份去了上海,知道自己做的email协议栈只是一个很小的组件,还是依附于彩信的,于是开始对彩信有种莫名的崇拜。

    还有一点,就是觉得彩信组的人都是大牛,人也特别好,感觉好亲切:)

    3、说说彩信
    其实到现在为止,发彩信的人还是很少,首先是终端设备支持不够。中国人穷,对于手机是能用就用,只有极少数人看到出新手机,不管多贵都去买一个尝鲜,大多数人用的还是那种比较老式,对彩信支持都不够好的手机。

    然后是网络支持不够,凭借现在2G的网络或者是CDMA的网络,对于发送小的彩信,还是不错的,来个小图片,来段短声音,都还凑活。但是据彩信组的大牛们说,在现在GPRS的网络上,超过80K的彩信,就极有可能发送不成功;移动的兄弟们说,我们的GSM网络就支持100K的数据,超过这个量,说断就断,不会事先跟你打招呼的。其实啊,各大厂商也就是把宝压在了3G的网络上头,在那个上头发彩信,感觉爽极了。

    说实话,彩信还有有她的缺陷,在能发送的情况下,彩信中心是不会保存发送的彩信的,因为她不是一个邮件服务器,不过她也有很有趣的地方,彩信可以通过网关,并且重新编码,发到用户的邮箱里头。

    彩信发到邮箱,需要各个方面的支持,网关需要支持,能对彩信进行解码和MIME封装,需要支持SMTP把彩信发到邮件服务器上,用户的客户端(Outlook和Foxmail)把彩信型邮件接收下来,也需要显示上的支持,不过我试过Outlook Express 6.00.2800.1807和Foxmail 5.0.800.0都不支持对SMIL的解析,所以也完全没有达到显示彩信的效果。唉,其实也很惭愧,我觉得既然OE和Foxmail都不支持,我们做的东西也不需要支持那么好,照着葫芦画瓢就好了。

    4、个人感觉
    虽然现在手机上的邮件用的人还是很少,不过我仍然希望手机邮件有一个美好的未来。从功能上说,彩信和邮件都能提供很完善的多媒体服务,而且邮件的历史远比彩信要长,使用的人更多,如果能把手机和邮件完美结合,潜在的客户群体肯定很大。邮件也有她的缺点,她属于被动式,用户需要主动去收取,才能够知道是否有新邮件,而彩信属于PUSH型,能主动通知用户是否有彩信到。不过在这一点上,加拿大有一家公司联合电信运营商推出了“Push Mail-黑莓”服务,来弥补邮件被动式的缺点,“Push Mail”是建立与现有的POP机制之上的,应该是更加的完善。令人高兴的是联通公司也提出了“红草莓”服务,跟那个“Push Mail”类似。

    个人感觉,邮件这个东西,发展到现在,很多方面都很完善了,ESMTP、POP3、IMAP4和MIME这些协议都有比较稳定的代码支撑着,可是邮件和手机结合的同时,要充分考虑手机的特点,手机在控制性和显示方面都有天生的缺陷,因此在显示邮件时,如何充分的利用MIME协议的特点,并且最大限度的满足用户的需求,才是难点。其实显示邮件方面Lotus Notes感觉是做得最好的,而OE和Foxmail要差一些。


    April 28

    十八点(转载)

      
    一、读大学,究竟读什么?
        大学生和非大学生最主要的区别绝对不在于是否掌握了一门专业技能……一个经过独立思考而坚持错误观点的人比一个不假思索而接受正确观点的人更值得肯定……草木可以在校园年复一年地生长,而我们却注定要很快被另外一群人替代……尽管每次网到鱼的不过是一个网眼,但要想捕到鱼,就必须要编织一张网……
          
    二、人生规划:三岔路口的抉择
        不走弯路就是捷径……仕途,商界,学术。在这人生的三岔路口,你将何去何从……与其跟一百个人去竞争五个职位,不如跟一个人去竞争一个职位……学术精神天然的应当与尘嚣和喧哗保持足够的距离……商场不忌讳任何神话。你也完全可能成为下一个传奇……
          
    三、专业无冷热,学校无高低
        没有哪个用人单位会认为你代表了你的学校或者你的专业……既然是概率,就存在不止一种可能性……如果是选择学术,冷门专业比热门专业更容易获得成就……跨专业几乎早已成为一种流行一种时尚……大学之间的实力之争到了考研考场和人才市场原来是那样的微不足道……
          
    四、不可一业不专,不可只专一业
        千招会,不如一招熟……十个百分之十并不是百分之百,而是零……在这个现实的社会,真正实现个人价值才是最体面最有面子最有尊严的事情……要想知道需要学什么,最好的方式就是留意招聘信息……很多专业因为不具备专长的有效性,所以成为了屠龙之术……为什么不将“买一送一”的促销思维运用到求职应聘的过程中来呢……
          
    五、不逃课的学生不是好学生
        什么课都不逃,跟什么课都逃掉没什么两样……读大学,关键是学会思考问题的方法……逃课没有错,但是不要逃错课……英语角绝对不是学英语的地方……为了英语丢了专业,那就舍本逐末了……招聘单位是用人才的地方,而不是培养人才的地方……既要逃课,又要让老师给高分……
          
    六、勤工俭学的辩证法
        对于贫困生来说,首先要做的不是挣钱,而是省钱……大部分女生将电脑当成了影碟机,大部分男生将电脑当成了游戏机……在这个处女膜都可以随意伪造的年代,还有什么值得轻易相信……态度决定一切……当学习下降到次要的地位,大学生就只能说是兼职的学生了……
          
    七、做事不如做人,人脉决定成败
        学问好不如做事好,做事好不如做人好……会说话,就能减少奋斗三十年……一个人有多少钱并不是指他拥有多少钱的所有权,而是指他拥有多少钱的使用权……一个人赚的钱,12.5%是靠自身的知识,87.5%则来自人脉关系……三十岁以前靠专业赚钱,三十岁以后拿人脉赚钱……你和世界上的任何一个人之间只隔着四个人……
          
    八、互联网:倚天剑与达摩克利斯之剑
        花两个小时就写出一篇天衣无缝的优秀毕业论文……在互联网领域创业的技术门槛并不高,关键的是市场眼光和营销能力……轻舞飞扬已经红颜薄命了,而痞子蔡却继续跟别的女孩发生着一次又一次的亲密接触……很多大学生的网友遍布祖国大江南北,可他们却从未主动向周围的人说一声:你好,我们可以聊聊吗……
          
    九、恋爱:花开堪折方须折
        爱情是不期而至的,可以期待,但不可以制造……越是寂寞,越要警惕爱情……既然单身是可耻的,那西门庆是不是应该被评为宋朝十大杰出青年……花开堪折方须折,莫让鲜花败残枝……一个有一万块钱的人为你花掉一百元,你只占了他的百分之一;而一个只有十块钱的人为你花掉十块,你就成了他的全部……
          
    十、性:上帝死了,众神在堕落
        爱要说,爱要做……我只有在肉体一下一下的撞击中才感到快乐。经过之后,将是更大的寂寞更大的空虚……为何要让别人的虚荣成为对自己的伤害……当她机械地躺在床上张开双腿,她的父母正在憧憬着女儿的未来……一朝春尽红颜老,花落人亡两不知……
          
    十一、考研:痛苦的安乐死
        没有比浪费青春更失败的事情了……研究生扩招的速度是30%,也就意味着硕士学历贬值的速度是30%……同样是付出三年的努力,你可以让E1的值增加1,也可以让E2的值增加2甚至增加3……读完硕士或博士并不等于工作能力更强……面对13.54万的成本,你还会毫不犹豫地投资读研究生吗……努力就会有结果,但不一定是好结果……
          
    十二、留学:“海龟”变“海带”
        月薪2500元的工作,居然引得三个“海归”硕士争相竞聘……对于某些专业而言,去美国留学和去埃塞俄比亚留学没什么两样……既然全世界的公司都想到中国的市场上来瓜分蛋糕,为什么中国人还要一门心思到国外去留学然后给外国人打工……
          
    十三、非统招:养卑照样处优
        她在中国信息产业界创下了几项纪录。她被称为中国的“打工皇后”。而她不过是一名自考大专生……要想把曾经输掉的东西赢回来,就必须把自己比别人少付出的努力补上来……非统招生不但要有一定的实力,而且必须掌握一定的技巧,做到扬长避短出奇制胜……路在脚下。好走,走好……
          
    十四、毕业:十面埋伏的陷阱
        母校不把自己当母亲,你又何必把自己当儿女……听辅导班不过是花钱买踏实……人才市场就是一个地雷阵……通过多种方式求职固然没有错,但是千万不要饥不择食……只要用人单位一说要你交钱,你掉头就走便是了……这年头立字尚且不足以为据,更何况一个口头约定……
          
    十五、求职:做人不要太厚道
        求职简历必须突出自己的核心竞争力……求职的时候大可不必像严守一那样“有一说一”……一个人说假话并不难,难的是把假话说到底,并且不露一丝破绽……在填写自己的特长时,一定要尽可能详细……一份求职简历只要用一张A4纸做个表格就足够了……面试其实是有规律的,每次面试的时候只要背标准答案就行了……
          
    十六、骑一头能找千里马的驴
        美国铁路两条铁轨之间的标准距离是4英尺8.5英寸,为什么呢?因为两匹马臀部之间的宽度是4英尺8.5英寸……垃圾是放错位置的人才……世界上最大的悲剧莫过于有太多的年轻人从来没有发现自己真正想做什么……中小型企业或许能够让你得到更充分的锻炼……从基层做起并不意味着可以从基层的每一个职位做起……要“钱途”,更要前途……
          
    十七、写字楼政治:白领必修课
        大公司是做人,小公司是做事……职员能否得到提升,很大程度不在于是否努力,而在于老板对你的赏识程度……公司的事情和秘密永远比你想象的还要复杂和深奥……在适当的时候装糊涂不但是必要的,而且是睿智的……就把你的同事当成一群你可以叫得出名字的陌生人好了……
          
    十八、创业:29岁以前做富翁
        瘦死的骆驼比马大……撑死胆大的,饿死胆小的……不再是“大鱼吃小鱼”,而是“快鱼吃慢鱼”……对于趋势的把握是一个创业者最重要的能力……高科技行业留给毕业生的空间已经很小……欲速则不达。在创业以前通过给别人打工而积累经验是非常必要的……市场永远比产品更重要……钱不够花,怎么办?第一,看菜吃饭;第二,借鸡生蛋……
      
    March 27

    2006-3-25 太湖鼋头渚

        真是天公作美,知道我们要去仙界和诸仙交流,所以太阳特别的好。过了会仙桥,各位都成了神仙了,一路游山玩水,好不快活。
        不过就是时间太短了,还有就是其他人手艺不太好,把我照得比较搓,见谅,见谅。
    February 27

    风雨夜上海

    ----2006.2.26
     
    偷偷的写这个日志,身边都是领导,最大的领导就在我旁边。所以尽量废话少说。
     
    昨天晚上去游了夜上海,在老大的带领下。不过很多的不凑巧,比较忙,导致精神不振;风大,导致形象不佳;人多,上海太大了,简直就不能算是一个城市了。不过总归来说,玩得还是很高兴得,到处都看了,终于知道,什么是国际型大都市了。大家凑活着看吧。
    February 09

    人生规划:20岁到60岁我该干什么(zz)

      一个人的职业生涯,贯穿一生,是一个漫长的过程。科学地将其划分为不同的阶段,明确每个阶段的特征和任务,做好规划,对更好地从事自己的职业,实现确立的人生目标,非常重要。
      职业生涯阶段如何划分,各国专家学者有不同的划分理论和方法,主要可分为按年龄层次划分、按专业层次划分和按管理层次划分三种类型。
    我国从事职业生涯规划研究的人事人才科学研究所副研究员罗双平认为:以年龄为依据,每十年作为一个阶段比较合适,即二十岁至三十岁为一个阶段,三十岁至四十岁为一个阶段,依次类推。我们每个人都要经历这几个阶段,那么你现在该做些什么呢?罗双平谈了他的看法,供读者朋友参考。
      二十岁至三十岁:走好第一步
      这一阶段的主要特征,是从学校走上工作岗位,是人生事业发展的起点。如何起步,直接关系到今后的成败。
      这一阶段的主要任务之一,就是选择职业。在充分做好自我分析和内外环境分析的基础上,选择适合自己的职业,设定人生目标,制定人生计划。再一个任务,就是要树立自己良好的形象。年轻人步入职业世界,表现如何,对未来的发展影响极大。有些年轻人,特别是刚毕业的大学生,总认为自己有知识,有文化,到单位工作后不屑于做零星小事,不能给同事们留下良好的印象,这对一个年轻人的发展而言,可以说是一个危机。还有一个重要任务,就是要坚持学习。根据日本科学家研究发现,人一生工作所需的知识,90%是工作后学习的。这个数据足以说明参加工作后学习的重要性。
      三十岁至四十岁:不可忽视修订目标
      这个时期是一个人风华正茂之时,是充分展现自己才能、获得晋升、事业得到迅速发展之时。此时的任务,除发奋努力,展示才能,拓展事业以外,对很多人来说,还有一个调整职业、修订目标的任务。人到30多岁,应当对自己、对环境有了更清楚的了解。看一看自己选择的职业、所选择的生涯路线、所确定的人生目标是否符合现实,如有出入,应尽快调整。
      四十岁至五十岁:及时充电
      这一阶段,是人生的收获季节,也是事业上获得成功的人大显身手的时期。对于到了这个年龄仍一无所得、事业无成的人应深刻反省一下原因何在?重点在自身上找原因,对环境因素也要做客观分析,切勿将一切原因都归咎于外界因素,他人之过。只有正确认识自己,找出客观原因,才能解决人生发展的困阻,把握今后的努力方向。
      此阶段的另一个任务是继续充电。很多人在此阶段都会遇到知识更新问题,特别是近年来科学技术高速发展,知识更新的周期日趋缩短,如不及时充电,将难以满足工作需要,甚至影响事业的发展。
      五十岁至六十岁:做好晚年生涯规划
      此阶段是人生的转折期,无论是在事业上继续发展,还是准备退休,都面临转折问题。由于医学的进步,生活水平的提高,很多人此时乃至以后的十几年,都能身体健康,照样工作,所以做好晚年生涯规划十分重要。日本的职工一般是45岁时,开始做晚年生涯规划;美国是50岁时做晚年生涯规划。我国的职工按退休年龄提前5年做晚年生涯规划即可。
      主要内容应包括以下几个方面:一是确定退化后的二三十年内,你准备干点什么事情,然后根据目标,制定行动方案;二是学习退休后的工作技能,最好是在退休前三年开始着手学习;三是了解退休后再就业的有关政策;四是寻找工作机会。目前我国已有离退休人员的人才职业介绍所,可提前与这些部门联系,取得他们的帮助。

    人生规划,一个研究生的回顾(zz)

      我今年39岁了, 25岁研究生毕业,工作14年,回头看看,应该说走了不少的弯路,有一些经验和教训。现在开一个小公司,赚的钱刚够养家糊口的。看看这些刚毕业的学生,对前景也很迷茫,想抛砖引玉,谈谈自己的看法,局限于理工科的学生,我对文科的不懂,身边的朋友也没有这一类型的。
      91年研究生毕业,那时出路就是1种:留在北京的国营单位,搞一个北京户口,这是最好的选择。到后来的2~3年内,户口落定了,又分成4条出路:
      1、 上国内的大企业,如:华为
      2、 自己做公司,做产品开发;
      3、 上外企,比如:爱立信、诺基亚
      4、 自己做公司,做买卖;
      5、 移民加拿大
      我想,首先要看自己适合做什么?做技术还是做买卖。
      做技术,需要你对技术感兴趣。我掰着数了一遍,我们研究生班的30来号人,实际上 ,适合做技术的,大概只有3、4个人,这几个人,1个现在还在华为,3个移民加拿大了,现在这4个人混的还可以,在华为的同学也移民加拿大了,他在华为呆了6年,在华为奖金工资加起来大概30万吧,还有华为的股票,再过几年,华为的股票一上市,也能值个100~200万。要是一毕业就去华为,那现在就绝对不是这个数字了。
      要是做技术,最好的就是上大公司,国内的大型企业,象华为中兴肯定是首选,能学到很多东西。华为虽然累,但是,年轻人不能怕累,要是到老了,还需要去打拼,那才是真的累啊。
      在外企,我想他们主要就是技术支持和销售,但是技术是学不到的,当然不能一概而论,我指的是象爱立信和诺基亚,真正的研发不会在中国做的,学到的也不如在华为多,其它的中兴我不是很了解,我想应该也不错啊。
    一个人都有一技之长,有傍身之技,那是最好的,走到哪里,都能有一口饭吃,还吃的不错,这是传统的观点。
      任何技术都是要在某个行业去应用,这个行业市场越大当然越好;要在一个领域之内,做深做精,成为绝对的专家,这是走技术道路的人的选择。不要跳来跳去,在中国,再小的行业你要做精深了,都可以产生很大的利润。
      研究生刚毕业的时候,做产品开发的有不少人,都是自己拍拍脑子,觉得这个产品有市场,就自己出来做。现在看来,我的这些同学,做产品开发的成功的没有一例,为什么?资源不足。
      1. 资金,刚毕业的学生啊,就是没钱;没钱,也意味着你开发的东西都是小产品;而且只能哥几个自己上,研发、生产、销售都是一个人或者几个人自己来,没有积累,什么都是重新来过。
      2. 人脉,任何一个行业,要想进去,需要有很深的人脉,否则,谁会用你的东西啊?谁敢用你的东西啊?
      我看到的,我这个班上开发产品的,自己还在坚持的,只剩下一个人了,说实在的,到现在,没有自己的汽车,也没有自己的房子,混的挺惨的。现在出国的不说了,在外企、在华为,至少都是几十万的年薪了,还有各种福利,就是产品开发成功了,又能如何?也就是这样了,但是以前那些年,都没有金钱的积累,等于白干。
      我身边的一个自动化系的研究生班的同学,能靠自己开发产品活得还可以的,也只有2个人。说明这条路不是那么好走的啊。
      其次就是上外企。我的2个同学,一个上了爱立信,一个先到爱立信后到诺基亚,都混的不错。到诺基亚的后来利用在诺基亚结识的人脉(就是哪些电信的头头脑脑),自己开了公司,也赚了不少的钱。
      外企最大的好处就是除了能学到比较规范的管理外,还能给你的职业生涯镀金。到了一个外企外,再到同行业的外企我想就很容易了。而且外企的收入高啊。
      自己做公司,做买卖,一开始有3~4个人走这条路,但是真正发财的只有一个人,其 他人后来上外企了。做买卖,还是要有一定的天赋,还有机遇。要有对金钱的赤裸裸的欲望,要有商业上的头脑。后来我们同学在一起谈,说,我们即使给自己这个机遇,也未必能做的好。何况当时那个同学看好的产品(做一个台湾产品的代理),我们大家都没有看好,说明,真理还是掌握在少数人手里。
      到后来,同学们纷纷移民移民加拿大。
      移民加拿大对搞技术的人来说,还是一个不错的选择,但是要尽早,练了几年的技术,就赶紧出去,大概是在1996年走了不到10个,现在都还可以,买了房子和车了。要是晚了,语言再学也难了,而且在国内都混的还可以了,也就没有必要出去了。
    我自己呢,先是在国营的研究所混了4年,后来到一家公司干了6年,2002年出来自己做公司,现在也就是混了一个温饱吧,算是有房有车,有点积蓄,但是不多,还有一个可爱的女儿。回首这10来年,有一些经验和教训。
      1. 要有一个职业生涯的规划。首先需要定位自己做什么合适,是做买卖还是做技术,一条路走到黑;当然,做了技术,后来改行也行;
      2. 做技术,就是要做精做深,成为这个行业的这个技术的专家;最好就是去国内的大公司,才能全面学到东西,能够给你培训的机会;如果大公司进不去,先到小公司练技术,找机会再到大公司去镀金,学高深的技术。千万不要自己做产品,要做也是对这个行业熟悉了,再去做。
      3. 积极争取机会。积极争取学习和进步的机会。比如,做技术,就需要多锻炼,多学习,来提高自己的水平。一门技术,只要有机会去学习,都能学的会;要是没有机会,天才也没有办法学到这个技术。柳传志就说,杨元庆就是"哭着喊着要进步",实际上,就是争取自己的机会;当然,这种强烈的进步欲望,也是领导看重的地方。每一步都走在前面,积累10年,你就有了比其他人更多的机会了。
      4. 积累个人的信誉。从你的职业生涯的第一天,就要按照诚信的原则办事。要做到,当人们提起你的名字的时候,说,这哥们还不错,做事还行。
      5. 注意利用资源。如果你有有钱的亲戚、成功的长辈或者朋友,可以充分利用这些机会,得到更加顺利的发展前景。
      6. 注意财富的不断积累。人生要想得到自由,财富是很关键的。否则,永远仰人鼻息,永远看人脸色。人都是势利眼。今后的家庭、职业生涯,金钱的积累很重要,没有钱,永远不能开张自己的事业,得到更多的机会;财富要做到逐年积累,你才能家庭生活幸福。没有钱是不可能有幸福的家庭的。
      7. 注意人脉的积累。最终,事业要靠在社会上的人脉的资源。要注意认识在你这个行业的人,结交他们,最终他们会成为你事业上的助力。
      8. 寻求贵人相助。要找大老板来帮助你,得到大老板的赏识。想想看,大蛋糕,切一点就够了,小蛋糕,都给你也吃不饱啊。
      9. 多听听成功的前辈和成功的朋友的意见。注意少听家里长辈的意见,尤其是都已经退休的长辈,他们对社会的认识还停留在很久以前,而这个社会已经发生很大的变化呢。最重要的是,长辈有时候会强求你做一些事情,但是,最终的结果他们是不负责的。只有你才能对自己负责。
      欢迎朋友们都能提出自己的看法。

    如何正确的进行人生规划(zz)

      20岁以前,大部份的人是相同的,升学读书升学读书...,建立自己基础。在父母亲友,社会价值观影响及误打误撞的情况下完成基本教育。
      选择读书,应该一鼓作气,在您尚未进入产业时,能读多高就多高,毕竟何时进入产业,您都是社会新鲜人。
      但是一旦您已经有工作经验而又有心进修,当然管道很多,相对的挣扎也多。
      因为您不知现在的年纪、条件、资历……再去做进修这样的投资是否值得?
      如果,您认定一辈子要当上班族,学历对您而言相信是很重要的,否则,时间宝贵,不容许您再走错路。
      20 ~ 25岁,您要懂得掌握与规划自己的未来,决定了就是一条无悔的不归路。
      刚得到法律付予您的种种权力,相对的您要尽您的义务及学习面对责任的承担。
      这时候的您,是喜悦、矛盾与痛苦交战,喜悦来自于开始被赋予一些自主权,矛盾来自于与父母割不断的脐带关系,痛苦的是开始要尝试错误。
      您要开始为自己的未来规划,如升学、就业、感情……拿回自己对人生的主控权,而非一直受人左右影响的去摇摆自己的未来。


    【学会人际关系,多认识积极的朋友,十年后这些朋友都将是产业的中坚】

      25 ~ 30岁,您像一块海绵,努力吸收也甘心被压榨,为的只是自我的成长。
      这时候的您,应是工作取向,薪水待遇。升迁调职您应该是斤斤计较。因为唯有努力付出,相对的您才敢积极争取,社会新鲜人的动力应该让您冲出自己的一片天,也因为没有经验,所以不懂挫折。
      因为资源不多,所以一切尽人事,听天命。现在的您:领取别人的薪水,学习别人的经验,付出自己的青春,建构自己的未来。


    【学会累积经验,接触机会,良师益友的提携更是提升您成长的大利器】

      30 ~ 35岁,您要学习判断机会、掌握机会,不能再有尝试错误的心态。
      这时候的您,应是事业取向和家庭取向,工作应该从体力转换为脑力。
      您应该看到的是远景,而非现况,面对的是宽广人生,而非局限于自我。
      结婚是许多人面临人生第一次的重大抉择,面对婚姻,很多人以为结婚就是一个责任的结束,殊不知正是学习的开始。
      就像一些刚上市上柜公司,以为目标达成了,忘了自己的企业责任,忽略本业。反而是一个恶梦的开始。
      人的本业不就是经营自己的家庭,赚钱的目的不就希望给家人更好的生活,但这可不能成为忽略家人的借口,一个经营不好家庭的人,纵使赚到全世界,他得到的只是表面的掌声,在他人生的这个圆,永远有一个缺口。
      家应该是您最大的精神支柱。动力来源和坚强后盾!


    【时间管理,转化心境;转化用头脑去工作,不要用身体去工作】

      35 ~ 40岁,您要享受给人希望,功德无量的格局。
      这时候的您,应是企业取向,工作只是一种休闲,更可转化为对他人的责任。如果您专注于研究,您应该不只穷毕生之力。
      24小时不眠不休的去做苦力您应该有成立研究机构,带领一群人做更多研发的雄心壮志。
      如果您是企业主管,您应该不只停留在汲汲营营,斤斤计较,您应该有能力担负主导周遭的员工、家人,带领他们享受更好的生活。
      格局的大小,会影响您成就的多少,做一个有影响力的人,而非被影响的人。


    【不论目前您多风光。多有成就,在您心中是否画得出十年后的你?】

      静心思考!我们现在所有努力的目的不就是为了父母、另一半、小孩……?
      工作,不应该等于是人生,更不应该是需要经营一辈子的事。
      试问健康、财富、自我成长、人际关系和时间自由,什么是您努力工作的动力?
      我相信没有人愿意放弃任何一点。
      这些正是促使我们年轻人前进的动力。
      十年后,您是提早完成它?还是提早放弃它?


    【宁可因梦想而忙碌,不要因忙碌而失去梦想】

      我看周遭有太多优秀甚于我数倍的朋友,可惜的是终日汲汲营营,投入更多的时间、精神、资源,却没有享受到应得的代价,原因无他,努力错方向,找错机会,拒绝机会而己。
      Jordan 打了一辈子的篮球,是很难在棒球场上找到自己的舞台。不要让忙碌蒙蔽了您的双眼再回头:廉颇老矣,尚能饭否?


    【你的时间在那里,成就就在那里】

      当您一个人成功,您只享受到一个人的快乐!
      懂得分享与付出,真正的快乐来自于:周遭的亲友因您的成长而提升,不论是精神或物质。
      真正的成功来自于:周遭的亲友因您付出获得改善,给人希望功德无量。
      我们不是在做慈善事业,尚没有能力普渡众生,但是,我们可以发挥一己之力,对亲友,对那些有缘相遇的陌生朋友。
      伸出您的手,在他们需要的时候!
      太多人在等生命中的贵人,聪明如您,何不先从帮助他人开始?
      “有人4、50岁了,还喜欢说出自某某名校,我觉得奇怪,他们把学校的那几年当成生命的巅峰,其实他出了校门便已开始走下坡路了。 ”
    January 25

    年前最后一笔


    ----2006.1.25

    年前最后一笔了,明天还有半天的班,估计是没有时间写了,放了假,27号中午的飞机回家。我的2005,再见,我的2006,你好。

    最近变化好大,有点压得透过气来。世间的事情就是无法预料,YanPeng因为导师的原因,回了学校再也不来了,小马哥被调离了项目组。这个消息太突然,而且我当时正在看《金刚》的结局,一下子震得我低落极了。

    事情已经是定局,我这种小角色肯定是无法扭转的,只能默默地承受,他俩的工作落到了我的手上,我要从零开始,N多莫名其妙的软件,狂负责的编译调试环境,我都得花时间去熟悉,上海那边一个劲的提故障,可是我连测试手机都没有摸热,脑子都快炸了。

    通讯来了一个老员工帮我忙,很感谢他,我着急上火的时候,他一个劲安慰我,唉,没有过不去的坎。心急的时候,就无法正常思考,其实很多时候静心想想,也就好了。

    来实习这么长时间了,第一次感觉到了压力,可能刚开始的时候,对小马哥依赖太多了,人还是要学着独立的。

    祝所有的人,春节快乐。

    BTW: 2006是我的本命年。

    January 24

    BREW平台Callback(回调)机制的最终分析

    ----2006.1.22
     
    摘要:回调机制是BREW平台中最关键的机制之一,很多接口类都是通过回调机制呈现在开发人员的面前,《对AEECallback结构体及其基础函数的分析》中队brew/src/thrdutil中的一些源代码进行分析,初略的理解了AEECallback结构体及其相关函数的实现,对开发能有一定的帮助,本文则更加深入的分析了该机制的特点,以及高通工作人员给出的一些答案。
     
    注:非常感谢手机之家_开发联盟的superant, 东方欲晓, record等人,是他们激烈的讨论才引出了最后的答案。本文很多内容都摘自他们的帖子。
     
    BREW平台的回调机制,通常是异步的,举例来说,在网络通信的时候,如果是发送数据,普通的方式是发送一快数据,判断是否发送成功,如果发送不成功,则空闲一段时间,在调用发送数据的函数,如此往复,但是如果在BREW平台上,调用发送函数发送数据,如果发送成功,函数会返回发送成功的字节数,如果返回的是-2,表示当前发送不成功,接下来,需要通过_Writeable注册一个回调函数,BREW会根据网络状况调用你注册的回调函数,继续发送需要发送的数据,直到所有数据发送成功。同样的道理,在接收数据的时候,如果数据量比较大,不是一次能够接收完,通常来说就是通过_Readable函数来注册接收回调函数,当有数据到的时候就会调用你注册的回调函数接收数据,直到所有数据接收成功。
     
    BREW平台回调机制的实现有几个关键的部分下面一一介绍。
    Callback机制中的核心数据结构:
    struct _AEECallback
    {
        AEECallback *pNext;
        void        *pmc;
        PFNCBCANCEL pfnCancel;
        void        *pCancelData;
        PFNNOTIFY    pfnNotify;
        void        *pNotifyData;
        void        *pReserved;
    };
    该结构体的每一个成员在《对AEECallback结构体及其基础函数的分析》中有介绍,在此略过。
    从结构的命名中可以猜测,其实AEECallback结构体是以链表形式存在,其中的pNext成员就是起到遍历链表的作用,该链表由系统维护。
    CALLBACK_Init和CALLBACK_Cancel是两个关键函数。CALLBACK_Init负责对AEECallback结构体进行初始化,该函数的参数列表第二个参数是PFNNOTIFY类型,就是需要回调的函数的函数指针类型,第三个参数是传递给该回调函数的参数指针,类型是void型指针。CALLBACK_Cancel函数是取消回调函数,也就是调用AEECallback结构体中的pfnCancel指针指向的函数来取消回调。
    ISHELL_Resume()函数允许向 AEE 外壳注册回调。 它可以向 AEE 外壳的待处理操作列表中添加回调。 AEE 外壳将在下一次调用事件循环时调用此回调函数,以使应用程序或对象协同处理多任务。如果已注册回调,则会先将其注销,然后再重新注册。
     
    举例来说,我们在APPLET里中定义m_pCB为AEECallback类型,调用CALLBACK_Init(&pme->m_pCB,FUNC1,pme),意在初始化m_pCB,将函数FUNC1注册到这个结构体中,这时候,m_pCB中的pfnNotify就指向了FUNC1,任何时候,不管是开发人员自己写代码调用ISHELL_Resume()函数,还是通过调用其它函数(BREW平台的APIs),在这些函数中有隐含的调用ISHELL_Resume(),该函数都会在BREW系统的回调链表中添加一个节点,该节点就是m_pCB,那么在适当的时候,BREW就会调用你的FUNC1函数。其实在很多BREW APIs中都会有调用ISHELL_Resume(),例如ISHELL_SetTimerEx()函数,不过这个只是猜测,因为根本无法看到该这些函数的原代码。
     
    归纳起来,如过要使用BREW平台的回调机制,则只要初始化适当的AEECallback结构体,再在需要回调的时候调用ISHELL_Resume来启动回调函数 。
     
    下面来叙述一下BREW平台的消息机制和回调机制的大概流程:
     
    我们假设BREW运行于Ui Task,特定于BREW的Sig为AEE_APP_SIG。
     
    For(;;)
    {
      waitfor sig         // 无限循环,捕捉到相应的Sig后,作相应的处理
    }
     
    if (receive aee app sig)
    {
      AEE_Dispatch         // 如果收到AEE_APP_SIG 说明BREW需要执行,调用AEE_Dispatch进行消息循环分发。
    }
     
    其中AEE_Dispathc是最关键的函数,他会检查BREW事件队列中的所有事件一一分发(调用APP->HandleEvent函数),同时也会检查Callback队列中注册的Callback,一一回调。所有都处理完后,表示本次BREW循环结束,将运行环境切换回UI Task中的其他非BREW程序,或者切换至其他Task。当下次BREW再需要执行时,BREW将再次设置AEE_APP_SIG,从而导致当系统任务调度到UI Task时,AEE_Dispatch再次被调用,以此往复。
     
    关于在ISHELL_Resume注册完回调函数之后,具体会在什么时候调用回调函数,这个并没有深究的价值,在Callback对列中,BREW根据自身的算法选择回调,当一个时间片不够回调所有的Callback的话,则会把未完成的Callback放到下一个或者下几个时间片执行。所以,对于正常的概念来说,只需要知道,当执行了ISHELL_Resume之后,很短的时间内BREW会调用完成Callback的调用。

    January 22

    对AEECallback结构体及其基础函数的分析

    ----2006.1.22

    typedef struct _AEECallback AEECallback;
    struct _AEECallback
    {
     AEECallback *pNext;  //保留,并且调用程序不得修改此成员
     void        *pmc;  //保留,并且调用程序不得修改此成员
     PFNCBCANCEL  pfnCancel;  //回调被取消时,指向回调处理程序所调用函数的指针。调用程序必须将此指针设置为 NULL。
     void        *pCancelData; //传递给 pfnCancel 的数据。 调用程序不得修改此成员。
     PFNNOTIFY    pfnNotify;  //AEE 调用的回调函数。调用程序必须将此指针设为指向 AEE 回调处理程序所调用的函数。
     void        *pNotifyData; //传递给 pfnNotify 的数据,调用程序必须将此指针设为指向须传递给 pfnNotify函数的数据。
     void        *pReserved;  //保留,并由回调处理程序使用此成员
    };
    该结构体,前两个参数pNext和pmc都是不可修改的,是系统维护其值;pfnCancel和pCancelData是在取消回调时使用的;pfnNotify和pNotifyData是在回调时使用的;最后一个pReserved是保留参数,可以存储任何数据。

    AEECallback_Setup函数:
    1、通过CALLBACK_Cancel宏调用了pcb中的pfnCancel函数。
    2、给pfnCancel赋值,给pCancelData赋值
    3、给pReserved赋值,其值是pfnCancelNotify
    备注:猜想该函数的功能就是为pcb结构体赋值,按次序分别是pfnCancel、pCancelData、pReserved

    static void AEECallback_Setup(AEECallback *pcb, PFNCBCANCEL pfnCancel,
                                  void *pvCancel, PFNSCHEDNOTIFY pfnCancelNotify)
    {
       CALLBACK_Cancel(pcb);
       pcb->pfnCancel   = pfnCancel;
       pcb->pCancelData = pvCancel;
       pcb->pReserved   = (void *)pfnCancelNotify;
    }

    AEECallback_Fire函数:
    1、把参数强制类型转换为AEECallback类型
    2、把pfnCancel赋值为0
    3、调用pfnNotify函数
    备注:猜想该函数的功能就是调用notify函数

    static void AEECallback_Fire(void *pvCxt)
    {
       AEECallback *pcb = (AEECallback *)pvCxt;

       pcb->pfnCancel = 0;
       pcb->pfnNotify(pcb->pNotifyData);
    }

    AEECallback_CancelNotify函数:
    1、从pReserved指针中取出保存的pfnCancelNofity,并且强制类型转换,猜想该成员的职责就是保存一些需要保存的指针
    2、把pfnCancel置为0
    3、调用pfnCancelNotify函数
    备注:该函数其实就是要调用其中的CancelNotify函数,只是该函数指针保存在了void类型的pReserved成员里头,需要进行强制类型转换,注意的一点是在调用notify类函数的时候都把pfcCancel置为了0,不知道是何用意

    static void AEECallback_CancelNotify(AEECallback *pcb)
    {
       PFNSCHEDNOTIFY pfnCancelNotify = (PFNSCHEDNOTIFY)pcb->pReserved;

       pcb->pfnCancel = 0;
       pfnCancelNotify(pcb->pCancelData, AEECallback_Fire, pcb);
    }

    AEECallback_SchedNotifyWait函数:
    1、先通过AEECallback_Setup函数来填充pcb结构体
    2、再调用pfnSched函数
    备注:不明白这个函数什么意思

    void AEECallback_SchedNotifyWait(AEECallback *pcb, void *pSchedNotifyObj,
                                     PFNSCHEDNOTIFY pfnSched,
                                     PFNSCHEDNOTIFY pfnCancel)
    {
       AEECallback_Setup(pcb, AEECallback_CancelNotify, pSchedNotifyObj, pfnCancel);
       pfnSched(pSchedNotifyObj, AEECallback_Fire, pcb);
    }

    January 20

    关于BREW多线程雏形的想法

    总感觉不舒服,这个多线程,什么东西都需要自己控制。
    以前也没有怎么接触过多线程这个东西,没什么经验,大家看看还有没有其他什么想法。

    大概想法就是在i=2500或者i=5000或者i=7500的实现线程休眠 500微秒,然后可以把时间片分给其他线程。
    大家看输出,可以看出来,基本上是这样的。

    /* From src/thrdutil */
    void IThread_Sleep(IThread *me,IShell *piShell, int nMS)
    {
        AEECallback *pcb = ITHREAD_GetResumeCBK(me);
        ISHELL_SetTimerEx(piShell,nMS,pcb);
        ITHREAD_Suspend(me);
    }

    /*
    *    pIThread1的回调
    */
    void Cbk_Runnable1(IThread * pIThread, void *po)
    {
        int i;
        TestFor * pMe = (TestFor *)po;
        if(NULL != pIThread)
        {
            DBGPRINTF("thread 1 started");
            for (i=0; i<10000; i++)
            {
                if ((2500 == i) || (5000 == i) || (7500 == i))
                {
                    IThread_Sleep(pIThread, pMe->pIShell, 500);
                }
                DBGPRINTF("Thread 1 Count = %d",i);
            }
        } else
        {
            DBGPRINTF("Thread 1 start fail");
        }
    }
    /*
    *    pIThread2的回调
    */
    void Cbk_Runnable2(IThread * pIThread, void *po)
    {
        int i;
        TestFor * pMe = (TestFor *)po;
        if(NULL != pIThread)
        {
            DBGPRINTF("thread 2 started");
            for (i=0; i<10000; i++)
            {
                if ((2500 == i) || (5000 == i) || (7500 == i))
                {
                    IThread_Sleep(pIThread, pMe->pIShell, 500);
                }
                DBGPRINTF("Thread 2 Count = %d",i);
            }
        } else
        {
            DBGPRINTF("Thread 2 start fail");
        }
    }
    /*
    *    pIThread3的回调
    */
    void Cbk_Runnable3(IThread * pIThread, void *po)
    {
        int i;
        TestFor * pMe = (TestFor *)po;
        if(NULL != pIThread)
        {
            DBGPRINTF("thread started3");
            for (i=0; i<10000; i++)
            {
                if ((2500 == i) || (5000 == i) || (7500 == i))
                {
                    IThread_Sleep(pIThread, pMe->pIShell, 500);
                }
                DBGPRINTF("Thread 3 Count = %d",i);
            }
        } else
        {
            DBGPRINTF("Thread 3 start fail");
        }
    }

    XXX_HandleEvent中添加的部分

    case EVT_APP_START:                        
        // Add your code here...
        {
            int nRtr;
            nRtr = ISHELL_CreateInstance(pMe->pIShell, AEECLSID_THREAD,
                (void **)&pMe->pIThread1);
            if (SUCCESS != nRtr)
                return FALSE;
            nRtr = ISHELL_CreateInstance(pMe->pIShell, AEECLSID_THREAD,
                (void **)&pMe->pIThread2);
            if (SUCCESS != nRtr)
                return FALSE;
            nRtr = ISHELL_CreateInstance(pMe->pIShell, AEECLSID_THREAD,
                (void **)&pMe->pIThread3);
            if (SUCCESS != nRtr)
                return FALSE;

            nRtr = ITHREAD_Start(pMe->pIThread1, 100,
                (PFNTHREAD)Cbk_Runnable1, (void *)pMe);
            nRtr = ITHREAD_Start(pMe->pIThread2, 100,
                (PFNTHREAD)Cbk_Runnable2, (void *)pMe);
            nRtr = ITHREAD_Start(pMe->pIThread3, 100,
                (PFNTHREAD)Cbk_Runnable3, (void *)pMe);
        }
        return(TRUE);

    January 16

    关于AEEClsCreateInstance中nSize的奇怪问题

    AEEClsCreateInstance函数的功能是用来创建接口类,具体可以见Blog中《开发BREW扩展类》一文。在该函数的实现代码中,有一个奇怪的nSize,他是需要创建的接口类申请空间的大小,但是代码中对于该nSize的赋值十分有意思,对于以下的分析比较合理,贴出来分享一下。

    转自:http://expert.imobile.com.cn/bbs

    作者:东方欲晓

    if(nSize < sizeof(ExtensionCls))

    nSize += sizeof(ExtensionCls);
    if((pMe = (ExtensionCls *)MALLOC(nSize +
    sizeof(VTBL(IExtensionCls)))) == NULL )
    return ENOMEMORY;

    ExtensionCls是用户定义的class结构,所以创建的内存nsize至少应该等于这个,
    if(nSize < sizeof(ExtensionCls))

    nSize += sizeof(ExtensionCls);
    是进行合理性检查,防止传入的size不合理(小于class的大小),如果不合理则在nsize的基础上加一个完整的class的内存大小,保证能正常创建class(通常传入的nsize就是sizeof(appclass),所以这一句不会走)
    pMe = (ExtensionCls *)MALLOC(nSize +
    sizeof(VTBL(IExtensionCls))))

    进行真正的内存分配,此时除了给class分配内存外,还要为vtbl分配内存,所以分配的大小为nSize +
    sizeof(VTBL(IExtensionCls))

    这样分配完后,在内存中,class之后就紧接着是一块vtbl区域,接着代码中会为这块vtbl区域初始化,即将其函数指针指向确切的函数地址,最后再将class中的pvt指针指向该vtbl,从而完成了整个class的初始化。

    January 14

    开发BREW扩展类

    看到一篇文章,觉得比较有指导意义,译过来,希望给大家有些帮助,能力有限,有错误的地方还望大家指出来。共同学习,共同进步。

    原文地址:
    https://brewx.qualcomm.com/bws/content/gi/common/appseng/en/knowledgebase/docs/extensions.htm

    1 简介:
    扩展类可以扩充BREW的功能,通常来说,扩展类都是以动态的形式存在并且以OTA方式提供下载,然而在某些情况下,OEM制造商会在他们的设备中添加了一些静态的扩展类,例如:OEM制造商经常会给BREW标准接口类IAddrBook添加许多额外的功能,他们通常会创建自己的IAddrBook接口,并且把新增的额外功能提供给开发者。

    2 静态 VS 动态
    静态扩展类通常是被包含在OEM制造商的SW版本中,不由第三方开发者提供。从另外一个角度看来,动态扩展则是由OTA方式下载。移动应用和它所需要的扩展类是同时被下载到手机上的(除非手机上已经存在了所需的扩展类),通过MIF文件提供的依存列表信息把所需要的扩展类下载到手机当中。每一个动态扩展都会有一个相关联的引用计数,该引用计数负责监视手机上所有应用对于该扩展类的依赖关系,如果引用计数减少到0,则该扩展类将被从手机内存中移除。静态扩展则不是,它没有引用计数,也永远不会被从手机上删除。

    3 特性
    扩展类不包含权限设置,它的权限级别继承于父应用,例如:如果某应用使用了一个具有文件操作功能的扩展类,那么该应用就是扩展类的父应用,除非父应用的MIF文件中设置了允许文件操作的权限,否则该扩展类的所有文件操作都会失败,这一点对于扩展来开发人员来说十分重要。
    与权限级别相似的是,扩展类中所有对于文件的操作都是通过父应用的上下文来执行的,如此来看,对于父应用不可见的文件,扩展类也是无法操作的。
    扩展类即可以是保护类型也可以是未保护类型。对于保护类型来说,如果父应用的MIF文件中的依存关系列表中有显示罗列出该扩展类,那么父应用就可以创建并使用它,反之,则不行;对于非保护类型则可以被所有应用使用,不用考虑该应用是否明确声明了依存关系,这样的扩展类通常被加上一个MIME类型的标记,应用只需要通过外壳查找目标MIME类型的句柄,就可以使用它的功能了。

    4 MIF文件
    扩展类和应用一样需要MIF文件,然而,他们却在根本上有所不同。首先,一个扩展类的MIF文件不需要定义一个applet,只需要在MIF编辑器的"Externsions"标签栏中的"Exported Classes"中设置其class ID即可。保护类型的属性也在该标签栏中设置。
    如果一个应用的运行以来与一个扩展类,那么在该应用的MIF文件中必须把扩展类的class ID设置为依赖,如果没有此类依赖关系,该扩展类将不会在下载应用的时候被一同下载,当该应用运行的时候就会失败。如果扩展类是可选的,就可以不用添加依存关系,然而应用必须能够处理扩展类不存在的情况。

    5 声明扩展类的结构体
    第一步,开发扩展类首先需要声明虚函数表。
    typedef struct IDemoExtension IDemoExtension;
    AEEINTERFACE(IDemoExtension)
    {
            INHERIT_IQueryInterface(IDemoExtension);
            int (*DisplayTime)(IDemoExtension *po);
            int (*DisplayDate)(IDemoExtension *po);
    };

    第一行声明了一个IDemoExtension的结构体,该结构体是一个apple结构体,将会返回给正在初始化的应用,其中的宏AEEINTERFACE声明了一个扩展类的虚函数表,从上面的声明看出,虚函数表中有5个函数。BREW的每一个接口类都定义了一个类似于上面定义中的宏INHERIT_XXX,用它来把已继承的函数添加到已存在的函数表中,至少,BREW强烈推荐如上例所示,从IQueryInterface派生而来。宏INHERIT_IQueryInterface添加三个函数到函数表中:AddRef, Release, QueryInterface。我们的例子中添加了DisplayTime和DisplayData两个函数。
    下一步我们定义宏来索引我们函数表中的函数
    #define IDEMOEXTENSION_AddRef(p)
     AEEGETPVTBL((p),IExtensionCls)->AddRef((p))
    #define IDEMOEXTENSION_Release(p)
        AEEGETPVTBL((p),IExtensionCls)->Release((p))
    #define IDEMOEXTENSION_QueryInterface(p)
        AEEGETPVTBL((p),IExtensionCls)-> QueryInterface((p),(clsid),(pp))
    #define IDEMOEXTENSION_DisplayTime(p)
     AEEGETPVTBL((p),IDemoExtension)->DisplayTime((p))
    #define IDEMOEXTENSION_DisplayDate(p)
     AEEGETPVTBL((p),IDemoExtension)->DisplayDate((p))
    上面的宏可以供父应用来调用扩展类的函数,宏AEEGETPVTBL能准确地指向函数表,并能方便的调用到函数表中的函数,需要注意的是AEEGETPVTBL是同AEEINTERFACE配合起来使用的,我们一旦声明好虚函数表,那么就应该定义扩展类结构体了。

    以上代码的第一行把虚函数表插入了扩展类的结构体定义之中,虚函数表必须总是被定义成为结构体的第一个成员,m_nRefs变量也必须在结构体定义中声明,一个扩展类在应用中将会数次被引用,这个变量用来保存在应用中被引用的总次数,AddRef和Release函数是用来对该变量进行增减操作的,一旦m_nRefs变为0,该扩展类就会释放所有占用的资源。结构体中接下来的三个成员是扩展类的可选成员,可以在扩展类中添加任何所需要的成员变量。
    6 定义函数
    定义的第一个函数必须是AEEClsCreateInstance,REW总是调用该函数来创建该class的接口,下面的代码表明了普遍实现的方法。
    int AEEClsCreateInstance(AEECLSID ClsId, IShell *pIShell,
           IModule *po, void **ppObj)
    {
       *ppObj = NULL;
       if( ClsId == AEECLSID_MYEXTENSION) {
      return MyExtension_New(sizeof(IDemoExtension), pIShell, po,
               (IModule **)ppObj);
       }
       return EFAILED;
    }
    我们的函数首先会检查由BREW传入的参数class ID,如果检查通过了,函数就尝试创建和初始化需要的资源。

    int MyExtension_New(int16 nSize, IShell *pIShell, IModule* pIModule,
                                                       IModule ** ppMod) {
       IDemoExtension*            pMe = NULL;
       VTBL(IDemoExtension) *     modFuncs;
       if( !ppMod || !pIShell || !pIModule )
          return EFAILED;
       *ppMod = NULL;
       // Allocate memory for the ExtensionCls object
       if( nSize < sizeof(IDemoExtension) )
          nSize += sizeof(IDemoExtension);
       // Allocate the module's struct and initialize it. Note that the
       // modules and apps must not have any static data.
       // Hence, we need to allocate the vtbl as well.
       if( (pMe = (IDemoExtension*)MALLOC(nSize +                                      
                                  sizeof(VTBL(IDemoExtension)))) == NULL )
          return ENOMEMORY;
       modFuncs = (VTBL(IDemoExtension)*)((byte *)pMe + nSize);
       //Initialize individual entries in the VTBL
       modFuncs->AddRef              = MyExtension_AddRef;
       modFuncs->Release             = MyExtension_Release;
       modFuncs->QueryInterface      = MyExtension_QueryInterace;
       modFuncs->DisplayTime         = MyExtension_DisplayTime;
       modFuncs->DisplayDate         = MyExtension_DisplayDate;
       // initialize the vtable
       INIT_VTBL(pMe, IModule, *modFuncs);
       // initialize the data members
       pMe->m_nRefs      = 1;
       pMe->m_pIShell    = pIShell;
       pMe->m_pIModule   = pIModule;
       // Add References and get IDisplay
       ISHELL_AddRef(pIShell);
       IMODULE_AddRef(pIModule);
       // Create an instance of the IDisplay
       if( ISHELL_CreateInstance(pIShell, AEECLSID_DISPLAY,
                             (void **)&pMe->m_pIDisplay) != SUCCESS )
          return EFAILED;
       // Set the pointer in the parameter
       *ppMod = (IModule*)pMe;
       return AEE_SUCCESS;
    }

    AddRef, Release和QueryInterface是从IQI接口继承过来的,AddRef负责增加引用计数的数值。

    static uint32 MyExtension_AddRef(IDemoExtension* pMe) {
       // Increment reference count
       return ++(pMe->m_nRefs);
    }

    Release函数负责减少引用计数的数值,当引用计数的值变为0的时候,该实例就会从内存中被释放掉。

    static uint32 MyExtension_Release(IDemoExtension* pMe) {
       // Decrement reference count
       if( --pMe->m_nRefs != 0 )
          return pMe->m_nRefs;

       // Ref count is zero.  Release memory associated with this object.
       if( pMe->m_pIDisplay )
          IDISPLAY_Release(pMe->m_pIDisplay);

       // Release interfaces
       ISHELL_Release(pMe->m_pIShell);
       IMODULE_Release(pMe->m_pIModule);
       //Free the object itself
       FREE_VTBL(pMe, IModule);
       FREE( pMe );
       return 0;
    }

    我们最后一个需要定义的函数就是QueryInterface,这个函数返回一个与扩展类相关联的接口,IWeb接口就是一个例子,当一个应用使用该接口完成一个网页请求的时候,底层的一个TCP套接字被创建,为了能够直接控制这个套接字,应用可以通过调用IWeb_QueryInterface函数,传递参数AEECLSID_SOCKET(AEECLSID_SOCKPORT在BREW3.x中),来获得这个接口,该函数将返回一个指针,下面是QueryInterface的一个具体实现。

    static int MyExtension_QueryInterface(IDemoExtension* me,
                                   AEECLSID class, void** ppo) {
       switch (class) {
          case AEECLSID_QUERYINTERFACE:
          case AEECLSID_MYEXTENSION:
          case AEECLSID_BASE:
             *ppo = me;
             MyExtension_AddRef(me);
             return SUCCESS;
          case AEECLSID_DISPLAY:
             *ppo = me->m_pIDisplay;
              return SUCCESS;
          default:
             *ppo = NULL;
             return ECLASSNOTSUPPORT;
       }
    }

    7 静态扩展类

    上面介绍的这个例子也可以用来创建静态扩展类,然而静态扩展类可以作为全局变量,有了这个特性,下面的代码可以为静态扩展类构造虚函数表。

    static const AEEVTBL(IDemoExtension) gvtIDemoExtension = {
       MyExtension_AddRef,
       MyExtension_Release,
       MyExtension_QueryInterface,
       MyExtension_DisplayTime,
       MyExtension_DisplayDate,
    };

    以上的代码被放置在函数之外,通常是在一个头文件中,初始化扩展类可以简单的用一行代码搞定。

    pMe->pvt = &gvtIDemoExtension;

    上面的代码初始化了整个虚函数表,不必对于虚函数表中每个函数都初始化一次,然而上面的代码仅对静态扩展类是有效的,一个动态扩展类的地址是在运行时确定的(于之不同的是,静态扩展类的地址是在编译时确定的),因此,虚函数表必须在运行时声明。

    8 关于宏
    强烈推荐在以上例子中所使用到的宏,然而在BREW中还有许多其他的可以用来创建扩展类的宏。其中很多宏被包含近来是为了向后的兼容性,一些宏例如:QINTERFACE, GET_PVTBL和DECLARE_IBASE都是十分令人气馁的,他们应该分别被AEEINTERFACE, AEEGETPVTBL和INHERIT_IQueryInterface替代。

    9 使用扩展类
    扩展类可以像标准的BREW APIs一样被创建和释放,每个函数都可以通过头文件的宏定义来调用,同BREW APIs一样,在编译调用扩展类函数的应用的时候需要包含其头文件,下面的例子叙述了如何在应用中调用扩展类的函数。

    if(ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_MYEXTENSION,
                  (void **) (&(pMe->pIDemoExtension))) != SUCCESS ) {  
       return FALSE;
    }

    IDEMOEXTENSION_DisplayTime(pMe->pIDemoExtension);
    if(pMe->pIDemoExtension) {
       IDEMOEXTENSION_Release(pMe->pIDemoExtension);
       pMe->pIDemoExtension = NULL;
    }

    10 通过MIME类型使用扩展类
    扩展类可以把其自身注册为一个MIME类型的句柄,它的注册最好通过MIF编辑器来完成。

    上面提及的例子展示了一个扩展类注册为MYTIME句柄,MIME类型的注册允许在运行时解决class ID的问题,应用可以通过ISHELL_GetHandler函数找到一个MIME类型的class ID,整个函数会遍历所有的MIF文件,寻找以MIME类型方式注册的class ID。
    下面这个例子阐述了一段能创建和显示多种不同格式文件的代码,在此例中,有一个抽象类IReader,一个扩展类可以扩充它的接口来显示多种格式的文件。

    // An Application will call this function when it intends to
    // open a file
    void DisplayFile(MyApp* pMe, char* mime, char* file) {
       AEECLSID myClass;
       // This call will create a handler for MYPDF, MYDOC, or
       // MYXML depending on mime
       myClass = ISHELL_GetHandler(pMe->pIShell, AEECLSID_APP, mime);
       // Create necessary Interface
       ISHELL_CreateInstance(pMe->pIShell, myClass, &pMe->pIReader);

       // Ishell Createinstance
       // Each extension implements DisplayFile, The virtual
       // table will point to the correct corresponding function
       // call.  We are ignoring error checking
       IREADER_DisplayFile(pMe->pIReader, file);
    }

    DisplayFile是一个被所有扩展类实现的扩充IReader的接口,虚函数表在创建扩展类的时候被初始化,这样就使IREADER_DisplayFile可以索引正确的函数来显示文件。

    11 更多信息
    BREW 2.x 察看AEE.h
    BREW 3.x 察看AEEInterface.h

     

    January 12

    关于如何利用AEEINTERFACE和QINTERFACE构造自己的类


    --2006.1.11

    1、关于AEEINTERFACE。

    typedef struct _ISample ISample;

    AEEINTERFACE(ISample)
    {
        INHERIT_IQueryInterface(ISample);

        // add your fun...
        void (* Fun)(ISample* po);
    };

    struct ISample
    {
        const AEEVTBL(ISample)       *pvt;

        // add your var...
        uint32                      m_uRef;
    };

    宏INHERIT_IQueryInterface的含义是:从基接口IBase继承它的几个关键函数指针AddRef, Rlease, QueryInterface
    这样,就很简单的构造出了一个基于AEEINTERFACE的ISample接口,你可以在Fun函数指针后面添加你所需要的函数指针,在m_uRef后面添加你所需要的成员变量,然后用面向对象的眼睛去观察它,那么他就是个类了,虽然他之前放的是个struct。
    还有,如果你比较勤快,或者你想模仿一下BREW平台的那些库函数,又或者你不想让别人知道你到底用了哪些成员,你就可以用宏来定义你的那些成员函数,就像BREW平台那样:
    #define  ISAMPLE_Fun(p)  AEEGETPVTBL(p, ISample)->Fun((p))
    这样,是不是比较cool,不过也有画蛇添足的功能。

    2、关于QINTERFACE
    example中mediaplayer的源代码实在是比较经典,所以就拿里头的IWindow作为例子吧

    typedef struct _IWindow  IWindow;

    QINTERFACE(IWindow)
    {
       void     (*Enable)(IWindow * po, boolean bEnable);
       void     (*Redraw)(IWindow * po);
       boolean  (*HandleEvent)(IWindow * po, AEEEvent eCode, uint16 wParam, uint32 dwParam);
       void     (*Delete)(IWindow * po);

       // add your fun in base struct
    };

    #define INHERIT_CWindow(iname) \
       DECLARE_VTBL(iname) \
       CMediaPlayer * m_pOwner; \
       IShell *       m_pIShell; \
       IDisplay *     m_pIDisplay; \
       flg            m_bActive:1
       // add your var in base struct

    struct CWindow
    {
       INHERIT_CWindow(IWindow);
    };

    struct CMainWin
    {
       INHERIT_CWindow(IWindow);

       IImage *       m_pLogo;
       AEERect        m_rectLogo;
       IMenuCtl *     m_pMainMenu;
       flg            m_bAbout:1;
       // add your var in sub struct
    };

    IWindow就是一个结构体,所有成员都是函数指针,因此也可以称它为接口。INHERIT_CWindow这个宏很重要,继承都靠它了。你可以在上面标注的地方,给基类添加你的数据成员和成员函数。那么当数据成员和成员函数合并,就构成为了CWindow基类。很明显CMainWin是从CWindow派生出来的,如果最好只添加数据成员,如果添加成员函数,就会暴露和成员函数同级的数据成员,那么封装性就会降低了。

    如果你还是想模仿BREW平台,那么可以这么写:
    #define IWINDOW_HandleEvent(p, e, w, dw)  GET_PVTBL(p, IWindow)->HandleEvent(p, e, w, dw)

    在这个应用里头,注意两个宏:DECLARE_VTBL和GET_PVTBL。
    还可以注意一下IWINDOW_HandleEvent这个函数,对于不同的CWindow,就对应不同的XXX_HandleEvent,那么不同的窗体就会体现不同的行为。

    大概也就这么多,不过到底是C语言的模拟,很难达到C++面向对象那么完善,不过对于简单的手机应用开发,已经能够胜任了,如果还想研究更加深入的东西,可以读一读高通平台的内部代码。

     

    January 10

    最近好像闲得憋出病来了(2005.12-2006.1)

    --2006.1.10
     
    老大是05.12去上海出差的。他走之前,我简直就是忙疯了,项目紧张是一方面,对于我这种新手,加上老大那种牛哄哄的脾气,还有就是他严谨的作风,我的神经一秒也没有松驰过,直接导致我有段时间睡眠严重不足,白天过于紧张兴奋,晚上在床上翻来覆去的睡不着,一天能睡上4-5个小时已经是奢侈了。有时候自己也想,拼什么命啊,我还只是个实习生啊,可是看着老大那紧锁的眉头,偷懒的心也就没有了,那段时间一直靠药维持着,幸运的是现在好了。
     
    老大走后,日子完全颠倒过来,没人管,偶尔老大发封邮件过来,让干点小活,不过大多是10分钟搞定的事情。忙惯了的人,闲下来还真不是个办法,于是开始琢磨写些文章,为公司和导师撑撑门面。哎,可是文章哪是那么好些了,花了半个多月的时间闭门造车,然后明白过来,根本不是个道,看了句名言:天下文章一大抄,抄来抄去有提高。哦!!!原来文章不是造出来的,是抄出来了,顿悟之后,开始疯狂下载文献资料,疯狂读前辈们的文章,多亏学校的同学,多亏了手机之家_开发联盟的朋友,不少好文章都出自那里,在那一段时间内,又忙了起来,读书,写比较,总结归纳,上论坛发帖子,还真挺忙活。延着BREW的脉络走下去,一步步的接口、COM,发现计算机还是那么神奇,那么可爱。渐渐的,写文章的计划被一拖再拖,文章读得越多,越觉得自己什么都不懂,越是不敢下笔写文章,越是疯狂的读文章,如此变成了一个程序中的死循环,随着激情的逐渐消退,发现自己偏离主道越来越远了。
     
    手头还有《COM技术内幕》没有看完,书不错,分析得浅显易懂,可是已经没有开始的那股子气了。也好,要不会变为书呆子的。读完这本,就不打算再读了。发现读书还是有几点比较重要:其一,读书,还是读纸质的好,电子版实在是累眼睛,还不便于查阅;其二,还是做笔记比较好,没事的时候把笔记、心得凑凑,发到网站上也可以给大家分享分享。
     
    听说老大要回来了。回来好,回来有人管了,他走之前,我还说,走了好,放羊的滋味美极了。现在只能说,快回来吧,放羊的滋味我是受够了。
     
    离我座位最近的卡座的那个饮水机前段时间烧掉了,一屋子烧糊橡胶的味道,让我觉得难受死了。然后这几天又换了个新的,嗯,不错,新的好啊,鼻尖又飘荡着我那久置的竹叶青,抽屉里头还有一包毛尖呢。
     
    昨天,游戏组的兄弟们一齐搬到对面的新办公楼里头了,算起来,我也曾经是他们中的一员。那些兄弟,都是很不错,最近星际水平突飞猛涨,也是靠了他们啊。CPC走了,没有人来搓我了,大非走了,没有人跟我互磕了。哎,不过又听说他们最近上档次,开始磕魔兽了。
     
    生活就是在变化着,变化了也就能开始新的生活!We are moving on.
     
    MSN的Spaces就是觉得乱,写了文章,看着就是那么觉得不爽,所以又在CSDN上头申请了一个,那块就看着舒服多了,到底看出Spaces的汉化做得还不够好,不过想想微软还是牛啊,随便弄一些东西,都有很大的影响,而且人家还免费,看看QQ,小家子气,弄个Qzone,还非得收钱,我看QQ是恨不得所有服务都收钱,其实我想给那老总发封信:您不如去抢得了!
    CSDN Blog: http://blog.csdn.net/jiyucn/

    朋友们:有钱的捧个钱场,没钱的捧个人场,谢过了。
    January 07

    #pragma 预处理指令详解

          看《COM技术内幕》,看到一个关于提到objbase.h的文件,于是上里头找找看,有没有些有价值的东西,可是一看发现,写了几个月的c了,还有关键字不认识的,它就是#pragma,上网找了篇资料,贴出来,以后查也方便。

     

          在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 
    其格式一般为:    #Pragma Para 
        其中Para 为参数,下面来看一些常用的参数。 

        (1)message 参数 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗 
    口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为: 
           #Pragma message(“消息文本”) 
           当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 
        当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 
           #ifdef _X86 
           #Pragma message(“_X86 macro activated!”) 
           #endif 
           当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_ 
    X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 
    。 
       
       (2)另一个使用得比较多的pragma参数是code_seg。格式如: 
          #pragma code_seg( ["section-name"[,"section-class"] ] ) 
          它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。 

       (3) #pragma once (比较常用) 
          只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。 
       
       (4) #pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。  
         有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。  
       
       (5) #pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。 *.dfm中包括窗体 
    外观的定义。  
        
       (6) #pragma warning( disable : 4507 34; once : 4385; error : 164 ) 
          等价于: 
          #pragma warning(disable:4507 34)  // 不显示4507和34号警告信息 
          #pragma warning(once:4385)        // 4385号警告信息仅报告一次 
          #pragma warning(error:164)        // 把164号警告信息作为一个错误。 
          同时这个pragma warning 也支持如下格式: 
          #pragma warning( push [ ,n ] ) 
          #pragma warning( pop ) 
          这里n代表一个警告等级(1---4)。 
          #pragma warning( push )保存所有警告信息的现有的警告状态。 
          #pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告 
    等级设定为n。  
          #pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的 
    一切改动取消。例如: 
          #pragma warning( push ) 
          #pragma warning( disable : 4705 ) 
          #pragma warning( disable : 4706 ) 
          #pragma warning( disable : 4707 ) 
          //....... 
          #pragma warning( pop )  
          在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。 
        (7)pragma comment(...)   //objbase.h里头用的最多的就是这个用来导入COM的一些基础库
           该指令将一个注释记录放入一个对象文件或可执行文件中。 
          常用的lib关键字,可以帮我们连入一个库文件。 
     
     
    每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如,对循环优化功能: 
    #pragma loop_opt(on)      // 激活 
    #pragma loop_opt(off)  // 终止 
    有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如“Parameter xxx is never used in function xxx”,可以这样: 
    #pragma warn —100      // Turn off the warning message for warning #100 
    int insert_record(REC *r) 
    { /* function body */ } 
    #pragma warn +100            // Turn the warning message for warning #100 back on 
    函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。 
    每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。 

    January 03

    在BREW平台上读文件的速度远大于写文件的速度

    没有在实际的手机上,只是在模拟器上头测试,不过听说在手机上写文件的效率也不够高。
    测试环境:BRWE SDK3.1.4
    代码:
    uint32 lasttime;
    uint32 curtime;
    uint32 timespan;
    //invoke IFILE_SetCacheSize()
    {
            uint32 uRtr;
            uRtr = IFILE_SetCacheSize(pIFile, SCS_MAX);
    }
    lasttime = GETTIMEMS();      //返回当前时间(毫秒)
    filesize = IFILE_Write(pIFile, content, length);
    curtime = GETTIMEMS();      //返回当前时间(毫秒)
    timespan = curtime - lasttime;
    大概就是创建ifilemgr和ifile,然后从一个527,660 字节的文本文件中,读取出数据,再写到另外一个文件中。
    IFILE_SetCacheSize函数用于设置文件高速缓存操作的大小。 这样便允许调用程序选择缓冲区文件访问权,以提高性能。可是对于这个函数的调用似乎并没有起到太大的作用。IFILE_SetCacheSize函数的返回值是153600,表示高速缓存的大小。
    如果调用了IFILE_SetCacheSize函数,则timespan第一次为26593,第二次为26609
    如果不调用该函数,timespan的值一直是26609
    发现这个函数对于提高性能没有太大的作用。

    阅读mediaplayer源代码(first)

    第一次阅读mediaplayer源代码
     
    2006-1-3
     
    mediaplayer源代码是BREW SDK 3.1.4中自带的,一个简单的多媒体应用,2000多行代码,实现了一个支持音频、视频和图片播放,还支持录音的程序。该程序出自高通公司内部开发人员之手,其与高通BREW平台的其他底层应用的实现有异曲同工之妙。该程序的实现充分利用了贯穿于BREW平台的QInterface宏,用C语言巧妙地模仿了面向对象中的多态,继承等特点。感兴趣的朋友,可以在高通的网站上下载最新的SDK安装后,在BREW 3.1.4\sdk\examples\mediaplayer中可以找到对应的源代码。
    由于是第一次阅读,很多东西理解还不深刻,叙述难免有偏差,希望大虾们人过留言,帮忙指出错误。
    /////////////////////////////////////////////////////////////////////
    下面是一些结构体的展开,从阅读代码的角度来说,莫名其妙的宏定义给阅读带来了不少麻烦,但是对于程序开发人员来说,精致的宏定义又给开发带来不小的渐变性,从阅读的角度来说,把宏展开,可以清晰地看到程序设计的脉络,不过在开发的过程中,可以直接使用宏,减少代码的输入量,同时也能保证伴随BREW平台升级所带来的兼容性问题。
    原定义
    typedef struct _IWindow  IWindow;
    QINTERFACE(IWindow)
    {
       void     (*Enable)(IWindow * po, boolean bEnable);
       void     (*Redraw)(IWindow * po);
       boolean  (*HandleEvent)(IWindow * po, AEEEvent eCode, uint16 wParam, uint32 dwParam);
       void     (*Delete)(IWindow * po);
    };
    展开后实际定义
    typedef struct _IWindow IWindow;
    struct _IWindow

     struct IWindowVtbl *pvt;
    };
    typedef struct IWindowVtbl IWindowVtbl;
    struct IWindowVtbl
    {
        void     (*Enable)(IWindow * po, boolean bEnable);
       void     (*Redraw)(IWindow * po);
         boolean  (*HandleEvent)(IWindow * po, AEEEvent eCode, uint16 wParam, uint32 dwParam);
        void     (*Delete)(IWindow * po);
    };
    //注意,其实以下的两者是一样的。
    #define VTBL(iname)   iname##Vtbl
    #define AEEVTBL(iname)  iname##Vtbl
     
    /////////////////////////////////////////////////////////////////////
    定义了枚举类型MPWindow(程序窗口类型)和MPPlayerWin(播放窗口类型:play,record,image)

    /////////////////////////////////////////////////////////////////////
    关于内部几个关键结构体的创建。
    #define INHERIT_CWindow(iname) \
       DECLARE_VTBL(iname) \
       CMediaPlayer * m_pOwner; \
       IShell *       m_pIShell; \
       IDisplay *     m_pIDisplay; \
       flg            m_bActive:1
    从命名的角度来看,主要是用来继承父类,虽然是C不过仍然用C++的思想来设计。CWindow是一个基类,其他所有显示的window都是根据这个基类派生出来的。用INHERIT_CWindow这个宏来完成派生功能。
    // Base class of all IWindow objects.
    struct CWindow
    {
       IWindow vtIWindow;
       CMediaPlayer * m_pOwner;
       IShell *       m_pIShell;
       IDisplay *     m_pIDisplay;
       flg            m_bActive:1;
    };
    对应创建函数CWindow_New
    // Main window: Displays main menu.
    struct CMainWin
    {
       //从这个地方,我们就简单的认为,CMainWin是从CWindow派生出来的,它继承了CWindow的所有数据成员和IWindow的函数指针
       IWindow vtIWindow;
       CMediaPlayer * m_pOwner;
       IShell *       m_pIShell;
       IDisplay *     m_pIDisplay;
       flg            m_bActive:1;
     
       IImage *       m_pLogo;
       AEERect        m_rectLogo;
       IMenuCtl *     m_pMainMenu;

       flg            m_bAbout:1;  //?为什么会有两个flg,如何使两者不冲突
    };

    对应创建函数CMainWin_New
    同样的道理适用于:CFileListWin(对应创建函数CFileListWin_New)、CPlayerWin(对应创建函数CPlayerWin_New)
    CProgCtl这个结构体是表示进程条和标题。
    子类的创建函数xxx_new(),会调用父类的创建函数CWindow_New,先创建CWindow,然后再实例化其自身的成员。
    /////////////////////////////////////////////////////////////////////
    关于消息传递,大概是这样,消息产生,系统调用CMediaPlayer_HandleEvent来处理消息,对于一些可以被处理的消息:EVT_APP_START、EVT_APP_BROWSE_FILE、EVT_APP_STOP、EVT_APP_SUSPEND、EVT_APP_RESUME,函数会调用相关的函数相应消息,对于一些在本函数内无法被处理的消息:EVT_KEY、EVT_COMMAND、EVT_CREATEMEDIA、EVT_CREATEMEDIA_QCP、EVT_COPYRIGHT_END则会调用IWINDOW_HandleEvent来处理消息。
    而IWINDOW_HandleEvent只是个宏,实际上它是:
    GET_PVTBL(p, IWindow)->HandleEvent(p, e, w, dw)
    实际上就是:
    ((IWindow *)p)->pvt->HandleEvent(p, e, w, dw)
    对应的p指针所指向的地址的不同,消息会分发的不同的HandleEvent函数里头,作相应的处理,然后返回TRUE
    /////////////////////////////////////////////////////////////////////
    HandleEvent函数指针的初始化,对于父类CWindow,其Vtbl的初始化是从CWindow_New函数的第三个参数传递进来的,对于CWindow他无法选择自己的Vtbl。而对于其子类和子类对应的XXX_New函数,首先它会申请一个 VTBL(IWindow)的vtbl,然后用MP_IWINDOW_SETVTBL宏给这个vtbl初始化,然后调用CWindow_New,把整个vtbl当成第三个参数传递进去,那么对应于不同的子类,虽然都调用的是IWindow->HandleEvent函数,不过却有了不同的行为,这就是对面向对象多态的精致模仿。