<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_克伟的博客</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/23857/rss</id><updated>2012-01-31T08:49:50Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/23857/rss"/><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/07/04/2097546.html</id><title type="text">我们的创业步伐：我们的产品</title><summary type="text">好久没写博客了，距离《我们的创业步伐》这篇文章有半年了，回首这半年，发现居然不知道说什么了，道不出其中的滋味。 坦白的说我们已经到了生死存亡的时刻了，想想我们的创业历程，刚开始的想法、现在的想法、我们的困难、我们的团队，可以这么概况下： 最初我们喜欢把创业比喻成游泳，不亲自下水游是没办法学会游泳的，但是你也要做好被淹的半死的准备。 穷人做事有穷人的办法，实在没办法的情况下白米饭也能养出来一个产品。...</summary><published>2011-07-04T08:40:00Z</published><updated>2011-07-04T08:40:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/07/04/2097546.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/07/04/2097546.html"/><content type="html">&lt;p&gt;好久没写博客了，距离《&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/20/1940559.html"&gt;我们的创业步伐&lt;/a&gt;》这篇文章有半年了，回首这半年，发现居然不知道说什么了，道不出其中的滋味。&lt;/p&gt;  &lt;p&gt;坦白的说我们已经到了生死存亡的时刻了，想想我们的创业历程，刚开始的想法、现在的想法、我们的困难、我们的团队，可以这么概况下：&lt;/p&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;p&gt;最初我们喜欢把创业比喻成游泳，不亲自下水游是没办法学会游泳的，但是你也要做好被淹的半死的准备。&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;p&gt;穷人做事有穷人的办法，实在没办法的情况下白米饭也能养出来一个产品。&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;p&gt;做有意见、有价值的东西。&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;p&gt;不变的是信念、原则，变的是经验、能力、机遇。&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;p&gt;少一些冲动，多一些冷静，少一些急躁，多一些坚持。我只会越挫越勇。一些事情我希望想的更清楚一些。&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;看看我们做的产品吧。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;第一个产品：MCOS，基于通讯录的IM&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;最初的MCOS的Windows Mobile客户端：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163437164.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="11" border="0" alt="11" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041634526693.png" width="272" height="361" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041634533464.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="12" border="0" alt="12" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041634577702.png" width="272" height="362" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635047914.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="13" border="0" alt="13" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635086613.png" width="275" height="365" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163514969.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="14" border="0" alt="14" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635257305.png" width="276" height="366" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635302316.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="15" border="0" alt="15" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635337908.png" width="278" height="369" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635359313.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="16" border="0" alt="16" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635372985.png" width="278" height="369" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635397247.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="17" border="0" alt="17" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635421062.png" width="280" height="372" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635452127.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="18" border="0" alt="18" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041635488176.png" width="281" height="373" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636009637.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="20" border="0" alt="20" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636069882.png" width="285" height="379" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636116911.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="19" border="0" alt="19" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163614659.png" width="285" height="379" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;你不会看到这个产品了，我们最开始做这个产品时Kik Messager、WhatsApp没有崛起，没有语音消息和群组消息，国内没有米聊、没有微信，个信、友信、速聊等等都还没有，当类Kik的应用遍地都是时我们放弃了，我们需要一个真正的有所差异的、解决用户某个痛的东西。&lt;/p&gt;  &lt;p&gt;虽然MCOS死掉了，但是她的灵魂仍然在，她的灵魂将以不同的形态在其它产品中出现。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;第二个产品：iKnow英语，与相同志趣的同学一起学英语，享受学习英语的乐趣&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;产品Logo：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041648234389.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="logo" border="0" alt="logo" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041648307317.png" width="188" height="188" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;最初的Android第一版：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636223181.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="31" border="0" alt="31" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636336693.png" width="279" height="417" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636382053.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="32" border="0" alt="32" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636423642.png" width="279" height="417" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163646639.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="33" border="0" alt="33" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636495042.png" width="279" height="417" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041636529205.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="34" border="0" alt="34" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637007234.png" width="277" height="414" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637024527.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="35" border="0" alt="35" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637051340.png" width="278" height="415" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637103636.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="36" border="0" alt="36" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637139435.png" width="277" height="414" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;第二版：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163715284.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="1" border="0" alt="1" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637271570.png" width="275" height="411" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637295309.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="2" border="0" alt="2" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163734778.png" width="275" height="411" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637406072.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="3" border="0" alt="3" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637517391.png" width="276" height="412" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041637558598.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="4" border="0" alt="4" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163758776.png" width="276" height="412" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;6月20号版本：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638066363.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="41" border="0" alt="41" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638089196.jpg" width="278" height="461" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638144632.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="42" border="0" alt="42" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638161370.jpg" width="279" height="462" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638201530.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="43" border="0" alt="43" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638254905.jpg" width="281" height="465" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638278229.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="44" border="0" alt="44" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163831897.jpg" width="281" height="465" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638341930.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="45" border="0" alt="45" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638398087.jpg" width="277" height="489" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638421170.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="46" border="0" alt="46" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638444004.jpg" width="295" height="489" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;iPhone版本也是我们从0开始学习开发、申请App Store的帐号、提交审核到发布：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638495918.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="51" border="0" alt="51" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638537473.jpg" width="281" height="420" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/20110704163855797.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="52" border="0" alt="52" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041638599354.jpg" width="281" height="420" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041639054790.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="53" border="0" alt="53" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041639195289.jpg" width="284" height="424" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041639214186.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="54" border="0" alt="54" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201107/201107041639297754.jpg" width="283" height="423" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Android客户端和iPhone客户端近期都会有新版本更新，下一个大的版本特色将是围绕着“如何与相同志趣的同学一起学习交流”这个点来展开，LBS会作为其中一个途径。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;等忙完这一阵子写一些更详细的分享文章。&lt;/p&gt;  &lt;p&gt;如果你有Android手机的话请点击这个链接：&lt;a title="https://market.android.com/details?id=com.iknow" href="https://market.android.com/details?id=com.iknow"&gt;https://market.android.com/details?id=com.iknow&lt;/a&gt;，在官方的电子市场、N多市场、应用汇、安卓市场等上搜索关键字”&lt;strong&gt;iKnow&lt;/strong&gt;“也可以找到。&lt;/p&gt;  &lt;p&gt;有iPhone的话，请点击这个链接：&lt;a title="http://itunes.apple.com/cn/app/id443923760?mt=8" href="http://itunes.apple.com/cn/app/id443923760?mt=8"&gt;http://itunes.apple.com/cn/app/id443923760?mt=8&lt;/a&gt;，在市场上搜索”&lt;strong&gt;iKnow&lt;/strong&gt;“也可以找到。&lt;/p&gt;  &lt;p&gt;来体验一下吧。&lt;/p&gt;  &lt;p&gt;我们一直致力于做一个有用、好用，能够给你带来帮助的产品，所以朋友们有任何想法什么的都可以跟我们提，可以在应用商店后面回复您的建议或者通过以下方式：&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;网站&lt;/strong&gt;：&lt;a href="http://www.imiknow.com"&gt;www.imiknow.com&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;QQ群&lt;/strong&gt;：138 787 125&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;微薄&lt;/strong&gt;：&lt;a title="http://weibo.com/1845827193" href="http://weibo.com/1845827193"&gt;http://weibo.com/1845827193&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;其它产品&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;最近提交App Store审核的应用包括”DotA视频“、”宠物视频“等，晚些跟朋友们分享。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;最后&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;感谢我的女朋友（一直没有抱怨的支持我，一直忍受我的坏脾气），感谢我的弟弟，感谢我的父母，感谢一直支持我们的朋友们。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/2097546.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/07/04/2097546.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/03/02/1969265.html</id><title type="text">Windows Phone 7 Series Developer General FAQ</title><summary type="text">今天查找WP7对TCP/UDP的支持时顺手把其它FAQ也转过来方便查找，原文：Windows Phone 7 Series Developer General FAQ (Updated 5/19/2010) Getting Started First, get the tools: • For Windows Phone Developer Tools CTP, you can download ...</summary><published>2011-03-02T13:09:00Z</published><updated>2011-03-02T13:09:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/03/02/1969265.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/03/02/1969265.html"/><content type="html">&lt;p&gt;今天查找WP7对TCP/UDP的支持时顺手把其它FAQ也转过来方便查找，原文：&lt;a title="http://social.msdn.microsoft.com/Forums/en-US/windowsphone7series/thread/2892a6f0-ab26-48d6-b63c-e38f62eda3b3" href="http://social.msdn.microsoft.com/Forums/en-US/windowsphone7series/thread/2892a6f0-ab26-48d6-b63c-e38f62eda3b3"&gt;Windows Phone 7 Series Developer General FAQ (Updated 5/19/2010)&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Getting Started &lt;/font&gt;    &lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;strong&gt;First, get the tools:&lt;/strong&gt;     &lt;br /&gt;• For Windows Phone Developer Tools CTP, you can download the complete (and free) toolset from &lt;a href="http://www.microsoft.com/downloads/details.aspx?displaylang=en&amp;amp;FamilyID=cabcd5ed-7dfc-4731-9d7e-3220603cad14"&gt;here&lt;/a&gt; .     &lt;br /&gt;• If you wish to use Expression Blend 4, visit &lt;a href="http://blogs.msdn.com/expression/archive/2010/03/15/expression-blend-4-beta-and-information-on-windows-phone-development.aspx"&gt;this page&lt;/a&gt; for download links and details. (Note if you already have the Windows Phone Developer Tools installed, you only need items 1, 3, and 4.)     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Learn about the technologies:&lt;/strong&gt;     &lt;br /&gt;• Silverlight: Visit &lt;strong&gt;&lt;a href="http://www.silverlight.net"&gt;The Official Microsoft Silverlight Site&lt;/a&gt; &lt;/strong&gt;for samples, tutorials, and general information about learning Silverlight.     &lt;br /&gt;• XNA: Visit the &lt;strong&gt;&lt;a href="http://creators.xna.com"&gt;XNA Creators Club Online&lt;/a&gt; &lt;/strong&gt;for information about XNA. Also check out the &lt;strong&gt;&lt;a href="http://forums.xna.com/forums/default.aspx?GroupID=6"&gt;XNA Creators Club Forums&lt;/a&gt; &lt;/strong&gt;.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Get Samples and Libraries and play!&lt;/strong&gt;     &lt;br /&gt;•&lt;strong&gt; &lt;a href="http://social.msdn.microsoft.com/Forums/en-US/windowsphone7series/thread/756c8d2e-4a3f-43cf-a6ed-28fa44a27ff7"&gt;The Forum's live list of samples for Windows Phone 7&lt;/a&gt; &lt;/strong&gt;&lt;strong&gt;(new!)&lt;/strong&gt;     &lt;br /&gt;• &lt;strong&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ff431744(VS.92).aspx"&gt;Code Samples for Windows Phone&lt;/a&gt; &lt;/strong&gt;    &lt;br /&gt;• &lt;strong&gt;&lt;a href="http://channel9.msdn.com/learn/courses/WP7TrainingKit"&gt;Windows Phone 7 Series Development Training Kit&lt;/a&gt;       &lt;br /&gt;&lt;/strong&gt;• &lt;strong&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=b251b247-70ca-4887-bab6-dccdec192f8d&amp;amp;displaylang=en"&gt;OData client Library for Windows Phone 7 Series&lt;/a&gt; &lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Tools Issues&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;My installation failed      &lt;br /&gt;&lt;/strong&gt;Before posting the issue in the forum, please check the event logs on your system for any errors that may provide clues.&lt;strong&gt;      &lt;br /&gt;      &lt;br /&gt;My installation completed, but I am unable to start Visual Studio 2010 Express for Windows Phone. What should I do?&lt;/strong&gt;     &lt;br /&gt;A common problem appears to be related to the user privileges during and after installation. To work around these issues, make sure you are logged in as Administrator, both during installation and when using the tools. Here is an installation checklist:     &lt;br /&gt;1. Check the Release Notes. Make sure your system and already-installed software have the prerequisite requirements, and that issue(s) you may be encountering are not already addressed there. (&lt;a href="http://download.microsoft.com/download/D/9/2/D926FB38-BB43-4D87-AE5A-1A3391279FAC/ReleaseNotes.htm"&gt;http://download.microsoft.com/download/D/9/2/D926FB38-BB43-4D87-AE5A-1A3391279FAC/ReleaseNotes.htm &lt;/a&gt;)     &lt;br /&gt;2. The CTP release does not currently support running from within a virtual machine.     &lt;br /&gt;3. Ensure that you are logged in as Administrator before launching the installer. Otherwise you may encounter registry or file permission issues during installation or when using the tools.     &lt;br /&gt;4. After installation completes, reboot the system. This clears up miscellaneous problems for some users.     &lt;br /&gt;5. Log in as Administrator before launching Visual Studio.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;I am getting the error &amp;quot;The application cannot start&amp;quot;&lt;/strong&gt;     &lt;br /&gt;Please see this blog note: &lt;a href="http://blogs.msdn.com/visualstudio/archive/2009/10/29/how-to-fix-the-application-cannot-start-error.aspx "&gt;http://blogs.msdn.com/visualstudio/archive/2009/10/29/how-to-fix-the-application-cannot-start-error.aspx &lt;/a&gt;    &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Deploying to the emulator is failing&lt;/strong&gt;     &lt;br /&gt;If deploying to the emulator is failing with an error like &amp;quot;Connection failed because of invalid command-line arguments&amp;quot;, try the following workarounds in the order indicated:     &lt;br /&gt;1. If you have just installed the Tools but have not rebooted yet, try rebooting first to see if that fixes the problem.     &lt;br /&gt;2. Try setting VS2010 to launch as Administrator. Here is the procedure. Make sure you are logged in as Administrator. Right click on Microsoft Visual Studio 2010 Express for Windows Phone, choose Properties, click on the Compatibility tab and turn on the check box for &amp;quot;Run this program as an Administrator&amp;quot;. Make sure you are logged in as Administrator and then re-launch Visual Studio.     &lt;br /&gt;3. If the above does not work, close the solution and then delete the solution .suo file and/or the project .user file. Reopen and see if the deployment works.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;The emulator seems slow&lt;/strong&gt;     &lt;br /&gt;1. Use a computer that has “hardware virtualization technology” indicated in its BIOS setup screen (this is the technology used to enable Virtual PC etc.). And then you must ensure that the feature is turned on in the BIOS.     &lt;br /&gt;2. As indicated in the Release Notes, use a high performance DirectX 10 capable card with a WDDM 1.1 driver that can take advantage of GPU acceleration on Windows Phone Emulator. Ensure that you are using the very latest driver from the video card manufacturer.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Application Process in Foreground / Background&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Can my app run in the background / circumvent the foreground requirement?&lt;/strong&gt;     &lt;br /&gt;No, the application model in Windows Phone 7 Series is foreground only execution. If any other application (3rd party or built-in) is started while your application is running, you will get an event notifying you that your application is about to be terminated.     &lt;br /&gt;&lt;strong&gt;     &lt;br /&gt;What happens when my application loses the foreground?       &lt;br /&gt;&lt;/strong&gt;Your application will receive a notification that it is being closed. The time allotted your application after the foreground is lost has not yet been determined. If state persistence is desired, you will need to include the logic in your application to handle this.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;What happens when my application is brought back after losing the foreground?      &lt;br /&gt;&lt;/strong&gt;Your application will be started from the initial entry point. If state persistence is required, your application should have logic to determine if a state was previously saved and work to restore that state.     &lt;br /&gt;&lt;strong&gt;     &lt;br /&gt;Does this means it is impossible to write apps that require background updates to be effective?&lt;/strong&gt;     &lt;br /&gt;No! We have provided a Push Notification service that your application can utilize. Concepts such as Chat Programs, Turn-Based games, and other types of applications that rely on external events are perfect candidates for using Push Notifications. You can read more information about Push Notifications from MSDN: &lt;a href="http://msdn.microsoft.com/en-us/library/ff402558%28VS.92%29.aspx"&gt;http://msdn.microsoft.com/en-us/library/ff402558%28VS.92%29.aspx&lt;/a&gt;     &lt;br /&gt;&lt;strong&gt;     &lt;br /&gt;Can I use Push Notifications to start my application if it is not running?&lt;/strong&gt;     &lt;br /&gt;Not directly: A cloud service can send a push notification to a device to update your application's tile, or to display a toast notification. The application will launch only if the user clicks either the tile or the toast notification.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I set a timer that will start my application?      &lt;br /&gt;&lt;/strong&gt;No, the decision to start your application is solely up to the user.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can my application be started based on system events?&lt;/strong&gt;     &lt;br /&gt;This is unlikely, but this is still an open question. Most likely, you will be unable to start your application due to a system event, but you may be able to get a notification cached for when the user starts your application. More information on this will be posted as we learn more about it.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I write a Service DLL that can stay resident in the OS if I don't have a UI?      &lt;br /&gt;&lt;/strong&gt;No, you will not be able to write service DLL's. All assemblies will run in their own sandbox, apart from the OS and each other. &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Application Installation&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;How do I install applications on my device? &lt;/strong&gt;    &lt;br /&gt;Applications will automatically be installed when downloaded from Marketplace.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I manually install applications without using the Marketplace?&lt;/strong&gt;     &lt;br /&gt;No, &amp;quot;side-loading&amp;quot; applications is not permitted. The only way to get released applications on your device is through Marketplace.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;How can I test my application on a device if I can't install it?&lt;/strong&gt;     &lt;br /&gt;As a registered developer with Marketplace, you will be able to register a number of devices that you can directly deploy your application to for testing. It is not necessary to publish your application to Marketplace just to test it.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;My application is for my company / specific group of people only, how do I distribute the app only to them on Marketplace?&lt;/strong&gt;     &lt;br /&gt;The Marketplace does not currently have the ability to provide locked-off areas for private applications. Other than a few key exceptions for Mobile Operators, there is no way to gate your application to a specific group of people or devices at this time.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;strong&gt;Marketplace Questions&lt;/strong&gt; &lt;strong&gt;(new!)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;How do I register as a developer for Marketplace?&lt;/strong&gt;     &lt;br /&gt;Visit &lt;a href="https://developer.windowsphone.com/Signup-Create-Account.aspx"&gt;this site&lt;/a&gt; and follow the directions to register as a developer for Windows Phone.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;How much does it cost to register and what do I get with the subscription?&lt;/strong&gt;     &lt;br /&gt;There is currently a $99 annual subscription. For details about this and many other common questions, please see &lt;a href="https://developer.windowsphone.com/Help.aspx"&gt;The Windows Marketplace FAQ&lt;/a&gt; .     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;This price and site indicates development for Windows Phone 6.x; will my account work for Windows Phone 7 as well?&lt;/strong&gt;     &lt;br /&gt;Details concerning how Windows Phone 7 development will be handled with existing subscriptions and future pricing have not yet been announced. This point will be updated once this information is announced.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Where can I get support for Marketplace?&lt;/strong&gt;     &lt;br /&gt;For general Windows Marketplace for Mobile questions, please visit &lt;a href="http://social.msdn.microsoft.com/Forums/en-US/mktplace/threads"&gt;their forum&lt;/a&gt; . (Note that there are not many details available for Marketplace's role with Windows Phone 7 yet.)&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Device Connectivity&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Is ActiveSync still used to connect the device to the PC?      &lt;br /&gt;&lt;/strong&gt;No, synchronization is automatic similar to the way the Zune HD connects to the PC.     &lt;br /&gt;&lt;strong&gt;     &lt;br /&gt;How should I transfer information over the Internet?&lt;/strong&gt;     &lt;br /&gt;Web technologies such as HTTPGET and WebServices are supported as methods of communicating on the Internet. You can also use Push Notifications for communication when your application is not running.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I use Sockets for peer to peer communication?      &lt;br /&gt;&lt;/strong&gt;&lt;font color="#ff0000"&gt;Windows Phone 7 Series currently does not expose the Socket classes&lt;/font&gt;. While the version of Silverlight on the phone follows closely the feature list for Silverlight 3 on the desktop, it is a subset of those features and will not contain all classes. (It also holds a superset of classes not available in Silverlight 3.)     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;How can I connect with another client in real-time?&lt;/strong&gt;     &lt;br /&gt;Peer-to-peer communication is not supported with this initial release of Windows Phone 7 Series. We are always evaluating the needs of our developers and users though and it may be determined that this feature be added in the future.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Hub/Pivot/Tab and other missing controls in Silverlight&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Where is that hub control I’ve seen in several demos?&lt;/strong&gt;     &lt;br /&gt;The “Hub” and “Pivot” controls that were seen in several demos at MIX are a work in progress and are not part of the current set of tools.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;When will they be available?&lt;/strong&gt;     &lt;br /&gt;There is no set schedule for these controls however the product group is closely watching the developer community to understand the kinds of controls that would be most useful. If there is a control you would like to see that is currently missing, please post a comment in the forum!     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I make my own?&lt;/strong&gt;     &lt;br /&gt;You can absolutely make your own controls. Note that the Pivot / Hub control is a popular request, chances are good that this is something that will be provided at some point as part of the official tools.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Device Storage&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;What is &amp;quot;Isolated Storage&amp;quot;      &lt;br /&gt;&lt;/strong&gt;Isolated Storage refers to the space in the device file system your application occupies. You are not permitted to make file system calls outside of this space (including other application spaces or OS space.) This provides your application with its own &amp;quot;safe&amp;quot; sandbox, and prevents other applications from inadvertently (or maliciously) affecting your application's space.     &lt;br /&gt;&lt;strong&gt;     &lt;br /&gt;Is there a local database available on Windows Phone 7 Series devices?&lt;/strong&gt;     &lt;br /&gt;No, for this initial release of Windows Phone 7 Series, there are no local database API’s available.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;How should I store information for my application?&lt;/strong&gt;     &lt;br /&gt;You can store information in your own store space. If you have need of a large database there are other options as well: Windows Phone 7 Series has support for WebServices, which enable you to easily access information that you store on the Internet. With a database that is accessible to a webservice, your application can get data in real time while connected to the Internet.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;How should I handle disconnected scenarios?&lt;/strong&gt;     &lt;br /&gt;Without a connection to the Internet, you will not be able to access cloud based services for data. In cases like this it is recommended that your application gracefully handle this in some way (be it to store a cache of data in a custom format on the device, or advise the user of the lack of connectivity.)     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;My application is intended to run in an off-line scenario, What do I do if I can’t access the cloud?&lt;/strong&gt;     &lt;br /&gt;There are two options for this scenario:     &lt;br /&gt;• Build your own data storage scheme on the device to be stored within device memory     &lt;br /&gt;• Continue developing for Windows Phone 6.5 or previous versions, which continue to be supported and do support local databases.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Will there be support for local databases in the future? &lt;/strong&gt;    &lt;br /&gt;There are no currently announced plans to add this functionality; however we are closely monitoring the needs of the developers and users to identify features that would benefit everyone.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;strong&gt;Development Technologies&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;What technologies and tools can I use to program for Windows Phone 7 Series?      &lt;br /&gt;&lt;/strong&gt;You can currently use the managed language C# using Silverlight and XNA based on Compact Framework.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I use Windows Forms?      &lt;br /&gt;&lt;/strong&gt;No, Windows Forms are not supported in the version of Compact Framework included with Windows Phone 7 Series.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;How do I upgrade my existing Windows Forms applications to Silverlight?&lt;/strong&gt;     &lt;br /&gt;First, become familiar with Silverlight. (You can learn more about Silverlight at &lt;strong&gt;&lt;a href="http://www.silverlight.net"&gt;The Official Microsoft Silverlight Site&lt;/a&gt; &lt;/strong&gt;.) This will help you properly convert the UI aspects of your application from Windows Forms to Silverlight. The next step is to convert your business logic. Most of this should work without change, however there are some major changes in class support that you may need to seriously consider such as socket support.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I write my application in C++?&lt;/strong&gt;     &lt;br /&gt;No, only managed code written in C# is supported for developing on the Windows Phone 7 Series platform. Unmanaged code and other languages (including Managed C++) are not supported.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I write my application in VB?&lt;/strong&gt;     &lt;br /&gt;Not at this time, however this is being strongly considered for a future release of the developer tools.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I P/Invoke into System API’s? &lt;/strong&gt;    &lt;br /&gt;No, as an ISV there is no access to the system API’s using P/Invoke. Any application that attempts to use P/Invoke will be rejected from being published to the Marketplace.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I P/Invoke my own native DLL?&lt;/strong&gt;     &lt;br /&gt;No, native code may not be run as any part of your application. P/Invokes are not permitted.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;What are the main benefits of using Expression Blend or Expression Studio?      &lt;br /&gt;&lt;/strong&gt;These are very effective productivity optimization tools, used to easily adjust the program’s appearance and elements ( Fit and Finish) such as gradients, shapes, vector paths using a very inuitive designer UI. You can run these tools at the same time as Visual Studio, against the same Project.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;Silverlight, XNA, and Compact Framework&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;What version of Silverlight does the Phone use?      &lt;br /&gt;&lt;/strong&gt;The version of Silverlight on the phone is a very close subset of Silverlight 3 on the desktop. There are some features available beyond Silverlight 3 that provide phone specific functionality, but many applications that work well in Silverlight 3 on the desktop should work well on the phone.     &lt;br /&gt;    &lt;br /&gt;&lt;strong&gt;Can I use XNA library calls in Silverlight?&lt;/strong&gt;     &lt;br /&gt;Silverlight and XNA share a common code base in the Compact Framework. If a call is not GUI based, in most cases it can be shared between Silverlight and XNA.&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1969265.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/03/02/1969265.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/20/1940559.html</id><title type="text">我们的创业步伐</title><summary type="text">2010年对我来说是个难忘的一年，在比亚迪时从深圳出差来到北京，然后又从北京回到深圳，从比亚迪离职之后收到了微软Vender职位的Offer，所以又回到北京。虽然从北京回南方时我就默默的下决定以后不来...</summary><published>2011-01-20T12:00:00Z</published><updated>2011-01-20T12:00:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/20/1940559.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/20/1940559.html"/><content type="html">&lt;p&gt;2010年对我来说是个难忘的一年，在比亚迪时从深圳出差来到北京，然后又从北京回到深圳，从比亚迪离职之后收到了微软Vender职位的Offer，所以又回到北京。虽然从北京回南方时我就默默的下决定以后不来北京了，但是我仍然禁不住这次这么好的学习机会，所以我放弃了更稳定的、看上去更好的工作重新来到陌生的北京。我就这么南北折腾，寻找离梦想更近的路子。&lt;/p&gt;  &lt;p&gt;在微软工作的那段时间我收获很多，我看到了更牛的技术专家，看到了更高的思想境界，之前觉得非常神秘的东西仿佛理所应当知道，之前觉得无解的难题仿佛轻而易举。认识到这个世界牛人满天飞，我要努力做到的仅仅是不要变成井底之蛙。怀着谦虚和开放的胸怀才能更快的提高。&lt;/p&gt;  &lt;p&gt;所以那为什么不变得更开放？尽力提高自己，帮助更多的人，尽力让自己更有价值？所以这就是为什么我在微软最大的收获不是技术，而是生活态度、眼界发生的微妙变化的原因。&lt;/p&gt;  &lt;p&gt;在微软的工作那些日子，我几乎把全部的时间放在工作上，查询和学习更多的资料，但是即使这样我仍然感觉时间不够用，这也导致了我分享的博客文章少了，但是我仍然想分享更多有价值的东西，但是我首先得提高自己，不是吗？此刻我更想分享创业的经历和感悟，我想这是第一篇。&lt;/p&gt;  &lt;p&gt;2010年10月中旬我辞职了，很多朋友不解，按照我之前的计划我会努力成为一个微软优秀的部门经理。收拾东西离开大门的时候我站在希格玛大厦前，热泪盈眶，走进的那一天我充满敬畏，但是离开的那一天我充满了感激。（在这里还要特别的感谢&lt;a href="http://aawolf.cnblogs.com/"&gt;马宁&lt;/a&gt;、肖翔等一直给予我帮助的朋友和老师，虽然不再一起共事了，他们仍然对我们的创业给予鼓励和帮助。）&lt;/p&gt;  &lt;p&gt;这次创业最初遇到的是发起人少辉，几次见面之后发现他就是个非常平易近人，爱讲好玩、搞笑经历的家伙，特别爱帮助别人，跟他在一起你发现创业是件充满乐趣的事。他甚至放弃了享有终身员工的待遇来创业。之后不断的进来新的团队成员：纯明、孙链、抱抱、宇光、于伟、iToday开源项目认识的锦峰，以及入职时认识的在微软做设计的庭宇，还有之后加入的晨龙等等。我们具有相似的价值观，我们都热爱我们做的事，我们给我们的团队起了一个名字 – MCOS Team。我们甚至有一个小泰迪：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101201959276872.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="http_imgloadCAI1RF05_thumb1" border="0" alt="http_imgloadCAI1RF05_thumb1" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101201959314171.jpg" width="205" height="305" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101201959338372.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="WP_000016_thumb2" border="0" alt="WP_000016_thumb2" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101201959366193.jpg" width="230" height="305" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;团队的其他成员跟我一样碰到这样的疑问：现在的生活挺好的，为什么要创业？&lt;/p&gt;  &lt;p&gt;这让我想起小时候经常听的那首歌 - 《&lt;a href="http://mp3.baidu.com/m?f=ms&amp;amp;rf=idx&amp;amp;tn=baidump3&amp;amp;ct=134217728&amp;amp;lf=&amp;amp;rn=&amp;amp;word=%D4%D9%D2%B2%B2%BB%C4%DC%D5%E2%D1%F9%BB%EE&amp;amp;lm=-1"&gt;再也不能这样活&lt;/a&gt;》，这是对我有很多影响的一首歌，我们总很容易变得安于现状，但是回首往事发现我们其实可以做更多事情的。人后悔的往往是没有做过的事情。我们是这么想的。&lt;/p&gt;  &lt;p&gt;其实我创业的最直接原因是我之前发起的开源项目 - &lt;a href="http://www.cnblogs.com/wangkewei/archive/2010/03/15/1686069.html"&gt;iToday&lt;/a&gt;，从这个开发项目中我认识了很多志同道合的朋友，而且让自己和别人有所收获的感觉太棒了，但是iToday的发展出现了问题，这其实就是开发项目的弊端，因为没有商业模式来支持开源开发团队，项目进度一拖再拖，开发人员也得赚钱吃饭，这个问题在中国显得更是问题，我们不得不放一放这个项目了。&lt;/p&gt;  &lt;p&gt;不管在来到北京之前在华南师范大学科技开发公司的一个部门中组建团队，还是进入现在的团队的最初原因，我都在尝试完成一个承诺：继续开源项目，分享更多有价值的东西。但是真的很坎坷。&lt;/p&gt;  &lt;p&gt;但是我一定会做到的。&lt;/p&gt;  &lt;p&gt;创业之初，我和少辉、纯明经常一起聚餐聊聊我们的创业梦想，如果我们赚钱了，我们一定要拿出来一部分做医疗和环境建设。等做的更好一点时，我要去旅游更多地方，我要开阔自己的眼界，纯明要有一个研究院好好研究技术。吃饭之余随便聊聊的、看似只是虚幻的东西一直藏在我们心中，在我们艰难的时候带给我们信念和动力。&lt;/p&gt;  &lt;p&gt;我们做沟通，出于我们有这样的感受：智能手机为什么不能让沟通变得更智能？为什么还要1角1条的短信？我们一直探索如何建立一个基于真实SNS的沟通工具，如何将虚拟世界跟真实世界的关系变得统一，怎样将一些只能在虚拟世界才能享受到的服务在真实世界中实现。&lt;/p&gt;  &lt;p&gt;我们会一直努力“让沟通变得更好。”&lt;/p&gt;  &lt;p&gt;MCOS - Magic Cube Of Soul，心灵魔方。为什么我们会给我们的产品和Team起一个这样的名字？我们每个人在生活中都扮演不同的角色，可以是员工，可以是父亲，可以是朋友，可以是老师…能够像玩魔方一样扮演好这些角色我们就能拥有幸福的生活，有的时候魔方的一面离另一面看似是这么的近，可又是那么的远。&lt;/p&gt;  &lt;p&gt;当然最重要的是踏踏实实走好现在的每一步。&lt;/p&gt;  &lt;p&gt;朋友问我我能坚持多久，我跟朋友说不要怀疑我的毅力。全职创业已经三个多月了，我也体验了三个月的酸甜苦辣，我也知道更丰富的体会还在后面，但是此刻我更加坚定了我的决心。与最开始朦胧的idea相比，我们已经清晰了我们的路子，当然也更坚定了我们的决心。&lt;/p&gt;  &lt;p&gt;这几天抽空余时间阅读了《&lt;a href="http://book.douban.com/subject/3699395/"&gt;观止-微软创建NT和未来的夺命狂奔&lt;/a&gt;》，&lt;a href="http://en.wikipedia.org/wiki/Dave_Cutler"&gt;卡特勒&lt;/a&gt;带领的Team花了5年时间来创建NT，换作其他Team又有几个能坚持下来？这样的路子本来不就是“结果总是那么美好而过程总是漫长而艰辛的”？&lt;/p&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 卡特勒很满意。他又一次战胜了所有怀疑他的人，证明了一个团队可以把混乱变得有序，不管它那么难控制。过去的一年如此艰难。这个团队去除了三万多个臭虫，难以置信。尽管有如此多的具体困难，但是许多人都坚持下来了，对此卡特勒“觉得有些吃惊，因为他们承受的压力是非常大的。”现在都结束了——包括所有人。&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 独自在办公室中，卡特勒在他的计算机面前坐下，开始写一封信：&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; NT正式发布到工厂生产了！&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 让我再说一遍——NT发布了！！！&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 这是非常漫长而艰难的一次奋斗。你们都出色地完成了自己的工作。&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 感谢所有的人，谢谢你们的贡献，特别是在最后的三个月中，我们想尽办法，每天铲除了200多个臭虫，而没有产生过严重的副作用。为了做到这一点，每个人都付出了无比的努力。&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 我们达到或者超过了所有发布的标准！&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 当他写完后，他把这封信发送到了把所有团队成员联系在一起的计算机网络上。一瞬间，他让这个团队的所有人都感动了。&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;那么你是否也在找这样的一个团队，一个可以安放自己梦想的地方？我们一直在寻找更多志同道合的移动互联网创业者，你不用担心因为创业而不能保证生活水平，我们会提供让你满意的薪资和股份期权。&lt;/p&gt;  &lt;p&gt;欢迎博友一起讨论创业的话题。&lt;/p&gt;  &lt;p&gt;QQ：3423 67 776 &lt;/p&gt;  &lt;p&gt;GMail/GTalk/MSN：&lt;a href="mailto:wangkeweinuaa@gmail.com"&gt;wangkeweinuaa@gmail.com&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1940559.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/20/1940559.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936488.html</id><title type="text">Android NDK r5 Native Activity</title><summary type="text">Convenience for implementing an activity that will be implemented purely in native code. That is, a game (or game-like thing). There is no need to derive from this class; you can simply declare it in ...</summary><published>2011-01-15T15:03:00Z</published><updated>2011-01-15T15:03:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936488.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936488.html"/><content type="html">&lt;p&gt;Convenience for implementing an activity that will be implemented &lt;strong&gt;purely in native code&lt;/strong&gt;. That is, a game (or game-like thing). There is no need to derive from this class; you can simply declare it in your manifest, and use the NDK APIs from there. &lt;/p&gt;  &lt;p&gt;A typical manifest would look like: &lt;/p&gt;  &lt;pre&gt;&amp;lt;manifest xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; package=&amp;quot;com.example.native_activity&amp;quot;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; android:versionCode=&amp;quot;1&amp;quot;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; android:versionName=&amp;quot;1.0&amp;quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;!-- This is the platform API where NativeActivity was introduced. --&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;8&amp;quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;!-- This .apk has no Java code itself, so set hasCode to false. --&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;application android:label=&amp;quot;@string/app_name&amp;quot; android:hasCode=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;!-- Our activity is the built-in NativeActivity framework class.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; This will take care of integrating with our NDK code. --&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;activity android:name=&amp;quot;android.app.NativeActivity&amp;quot;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;!-- Tell NativeActivity the name of or .so --&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;meta-data android:name=&amp;quot;android.app.lib_name&amp;quot;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; android:value=&amp;quot;native-activity&amp;quot; /&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;intent-filter&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;action android:name=&amp;quot;android.intent.action.MAIN&amp;quot; /&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;category android:name=&amp;quot;android.intent.category.LAUNCHER&amp;quot; /&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/activity&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/application&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/manifest&amp;gt; &lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;A very simple example of native code that is run by NativeActivity follows. This reads input events from the user and uses OpenGLES to draw into the native activity's window. &lt;/p&gt;&#xD;
&#xD;
&lt;pre&gt;#include &amp;lt;jni.h&amp;gt;&lt;br /&gt;#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;EGL/egl.h&amp;gt;&lt;br /&gt;#include &amp;lt;GLES/gl.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;android/sensor.h&amp;gt;&lt;br /&gt;#include &amp;lt;android/log.h&amp;gt;&lt;br /&gt;#include &amp;lt;android_native_app_glue.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, &amp;quot;native-activity&amp;quot;, __VA_ARGS__))&lt;br /&gt;#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, &amp;quot;native-activity&amp;quot;, __VA_ARGS__))&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Our saved state data.&lt;br /&gt; */&lt;br /&gt;struct saved_state {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; float angle;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; int32_t x;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; int32_t y;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Shared state for our app.&lt;br /&gt; */&lt;br /&gt;struct engine {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; struct android_app* app;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ASensorManager* sensorManager;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; const ASensor* accelerometerSensor;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ASensorEventQueue* sensorEventQueue;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; int animating;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLDisplay display;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLSurface surface;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLContext context;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; int32_t width;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; int32_t height;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; struct saved_state state;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Initialize an EGL context for the current display.&lt;br /&gt; */&lt;br /&gt;static int engine_init_display(struct engine* engine) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; // initialize OpenGL ES and EGL&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; /*&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * Here specify the attributes of the desired configuration.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * Below, we select an EGLConfig with at least 8 bits per color&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * component compatible with on-screen windows&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; */&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; const EGLint attribs[] = {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; EGL_SURFACE_TYPE, EGL_WINDOW_BIT,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; EGL_BLUE_SIZE, 8,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; EGL_GREEN_SIZE, 8,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; EGL_RED_SIZE, 8,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; EGL_NONE&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; };&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLint w, h, dummy, format;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLint numConfigs;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLConfig config;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLSurface surface;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLContext context;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; eglInitialize(display, 0, 0);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; /* Here, the application chooses the configuration it desires. In this&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * sample, we have a very simplified selection process, where we pick&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * the first EGLConfig that matches our criteria */&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; eglChooseConfig(display, attribs, &amp;amp;config, 1, &amp;amp;numConfigs);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * guaranteed to be accepted by ANativeWindow_setBuffersGeometry().&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * As soon as we picked a EGLConfig, we can safely reconfigure the&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &amp;amp;format);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ANativeWindow_setBuffersGeometry(engine-&amp;gt;app-&amp;gt;window, 0, 0, format);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; surface = eglCreateWindowSurface(display, config, engine-&amp;gt;app-&amp;gt;window, NULL);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; context = eglCreateContext(display, config, NULL, NULL);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; LOGW(&amp;quot;Unable to eglMakeCurrent&amp;quot;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return -1;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; eglQuerySurface(display, surface, EGL_WIDTH, &amp;amp;w);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; eglQuerySurface(display, surface, EGL_HEIGHT, &amp;amp;h);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;display = display;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;context = context;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;surface = surface;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;width = w;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;height = h;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;state.angle = 0;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; // Initialize GL state.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; glEnable(GL_CULL_FACE);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; glShadeModel(GL_SMOOTH);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; glDisable(GL_DEPTH_TEST);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Just the current frame in the display.&lt;br /&gt; */&lt;br /&gt;static void engine_draw_frame(struct engine* engine) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; if (engine-&amp;gt;display == NULL) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // No display.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; // Just fill the screen with a color.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; glClearColor(((float)engine-&amp;gt;state.x)/engine-&amp;gt;width, engine-&amp;gt;state.angle,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ((float)engine-&amp;gt;state.y)/engine-&amp;gt;height, 1);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; glClear(GL_COLOR_BUFFER_BIT);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; eglSwapBuffers(engine-&amp;gt;display, engine-&amp;gt;surface);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Tear down the EGL context currently associated with the display.&lt;br /&gt; */&lt;br /&gt;static void engine_term_display(struct engine* engine) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; if (engine-&amp;gt;display != EGL_NO_DISPLAY) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; eglMakeCurrent(engine-&amp;gt;display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine-&amp;gt;context != EGL_NO_CONTEXT) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; eglDestroyContext(engine-&amp;gt;display, engine-&amp;gt;context);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine-&amp;gt;surface != EGL_NO_SURFACE) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; eglDestroySurface(engine-&amp;gt;display, engine-&amp;gt;surface);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; eglTerminate(engine-&amp;gt;display);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;animating = 0;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;display = EGL_NO_DISPLAY;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;context = EGL_NO_CONTEXT;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;surface = EGL_NO_SURFACE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Process the next input event.&lt;br /&gt; */&lt;br /&gt;static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; struct engine* engine = (struct engine*)app-&amp;gt;userData;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;animating = 1;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;state.x = AMotionEvent_getX(event, 0);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;state.y = AMotionEvent_getY(event, 0);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return 1;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Process the next main command.&lt;br /&gt; */&lt;br /&gt;static void engine_handle_cmd(struct android_app* app, int32_t cmd) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; struct engine* engine = (struct engine*)app-&amp;gt;userData;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; switch (cmd) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; case APP_CMD_SAVE_STATE:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // The system has asked us to save our current state.&amp;#160; Do so.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;app-&amp;gt;savedState = malloc(sizeof(struct saved_state));&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; *((struct saved_state*)engine-&amp;gt;app-&amp;gt;savedState) = engine-&amp;gt;state;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;app-&amp;gt;savedStateSize = sizeof(struct saved_state);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; break;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; case APP_CMD_INIT_WINDOW:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // The window is being shown, get it ready.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine-&amp;gt;app-&amp;gt;window != NULL) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine_init_display(engine);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine_draw_frame(engine);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; break;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; case APP_CMD_TERM_WINDOW:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // The window is being hidden or closed, clean it up.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine_term_display(engine);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; break;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; case APP_CMD_GAINED_FOCUS:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // When our app gains focus, we start monitoring the accelerometer.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine-&amp;gt;accelerometerSensor != NULL) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ASensorEventQueue_enableSensor(engine-&amp;gt;sensorEventQueue,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;accelerometerSensor);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // We'd like to get 60 events per second (in us).&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ASensorEventQueue_setEventRate(engine-&amp;gt;sensorEventQueue,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;accelerometerSensor, (1000L/60)*1000);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; break;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; case APP_CMD_LOST_FOCUS:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // When our app loses focus, we stop monitoring the accelerometer.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // This is to avoid consuming battery while not being used.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine-&amp;gt;accelerometerSensor != NULL) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ASensorEventQueue_disableSensor(engine-&amp;gt;sensorEventQueue,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;accelerometerSensor);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Also stop animating.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine-&amp;gt;animating = 0;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine_draw_frame(engine);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; break;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * This is the main entry point of a native application that is using&lt;br /&gt; * android_native_app_glue.&amp;#160; It runs in its own thread, with its own&lt;br /&gt; * event loop for receiving input events and doing other things.&lt;br /&gt; */&lt;br /&gt;void android_main(struct android_app* state) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; struct engine engine;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; // Make sure glue isn't stripped.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; app_dummy();&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; memset(&amp;amp;engine, 0, sizeof(engine));&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; state-&amp;gt;userData = &amp;amp;engine;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; state-&amp;gt;onAppCmd = engine_handle_cmd;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; state-&amp;gt;onInputEvent = engine_handle_input;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine.app = state;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; // Prepare to monitor accelerometer&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine.sensorManager = ASensorManager_getInstance();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ASENSOR_TYPE_ACCELEROMETER);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; state-&amp;gt;looper, LOOPER_ID_USER, NULL, NULL);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; if (state-&amp;gt;savedState != NULL) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // We are starting with a previous saved state; restore from it.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine.state = *(struct saved_state*)state-&amp;gt;savedState;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; // loop waiting for stuff to do.&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; while (1) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Read all pending events.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; int ident;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; int events;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; struct android_poll_source* source;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // If not animating, we will block forever waiting for events.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // If animating, we loop until all events are read, then continue&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // to draw the next frame of animation.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &amp;amp;events,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (void**)&amp;amp;source)) &amp;gt;= 0) {&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Process this event.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (source != NULL) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; source-&amp;gt;process(state, source);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // If a sensor has data, process it now.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (ident == LOOPER_ID_USER) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine.accelerometerSensor != NULL) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ASensorEvent event;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; while (ASensorEventQueue_getEvents(engine.sensorEventQueue,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;amp;event, 1) &amp;gt; 0) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; LOGI(&amp;quot;accelerometer: x=%f y=%f z=%f&amp;quot;,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; event.acceleration.x, event.acceleration.y,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; event.acceleration.z);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Check if we are exiting.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (state-&amp;gt;destroyRequested != 0) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine_term_display(&amp;amp;engine);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine.animating) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Done with events; draw next animation frame.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine.state.angle += .01f;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (engine.state.angle &amp;gt; 1) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine.state.angle = 0;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Drawing is throttled to the screen update rate, so there&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // is no need to do timing here.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; engine_draw_frame(&amp;amp;engine);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;/pre&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1936488.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936488.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936338.html</id><title type="text">Build Android Source Code</title><summary type="text">Setting up your machine  To build the Android source files, you will need to use Linux or Mac OS. Building under Windows is not currently supported.  Linux The Android build is routinely tested on rec...</summary><published>2011-01-15T08:39:00Z</published><updated>2011-01-15T08:39:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936338.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936338.html"/><content type="html">&lt;p&gt;&lt;strong&gt;Setting up your machine&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;To build the Android source files, you will need to use Linux or Mac OS. Building under Windows is not currently supported.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt;&lt;/p&gt; The Android build is routinely tested on recent versions of Ubuntu (6.06 and later), but reports of successes or failures on other distributions are welcome.  &lt;p&gt;&lt;strong&gt;Ubuntu Linux (32-bit x86)&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;To set up your Linux development environment, make sure you have the following:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Required Packages:      &lt;ul&gt;       &lt;li&gt;Git 1.5.4 or newer and the GNU Privacy Guard.&lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;   &lt;ul&gt;     &lt;li&gt;JDK 5.0, update 12 or higher.Java 6 is not supported, because of incompatibilities with @Override.&lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;ul&gt;   &lt;ul&gt;     &lt;li&gt;flex, bison, gperf, libsdl-dev, libesd0-dev, libwxgtk2.6-dev (optional), build-essential, zip, curl.&lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;$ sudo apt-get install git-core gnupg sun-java5-jdk flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl libncurses5-dev zlib1g-dev &lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;You might also want Valgrind, a tool that will help you find memory leaks, stack corruption, array bounds overflows, etc.&lt;/li&gt; &lt;/ul&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;p&gt;$ sudo apt-get install valgrind &lt;/p&gt; &lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;Intrepid ( 8.10) users may need a newer version of libreadline:&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;$ sudo apt-get install lib32readline5-dev &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Ubuntu Linux (64-bit x86)&lt;/strong&gt;&lt;/p&gt; This has not been as well tested. Please send success or failure reports to &lt;a href="mailto:android-porting@googlegroups.com"&gt;android-porting@googlegroups.com&lt;/a&gt; .  &lt;p&gt;The Android build requires a 32-bit build environment as well as some other tools:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Required Packages:      &lt;ul&gt;       &lt;li&gt;Git, JDK, flex, and the other packages as listed above in the i386 instructions:&lt;/li&gt;        &lt;li&gt;JDK 5.0, update 12 or higher.Java 6 is not supported, because of incompatibilities with @Override. &lt;/li&gt;        &lt;li&gt;Pieces from the 32-bit cross-building environment &lt;/li&gt;        &lt;li&gt;X11 development &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;$ sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl sun-java5-jdk zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev&lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;Set the system to use the right version of java by default:     &lt;br /&gt;$ sudo update-java-alternatives -s java-1.5.0-sun &lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;   &lt;li&gt;X11: Ubuntu doesn't have packages for the X11 libraries, but that can be worked around with the following command:     &lt;br /&gt;$ sudo ln -s /usr/lib32/libX11.so.6 /usr/lib32/libX11.so &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;Running Linux in a virtual machine&lt;/strong&gt;&lt;/p&gt; If you are running Linux in a virtual machine, you will need at least 1.5GB of RAM and 10GB or more of disk space in order to build the Android tree.  &lt;p&gt;&lt;strong&gt;Other Linux&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;There's no reason why Android cannot be built on non-Ubuntu systems. Please send any success or failure reports to &lt;a href="mailto:android-porting@googlegroups.com"&gt;android-porting@googlegroups.com&lt;/a&gt; . In general you will need:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Python 2.4, which you can &lt;a href="http://www.python.org/download/"&gt;download from python.org&lt;/a&gt;. &lt;/li&gt;    &lt;li&gt;JDK 5.0, update 12 or higher, which you can &lt;a href="http://java.sun.com/javase/downloads/"&gt;download from java.sun.com&lt;/a&gt;. Java 6 is not supported, because of incompatibilities with @Override. &lt;/li&gt;    &lt;li&gt;Git 1.5.4 or newer. You can find it at &lt;a href="http://git.or.cz/"&gt;http://git.or.cz/&lt;/a&gt;.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Anything missing from this list? Please let us know!&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Mac OS &lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Requirements:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;To build the Android files in a Mac OS environment, you need an Intel/x86 machine running MacOS 10.4 (&amp;quot;Tiger&amp;quot;) or 10.5 (&amp;quot;Leopard&amp;quot;). At the moment MacOS 10.6 (&amp;quot;Snow Leopard&amp;quot;) is not supported. The Android build system and tools do not support the obsolete PowerPC architecture.&lt;/li&gt;    &lt;li&gt;Android must be built on a case-sensitive file system.     &lt;ul&gt;       &lt;li&gt;We recommend that you build Android on a partition that has been formatted with the &amp;quot;Case-sensitive Journaled HFS+&amp;quot; file system:          &lt;ul&gt;           &lt;li&gt;A case-sensitive file system is required because the sources contain files that differ only in case. &lt;/li&gt;            &lt;li&gt;Journaled systems are more robust. (This is optional, but recommended.) &lt;/li&gt;            &lt;li&gt;HFS+ is required to successfully build Mac OS applications such as the Android Emulator for OS X.&lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;If you want to avoid partitioning/formatting your hard drive, you can use a case-sensitive disk image instead.          &lt;ul&gt;           &lt;li&gt;To create the image:             &lt;ul&gt;               &lt;li&gt;launch /Applications/Utilities/Disk Utility &lt;/li&gt;                &lt;li&gt;select &amp;quot;New Image&amp;quot; &lt;/li&gt;                &lt;li&gt;size: 8 GB (this will work, but you can choose more if you want to) &lt;/li&gt;                &lt;li&gt;volume format: case sensitive, journaled&lt;/li&gt;             &lt;/ul&gt;           &lt;/li&gt;            &lt;li&gt;This will create a .dmg file which, once mounted, acts as a drive with the required formatting for Android development. For a disk image named &amp;quot;android.dmg&amp;quot; stored in your home directory, you can add the following to your ~/.bash_profile to mount the image when you execute &amp;quot;mountAndroid&amp;quot;:             &lt;p&gt;# command to mount the android file image                &lt;br /&gt;function mountAndroid { hdiutil attach ~/android.dmg -mountpoint /Volumes/android; }&lt;/p&gt;              &lt;p&gt;Once mounted, you'll do all your work in the &amp;quot;android&amp;quot; volume. You can eject it (unmount it) just like you would with an external drive.&lt;/p&gt;           &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;&lt;/ul&gt; To set up your Mac OS development environment, follow these steps:  &lt;ol&gt;   &lt;li&gt;Install the XCode version 2.4 or later from &lt;a href="http://developer.apple.com/"&gt;http://developer.apple.com&lt;/a&gt;. We recommend version 3.0 or newer.&lt;/li&gt;    &lt;li&gt;Install MacPorts. To do this:      &lt;ol&gt;       &lt;li&gt;Download and run the installer from &lt;a href="http://www.macports.org/install.php"&gt;http://www.macports.org/install.php&lt;/a&gt;&lt;/li&gt;        &lt;li&gt;Make sure that &lt;code&gt;/opt/local/bin&lt;/code&gt; is in your path before &lt;code&gt;/usr/bin&lt;/code&gt; by running           &lt;p&gt;&lt;code&gt;$ echo $PATH&lt;/code&gt;&lt;/p&gt;          &lt;p&gt;If you don't see /opt/local/bin, edit &lt;code&gt;$HOME/.bash_profile&lt;/code&gt; and add the line &lt;code&gt;export PATH=/opt/local/bin:$PATH&lt;/code&gt; after any other PATH-related lines. To verify that your path is now correct, open a new terminal and run &lt;code&gt;echo $PATH&lt;/code&gt; again.&lt;/p&gt;       &lt;/li&gt;     &lt;/ol&gt;   &lt;/li&gt;    &lt;li&gt;Get the following packages from port:     &lt;br /&gt;$ POSIXLY_CORRECT=1 sudo port install gmake libsdl git-core gnupg       &lt;br /&gt;If using Mac OS 10.4, also install:      &lt;br /&gt;$ POSIXLY_CORRECT=1 sudo port install bison &lt;/li&gt;    &lt;li&gt;Upgrade GNU make to 3.81 or later by running the following command. (Mac OS doesn't come with a recent enough version.)     &lt;br /&gt;$ sudo ln -s gmake /opt/local/bin/make &lt;/li&gt;    &lt;li&gt;Set an appropriate per-process file descriptor limit. To do this, add the following lines to your &lt;i&gt;.bash_profile &lt;/i&gt;file:      &lt;br /&gt;# set the number of open files to be 1024      &lt;br /&gt;$ ulimit -S -n 1024      &lt;br /&gt;Note that this may not be necessary; on some systems, the output of &amp;quot;ulimit -S&amp;quot; will show &amp;quot;unlimited&amp;quot;. In this case, there is no need to set the limit to 1024.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;strong&gt;Installing Repo &lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Repo is a tool that makes it easier to work with Git in the context of Android. For more information about Repo, see &lt;a href="http://source.android.com/source/git-repo.html"&gt;Using Repo and Git&lt;/a&gt; .&lt;/p&gt;  &lt;p&gt;To install, initialize, and configure Repo, follow these steps:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Make sure you have a~/bindirectory in your home directory, and check to be sure that this bin directory is in your path:     &lt;br /&gt;$ cd ~      &lt;br /&gt;$ mkdir bin       &lt;br /&gt;$ echo $PATH &lt;/li&gt;    &lt;li&gt;Download thereposcript and make sure it is executable:     &lt;br /&gt;$ curl http://android.git.kernel.org/repo &amp;gt;~/bin/repo       &lt;p&gt;$ chmod a+x ~/bin/repo&lt;/p&gt;   &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;strong&gt;Initializing a Repo client &lt;/strong&gt;&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Create an empty directory to hold your working files:     &lt;br /&gt;$ mkdir mydroid      &lt;br /&gt;$ cd mydroid&lt;/li&gt;    &lt;li&gt;Run &amp;quot;repo init&amp;quot; to bring down the latest version of Repo with all its most recent bug fixes. You must specify a URL for the manifest:     &lt;br /&gt;$ repo init -u git://android.git.kernel.org/platform/manifest.git       &lt;ul&gt;       &lt;li&gt;If you would like to check out a branch other than &amp;quot;master&amp;quot;, specify it with -b, like:         &lt;br /&gt;$ repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;When prompted, configure Repo with your real name and email address. If you plan to submit code, use an email address that is associated with a &lt;a href="https://www.google.com/accounts"&gt;Google account&lt;/a&gt; .&lt;/li&gt; &lt;/ol&gt; A successful initialization will end with a message such as  &lt;br /&gt;repo initialized in /mydroid  &lt;br /&gt;Your client directory should now contain a.repodirectory where files such as the manifest will be kept.  &lt;br /&gt;&lt;b&gt;What will my name and email be used for?&lt;/b&gt;  &lt;br /&gt;To use the Gerrit code-review tool, you will need an email address that is connected with a &lt;a href="http://www.google.com/accounts"&gt;registered Google account&lt;/a&gt; (which does not have to be a Gmail address). Make sure this is a live address at which you can receive messages . The real name that you provide here will show up in attributions for your code submissions.  &lt;p&gt;&lt;b&gt;What is a manifest file?&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;The Android source files are divided among a number of different repositories. A manifest file contains a mapping of where the files from these repositories will be placed within your working directory w hen you synchronize your files.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Getting the files&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;To pull down files to your working directory from the repositories as specified in the default manifest, run   &lt;br /&gt;$ repo sync &lt;i&gt;     &lt;br /&gt;&lt;/i&gt;    &lt;br /&gt;For more about &amp;quot;repo sync&amp;quot; and other Repo commands, see &lt;a href="http://source.android.com/source/git-repo.html"&gt;Using Repo and Git&lt;/a&gt; .    &lt;br /&gt;The Android source files will be located in your working directory under their project names.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Verifying Git Tags&lt;/strong&gt;&lt;/p&gt; Load the following public key into your GnuPG key database.The key is used to sign annotated tags that represent releases.  &lt;br /&gt;$ gpg --import   &lt;br /&gt;then paste the key(s) below, and press Control-D to end the input and process the keys. After importing the keys, you can verify any tag with   &lt;br /&gt;$ git tag -v &lt;i&gt;tagname&lt;/i&gt;  &lt;br /&gt;&lt;b&gt;&lt;u&gt;key 9AB10E78: &amp;quot;The Android Open Source Projectinitial-contribution@android.com&amp;quot;&lt;/u&gt; &lt;/b&gt;  &lt;pre&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----&lt;br /&gt;Version: GnuPG v1.4.2.2 (GNU/Linux)&lt;br /&gt;&lt;br /&gt;mQGiBEnnWD4RBACt9/h4v9xnnGDou13y3dvOx6/t43LPPIxeJ8eX9WB+8LLuROSV &lt;br /&gt;lFhpHawsVAcFlmi7f7jdSRF+OvtZL9ShPKdLfwBJMNkU66/TZmPewS4m782ndtw7&lt;br /&gt;8tR1cXb197Ob8kOfQB3A9yk2XZ4ei4ZC3i6wVdqHLRxABdncwu5hOF9KXwCgkxMD &lt;br /&gt;u4PVgChaAJzTYJ1EG+UYBIUEAJmfearb0qRAN7dEoff0FeXsEaUA6U90sEoVks0Z &lt;br /&gt;wNj96SA8BL+a1OoEUUfpMhiHyLuQSftxisJxTh+2QclzDviDyaTrkANjdYY7p2cq &lt;br /&gt;/HMdOY7LJlHaqtXmZxXjjtw5Uc2QG8UY8aziU3IE9nTjSwCXeJnuyvoizl9/I1S5&lt;br /&gt;jU5SA/9WwIps4SC84ielIXiGWEqq6i6/sk4I9q1YemZF2XVVKnmI1F4iCMtNKsR4&lt;br /&gt;MGSa1gA8s4iQbsKNWPgp7M3a51JCVCu6l/8zTpA+uUGapw4tWCp4o0dpIvDPBEa9&lt;br /&gt;b/aF/ygcR8mh5hgUfpF9IpXdknOsbKCvM9lSSfRciETykZc4wrRCVGhlIEFuZHJv &lt;br /&gt;aWQgT3BlbiBTb3VyY2UgUHJvamVjdCA8aW5pdGlhbC1jb250cmlidXRpb25AYW5k &lt;br /&gt;cm9pZC5jb20+iGAEExECACAFAknnWD4CGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIX &lt;br /&gt;gAAKCRDorT+BmrEOeNr+AJ42Xy6tEW7r3KzrJxnRX8mij9z8tgCdFfQYiHpYngkI &lt;br /&gt;2t09Ed+9Bm4gmEO5Ag0ESedYRBAIAKVW1JcMBWvV/0Bo9WiByJ9WJ5swMN36/vAl &lt;br /&gt;QN4mWRhfzDOk/Rosdb0csAO/l8Kz0gKQPOfObtyYjvI8JMC3rmi+LIvSUT9806Up &lt;br /&gt;hisyEmmHv6U8gUb/xHLIanXGxwhYzjgeuAXVCsv+EvoPIHbY4L/KvP5x+oCJIDbk &lt;br /&gt;C2b1TvVk9PryzmE4BPIQL/NtgR1oLWm/uWR9zRUFtBnE411aMAN3qnAHBBMZzKMX &lt;br /&gt;LWBGWE0znfRrnczI5p49i2YZJAjyX1P2WzmScK49CV82dzLo71MnrF6fj+Udtb5+&lt;br /&gt;OgTg7Cow+8PRaTkJEW5Y2JIZpnRUq0CYxAmHYX79EMKHDSThf/8AAwUIAJPWsB/M &lt;br /&gt;pK+KMs/s3r6nJrnYLTfdZhtmQXimpoDMJg1zxmL8UfNUKiQZ6esoAWtDgpqt7Y7s &lt;br /&gt;KZ8laHRARonte394hidZzM5nb6hQvpPjt2OlPRsyqVxw4c/KsjADtAuKW9/d8phb &lt;br /&gt;N8bTyOJo856qg4oOEzKG9eeF7oaZTYBy33BTL0408sEBxiMior6b8LrZrAhkqDjA &lt;br /&gt;vUXRwm/fFKgpsOysxC6xi553CxBUCH2omNV6Ka1LNMwzSp9ILz8jEGqmUtkBszwo &lt;br /&gt;G1S8fXgE0Lq3cdDM/GJ4QXP/p6LiwNF99faDMTV3+2SAOGvytOX6KjKVzKOSsfJQ &lt;br /&gt;hN0DlsIw8hqJc0WISQQYEQIACQUCSedYRAIbDAAKCRDorT+BmrEOeCUOAJ9qmR0l &lt;br /&gt;EXzeoxcdoafxqf6gZlJZlACgkWF7wi2YLW3Oa+jv2QSTlrx4KLM=&lt;br /&gt;=Wi5D &lt;br /&gt;-----END PGP PUBLIC KEY BLOCK-----&lt;br /&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;strong&gt;Building the code&lt;/strong&gt;&lt;/p&gt;&#xD;
To build the files, run make from within your working directory:&#xD;
&#xD;
&lt;br /&gt;$ cd ~/mydroid&#xD;
&#xD;
&lt;br /&gt;$ source build/envsetup.sh&#xD;
&#xD;
&lt;br /&gt;$ lunch&#xD;
&#xD;
&lt;br /&gt;$ make&#xD;
&#xD;
&lt;p&gt;If your build fails, complaining about a missing &amp;quot;run-java-tool&amp;quot;, try setting the ANDROID_JAVA_HOME env var to $JAVA_HOME before making. E.g.,&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;$ export ANDROID_JAVA_HOME=$JAVA_HOME&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;strong&gt;Using an IDE&lt;/strong&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;ul&gt;&#xD;
  &lt;li&gt;&lt;a href="http://source.android.com/source/using-eclipse.html"&gt;Using Eclipse&lt;/a&gt; for Android platform development &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&#xD;
&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;b&gt;ImportError: No module na&lt;/b&gt; &lt;b&gt;med&lt;/b&gt; &lt;b&gt;readline&lt;/b&gt; &lt;b&gt;&#xD;
    &lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;Mac users getting this should install Python 2.5.2.&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;Linux users that installed Python from source, make sure the dependencies for libreadline are installed, and rebuild Python.&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1936338.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936338.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936314.html</id><title type="text">Android Code Style Guidelines for Contributors</title><summary type="text">The rules below are not guidelines or recommendations, but strict rules. Contributions to Android generally will not be accepted if they do not adhere to these rules.  Not all existing code follows th...</summary><published>2011-01-15T08:01:00Z</published><updated>2011-01-15T08:01:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936314.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936314.html"/><content type="html">&lt;p&gt;The rules below are not guidelines or recommendations, but strict rules. Contributions to Android generally &lt;b&gt;will not be accepted if they do not adhere to these rules.&lt;/b&gt;  &lt;p&gt;Not all existing code follows these rules, but all new code is expected to.  &lt;p&gt;&lt;strong&gt;&lt;a&gt;Java Language Rules&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;We follow standard Java coding conventions. We add a few rules:  &lt;ol&gt; &lt;li&gt;&lt;a href="http://source.android.com/source/#exceptionsIgnore"&gt;Exceptions&lt;/a&gt;: Never catch and ignore them without explanation.  &lt;li&gt;&lt;a href="http://source.android.com/source/#exceptionsAll"&gt;Exceptions&lt;/a&gt;: do not catch generic Exception, except in library code at the root of the stack.  &lt;li&gt;&lt;a href="http://source.android.com/source/#finalizers"&gt;Finalizers&lt;/a&gt;: generally don't use them.  &lt;li&gt;&lt;a href="http://source.android.com/source/#imports"&gt;Imports&lt;/a&gt;: Fully qualify imports&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;strong&gt;Java Library Rules&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;There are conventions for using Android's Java libraries and tools. In some cases, the convention has changed in important ways and older code might use a deprecated pattern or library. When working with such code, it's okay to continue the existing style (see &lt;a href="http://source.android.com/source/#consistency"&gt;Consistency&lt;/a&gt;). When creating new components never use deprecated libraries.  &lt;p&gt;&lt;strong&gt;Java Style Rules&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Programs are much easier to maintain when all files have a consistent style. We follow the standard Java coding style, as defined by Sun in their &lt;a href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html"&gt;Code Conventions for the Java Programming Language&lt;/a&gt;, with a few exceptions and additions. This style guide is comprehensive and detailed and is in common usage in the Java community.  &lt;p&gt;In addition, we enforce the following style rules:  &lt;ol&gt; &lt;li&gt;&lt;a href="http://source.android.com/source/#javadoc"&gt;Comments/Javadoc&lt;/a&gt;: write it; use standard style  &lt;li&gt;&lt;a href="http://source.android.com/source/#shortmethods"&gt;Short methods&lt;/a&gt;: don't write giant methods  &lt;li&gt;Fields: should either be at the top of the file, or immediately before the methods that use them  &lt;li&gt;&lt;a href="http://source.android.com/source/#localvariables"&gt;Local variables&lt;/a&gt;: limit the scope  &lt;li&gt;&lt;a href="http://source.android.com/source/#import_style"&gt;Imports&lt;/a&gt;: android; third party alphabetical; java(x)  &lt;li&gt;&lt;a href="http://source.android.com/source/#indentation"&gt;Indentation&lt;/a&gt;: 4 spaces, no tabs.  &lt;li&gt;&lt;a href="http://source.android.com/source/#linelen"&gt;Line length&lt;/a&gt;: 100 columns  &lt;li&gt;&lt;a href="http://source.android.com/source/#field_names"&gt;Field names&lt;/a&gt;: Non-public, non-static fields start with m.  &lt;li&gt;&lt;a href="http://source.android.com/source/#braces"&gt;Braces&lt;/a&gt;: Opening braces don't go on their own line.  &lt;li&gt;&lt;a href="http://source.android.com/source/#annotations"&gt;Annotations&lt;/a&gt;: Use the standard annotations.  &lt;li&gt;&lt;a href="http://source.android.com/source/#acronyms"&gt;Acronyms are words&lt;/a&gt;: Treat acronyms as words in names, yielding XmlHttpRequest, getUrl(), etc.  &lt;li&gt;&lt;a href="http://source.android.com/source/#todo"&gt;TODO style&lt;/a&gt;: "TODO: write this description"  &lt;li&gt;&lt;a href="http://source.android.com/source/#consistency"&gt;Consistency&lt;/a&gt;: Look at what's around you!  &lt;li&gt;&lt;a href="http://source.android.com/source/#logging"&gt;Logging&lt;/a&gt;: Be careful with logging. It's expensive.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;strong&gt;Javatests Style Rules&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href="http://source.android.com/source/#testmethodnames"&gt;Naming test methods&lt;/a&gt;: testMethod_specificCase is ok&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Java Language Rules&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Exceptions: do not ignore&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Sometimes it is tempting to write code that completely ignores an exception like this:&lt;pre &gt;void setServerPort(String value) {&#xD;
    try {&#xD;
        serverPort = Integer.parseInt(value);&#xD;
    } catch (NumberFormatException e) { }&#xD;
}&lt;/pre&gt;&#xD;
&lt;p&gt;You must never do this. While you may think that your code will never encounter this error condition or that it is not important to handle it, ignoring exceptions like above creates mines in your code for someone else to trip over some day. You must handle every Exception in your code in some principled way. The specific handling varies depending on the case. &#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;Anytime somebody has an empty catch clause they should have a creepy feeling. There are definitely times when it is actually the correct thing to do, but at least you have to think about it. In Java you can't escape the creepy feeling. -&lt;a href="http://www.artima.com/intv/solid4.html"&gt;James Gosling&lt;/a&gt;&lt;/blockquote&gt;&#xD;
&lt;p&gt;Acceptable alternatives (in order of preference) are: &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;Throw the exception up to the caller of your method. &lt;pre &gt;void setServerPort(String value) throws NumberFormatException {&#xD;
    serverPort = Integer.parseInt(value);&#xD;
}&lt;/pre&gt;&#xD;
&lt;li&gt;Throw a new exception that's appropriate to your level of abstraction. &lt;pre &gt;void setServerPort(String value) throws ConfigurationException {&#xD;
    try {&#xD;
        serverPort = Integer.parseInt(value);&#xD;
    } catch (NumberFormatException e) {&#xD;
        throw new ConfigurationException("Port " + value + " is not valid.");&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&lt;li&gt;Handle the error gracefully and substitute an appropriate value in the catch {} block. &lt;pre &gt;/** Set port. If value is not a valid number, 80 is substituted. */&#xD;
void setServerPort(String value) {&#xD;
    try {&#xD;
        serverPort = Integer.parseInt(value);&#xD;
    } catch (NumberFormatException e) {&#xD;
        serverPort = 80;  // default port for server &#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&lt;li&gt;Catch the Exception and throw a new RuntimeException. This is dangerous: only do it if you are positive that if this error occurs, the appropriate thing to do is crash. &lt;pre &gt;/** Set port. If value is not a valid number, die. */&#xD;
void setServerPort(String value) {&#xD;
    try {&#xD;
        serverPort = Integer.parseInt(value);&#xD;
    } catch (NumberFormatException e) {&#xD;
        throw new RuntimeException("port " + value " is invalid, ", e);&#xD;
    }&#xD;
}&lt;/pre&gt;Note that the original exception is passed to the constructor for RuntimeException. If your code must compile under Java 1.3, you will need to omit the exception that is the cause. &#xD;
&lt;li&gt;Last resort: if you are confident that actually ignoring the exception is appropriate then you may ignore it, but you must also comment why with a good reason: &lt;pre &gt;/** If value is not a valid number, original port number is used. */&#xD;
void setServerPort(String value) {&#xD;
    try {&#xD;
        serverPort = Integer.parseInt(value);&#xD;
    } catch (NumberFormatException e) {&#xD;
        // Method is documented to just ignore invalid user input.&#xD;
        // serverPort will just be unchanged.&#xD;
    }&#xD;
}&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Exceptions: do not catch generic Exception&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Sometimes it is tempting to be lazy when catching exceptions and do something like this:&lt;pre &gt;try {&#xD;
    someComplicatedIOFunction();        // may throw IOException &#xD;
    someComplicatedParsingFunction();   // may throw ParsingException &#xD;
    someComplicatedSecurityFunction();  // may throw SecurityException &#xD;
    // phew, made it all the way &#xD;
} catch (Exception e) {               // I'll just catch all exceptions &#xD;
    handleError();                      // with one generic handler!&#xD;
}&lt;/pre&gt;&#xD;
&lt;p&gt;You should not do this. In almost all cases it is inappropriate to catch generic Exception or Throwable, preferably not Throwable, because it includes Error exceptions as well. It is very dangerous. It means that Exceptions you never expected (including RuntimeExceptions like ClassCastException) end up getting caught in application-level error handling. It obscures the failure handling properties of your code. It means if someone adds a new type of Exception in the code you're calling, the compiler won't help you realize you need to handle that error differently. And in most cases you shouldn't be handling different types of exception the same way, anyway. &#xD;
&lt;p&gt;There are rare exceptions to this rule: certain test code and top-level code where you want to catch all kinds of errors (to prevent them from showing up in a UI, or to keep a batch job running). In that case you may catch generic Exception (or Throwable) and handle the error appropriately. You should think very carefully before doing this, though, and put in comments explaining why it is safe in this place. &#xD;
&lt;p&gt;Alternatives to catching generic Exception: &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;Catch each exception separately as separate catch blocks after a single try. This can be awkward but is still preferable to catching all Exceptions. Beware repeating too much code in the catch blocks. &#xD;
&lt;li&gt;Refactor your code to have more fine-grained error handling, with multiple try blocks. Split up the IO from the parsing, handle errors separately in each case. &#xD;
&lt;li&gt;Rethrow the exception. Many times you don't need to catch the exception at this level anyway, just let the method throw it.&lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;Remember: exceptions are your friend! When the compiler complains you're not catching an exception, don't scowl. Smile: the compiler just made it easier for you to catch runtime problems in your code. &#xD;
&lt;p&gt;&lt;strong&gt;Finalizers&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;b&gt;What it is&lt;/b&gt;: Finalizers are a way to have a chunk of code executed when an object is garbage collected. &#xD;
&lt;p&gt;&lt;b&gt;Pros&lt;/b&gt;: can be handy for doing cleanup, particularly of external resources. &#xD;
&lt;p&gt;&lt;b&gt;Cons&lt;/b&gt;: there are no guarantees as to when a finalizer will be called, or even that it will be called at all. &#xD;
&lt;p&gt;&lt;b&gt;Decision&lt;/b&gt;: we don't use finalizers. In most cases, you can do what you need from a finalizer with good exception handling. If you absolutely need it, define a close() method (or the like) and document exactly when that method needs to be called. See InputStream for an example. In this case it is appropriate but not required to print a short log message from the finalizer, as long as it is not expected to flood the logs. &#xD;
&lt;p&gt;&lt;strong&gt;Imports&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Wildcards in imports&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;b&gt;What it is&lt;/b&gt;: When you want to use class Bar from package foo,there are two possible ways to import it: &#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;&lt;code&gt;import foo.*;&lt;/code&gt; &#xD;
&lt;li&gt;&lt;code&gt;import foo.Bar;&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&#xD;
&lt;p&gt;&lt;b&gt;Pros of #1&lt;/b&gt;: Potentially reduces the number of import statements. &#xD;
&lt;p&gt;&lt;b&gt;Pros of #2&lt;/b&gt;: Makes it obvious what classes are actually used. Makes code more readable for maintainers. &#xD;
&lt;p&gt;&lt;b&gt;Decision&lt;/b&gt;: Use style #2 for importing all Android code. An explicit exception is made for java standard libraries (java.util.*, java.io.*, etc.) and unit test code (junit.framework.*). &#xD;
&lt;p&gt;&lt;strong&gt;Comments/Javadoc&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Every file should have a copyright statement at the top. Then a package statement and import statements should follow, each block separated by a blank line. And then there is the class or interface declaration. In the Javadoc comments, describe what the class or interface does.&lt;pre &gt;/*&#xD;
 * Copyright (C) 2010 The Android Open Source Project &#xD;
 *&#xD;
 * Licensed under the Apache License, Version 2.0 (the "License");&#xD;
 * you may not use this file except in compliance with the License.&#xD;
 * You may obtain a copy of the License at &#xD;
 *&#xD;
 *      http://www.apache.org/licenses/LICENSE-2.0&#xD;
 *&#xD;
 * Unless required by applicable law or agreed to in writing, software &#xD;
 * distributed under the License is distributed on an "AS IS" BASIS,&#xD;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#xD;
 * See the License for the specific language governing permissions and &#xD;
 * limitations under the License.&#xD;
 */&#xD;
&#xD;
package com.android.internal.foo;&#xD;
&#xD;
import android.os.Blah;&#xD;
import android.view.Yada;&#xD;
&#xD;
import java.sql.ResultSet;&#xD;
import java.sql.SQLException;&#xD;
&#xD;
/**&#xD;
 * Does X and Y and provides an abstraction for Z.&#xD;
 */&#xD;
public class Foo {&#xD;
    ...&#xD;
}&lt;/pre&gt;&#xD;
&lt;p&gt;Every class and nontrivial public method you write &lt;b&gt;must&lt;/b&gt; contain a Javadoc comment with at least one sentence describing what the class or method does. This sentence should start with a 3rd person descriptive verb. Examples:&lt;pre &gt;/** Returns the correctly rounded positive square root of a double value. */&#xD;
static double sqrt(double a) {&#xD;
}&#xD;
&#xD;
/**&#xD;
 * Constructs a new String by converting the specified array of &#xD;
 * bytes using the platform's default character encoding.&#xD;
 */&#xD;
public String(byte[] bytes) {&#xD;
}&lt;/pre&gt;&#xD;
&lt;p&gt;You do not need to write Javadoc for trivial get and set methods such as setFoo() if all your Javadoc would say is "sets Foo". If the method does something more complex (such as enforcing a constraint or having an important side effect), then you must document it. And if it's not obvious what the property "Foo" means, you should document it. &#xD;
&lt;p&gt;Every method you write, whether public or otherwise, would benefit from Javadoc. Public methods are part of an API and therefore require Javadoc. &#xD;
&lt;p&gt;Android does not currently enforce a specific style for writing Javadoc comments, but you &lt;b&gt;should&lt;/b&gt; follow the &lt;a href="http://java.sun.com/j2se/javadoc/writingdoccomments/"&gt;Sun Javadoc conventions&lt;/a&gt;. &#xD;
&lt;p&gt;&lt;strong&gt;Short methods&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;To the extent that it is feasible, methods should be kept small and focused. It is, however, recognized that long methods are sometimes appropriate, so no hard limit is placed on method length. If a method exceeds 40 lines or so, think about whether it can be broken up without harming the structure of the program. &#xD;
&lt;p&gt;&lt;strong&gt;Local variables&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;The scope of local variables should be kept to a minimum (&lt;i&gt;Effective Java&lt;/i&gt; Item 29). By doing so, you increase the readability and maintainability of your code and reduce the likelihood of error. Each variable should be declared in the innermost block that encloses all uses of the variable. &#xD;
&lt;p&gt;Local variables should be declared at the point they are first used. Nearly every local variable declaration should contain an initializer. If you don't yet have enough information to initialize a variable sensibly, you should postpone the declaration until you do. &#xD;
&lt;p&gt;One exception to this rule concerns try-catch statements. If a variable is initialized with the return value of a method that throws a checked exception, it must be initialized inside a try block. If the value must be used outside of the try block, then it must be declared before the try block, where it cannot yet be sensibly initialized:&lt;pre &gt;// Instantiate class cl, which represents some sort of Set &#xD;
Set s = null;&#xD;
try {&#xD;
    s = (Set) cl.newInstance();&#xD;
} catch(IllegalAccessException e) {&#xD;
    throw new IllegalArgumentException(cl + " not accessible");&#xD;
} catch(InstantiationException e) {&#xD;
    throw new IllegalArgumentException(cl + " not instantiable");&#xD;
}&#xD;
&#xD;
// Exercise the set &#xD;
s.addAll(Arrays.asList(args));&lt;/pre&gt;&#xD;
&lt;p&gt;But even this case can be avoided by encapsulating the try-catch block in a method:&lt;pre &gt;Set createSet(Class cl) {&#xD;
    // Instantiate class cl, which represents some sort of Set &#xD;
    try {&#xD;
        return (Set) cl.newInstance();&#xD;
    } catch(IllegalAccessException e) {&#xD;
        throw new IllegalArgumentException(cl + " not accessible");&#xD;
    } catch(InstantiationException e) {&#xD;
        throw new IllegalArgumentException(cl + " not instantiable");&#xD;
    }&#xD;
}&#xD;
&#xD;
...&#xD;
&#xD;
// Exercise the set &#xD;
Set s = createSet(cl);&#xD;
s.addAll(Arrays.asList(args));&lt;/pre&gt;&#xD;
&lt;p&gt;Loop variables should be declared in the for statement itself unless there is a compelling reason to do otherwise:&lt;pre &gt;for (int i = 0; i n; i++) {&#xD;
    doSomething(i);&#xD;
}&#xD;
&#xD;
for (Iterator i = c.iterator(); i.hasNext(); ) {&#xD;
    doSomethingElse(i.next());&#xD;
}&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Imports&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;The ordering of import statements is: &#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;Android imports &#xD;
&lt;li&gt;Imports from third parties (com, junit, net, org) &#xD;
&lt;li&gt;java and javax&lt;/li&gt;&lt;/ol&gt;&#xD;
&lt;p&gt;To exactly match the IDE settings, the imports should be: &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;Alphabetical within each grouping. &#xD;
&lt;li&gt;Capital letters are considered to come before lower case letter (e.g. Z before a). &#xD;
&lt;li&gt;There should be a blank line between each major grouping (android, com, junit, net, org, java, javax).&lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Originally there was no style requirement on the ordering. This meant that the IDE's were either always changing the ordering, or IDE developers had to disable the automatic import management features and maintain the imports by hand. This was deemed bad. When java-style was asked, the preferred styles were all over the map. It pretty much came down to our needing to "pick an ordering and be consistent." So we chose a style, updated the style guide, and made the IDEs obey it. We expect that as IDE users work on the code, the imports in all of the packages will end up matching this pattern without any extra engineering effort. &#xD;
&lt;p&gt;The style chosen such that: &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;The imports people want to look at first tend to be at the top (android) &#xD;
&lt;li&gt;The imports people want to look at least tend to be at the bottom (java) &#xD;
&lt;li&gt;Humans can easily follow the style &#xD;
&lt;li&gt;The IDE's can follow the style&lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;What about static imports?&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;The use and location of static imports have been mildly controversial issues. Some people would prefer static imports to be interspersed with the remaining imports, some would prefer them reside above or below all other imports. Additinally, we have not yet come up with a way to make all IDEs use the same ordering. &#xD;
&lt;p&gt;Since most people consider this a low priority issue, just use your judgement and please be consistent. &#xD;
&lt;p&gt;&lt;strong&gt;Indentation&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;We use 4 space indents for blocks. We never use tabs. When in doubt, be consistent with code around you. &#xD;
&lt;p&gt;We use 8 space indents for line wraps, including function calls and assignments. For example, this is correct:&lt;pre &gt;Instrument i =&#xD;
        someLongExpression(that, wouldNotFit, on, one, line);&lt;/pre&gt;&#xD;
&lt;p&gt;and this is not correct:&lt;pre &gt;Instrument i =&#xD;
    someLongExpression(that, wouldNotFit, on, one, line);&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Field Names&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;Non-public, non-static field names start with m. &#xD;
&lt;li&gt;Static field names start with s. &#xD;
&lt;li&gt;Other fields start with a lower case letter. &#xD;
&lt;li&gt;Public static final fields (constants) are ALL_CAPS_WITH_UNDERSCORES.&lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;For example:&lt;pre &gt;public class MyClass {&#xD;
    public static final int SOME_CONSTANT = 42;&#xD;
    public int publicField;&#xD;
    private static MyClass sSingleton;&#xD;
    int mPackagePrivate;&#xD;
    private int mPrivate;&#xD;
    protected int mProtected;&#xD;
}&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Braces&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Braces do not go on their own line; they go on the same line as the code before them. So:&lt;pre &gt;class MyClass {&#xD;
    int func() {&#xD;
        if (something) {&#xD;
            // ...&#xD;
        } else if (somethingElse) {&#xD;
            // ...&#xD;
        } else {&#xD;
            // ...&#xD;
        }&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&lt;p&gt;We require braces around the statements for a conditional. Except, if the entire conditional (the condition and the body) fit on one line, you may (but are not obligated to) put it all on one line. That is, this is legal:&lt;pre &gt;if (condition) {&#xD;
    body(); // ok &#xD;
}&#xD;
if (condition) body(); // ok&lt;/pre&gt;&#xD;
&lt;p&gt;but this is still illegal:&lt;pre &gt;if (condition)&#xD;
    body(); // bad&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Line length&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Each line of text in your code should be at most 100 characters long. &#xD;
&lt;p&gt;There has been lots of discussion about this rule and the decision remains that 100 characters is the maximum. &#xD;
&lt;p&gt;Exception: if a comment line contains an example command or a literal URL longer than 100 characters, that line may be longer than 100 characters for ease of cut and paste. &#xD;
&lt;p&gt;Exception: import lines can go over the limit because humans rarely see them. This also simplifies tool writing. &#xD;
&lt;p&gt;&lt;strong&gt;&lt;a name="annotations"&gt;&lt;/a&gt;Java 1.5 Annotations&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Annotations should precede other modifiers for the same language element. Simple marker annotations (e.g. @Override) can be listed on the same line with the language element. If there are multiple annotations, or parameterized annotations, they should each be listed one-per-line in alphabetical order. &#xD;
&lt;p&gt;Android -standard practices for the three predefined annotations in Java 1.5's are: &#xD;
&lt;p&gt;&lt;strong&gt;@Deprecated&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;The @Deprecated annotation must be used whenever the use of the annotated element is discouraged. If you use the @Deprecated annotation, you must also have a @deprecated Javadoc tag and it should name an alternate implementation. In addition, remember that a @Deprecated method is &lt;b&gt;still&lt;/b&gt; supposed to work. &#xD;
&lt;p&gt;If you see old code that has a @deprecated Javadoc tag, please add the @Deprecated annotation. &#xD;
&lt;p&gt;&lt;strong&gt;@Override&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;The @Override annotation must be used whenever a method overrides the declaration or implementation from a super-class. &#xD;
&lt;p&gt;For example, if you use the @inheritdocs Javadoc tag, and derive from a class (not an interface), you must also annotate that the method @Overrides the parent class's method. &#xD;
&lt;p&gt;&lt;strong&gt;@SuppressWarnings&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;The @SuppressWarnings annotation should only be used under circumstances where it is impossible to eliminate a warning. If a warning passes this "impossible to eliminate" test, the @SuppressWarnings annotation &lt;b&gt;must&lt;/b&gt; be used, so as to ensure that all warnings reflect actual problems in the code. &#xD;
&lt;p&gt;When a @SuppressWarnings annotation is necessary, it must be prefixed with a TODO comment that explains the "impossible to eliminate" condition. This will normally identify an offending class that has an awkward interface. For example:&lt;pre&gt;// TODO: The third-party class com.third.useful.Utility.rotate() needs generics &#xD;
@SuppressWarnings("generic-cast")&#xD;
List&amp;lt;String&amp;gt; blix = Utility.rotate(blax);&lt;/pre&gt;&#xD;
&lt;p&gt;When a @SuppressWarnings annotation is required, the code should be refactored to isolate the software elements where the annotation applies. &#xD;
&lt;p&gt;&lt;strong&gt;Acronyms in names&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Treat acronyms and abbreviations as words. The names are much more readable: &#xD;
&lt;p&gt;Good&lt;br&gt;Bad &#xD;
&lt;p&gt;XmlHttpRequest&lt;br&gt;XMLHTTPRequest &#xD;
&lt;p&gt;getCustomerId&lt;br&gt;getCustomerID &#xD;
&lt;p&gt;This style rule also applies when an acronym or abbreviation is the entire name: &#xD;
&lt;p&gt;Good&lt;br&gt;Bad &#xD;
&lt;p&gt;class Html&lt;br&gt;class HTML &#xD;
&lt;p&gt;String url;&lt;br&gt;String URL; &#xD;
&lt;p&gt;long id;&lt;br&gt;long ID; &#xD;
&lt;p&gt;Both the JDK and the Android code bases are very inconsistent with regards to acronyms, therefore, it is virtually impossible to be consistent with the code around you. Bite the bullet, and treat acronyms as words. &#xD;
&lt;p&gt;For further justifications of this style rule, see &lt;i&gt;Effective Java&lt;/i&gt; Item 38 and &lt;i&gt;Java Puzzlers&lt;/i&gt; Number 68. &#xD;
&lt;p&gt;&lt;strong&gt;TODO style&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect. &#xD;
&lt;p&gt;TODOs should include the string TODO in all caps, followed by a colon:&lt;pre &gt;// TODO: Remove this code after the UrlTable2 has been checked in.&#xD;
&#xD;
// TODO: Change this to use a flag instead of a constant.&lt;/pre&gt;&#xD;
&lt;p&gt;If your TODO is of the form "At a future date do something" make sure that you either include a very specific date ("Fix by November 2005") or a very specific event ("Remove this code after all production mixers understand protocol V7."). &#xD;
&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Our parting thought: BE CONSISTENT. If you're editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around their if clauses, you should too. If their comments have little boxes of stars around them, make your comments have little boxes of stars around them too. &#xD;
&lt;p&gt;The point of having style guidelines is to have a common vocabulary of coding, so people can concentrate on what you're saying, rather than on how you're saying it. We present global style rules here so people know the vocabulary. But local style is also important. If code you add to a a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Try to avoid this. &#xD;
&lt;p&gt;&lt;strong&gt;Logging&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;While logging is necessary it has a significantly negative impact on performance and quickly loses its usefulness if it's not kept reasonably terse. The logging facilities provides five different levels of logging. Below are the different levels and when and how they should be used. &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;b&gt;ERROR:&lt;/b&gt; This level of logging should be used when something fatal has happened, i.e. something that will have user-visible consequences and won't be recoverable without explicitly deleting some data, uninstalling applications, wiping the data partitions or reflashing the entire phone (or worse). This level is always logged. Issues that justify some logging at the ERROR level are typically good candidates to be reported to a statistics-gathering server. &#xD;
&lt;li&gt;&lt;b&gt;WARNING:&lt;/b&gt; This level of logging should used when something serious and unexpected happened, i.e. something that will have user-visible consequences but is likely to be recoverable without data loss by performing some explicit action, ranging from waiting or restarting an app all the way to re-downloading a new version of an application or rebooting the device. This level is always logged. Issues that justify some logging at the WARNING level might also be considered for reporting to a statistics-gathering server. &#xD;
&lt;li&gt;&lt;b&gt;INFORMATIVE:&lt;/b&gt; This level of logging should used be to note that something interesting to most people happened, i.e. when a situation is detected that is likely to have widespread impact, though isn't necessarily an error. Such a condition should only be logged by a module that reasonably believes that it is the most authoritative in that domain (to avoid duplicate logging by non-authoritative components). This level is always logged. &#xD;
&lt;li&gt;&lt;b&gt;DEBUG:&lt;/b&gt; This level of logging should be used to further note what is happening on the device that could be relevant to investigate and debug unexpected behaviors. You should log only what is needed to gather enough information about what is going on about your component. If your debug logs are dominating the log then you probably should be using verbose logging. This level will be logged, even on release builds, and is required to be surrounded by an if (LOCAL_LOG) or if (LOCAL_LOGD) block, where LOCAL_LOG[D] is defined in your class or subcomponent, so that there can exist a possibility to disable all such logging. There must therefore be no active logic in an if (LOCAL_LOG) block. All the string building for the log also needs to be placed inside the if (LOCAL_LOG) block. The logging call should not be re-factored out into a method call if it is going to cause the string building to take place outside of the if (LOCAL_LOG) block. There is some code that still says if (localLOGV). This is considered acceptable as well, although the name is nonstandard. &#xD;
&lt;li&gt;&lt;b&gt;VERBOSE:&lt;/b&gt; This level of logging should be used for everything else. This level will only be logged on debug builds and should be surrounded by if (LOCAL_LOGV) block (or equivalent) so that it can be compiled out by default. Any string building will be stripped out of release builds and needs to appear inside the if (LOCAL_LOGV) block.&lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Within a given module, other than at the VERBOSE level, an error should only be reported once if possible: within a single chain of function calls within a module, only the innermost function should return the error, and callers in the same module should only add some logging if that significantly helps to isolate the issue. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; In a chain of modules, other than at the VERBOSE level, when a lower-level module detects invalid data coming from a higher-level module, the lower-level module should only log this situation to the DEBUG log, and only if logging provides information that is not otherwise available to the caller. Specifically, there is no need to log situations where an exception is thrown (the exception should contain all the relevant information), or where the only information being logged is contained in an error code. This is especially important in the interaction between the framework and applications, and conditions caused by third-party applications that are properly handled by the framework should not trigger logging higher than the DEBUG level. The only situations that should trigger logging at the INFORMATIVE level or higher is when a module or application detects an error at its own level or coming from a lower level. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; When a condition that would normally justify some logging is likely to occur many times, it can be a good idea to implement some rate-limiting mechanism to prevent overflowing the logs with many duplicate copies of the same (or very similar) information. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Losses of network connectivity are considered common and fully expected and should not be logged gratuitously. A loss of network connectivity that has consequences within an app should be logged at the DEBUG or VERBOSE level (depending on whether the consequences are serious enough and unexpected enough to be logged in a release build). &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; A full filesystem on a filesystem that is acceessible to or on behalf of third-party applications should not be logged at a level higher than INFORMATIVE. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Invalid data coming from any untrusted source (including any file on shared storage, or data coming through just about any network connections) is considered expected and should not trigger any logging at a level higher then DEBUG when it's detected to be invalid (and even then logging should be as limited as possible). &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Keep in mind that the '+' operator, when used on Strings, implicitly creates a StringBuilder with the default buffer size (16 characters) and potentially quite a few other temporary String objects, i.e. that explicitly creating StringBuilders isn't more expensive than relying on the default '+' operator (and can be a lot more efficient in fact). Also keep in mind that code that calls Log.v() is compiled and executed on release builds, including building the strings, even if the logs aren't being read. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Any logging that is meant to be read by other people and to be available in release builds should be terse without being cryptic, and should be reasonably understandable. This includes all logging up to the DEBUG level. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; When possible, logging should be kept on a single line if it makes sense. Line lengths up to 80 or 100 characters are perfectly acceptable, while lengths longer than about 130 or 160 characters (including the length of the tag) should be avoided if possible. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Logging that reports successes should never be used at levels higher than VERBOSE. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Temporary logging that is used to diagnose an issue that's hard to reproduce should be kept at the DEBUG or VERBOSE level, and should be enclosed by if blocks that allow to disable it entirely at compile-time. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; Be careful about security leaks through the log. Private information should be avoided. Information about protected content must definitely be avoided. This is especially important when writing framework code as it's not easy to know in advance what will and will not be private information or protected content. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; System.out.println() (or printf() for native code) should never be used. System.out and System.err get redirected to /dev/null, so your print statements will have no visible effects. However, all the string building that happens for these calls still gets executed. &#xD;
&lt;p&gt;&lt;i&gt;Note:&lt;/i&gt; &lt;b&gt;The golden rule of logging is that your logs may not unnecessarily push other logs out of the buffer, just as others may not push out yours.&lt;/b&gt; &#xD;
&lt;p&gt;&lt;strong&gt;Javatests Style Rules&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;a name="testmethodnames"&gt;&lt;/a&gt;Naming test methods&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;When naming test methods, you can use an underscore to seperate what is being tested from the specific case being tested. This style makes it easier to see exactly what cases are being tested. &#xD;
&lt;p&gt;&lt;a&gt;For example:&lt;/a&gt;&lt;pre &gt;testMethod_specificCase1 testMethod_specificCase2void testIsDistinguishable_protanopia() {&#xD;
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)&#xD;
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))&#xD;
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))&#xD;
}&#xD;
&lt;/pre&gt;&lt;pre&gt;&lt;/pre&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1936314.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/15/1936314.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935858.html</id><title type="text">基于 OAuth 安全协议的 Java 应用编程</title><summary type="text">原文地址：http://www.ibm.com/developerworks/cn/java/j-lo-oauth/index.html  OAuth 简介  OAuth 是由 Blaine Cook...</summary><published>2011-01-14T11:16:00Z</published><updated>2011-01-14T11:16:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935858.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935858.html"/><content type="html">&lt;p&gt;原文地址：&lt;a title="http://www.ibm.com/developerworks/cn/java/j-lo-oauth/index.html" href="http://www.ibm.com/developerworks/cn/java/j-lo-oauth/index.html"&gt;http://www.ibm.com/developerworks/cn/java/j-lo-oauth/index.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a name="major1"&gt;OAuth 简介&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;OAuth 是由 Blaine Cook、Chris Messina、Larry Halff 及 David Recordon 共同发起的，目的在于为 API 访问授权提供一个安全、开放的标准。&lt;/p&gt;  &lt;p&gt;基于 OAuth 认证授权具有以下特点：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;安全。OAuth 与别的授权方式不同之处在于：OAuth 的授权不会使消费方（Consumer）触及到用户的帐号信息（如用户名与密码），也是是说，消费方无需使用用户的用户名与密码就可以申请获得该用户资源的授权。 &lt;/li&gt;    &lt;li&gt;开放。任何消费方都可以使用 OAuth 认证服务，任何服务提供方 (Service Provider) 都可以实现自身的 OAuth 认证服务。 &lt;/li&gt;    &lt;li&gt;简单。不管是消费方还是服务提供方，都很容易于理解与使用。&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;OAuth 的解决方案如下图所示。&lt;/p&gt;  &lt;p&gt;&lt;a name="fig1"&gt;&lt;b&gt;图 1. OAuth Solution&lt;/b&gt;&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141903545812.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image001" border="0" alt="image001" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141904283315.png" width="532" height="237" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;如图 1 所示 OAuth 解决方案中用户、消费方及其服务提供方之间的三角关系：当用户需要 Consumer 为其提供某种服务时，该服务涉及到需要从服务提供方那里获取该用户的保护资源。OAuth 保证：只有在用户显式授权的情况下（步骤 4），消费方才可以获取该用户的资源，并用来服务于该用户。&lt;/p&gt;  &lt;p&gt;从宏观层次来看，OAuth 按以下方式工作：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;消费方与不同的服务提供方建立了关系。 &lt;/li&gt;    &lt;li&gt;消费方共享一个密码短语或者是公钥给服务提供方，服务提供方使用该公钥来确认消费方的身份。 &lt;/li&gt;    &lt;li&gt;消费方根据服务提供方将用户重定向到登录页面。 &lt;/li&gt;    &lt;li&gt;该用户登录后告诉服务提供方该消费方访问他的保护资源是没问题的。&lt;/li&gt; &lt;/ol&gt;  &lt;hr /&gt;  &lt;p&gt;&lt;a name="major2"&gt;OAuth 认证授权流程&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;在了解 OAuth 认证流程之前，我们先来了解一下 OAuth 协议的一些基本术语定义：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Consumer Key：消费方对于服务提供方的身份唯一标识。 &lt;/li&gt;    &lt;li&gt;Consumer Secret：用来确认消费方对于 Consumer Key 的拥有关系。 &lt;/li&gt;    &lt;li&gt;Request Token：获得用户授权的请求令牌，用于交换 Access Token。 &lt;/li&gt;    &lt;li&gt;Access Token：用于获得用户在服务提供方的受保护资源。 &lt;/li&gt;    &lt;li&gt;Token Secret：用来确认消费方对于令牌（Request Token 和 Access Token）的拥有关系。&lt;/li&gt; &lt;/ul&gt;  &lt;br /&gt;&lt;a name="fig2"&gt;&lt;b&gt;图 2. OAuth 授权流程（摘自 OAuth 规范）&lt;/b&gt;&lt;/a&gt;  &lt;br /&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141908448514.gif"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image002" border="0" alt="image002" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141909124343.gif" width="576" height="433" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;对于图 2 具体每一执行步骤，解释如下：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;消费方向 OAuth 服务提供方请求未授权的 Request Token。 &lt;/li&gt;    &lt;li&gt;OAuth 服务提供方在验证了消费方的合法请求后，向其颁发未经用户授权的 Request Token 及其相对应的 Token Secret。 &lt;/li&gt;    &lt;li&gt;消费方使用得到的 Request Token，通过 URL 引导用户到服务提供方那里，这一步应该是浏览器的行为。接下来，用户可以通过输入在服务提供方的用户名 / 密码信息，授权该请求。一旦授权成功，转到下一步。 &lt;/li&gt;    &lt;li&gt;服务提供方通过 URL 引导用户重新回到消费方那里，这一步也是浏览器的行为。 &lt;/li&gt;    &lt;li&gt;在获得授权的 Request Token 后，消费方使用授权的 Request Token 从服务提供方那里换取 Access Token。 &lt;/li&gt;    &lt;li&gt;OAuth 服务提供方同意消费方的请求，并向其颁发 Access Token 及其对应的 Token Secret。 &lt;/li&gt;    &lt;li&gt;消费方使用上一步返回的 Access Token 访问用户授权的资源。&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;总的来讲，在 OAuth 的技术体系里，服务提供方需要提供如下基本的功能：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;第 1、实现三个 Service endpoints，即：提供用于获取未授权的 Request Token 服务地址，获取用户授权的 Request Token 服务地址，以及使用授权的 Request Token 换取 Access Token 的服务地址。 &lt;/li&gt;    &lt;li&gt;第 2、提供基于 Form 的用户认证，以便于用户可以登录服务提供方做出授权。 &lt;/li&gt;    &lt;li&gt;第 3、授权的管理，比如用户可以在任何时候撤销已经做出的授权。&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;而对于消费方而言，需要如下的基本功能：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;第 1、从服务提供方获取 Customer Key/Customer Secret。 &lt;/li&gt;    &lt;li&gt;第 2、提供与服务提供方之间基于 HTTP 的通信机制，以换取相关的令牌。&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;我们具体来看一个使用 OAuth 认证的例子。&lt;/p&gt;  &lt;p&gt;在传统的网站应用中，如果您想在网站 A 导入网站 B 的联系人列表，需要在网站 A 输入您网站 B 的用户名、密码信息。例如，您登陆 Plaxo (https://www.plaxo.com )，一个联系人管理网站，当您想把 GMail 的联系人列表导入到 Plaxo，您需要输入您的 GMail 用户名 / 密码，如图 3 所示：&lt;/p&gt;  &lt;p&gt;&lt;a name="fig3"&gt;&lt;b&gt;图 3. 在 Plaxo 获得 GMail 联系人&lt;/b&gt;&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141909448824.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image003" border="0" alt="image003" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141911012802.png" width="576" height="386" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;在这里，Plaxo 承诺不会保存您在 Gmail 的密码。&lt;/p&gt;  &lt;p&gt;如果使用 OAuth 认证，情况是不同的，您不需要向网站 A（扮演 Consumer 角色）暴露您网站 B（扮演 Service Provider 角色）的用户名、密码信息。例如，您登录 http://lab.madgex.com/oauth-net/googlecontacts/default.aspx 网站， 如图 4 所示：&lt;/p&gt;  &lt;p&gt;&lt;a name="fig4"&gt;&lt;b&gt;图 4. 在 lab.madgex.com 获得 GMail 联系人&lt;/b&gt;&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141911465115.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image004" border="0" alt="image004" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/20110114191206576.png" width="576" height="213" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;点击“Get my Google Contacts”，浏览器将会重定向到 Google，引导您登录 Google，如图 5 所示：&lt;/p&gt;  &lt;p&gt;&lt;a name="fig5"&gt;&lt;b&gt;图 5. 登录 Google&lt;/b&gt;&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141912128662.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image005" border="0" alt="image005" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141912207601.png" width="331" height="346" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;登录成功后，将会看到图 6 的信息：&lt;/p&gt;  &lt;p&gt;&lt;a name="fig6"&gt;&lt;b&gt;图 6. Google 对 lab.madgex.com 网站授权&lt;/b&gt;&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141912309214.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image006" border="0" alt="image006" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141912439488.png" width="576" height="245" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;在您登录 Google，点击“Grant access”，授权 lab.madgex.com 后，lab.madgex.com 就能获得您在 Google 的联系人列表。&lt;/p&gt;  &lt;p&gt;在上面的的例子中，网站 lab.madgex.com 扮演着 Consumer 的角色，而 Google 是 Service Provider，lab.madgex.com 使用基于 OAuth 的认证方式从 Google 获得联系人列表。&lt;/p&gt;  &lt;p&gt;下一节，本文会给出一个消费方实现的例子，通过 OAuth 机制请求 Google Service Provider 的 OAuth Access Token，并使用该 Access Token 访问用户的在 Google 上的日历信息 (Calendar)。&lt;/p&gt;  &lt;hr /&gt;  &lt;p&gt;&lt;a name="major3"&gt;示例&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a name="minor3.1"&gt;准备工作&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;作为消费方，首先需要访问 https://www.google.com/accounts/ManageDomains，从 Google 那里获得标志我们身份的 Customer Key 及其 Customer Secret。另外，您可以生成自己的自签名 X509 数字证书，并且把证书上传给 Google，Google 将会使用证书的公钥来验证任何来自您的请求。&lt;/p&gt;  &lt;p&gt;具体的操作步骤，请读者参考 Google 的说明文档：http://code.google.com/apis/gdata/articles/oauth.html。&lt;/p&gt;  &lt;p&gt;在您完成这些工作，您将会得到 OAuth Consumer Key 及其 OAuth Consumer Secret，用于我们下面的开发工作。&lt;/p&gt;  &lt;p&gt;&lt;a name="minor3.2"&gt;如何获得 OAuth Access Token&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;以下的代码是基于 Google Code 上提供的 OAuth Java 库进行开发的，读者可以从 http://oauth.googlecode.com/svn/code/java/core/ 下载获得。&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;指定 Request Token URL，User Authorization URL，以及 Access Token URL，构造 OAuthServiceProvider 对象：      &lt;pre &gt;OAuthServiceProvider serviceProvider = new OAuthServiceProvider( &#xD;
    &amp;quot;https://www.google.com/accounts/OAuthGetRequestToken&amp;quot;, &#xD;
    &amp;quot;https://www.google.com/accounts/OAuthAuthorizeToken&amp;quot;, &#xD;
    &amp;quot;https://www.google.com/accounts/OAuthGetAccessToken&amp;quot;);&lt;/pre&gt;&#xD;
  &lt;/li&gt;&#xD;
&#xD;
  &lt;li&gt;指定 Customer Key，Customer Secret 以及 OAuthServiceProvider，构造 OAuthConsumer 对象： &#xD;
    &lt;pre &gt;OAuthConsumer oauthConsumer = new OAuthConsumer(null &#xD;
    , &amp;quot;www.example.com&amp;quot; &#xD;
    , &amp;quot;hIsGkM+T4+90fKNesTtJq8Gs&amp;quot;&#xD;
    , serviceProvider); &lt;/pre&gt;&#xD;
  &lt;/li&gt;&#xD;
&#xD;
  &lt;li&gt;为 OAuthConsumer 指定签名方法，以及提供您自签名 X509 数字证书的 private key。 &#xD;
    &lt;pre &gt;oauthConsumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);&#xD;
oauthConsumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey); &lt;/pre&gt;&#xD;
  &lt;/li&gt;&#xD;
&#xD;
  &lt;li&gt;由 OAuthConsumer 对象生成相应的 OAuthAccessor 对象： &#xD;
    &lt;pre &gt;accessor = new OAuthAccessor(consumer); &lt;/pre&gt;&#xD;
  &lt;/li&gt;&#xD;
&#xD;
  &lt;li&gt;指定您想要访问的 Google 服务，在这里我们使用的是 Calendar 服务： &#xD;
    &lt;pre &gt;Collection&amp;lt;? extends Map.Entry&amp;gt; parameters &#xD;
    = OAuth.newList(&amp;quot;scope&amp;quot;,&amp;quot;http://www.google.com/calendar/feeds/&amp;quot;);&lt;/pre&gt;&#xD;
  &lt;/li&gt;&#xD;
&#xD;
  &lt;li&gt;通过 OAuthClient 获得 Request Token: &#xD;
    &lt;pre &gt;OAuthMessage response = getOAuthClient().getRequestTokenResponse( &#xD;
    accessor, null, parameters); &lt;/pre&gt;&#xD;
&#xD;
    &lt;p&gt;使用 Request Token, 将用户重定向到授权页面，如图 7 所示：&lt;/p&gt;&#xD;
&#xD;
    &lt;p&gt;&lt;a name="fig7"&gt;&lt;b&gt;图 7. OAuth User Authorization&lt;/b&gt;&lt;/a&gt;&#xD;
&#xD;
      &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141913145614.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image007" border="0" alt="image007" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141914052948.png" width="576" height="344" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
  &lt;/li&gt;&#xD;
&#xD;
  &lt;li&gt;当用户点击“Grant access”按钮，完成授权后，再次通过 OAuthClient 获得 Access Token： &#xD;
    &lt;pre &gt;oauthClient.getAccessToken(accessor, null, null); &lt;/pre&gt;&#xD;
  &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&#xD;
&lt;p&gt;在上述步骤成功完成后，Access Token 将保存在 accessor 对象的 accessToken 成员变量里。查看您的 Google Account 安全管理页面，可以看到您授权的所有消费方，如图 8 所示。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="fig8"&gt;&lt;b&gt;图 8. Authorized OAuth Access to your Google Account&lt;/b&gt;&lt;/a&gt;&#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141914379414.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image008" border="0" alt="image008" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141915385263.png" width="576" height="202" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="minor3.3"&gt;使用 OAuth Access Token 访问 Google 服务&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;接下来，我们使用上一节获得的 Access Token 设置 Google Service 的 OAuth 认证参数，然后从 Google Service 获取该用户的 Calendar 信息：&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;OAuthParameters para = new OAuthParameters(); &#xD;
para.setOAuthConsumerKey(&amp;quot;www.example.com&amp;quot;); &#xD;
para.setOAuthToken(accessToken); &#xD;
googleService.setOAuthCredentials(para, signer); &lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;清单 1 是完整的示例代码，供读者参考。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="listing1"&gt;&lt;b&gt;清单 1. 基于 OAuth 认证的 Google Service 消费方实现&lt;/b&gt;&lt;/a&gt;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;pre &gt;import java.util.Collection; &#xD;
import java.util.Map; &#xD;
import net.oauth.OAuth; &#xD;
import net.oauth.OAuthAccessor; &#xD;
import net.oauth.OAuthConsumer; &#xD;
import net.oauth.client.OAuthClient; &#xD;
&#xD;
 public class DesktopClient { &#xD;
    private final OAuthAccessor accessor; &#xD;
    private OAuthClient oauthClient = null; &#xD;
    public DesktopClient(OAuthConsumer consumer) { &#xD;
        accessor = new OAuthAccessor(consumer); &#xD;
    } &#xD;
&#xD;
    public OAuthClient getOAuthClient() { &#xD;
        return oauthClient; &#xD;
    } &#xD;
&#xD;
    public void setOAuthClient(OAuthClient client) { &#xD;
        this.oauthClient = client; &#xD;
    } &#xD;
&#xD;
    //get the OAuth access token. &#xD;
    public String getAccessToken(String httpMethod, 	&#xD;
	    Collection&amp;lt;? extends Map.Entry&amp;gt; parameters) throws Exception { &#xD;
        getOAuthClient().getRequestTokenResponse(accessor, null,parameters); &#xD;
&#xD;
        String authorizationURL = OAuth.addParameters( &#xD;
		    accessor.consumer.serviceProvider.userAuthorizationURL, &#xD;
			 OAuth.OAUTH_TOKEN, accessor.requestToken); &#xD;
&#xD;
        //Launch the browser and redirects user to authorization URL &#xD;
        Runtime.getRuntime().exec(&amp;quot;rundll32 url.dll,FileProtocolHandler &amp;quot; &#xD;
		    + authorizationURL); &#xD;
&#xD;
        //wait for user's authorization &#xD;
        System.out.println(&amp;quot;Please authorize your OAuth request token. &amp;quot; &#xD;
		    + &amp;quot;Once that is complete, press any key to continue...&amp;quot;); &#xD;
        System.in.read(); &#xD;
        oauthClient.getAccessToken(accessor, null, null); &#xD;
        return accessor.accessToken; &#xD;
    } &#xD;
 } &lt;/pre&gt; &lt;pre &gt;import java.net.URL; &#xD;
 import java.security.KeyFactory; &#xD;
 import java.security.PrivateKey; &#xD;
 import java.security.spec.EncodedKeySpec; &#xD;
 import java.security.spec.PKCS8EncodedKeySpec; &#xD;
 import java.util.Collection; &#xD;
 import java.util.Map; &#xD;
 import com.google.gdata.client.GoogleService; &#xD;
 import com.google.gdata.client.authn.oauth.OAuthParameters; &#xD;
 import com.google.gdata.client.authn.oauth.OAuthRsaSha1Signer; &#xD;
 import com.google.gdata.client.authn.oauth.OAuthSigner; &#xD;
 import com.google.gdata.data.BaseEntry; &#xD;
 import com.google.gdata.data.BaseFeed; &#xD;
 import com.google.gdata.data.Feed; &#xD;
 import net.oauth.OAuth; &#xD;
 import net.oauth.OAuthConsumer; &#xD;
 import net.oauth.OAuthMessage; &#xD;
 import net.oauth.OAuthServiceProvider; &#xD;
 import net.oauth.client.OAuthClient; &#xD;
 import net.oauth.client.httpclient4.HttpClient4; &#xD;
 import net.oauth.example.desktop.MyGoogleService; &#xD;
 import net.oauth.signature.OAuthSignatureMethod; &#xD;
 import net.oauth.signature.RSA_SHA1; &#xD;
&#xD;
 public class GoogleOAuthExample { &#xD;
    //Note, use the private key of your self-signed X509 certificate. &#xD;
    private static final String PRIVATE_KEY = &amp;quot;XXXXXXXX&amp;quot;; &#xD;
&#xD;
    public static void main(String[] args) throws Exception { &#xD;
        KeyFactory fac = KeyFactory.getInstance(&amp;quot;RSA&amp;quot;); &#xD;
        //PRIVATE_KEY is the private key of your self-signed X509 certificate. &#xD;
        EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec( &#xD;
		    OAuthSignatureMethod.decodeBase64(PRIVATE_KEY)); &#xD;
        fac = KeyFactory.getInstance(&amp;quot;RSA&amp;quot;); &#xD;
        PrivateKey privateKey = fac.generatePrivate(privKeySpec); &#xD;
        OAuthServiceProvider serviceProvider = new OAuthServiceProvider( &#xD;
            //used for obtaining a request token &#xD;
			 //&amp;quot;https://www.google.com/accounts/OAuthGetRequestToken&amp;quot;, &#xD;
	        //used for authorizing the request token &#xD;
            &amp;quot;https://www.google.com/accounts/OAuthAuthorizeToken&amp;quot;, &#xD;
             //used for upgrading to an access token &#xD;
            &amp;quot;https://www.google.com/accounts/OAuthGetAccessToken&amp;quot;); &#xD;
&#xD;
        OAuthConsumer oauthConsumer = new OAuthConsumer(null &#xD;
            , &amp;quot;lszhy.weebly.com&amp;quot; //consumer key &#xD;
            , &amp;quot;hIsGnM+T4+86fKNesUtJq7Gs&amp;quot; //consumer secret &#xD;
            , serviceProvider); &#xD;
&#xD;
        oauthConsumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1); &#xD;
        oauthConsumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey); &#xD;
&#xD;
        DesktopClient client = new DesktopClient(oauthConsumer); &#xD;
        client.setOAuthClient(new OAuthClient(new HttpClient4())); &#xD;
		&#xD;
        Collection&amp;lt;? extends Map.Entry&amp;gt; parameters = &#xD;
		    OAuth.newList(&amp;quot;scope&amp;quot;,&amp;quot;http://www.google.com/calendar/feeds/&amp;quot;);&#xD;
		&#xD;
        String accessToken = client.getAccessToken(OAuthMessage.GET,parameters); &#xD;
		&#xD;
		&#xD;
        //Make an OAuth authorized request to Google &#xD;
		&#xD;
        // Initialize the variables needed to make the request &#xD;
        URL feedUrl = new URL( &#xD;
		    &amp;quot;http://www.google.com/calendar/feeds/default/allcalendars/full&amp;quot;);&#xD;
        &#xD;
        System.out.println(&amp;quot;Sending request to &amp;quot; + feedUrl.toString()); &#xD;
        System.out.println(); &#xD;
        &#xD;
        GoogleService googleService = new GoogleService(&amp;quot;cl&amp;quot;, &amp;quot;oauth-sample-app&amp;quot;); &#xD;
&#xD;
        OAuthSigner signer = new OAuthRsaSha1Signer(MyGoogleService.PRIVATE_KEY); &#xD;
        &#xD;
        // Set the OAuth credentials which were obtained from the step above. &#xD;
        OAuthParameters para = new OAuthParameters(); &#xD;
        para.setOAuthConsumerKey(&amp;quot;lszhy.weebly.com&amp;quot;); &#xD;
        para.setOAuthToken(accessToken); &#xD;
        googleService.setOAuthCredentials(para, signer); &#xD;
        &#xD;
        // Make the request to Google &#xD;
        BaseFeed resultFeed = googleService.getFeed(feedUrl, Feed.class); &#xD;
        System.out.println(&amp;quot;Response Data:&amp;quot;);               &#xD;
        System.out.println(&amp;quot;==========================================&amp;quot;); &#xD;
&#xD;
        System.out.println(&amp;quot;|TITLE: &amp;quot; + resultFeed.getTitle().getPlainText()); &#xD;
        if (resultFeed.getEntries().size() == 0) { &#xD;
           System.out.println(&amp;quot;|\tNo entries found.&amp;quot;); &#xD;
        } else { &#xD;
            for (int i = 0; i &amp;lt; resultFeed.getEntries().size(); i++) { &#xD;
               BaseEntry entry = (BaseEntry) resultFeed.getEntries().get(i); &#xD;
               System.out.println(&amp;quot;|\t&amp;quot; + (i + 1) + &amp;quot;: &amp;quot;&#xD;
                    + entry.getTitle().getPlainText()); &#xD;
            } &#xD;
        } &#xD;
        System.out.println(&amp;quot;==========================================&amp;quot;); 	&#xD;
    } &#xD;
 } &lt;/pre&gt;			&lt;hr /&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="major4"&gt;小结&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;OAuth 协议作为一种开放的，基于用户登录的授权认证方式，目前互联网很多 Open API 都对 OAuth 提供了支持，这包括 Google, Yahoo，Twitter 等。本文以 Google 为例子，介绍了 Java 桌面程序如何开发 OAuth 认证应用。在开发桌面应用访问 Web 资源这样一类程序时，一般通行的步骤是：使用 OAuth 做认证，然后使用获得的 OAuth Access Token，通过 REST API 访问用户在服务提供方的资源。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;事实上，目前 OAuth 正通过许多实现（包括针对 Java、C#、Objective-C、Perl、PHP 及 Ruby 语言的实现）获得巨大的动力。大部分实现都由 OAuth 项目维护并放在 Google 代码库 (http://oauth.googlecode.com/svn/) 上。开发者可以利用这些 OAuth 类库编写自己需要的 OAuth 应用。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1935858.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935858.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935849.html</id><title type="text">OAuth介绍</title><summary type="text">简介 OAuth (开放授权) 是一个开放标准，允许用户让第三方网站访问该用户在某一网站上存储的私密的资源（如照片，视频，联系人列表），而无需将用户名和密码提供给第三方网站。  OAuth允许用户提供...</summary><published>2011-01-14T10:45:00Z</published><updated>2011-01-14T10:45:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935849.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935849.html"/><content type="html">&lt;p&gt;&lt;strong&gt;简介&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/2011011418433022.png"&gt;&lt;img style="display: inline; float: right" title="My-Endpoints-300x267" alt="My-Endpoints-300x267" align="right" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141845066014.png" width="240" height="214" /&gt;&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;OAuth&lt;/b&gt; (开放授权) 是一个&lt;a href="http://zh.wikipedia.org/w/index.php?title=%E5%BC%80%E6%94%BE%E6%A0%87%E5%87%86&amp;amp;action=edit&amp;amp;redlink=1"&gt;开放标准&lt;/a&gt;，允许用户让第三方网站访问该用户在某一网站上存储的私密的资源（如照片，视频，联系人列表），而无需将用户名和密码提供给第三方网站。&lt;/p&gt;  &lt;p&gt;OAuth允许用户提供一个令牌，而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站（例如，视频编辑网站)在特定的时段（例如，接下来的2小时内）内访问特定的资源（例如仅仅是某一相册中的视频）。这样，OAuth允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息，而不需要分享他们的访问许可或他们数据的所有内容。&lt;/p&gt;  &lt;p&gt;OAuth是&lt;a href="http://zh.wikipedia.org/wiki/OpenID"&gt;OpenID&lt;/a&gt;的一个补充，但是完全不同的服务。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;认证和授权过程&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;在认证和授权的过程中涉及的三方包括：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;服务提供方&lt;/b&gt;，用户使用服务提供方来存储受保护的资源，如照片，视频，联系人列表。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;用户&lt;/b&gt;，存放在服务提供方的受保护的资源的拥有者 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;客户端&lt;/b&gt;，要访问服务提供方资源的第三方应用，通常是网站，如提供照片打印服务的网站。在认证过程之前，客户端要向服务提供者申请客户端标识。&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;使用OAuth进行认证和授权的过程如下所示:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;b&gt;用户&lt;/b&gt;访问&lt;b&gt;客户端&lt;/b&gt;的网站，想操作用户存放在&lt;b&gt;服务提供方&lt;/b&gt;的资源。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;客户端&lt;/b&gt;向&lt;b&gt;服务提供方&lt;/b&gt;请求一个临时令牌。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;服务提供方&lt;/b&gt;验证&lt;b&gt;客户端&lt;/b&gt;的身份后，授予一个临时令牌。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;客户端&lt;/b&gt;获得临时令牌后，将用户引导至&lt;b&gt;服务提供方&lt;/b&gt;的授权页面请求用户授权。在这个过程中将临时令牌和客户端的回调连接发送给&lt;b&gt;服务提供方&lt;/b&gt;。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;用户&lt;/b&gt;在&lt;b&gt;服务提供方&lt;/b&gt;的网页上输入用户名和密码，然后授权该&lt;b&gt;客户端&lt;/b&gt;访问所请求的资源。 &lt;/li&gt;    &lt;li&gt;授权成功后，&lt;b&gt;服务提供方&lt;/b&gt;引导&lt;b&gt;用户&lt;/b&gt;返回&lt;b&gt;客户端&lt;/b&gt;的网页。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;客户端&lt;/b&gt;根据临时令牌从&lt;b&gt;服务提供方&lt;/b&gt;那里获取访问令牌。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;服务提供方&lt;/b&gt;根据临时令牌和&lt;b&gt;用户&lt;/b&gt;的授权情况授予&lt;b&gt;客户端&lt;/b&gt;访问令牌。 &lt;/li&gt;    &lt;li&gt;&lt;b&gt;客户端&lt;/b&gt;使用获取的访问令牌访问存放在&lt;b&gt;服务提供方&lt;/b&gt;上的受保护的资源。&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;以上资料整理自：&lt;a title="http://zh.wikipedia.org/zh/OAuth" href="http://zh.wikipedia.org/zh/OAuth"&gt;http://zh.wikipedia.org/zh/OAuth&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;目前采用OAuth的API&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://zh.wikipedia.org/wiki/Facebook"&gt;Facebook&lt;/a&gt;的新的Graph API只支持OAuth 2.0，是这一新兴标准的最大实现：&lt;a title="http://developers.facebook.com/docs/reference/api/" href="http://developers.facebook.com/docs/reference/api/"&gt;http://developers.facebook.com/docs/reference/api/&lt;/a&gt;（Facebook SDK for Android 协同 Facebook Platform 一起，隐藏了 OAuth 身份认证的复杂性：&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935767.html"&gt;Facebook API 简介&lt;/a&gt;）&lt;/p&gt;  &lt;p&gt;Twitter API Wiki：&lt;a title="http://apiwiki.twitter.com/w/browse/#view=ViewFolder&amp;amp;param=OAuth" href="http://apiwiki.twitter.com/w/browse/#view=ViewFolder&amp;amp;param=OAuth"&gt;http://apiwiki.twitter.com/w/browse/#view=ViewFolder&amp;amp;param=OAuth&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;新浪微博API：&lt;a title="http://open.t.sina.com.cn/wiki/index.php/OAuth" href="http://open.t.sina.com.cn/wiki/index.php/OAuth"&gt;http://open.t.sina.com.cn/wiki/index.php/OAuth&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;豆瓣API：&lt;a title="http://www.douban.com/service/apidoc/auth" href="http://www.douban.com/service/apidoc/auth"&gt;http://www.douban.com/service/apidoc/auth&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;目前有6个Google Apps API支持OAuth登录机制：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;a href="http://code.google.com/googleapps/domain/gdata_provisioning_api_v2.0_developers_protocol.html"&gt;Provisioning API&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://code.google.com/googleapps/domain/email_migration/developers_guide_protocol.html"&gt;Email Migration API&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://code.google.com/googleapps/domain/admin_settings/docs/1.0/admin_settings_developers_guide_protocol.html"&gt;Admin Settings API&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://code.google.com/googleapps/domain/calendar_resource/docs/1.0/calendar_resource_developers_guide_protocol.html"&gt;Calendar Resource API&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://code.google.com/googleapps/domain/email_settings/developers_guide_protocol.html"&gt;Email Settings API&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://code.google.com/googleapps/domain/audit/docs/1.0/audit_developers_guide_protocol.html"&gt;Audit API&lt;/a&gt;&lt;/li&gt; &lt;/ol&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1935849.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935849.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935767.html</id><title type="text">Facebook API 简介</title><summary type="text">原文：http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/index.html  Facebook Platform API 概述  在介绍 Facebook SDK for Android 之前，有必要了解一下 Facebook Platform 及其 API。据 Facebook 声称，Facebook Platform ...</summary><published>2011-01-14T08:55:00Z</published><updated>2011-01-14T08:55:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935767.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935767.html"/><content type="html">&lt;p&gt;原文：&lt;a title="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/index.html" href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/index.html"&gt;http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/index.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a name="ovrvw"&gt;Facebook Platform API 概述&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;在介绍 Facebook SDK for Android 之前，有必要了解一下 Facebook Platform 及其 API。据 Facebook 声称，Facebook Platform 允许任何人 “在 Facebook 和 Web 上构建社交应用程序”。为了允许您构建此类应用程序，Facebook 提供广泛的一组核心且高级的 API 和 SDK（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#resources"&gt;参考资料&lt;/a&gt;）。&lt;/p&gt;  &lt;p&gt;核心 Facebook Platform API 是 Graph API，它允许您从 Facebook 读写数据。Facebook 也具有 Old Rest API。新的 Graph API 将 API 范型从面向方法的从 Facebook 读写数据的方式更改为一种新的方式，即使用对象（比如说用户简介、好友、帖子、照片，诸如此类）及其相互之间的关系或连接。该方法简化了 Facebook API，使之处理对象时更加一致。注意，尽管 Graph API 是首选的 Facebook API，但是 Old REST API 仍然是活跃且受支持的。Graph 和 REST API 都适用于移动应用程序（包括原生和移动 web 应用程序），它们通过使用 WebViews 在原生应用程序中包含移动 web 内容。&lt;/p&gt;  &lt;p&gt;Graph API 对象被分配一个惟一的 ID，很容易使用一个 URL 访问它，此 URL 可被进一步限定，以寻址一个特定的对象/连接。对象 URL 的一般结构类似如下： &lt;code&gt;https://graph.facebook.com/OBJECT_ID/CONNECTION_TYPE&lt;/code&gt;，其中 &lt;code&gt;OBJECT_ID&lt;/code&gt; 是对象的惟一 ID，&lt;code&gt;CONNECTION_TYPE&lt;/code&gt; 是对象支持的一种连接类型。例如，一个页面支持以下连接：feed/wall、photos、notes、posts、members，等等。&lt;/p&gt;  &lt;p&gt;利用 Graph API，您可以检索对象，删除对象和发布对象。您可以搜索、更新对象、过滤结果，甚至动态地发现对象的连接/关系。&lt;/p&gt;  &lt;p&gt;默认情况下，应用程序对用户的公共数据具有访问权限。要访问私有数据，应用程序必须首先请求用户的权限（被称之为扩展权限）。Facebook 定义了大量权限，您可以在 Extended Permissions 页面了解它们（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#resources"&gt;参考资料&lt;/a&gt;）。&lt;/p&gt;  &lt;hr /&gt;  &lt;p&gt;&lt;a name="N100DF"&gt;Facebook SDK for Android 简介&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;既然您较好地了解了 Facebook Platform API，下面就来看一下 Facebook SDK for Android。&lt;/p&gt;  &lt;p&gt;Facebook SDK for Android 是 Facebook Graph 和 Old REST API 的一个 Java 编程语言包装器。此 SDK 是开源的，宿主在 github 的 facebook / facebook-android-sdk 仓库中（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#resources"&gt;参考资料&lt;/a&gt;）。注意，由于开源 SDK 的演变特性，它有望发生进一步的更改。SDK 发布于 Apache License, Version 2.0 之下。&lt;/p&gt;  &lt;p&gt;Facebook SDK for Android 隐藏了前一节（&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#ovrvw"&gt;Facebook Platform API 概述&lt;/a&gt;）中介绍的很多细节。这是通过提供 表 1 中描述的 6 个 Java 类来实现的。&lt;/p&gt;  &lt;p&gt;&lt;a name="table1"&gt;&lt;b&gt;表 1. &lt;code&gt;包 com.facebook.android&lt;/code&gt; &lt;/b&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;类    &lt;br /&gt;说明&lt;/p&gt;  &lt;p&gt;&lt;code&gt;AsyncFacebookRunner&lt;/code&gt;     &lt;br /&gt;一个实现异步 Facebook API 调用的帮助器类&lt;/p&gt;  &lt;p&gt;&lt;code&gt;DialogError&lt;/code&gt;     &lt;br /&gt;一个封装对话框错误的类&lt;/p&gt;  &lt;p&gt;&lt;code&gt;Facebook&lt;/code&gt;     &lt;br /&gt;用于与 Facebook Platform API 交互的主 Facebook 类&lt;/p&gt;  &lt;p&gt;&lt;code&gt;FacebookError&lt;/code&gt;     &lt;br /&gt;一个封装 Facebook 错误的类&lt;/p&gt;  &lt;p&gt;&lt;code&gt;FbDialog&lt;/code&gt;     &lt;br /&gt;一个为 Facebook 对话框实现 WebView 的类&lt;/p&gt;  &lt;p&gt;&lt;code&gt;Util&lt;/code&gt;     &lt;br /&gt;一个带有大量实用方法的帮助器类&lt;/p&gt;  &lt;p&gt;Facebook SDK for Android 也带来一些有用的例子，您可以用来作为自己应用程序的起点。&lt;/p&gt;  &lt;p&gt;尤其有趣的是核心 &lt;code&gt;Facebook&lt;/code&gt; 类和 &lt;code&gt;Facebook Dialog&lt;/code&gt; 类，下面将更为详细地介绍它们。核心 &lt;code&gt;Facebook&lt;/code&gt; 类封装了对用户进行授权、创建 Facebook 对话框、发出 API 请求、注销用户以及获得或设置访问和会话信息及状态的方法。&lt;code&gt;Facebook Dialog&lt;/code&gt; 类实现了一个 WebView 及其创建它的方法以及用于处理 Facebook URL（状态）响应的逻辑。SDK 操作离不开对话框。SDK 提供两种方法进行身份认证，一种称之为单点登录，如果安装了的话，它使用原生的 Facebook 应用程序对话框，另一种是默认的 WebView 方法。本文我将重点介绍 WebView 方法。其余 SDK 类是帮助器类，用于封装错误信息或者提供整个 SDK 中使用的有用工具。&lt;/p&gt;  &lt;p&gt;下面几节着重介绍一个典型 Facebook 应用程序的用例：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;安装 Facebook SDK for Android &lt;/li&gt;    &lt;li&gt;注册您的应用程序 &lt;/li&gt;    &lt;li&gt;创建 SampleApp &lt;/li&gt;    &lt;li&gt;显示 Facebook 对话框 &lt;/li&gt;    &lt;li&gt;对用户进行授权 &lt;/li&gt;    &lt;li&gt;发出 API 请求 &lt;/li&gt; &lt;/ul&gt;  &lt;hr /&gt;  &lt;p&gt;&lt;a name="N1017F"&gt;安装 Android SDK&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;您必须下载并安装 Eclipse 或者您喜欢的 IDE。此外，还必须安装 Android SDK。有关如何下载并安装 Eclipse 和 Android SDK 的信息，请参见 Android Developer 网站（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#resources"&gt;参考资料&lt;/a&gt;）的 Download the Android SDK 页面。&lt;/p&gt;  &lt;hr /&gt;  &lt;p&gt;&lt;a name="N1018C"&gt;安装 Facebook SDK for Android&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Facebook SDK for Android 宿主在 github 的 facebook / facebook-android-sdk 仓库中。下载 SDK 源文件，将之解压到您的工作目录。启动 Eclipse，通过选择 &lt;strong&gt;File Menu -&amp;gt; New -&amp;gt; Project&lt;/strong&gt; 创建一个 Android Project，并选中 Android Project。从 &lt;code&gt;existing source&lt;/code&gt; 创建项目，并指向解压的源文件目录（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#download"&gt;下载&lt;/a&gt; 获得源代码）。&lt;/p&gt;  &lt;hr /&gt;  &lt;p&gt;&lt;a name="N101A0"&gt;注册您的应用程序&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;在开始之前，您必须注册您的 Facebook 应用程序并得到一个应用程序 ID (&lt;code&gt;client_id&lt;/code&gt;) 和相关的密钥 (&lt;code&gt;client_secret&lt;/code&gt;)。在进行不同的 Facebook API 调用时，会在您的应用程序中使用客户机 ID。&lt;/p&gt;  &lt;hr /&gt;  &lt;p&gt;&lt;a name="N101B1"&gt;样例应用程序&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;样例应用程序（我将之简称为 SampleApp）展示了如何使用 Facebook SDK for Android。此应用程序包含单个文件，该文件实现 SampleApp 活动，使用一个屏幕来显示消息、Facebook 好友列表，以及用于登录/身份认证用户、获得好友列表和发布到已通过身份认证用户 (&lt;code&gt;me&lt;/code&gt;) 涂鸦墙的菜单项。&lt;/p&gt;  &lt;p&gt;在开始之前，请记住您必须像前面解释的那样将您的应用程序注册到 Facebook，还必须将 SampleApp.java 中的属性 &lt;code&gt;APP_ID&lt;/code&gt; 设置为由 Facebook 提供的 &lt;code&gt;client_id&lt;/code&gt;（参见 清单 1 ）。&lt;/p&gt;  &lt;p&gt;&lt;a name="list1"&gt;&lt;b&gt;清单 1. 初始化应用程序 ID&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;  &lt;pre &gt;// Set application ID to your registered app client_id&#xD;
// See http://www.facebook.com/developers/createapp.php&#xD;
public static final String APP_ID = &amp;quot;.....&amp;quot;;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;对于 Sample Application 屏幕，使用一种线性布局，包含一个 TextView（用于简单状态消息）、一个 ListView（用于显示从服务器检索到的 Facebook 好友列表）和一些菜单项（用于登录/身份认证用户、获得好友列表和发布到已通过身份认证用户 (me) 的涂鸦墙）。清单 2 展示了主 UI 屏幕布局的 XML UI 声明。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list2a"&gt;&lt;b&gt;清单 2. 主屏幕 UI 声明&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&#xD;
&amp;lt;LinearLayout &#xD;
    xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&#xD;
    android:orientation=&amp;quot;vertical&amp;quot;&#xD;
    android:layout_width=&amp;quot;fill_parent&amp;quot;&#xD;
    android:layout_height=&amp;quot;fill_parent&amp;quot;&#xD;
    android:background=&amp;quot;@drawable/black&amp;quot;&#xD;
    &amp;gt;&#xD;
    &#xD;
    &amp;lt;TextView android:id=&amp;quot;@+id/txt&amp;quot;     &#xD;
        android:text=&amp;quot;@string/hello&amp;quot;&#xD;
        android:textColor=&amp;quot;@drawable/white&amp;quot;&#xD;
        android:layout_width=&amp;quot;wrap_content&amp;quot;&#xD;
        android:layout_height=&amp;quot;wrap_content&amp;quot;&#xD;
        android:paddingRight=&amp;quot;10dp&amp;quot; &#xD;
        android:paddingLeft=&amp;quot;10dp&amp;quot;&#xD;
        android:layout_margin=&amp;quot;10dp&amp;quot;&#xD;
        android:textSize=&amp;quot;10sp&amp;quot; &#xD;
        android:layout_marginTop=&amp;quot;5px&amp;quot; &#xD;
        android:layout_marginBottom=&amp;quot;5px&amp;quot;&#xD;
        android:layout_marginRight=&amp;quot;0px&amp;quot; &#xD;
        android:layout_marginLeft=&amp;quot;0px&amp;quot;&#xD;
        android:gravity=&amp;quot;left&amp;quot;&#xD;
    /&amp;gt;&#xD;
    &#xD;
    &amp;lt;ListView  &#xD;
        android:id=&amp;quot;@+id/friendsview&amp;quot;&#xD;
        android:layout_width=&amp;quot;fill_parent&amp;quot; &#xD;
        android:layout_height=&amp;quot;wrap_content&amp;quot;&#xD;
        android:textFilterEnabled=&amp;quot;true&amp;quot; &#xD;
    /&amp;gt;&#xD;
    &amp;lt;TextView &#xD;
        android:id=&amp;quot;@id/android:empty&amp;quot;&#xD;
        android:layout_width=&amp;quot;match_parent&amp;quot;&#xD;
        android:layout_height=&amp;quot;match_parent&amp;quot;&#xD;
        android:text=&amp;quot;No data&amp;quot;&#xD;
     /&amp;gt;               &#xD;
&amp;lt;/LinearLayout&amp;gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;清单 3 展示了 ListView 中每一行的 XML UI 声明。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list2b"&gt;&lt;b&gt;清单 3. ListView 行 UI 声明&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&#xD;
&amp;lt;LinearLayout &#xD;
    xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&#xD;
    android:layout_width=&amp;quot;fill_parent&amp;quot;&#xD;
    android:layout_height=&amp;quot;?android:attr/listPreferredItemHeight&amp;quot;&#xD;
    android:padding=&amp;quot;10dip&amp;quot;&#xD;
    &amp;gt;&#xD;
     &amp;lt;TextView&#xD;
         android:id=&amp;quot;@+id/rowtext_top&amp;quot;&#xD;
         android:layout_width=&amp;quot;wrap_content&amp;quot;&#xD;
         android:layout_height=&amp;quot;wrap_content&amp;quot;&#xD;
         android:gravity=&amp;quot;center_vertical&amp;quot;&#xD;
     /&amp;gt;&#xD;
&amp;lt;/LinearLayout&amp;gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;清单 4 展示了菜单项的 XML UI 声明。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list2c"&gt;&lt;b&gt;清单 4. 菜单项 UI 声明&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;&amp;lt;menu xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&amp;gt;&#xD;
    &amp;lt;item android:id=&amp;quot;@+id/login&amp;quot; android:title=&amp;quot;Login&amp;quot;/&amp;gt;&#xD;
    &amp;lt;item android:id=&amp;quot;@+id/getfriends&amp;quot; android:title=&amp;quot;Get Friends&amp;quot;/&amp;gt;&#xD;
    &amp;lt;item android:id=&amp;quot;@+id/wallpost&amp;quot; android:title=&amp;quot;Wall Post&amp;quot; /&amp;gt;&#xD;
&amp;lt;/menu&amp;gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;图 1 展示了 SampleApp 在登录之前（带有 Login 按钮）和之后（带有 Logout、Get Friends 和 Wall Post 按钮）XML UI 声明的结果。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="fig1"&gt;&lt;b&gt;图 1. SampleApp 屏幕布局&lt;/b&gt;&lt;/a&gt; &#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654345617.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig01" border="0" alt="fig01" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654361289.jpg" width="343" height="506" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;SampleApp &lt;code&gt;onCreate()&lt;/code&gt; 方法在创建 SampleApp 实例时由 Android 平台调用。此方法执行一个基本的检查（以确保在继续之前已经设置了 App ID）、初始化 UI 资源和初始化 Facebook 会话实例（参见 清单 5 ）。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list3"&gt;&lt;b&gt;清单 5. 初始化应用程序&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;/** Called when the activity is first created. */&#xD;
@Override&#xD;
public void onCreate(Bundle savedInstanceState) {&#xD;
    super.onCreate(savedInstanceState);&#xD;
&#xD;
    // Make sure the app client_app has been set&#xD;
    if (APP_ID == null) {&#xD;
        Util.showAlert(this, &#xD;
        &amp;quot;Warning&amp;quot;, &amp;quot;Facebook Application ID must be set...&amp;quot;);&#xD;
    }&#xD;
&#xD;
    // Initialize the content view&#xD;
    setContentView(R.layout.main);&#xD;
    // Get the status text line resource&#xD;
    mText = (TextView) SampleApp.this.findViewById(R.id.txt);&#xD;
&#xD;
    // Setup the ListView Adapter that is loaded when selecting &amp;quot;get friends&amp;quot;&#xD;
    listView = (ListView) findViewById(R.id.friendsview);&#xD;
    friendsArrayAdapter = new FriendsArrayAdapter(this, R.layout.rowlayout, friends);&#xD;
    listView.setAdapter(friendsArrayAdapter);&#xD;
&#xD;
    // Define a spinner used when loading the friends over the network&#xD;
    mSpinner = new ProgressDialog(listView.getContext());&#xD;
    mSpinner.requestWindowFeature(Window.FEATURE_NO_TITLE);&#xD;
    mSpinner.setMessage(&amp;quot;Loading...&amp;quot;);&#xD;
&#xD;
    // Initialize the Facebook session&#xD;
    mFacebook = new Facebook(APP_ID);&#xD;
    mAsyncRunner = new AsyncFacebookRunner(mFacebook);&#xD;
&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;应用程序流的其余部分通过菜单项由 UI 交互触发。定义了三个菜单项：1) login/logout（切换），2) get friends 和 3) post to the wall，如 清单 4 所示。当用户选择一个菜单项时，应用程序就执行一个适当的操作。下面的清单展示了菜单项是如何处理的。清单 6 展示了如何通过具体化其 XML 定义而实例化菜单的。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list4a"&gt;&lt;b&gt;清单 6. 创建菜单&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;/**&#xD;
 * Invoked at the time to create the menu&#xD;
 * @param the menu to create&#xD;
 */&#xD;
@Override&#xD;
public boolean onCreateOptionsMenu(Menu menu) {&#xD;
    MenuInflater inflater = getMenuInflater();&#xD;
    inflater.inflate(R.menu.main_menu, menu);&#xD;
    return true;&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;清单 7 展示了菜单项在显示之前是如何被修改的，就是说，根据实际情况显示适当的 &amp;quot;login&amp;quot; 或 &amp;quot;logout&amp;quot; 状态并启用/禁用 &amp;quot;get friends&amp;quot; 和 &amp;quot;post to wall&amp;quot;。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list4b"&gt;&lt;b&gt;清单 7. 创建菜单&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;/**&#xD;
 * Invoked when preparing to display the menu&#xD;
 * @param menu is the menu to prepare&#xD;
 */&#xD;
@Override&#xD;
public boolean onPrepareOptionsMenu(Menu menu) {&#xD;
    MenuItem loginItem = menu.findItem(R.id.login);&#xD;
    MenuItem postItem = menu.findItem(R.id.wallpost);&#xD;
    MenuItem getfriendsItem = menu.findItem(R.id.getfriends);&#xD;
    if (mFacebook.isSessionValid()) {&#xD;
        loginItem.setTitle(&amp;quot;Logout&amp;quot;);&#xD;
        postItem.setEnabled(true);&#xD;
        getfriendsItem.setEnabled(true);&#xD;
    } else {&#xD;
        loginItem.setTitle(&amp;quot;Login&amp;quot;);&#xD;
        postItem.setEnabled(false);&#xD;
        getfriendsItem.setEnabled(false);&#xD;
    }&#xD;
    loginItem.setEnabled(true);&#xD;
    return super.onPrepareOptionsMenu(menu);&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;清单 8 展示了菜单项是如何被处理的，以及适当的操作（login/logout、get friends、post to wall）是如何被调用的。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list4c"&gt;&lt;b&gt;清单 8. 处理菜单选择&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;/**&#xD;
 * Invoked when a menu item has been selected&#xD;
 * @param item is the selected menu item&#xD;
 */&#xD;
@Override&#xD;
public boolean onOptionsItemSelected(MenuItem item) {&#xD;
    switch (item.getItemId()) {&#xD;
&#xD;
        // Login/logout toggle&#xD;
        case R.id.login:&#xD;
            // Toggle the button state. &#xD;
            //  If coming from login transition to logout.&#xD;
            if (mFacebook.isSessionValid()) {&#xD;
                AsyncFacebookRunner asyncRunner = new AsyncFacebookRunner(mFacebook);&#xD;
                asyncRunner.logout(this.getBaseContext(), new LogoutRequestListener());&#xD;
            } else {&#xD;
                // Toggle the button state. &#xD;
                //  If coming from logout transition to login (authorize).&#xD;
                mFacebook.authorize(this, PERMISSIONS, new LoginDialogListener());&#xD;
            }&#xD;
            break;&#xD;
&#xD;
        // Wall Post&#xD;
        case R.id.wallpost: // Wall Post&#xD;
            mFacebook.dialog(SampleApp.this, &amp;quot;stream.publish&amp;quot;, new &#xD;
                                                  WallPostDialogListener());&#xD;
            break;&#xD;
&#xD;
        // Get Friend's List&#xD;
        case R.id.getfriends: // Wall Post&#xD;
            // Get the authenticated user's friends&#xD;
            mSpinner.show();&#xD;
            mAsyncRunner.request(&amp;quot;me/friends&amp;quot;, new FriendsRequestListener());&#xD;
            break;&#xD;
&#xD;
        default:&#xD;
            return false;&#xD;
&#xD;
    }&#xD;
    return true;&#xD;
}   &lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;应用程序使用很多回调来处理每个异步调用的状态。SampleApp 定义一个回调来处理 login 请求、logout 对话框、post to wall 对话框和 get friends 请求的成功或失败。您可以利用这个样例应用程序，并添加您自己的操作，遵循添加菜单和相应 Facebook 调用的相同模式，利用对应的回调来接收状态信息。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;login 和 post to wall 菜单项都调用 Facebook 对话框（在默认情况下，如果没有定义单点登录选项，此对话框使用 SDK 基于 WebView 的对话框）。下一节将介绍此内容。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;code&gt;FriendsRequestListener&lt;/code&gt; 回调在 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#makerequests"&gt;发出 Facebook API 请求&lt;/a&gt; 一节中介绍。最后一个回调是 &lt;code&gt;MyDialogListener&lt;/code&gt;，它实现 Facebook SDK for Android &lt;code&gt;DialogListener&lt;/code&gt;，这用于使用 SDK 基于 WebView 的对话框的 Facebook 请求，下一节中将会介绍。&lt;code&gt;DialogListener&lt;/code&gt; 的实现者必须实现适当的完成逻辑；例如，这在 login 和 wall post 请求过程中使用。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N1025F"&gt;显示 Facebook 对话框&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;对于诸如用户授权、分配权限和消息发布之类的用户交互，Facebook SDK for Android 依赖于 Facebook 基于 web/服务器的对话框。核心 SDK &lt;code&gt;Facebook.java&lt;/code&gt; 类在为请求操作产生 UI 对话框时定义方法 &lt;code&gt;dialog()&lt;/code&gt;（参见 清单 9 ）。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list5"&gt;&lt;b&gt;清单 9. Facebook Dialog 方法&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;/**&#xD;
 * Generate a UI dialog for the request action in the given Android context&#xD;
 * with the provided parameters.&#xD;
 */&#xD;
public void dialog(&#xD;
        Context context, &#xD;
        String action, &#xD;
        Bundle parameters,&#xD;
        final DialogListener listener) {&#xD;
    :&#xD;
    :&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;此异步方法接受应用程序上下文的输入参数、执行的操作（比如 &lt;code&gt;login&lt;/code&gt;、&lt;code&gt;publish_stream&lt;/code&gt;、&lt;code&gt;read_stream&lt;/code&gt;、&lt;code&gt;offline_access&lt;/code&gt;）、特定请求或操作需要的参数和异步方法完成执行时调用的侦听器方法。该方法创建适当的 WebView 对话框。例如，要发布一个状态消息，需要调用核心 Facebook 类方法 &lt;code&gt;dialog()&lt;/code&gt;，传递操作 &lt;code&gt;stream.publish&lt;/code&gt;（参见 清单 10 ）。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list6"&gt;&lt;b&gt;清单 10. 调用 &lt;code&gt;dialog()&lt;/code&gt; 方法和处理回调&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;package com.myapp.facebook.android;&#xD;
:&#xD;
&#xD;
import com.facebook.android.*;&#xD;
:&#xD;
&#xD;
mFacebook = new Facebook(); // Facebook core&#xD;
:&#xD;
&#xD;
// Create a Facebook dialog (using the call asynchronous API)&#xD;
mFacebook.dialog(&#xD;
    this,&#xD;
    &amp;quot;stream.publish&amp;quot;, &#xD;
    new MyDialogListener());   &#xD;
&#xD;
:&#xD;
:&#xD;
&#xD;
//&#xD;
// My asynchronous dialog listener&#xD;
//&#xD;
public class MyDialogListener extends com.facebook.android.Facebook.DialogListener {&#xD;
&#xD;
    public void onComplete(Bundle values) {&#xD;
&#xD;
        final String postId = values.getString(&amp;quot;post_id&amp;quot;);&#xD;
&#xD;
        if (postId != null) {&#xD;
&#xD;
            // &amp;quot;Wall post made...&amp;quot;&#xD;
&#xD;
        } else {&#xD;
            // &amp;quot;No wall post made...&amp;quot;&#xD;
        }&#xD;
&#xD;
    }&#xD;
&#xD;
    public void onFacebookError(FacebookError e) {...}&#xD;
    public void onError(DialogError e) {...}&#xD;
    public void onCancel() {...}&#xD;
&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;该调用的结果是 Facebook 基于 web 的 &lt;strong&gt;publish to wall&lt;/strong&gt; 对话框，如 图 2 所示。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="fig2"&gt;&lt;b&gt;图 2. 发布到涂鸦墙&lt;/b&gt;&lt;/a&gt; &#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654373057.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig02" border="0" alt="fig02" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654394650.jpg" width="349" height="507" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;这是熟悉的发布到用户涂鸦墙的 Web 页面，与 Facebook 提供的一样。用户可以跳过或者继续前进并发布消息。这种在同一移动应用程序中混合使用原生和 web 内容是混合应用程序强大之处的很好示例。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N102BB"&gt;对用户进行授权&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;Facebook Platform 对 OAuth 2.0 身份认证提供支持，也支持一种较老的、定制的授权签名方案。编写新应用程序时应该避免使用老的身份认证方案，因为对这种老方案的支持很快就会取消了。更多有关 OAuth 的信息，请参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#resources"&gt;参考资料&lt;/a&gt; 中的 OAuth 2.0 Protocol 规范。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;Facebook SDK for Android 协同 Facebook Platform 一起，隐藏了 OAuth 身份认证的复杂性，如 图 3 所示。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="fig3"&gt;&lt;b&gt;图 3. OAuth 2.0 Protocol (IETF)&lt;/b&gt;&lt;/a&gt; &#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654406211.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig03" border="0" alt="fig03" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654427456.jpg" width="508" height="353" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;Facebook SDK for Android 采用移动 web 身份认证方法，而不是采用原生方法。注意，未来版本的 SDK 有可能对 OAuth 提供原生的 Android 支持。对于此方法，SDK 在 WebView 中使用 Facebook 基于 web 的身份认证对话框。这是一个有趣的方法，理由如下：&lt;/p&gt;&#xD;
&#xD;
&lt;ul&gt;&#xD;
  &lt;li&gt;它减少了对应用程序实现 OAuth 原生功能的复杂性。 &lt;/li&gt;&#xD;
&#xD;
  &lt;li&gt;更为重要的是，它通过显示跟在常规浏览器上使用 Facebook 时看到的相同的标准而熟悉的登录和分配权限对话框，赢得用户的信任。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&#xD;
&lt;p&gt;该方法的一个优点是，它有时可能感觉比纯粹的原生实现要慢一些。为此，SDK 包含一个备选的身份认证/登录方法，即使用原生 Facebook 应用程序的单点登录。这就是说，如果官方 Facebook 原生应用程序安装在手机上，那么 Facebook SDK for Android 可以使用它的身份认证/登录活动。但是要使之工作，您必须首先签署您的应用程序（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#resources"&gt;参考资料&lt;/a&gt; 中 Frank Ableson 的文章 “Introduction to Android Security”），然后生成一个签名或密钥散列（参见 清单 11 ）。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list7"&gt;&lt;b&gt;清单 11. 生成应用程序密钥散列&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;keytool -exportcert -alias [alias] -keystore [keystore] | openssl sha1 -binary | &#xD;
  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; openssl base64&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;， 清单 11 中的代码通常在一行中。这里出于格式化目的，将它分成了多行。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;然后您必须在 图 4 所示 mobile and devices 部分下面（查看 图 4 的 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/sidefile-figure4.html"&gt;大图&lt;/a&gt;），为您的特定应用程序注册 Facebook 上生成的密钥散列。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="fig3new"&gt;&lt;b&gt;图 4. 输入应用程序密钥散列&lt;/b&gt;&lt;/a&gt; &#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654427555.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig03new" border="0" alt="fig03new" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654435702.jpg" width="584" height="106" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;一旦注册了，您就必须为您的应用程序定义一个 “活动结果处理程序”；实现 &lt;code&gt;onActivityResult(...)&lt;/code&gt;（您必须在它上面调用方法 &lt;code&gt;facebook.authorizeCallback(...)&lt;/code&gt;，如 清单 12 所示）。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list7b"&gt;&lt;b&gt;清单 12. 清单 12. 定义一个活动结果处理程序&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;@Override&#xD;
public void onActivityResult(int requestCode, int resultCode, Intent data) {&#xD;
  super.onActivityResult(requestCode, resultCode, data);&#xD;
  facebook.authorizeCallback(requestCode, resultCode, data);&#xD;
  :&#xD;
  :&#xD;
  // ... Other onActivityResult steps per your app...&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;注意，如果没有定义单点登录方法，或者没有给出原生 Facebook 应用程序，那么 SDK 将默认对身份认证/登录使用 WebView 方法。本文其余部分将着重介绍这种默认方法。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;正如前面所提到的，默认情况下，应用程序对用户简介中的所有公共数据具有访问权限。要访问私有数据，应用程序必须请求权限，并由用户对应用程序授予权限，才能进行访问。获得对私有数据进行访问的权限被称作扩展权限。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;Facebook SDK for Android &lt;code&gt;authorize()&lt;/code&gt; 方法实现 OAuth 2.0 User-Agent 流，以检索一个访问令牌，用于后续的 API 请求中。该方法开始一个活动（原生的 Facebook 应用程序，如果给出并配置了的话）或者一个 WebView 对话框，以提示用户输入凭证（身份认证）和权限（授权），参见 清单 13 ）。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list7orig"&gt;&lt;b&gt;清单 13.授权方法&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;/**&#xD;
 * Authorize method that grants custom permissions.&#xD;
 */&#xD;
public void authorize(Activity activity, String[] permissions,&#xD;
        final DialogListener listener) {&#xD;
    :&#xD;
    :&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;注意，为了单点登录身份认证正确工作，您必须在 &lt;code&gt;onActivityResult()&lt;/code&gt; 函数中包含一个对 &lt;code&gt;authorizeCallback()&lt;/code&gt; 方法的调用。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;回想一下 清单 8 中登录菜单项是如何利用权限调用 &lt;code&gt;authorize()&lt;/code&gt; 方法的。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;清单 14 展示了如何利用权限调用 &lt;code&gt;authorize()&lt;/code&gt; 方法，以写入内容。它也具有权限来写评论和喜好（&lt;code&gt;publish_stream&lt;/code&gt;）、阅读用户的提要、执行搜索（&lt;code&gt;read_stream&lt;/code&gt;）和让访问凭证长期有效（&lt;code&gt;offline_access&lt;/code&gt;）。不要忘记设置您的 &lt;code&gt;APP_ID&lt;/code&gt;。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list8"&gt;&lt;b&gt;清单 14. 利用权限调用 &lt;code&gt;authorize()&lt;/code&gt; 方法&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;package com.myapp.facebook.android;&#xD;
:&#xD;
import com.facebook.android.*;&#xD;
:&#xD;
&#xD;
public static final String APP_ID = &amp;quot;13234...&amp;quot;;&#xD;
&#xD;
private static final String[] PERMISSIONS = new String[] {&amp;quot;publish_stream&amp;quot;, &#xD;
        &amp;quot;read_stream&amp;quot;, &amp;quot;offline_access&amp;quot;};&#xD;
&#xD;
:&#xD;
:&#xD;
&#xD;
// Call the authorization method&#xD;
mFacebook.authorize(&#xD;
    getContext(), &#xD;
    APP_ID, &#xD;
    mPermissions,&#xD;
    new MyLoginDialogListener());&#xD;
&#xD;
:&#xD;
:&#xD;
&#xD;
// Asynchronous Callback when authorization completes&#xD;
private final class MyLoginDialogListener implements com.facebook.android&#xD;
          .Facebook.DialogListener {&#xD;
&#xD;
    public void onComplete(Bundle values) {...} // here enable logout&#xD;
    public void onFacebookError(FacebookError error) {...}&#xD;
    public void onError(DialogError error) {...}&#xD;
    public void onCancel() {...}&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;code&gt;authorize()&lt;/code&gt; 方法在内部调用前面介绍过的 &lt;code&gt;dialog()&lt;/code&gt; 方法，但是这一次它传递一个针对操作的 Login 请求。在 Facebook Platform 及其 OAuth 实现的帮助下，&lt;code&gt;authorize()&lt;/code&gt; 方法也处理到 Facebook 的身份认证请求，并返回整个 Facebook 会话和 API 调用期间使用的 access_token：&lt;code&gt;https://graph.facebook.com/ID?access_token=...&lt;/code&gt;。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;有关身份认证如何工作的详细信息，请参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-androidfacebookapi/#resources"&gt;参考资料&lt;/a&gt; 中的 Facebook Authentication 页面。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;调用 &lt;code&gt;authorize()&lt;/code&gt; 方法导致 图 5 中基于 WebView 的对话框。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="fig4"&gt;&lt;b&gt;图 5. 授权和权限屏幕 (WebView)&lt;/b&gt;&lt;/a&gt; &#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654468508.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig04" border="0" alt="fig04" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654494137.jpg" width="584" height="439" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;用户登录之后，会被提示按照应用程序的请求输入权限（email 或电话信息和密码）。用户可以允许或拒绝应用程序权限访问她的 Facebook 和执行如下操作：访问基本信息、发布到用户的涂鸦墙、访问 News Feed 中的帖子或者访问用户数据。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="makerequests"&gt;发出 Facebook API 请求&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;核心 Facebook 类为 Old REST 和 Graph API 调用实现很多 &lt;code&gt;request()&lt;/code&gt; 方法。此外，SDK 提供帮助器封装器类来异步地发出核心请求 API 调用。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;作为一个 API 请求的例子，您将发出一个请求，为经过身份认证的用户检索好友。回想一下 Graph API 的结构：&lt;/p&gt;&#xD;
&#xD;
&lt;pre&gt;https://graph.facebook.com/ID/CONNECTION_TYPE&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;另外，回想一下特殊的 ID &lt;code&gt;me&lt;/code&gt;，它用于识别经过身份认证的用户，此用户已经登录：&lt;code&gt;https://graph.facebook.com/me/friends&lt;/code&gt;。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;清单 15 展示了一个来自 清单 8 的代码片段，即如何实现 &lt;code&gt;request()&lt;/code&gt; 方法调用。在本例中，您使用 &lt;code&gt;AsyncFacebookRunner.request(...)&lt;/code&gt; 方法，这是一个帮助器封装器方法，它通过运行在自己的线程中，使得底层的请求方法是异步的。这一步是为了避免阻塞主 UI 线程。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list9"&gt;&lt;b&gt;清单 15.异步地分配请求&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;// Get Friend's List&#xD;
case R.id.getfriends: // Wall Post&#xD;
    // Get the authenticated user's friends&#xD;
    mSpinner.show();&#xD;
    mAsyncRunner.request(&amp;quot;me/friends&amp;quot;, new FriendsRequestListener());&#xD;
    break;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;code&gt;FriendsRequestListener onComplete(...)&lt;/code&gt; 回调在取得好友列表的请求执行完成时被调用。该方法解析返回的好友列表 JSON。注意， 清单 16 并没有展示所有实现的方法。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="list16"&gt;&lt;b&gt;清单 16. 处理取得好友请求&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;/**&#xD;
 * FriendsRequestListener implements a request lister/callback&#xD;
 *  for &amp;quot;get friends&amp;quot; requests&#xD;
 */&#xD;
public class FriendsRequestListener implements &#xD;
        com.facebook.android.AsyncFacebookRunner.RequestListener {&#xD;
&#xD;
    /**&#xD;
     * Called when the request to get friends has been completed.&#xD;
     * Retrieve and parse and display the JSON stream.&#xD;
     */&#xD;
    public void onComplete(final String response) {&#xD;
        mSpinner.dismiss();&#xD;
        try {&#xD;
            // process the response here: executed in background thread&#xD;
            Log.d(&amp;quot;Facebook-Example-Friends Request&amp;quot;, &amp;quot;response.length(): &amp;quot; + &#xD;
                                                                response.length());&#xD;
            Log.d(&amp;quot;Facebook-Example-Friends Request&amp;quot;, &amp;quot;Response: &amp;quot; + response);&#xD;
&#xD;
            final JSONObject json = new JSONObject(response);&#xD;
            JSONArray d = json.getJSONArray(&amp;quot;data&amp;quot;);&#xD;
            int l = (d != null ? d.length() : 0);&#xD;
            Log.d(&amp;quot;Facebook-Example-Friends Request&amp;quot;, &amp;quot;d.length(): &amp;quot; + l);&#xD;
&#xD;
            for (int i=0; i&amp;lt;l; i++) {&#xD;
                JSONObject o = d.getJSONObject(i);&#xD;
                String n = o.getString(&amp;quot;name&amp;quot;);&#xD;
                String id = o.getString(&amp;quot;id&amp;quot;);&#xD;
                Friend f = new Friend();&#xD;
                f.id = id;&#xD;
                f.name = n;&#xD;
                friends.add(f);&#xD;
            }&#xD;
&#xD;
            // Only the original owner thread can touch its views&#xD;
            SampleApp.this.runOnUiThread(new Runnable() {&#xD;
                public void run() {&#xD;
                    friendsArrayAdapter = new FriendsArrayAdapter(&#xD;
                            SampleApp.this, R.layout.rowlayout, friends);&#xD;
                    listView.setAdapter(friendsArrayAdapter);&#xD;
                    friendsArrayAdapter.notifyDataSetChanged();&#xD;
                }&#xD;
            });&#xD;
        } catch (JSONException e) {&#xD;
            Log.w(&amp;quot;Facebook-Example&amp;quot;, &amp;quot;JSON Error in response&amp;quot;);&#xD;
        }&#xD;
    }&#xD;
&#xD;
    :&#xD;
    :&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;根据您的应用程序的要求，来自 Facebook 服务器的响应（JSON 格式）必须相应地被解析和处理。您可能决定通过使用 WebView 或者跟本 SampleApp（参见 图 6 ）中一样使用 ListView 显示这样的信息。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="fig5"&gt;&lt;b&gt;图 6. 显示结果&lt;/b&gt;&lt;/a&gt; &lt;/p&gt;&#xD;
&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654515382.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig05" border="0" alt="fig05" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101141654529135.jpg" width="347" height="507" /&gt;&lt;/a&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N103FA"&gt;结束语&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;在本文中，您学习了 Facebook API。首先是 Facebook Platform 及其 API 的一般概述，然后是 Facebook SDK for Android 和一个样例应用程序。学习本文之后，您更好地了解了各种不同的 Facebook API 以及如何开始为 Android 平台编写 Facebook 应用程序。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1935767.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/14/1935767.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wangkewei/archive/2011/01/13/1934597.html</id><title type="text">在 Android 应用程序中使用 Internet 数据</title><summary type="text">原文：http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/index.html  Android 应用程序必须访问位于 Internet 上的数据，而 Internet 数据可以有几种不同的格式。本文将介绍在 Android 应用程序中如何使用三种数据格式： XML JSON Google 的 protocol buffers 首先开发一个...</summary><published>2011-01-13T07:21:00Z</published><updated>2011-01-13T07:21:00Z</updated><author><name>王克伟</name><uri>http://www.cnblogs.com/wangkewei/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wangkewei/archive/2011/01/13/1934597.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wangkewei/archive/2011/01/13/1934597.html"/><content type="html">&lt;p&gt;原文：&lt;a title="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/index.html" href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/index.html"&gt;http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/index.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Android 应用程序必须访问位于 Internet 上的数据，而 Internet 数据可以有几种不同的格式。本文将介绍在 Android 应用程序中如何使用三种数据格式：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;XML &lt;/li&gt;    &lt;li&gt;JSON &lt;/li&gt;    &lt;li&gt;Google 的 protocol buffers&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;首先开发一个 Web 服务，将 CSV 数据转换成 XML、JSON 和 protocol-buffers 格式。然后构建一个样例 Android 应用程序，可以从 Web 服务中以任何一种格式提取数据并将其解析并显示给用户。&lt;/p&gt;  &lt;p&gt;要进行本文中的练习，您需要最新的 Android SDK（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#resources"&gt;参考资料&lt;/a&gt;）和 Android 2.2 平台。SDK 还要求您安装一个 Java™ 开发包（JDK）；本文中使用了 JDK 1.6.0_17。您不需要有 Android 物理设备；所有代码都将在 SDK 的 Android 仿真器中运行。本文并没有教您如何进行 Android 开发，因此建议您熟悉 Android 编程。当然，只凭借 Java 编程语言的知识也可以完成本文的学习。&lt;/p&gt;  &lt;p&gt;您还需要一个 Java web 应用程序服务器来运行 Android 应用程序使用的 Web 服务。此外，也可以将服务器端代码部署到 Google App Engine。参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#download"&gt;下载&lt;/a&gt; 部分获得完整的源代码。&lt;/p&gt;  &lt;p&gt;&lt;a name="N100C6"&gt;Day Trader 应用程序&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;您将开发一个简单的 Android 应用程序，叫做 Day Trader。Day Trader 允许用户输入一个或更多的股票代码并获取其所代表股票的最新价格信息。用户可以指定数据使用的格式：XML、JSON 或 protocol buffers。实际的 Android 应用程序通常不会提供此选择，但是通过实现此功能，您可以了解如何让您的应用程序处理每一种格式。&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f1"&gt;图 1&lt;/a&gt; 展示了 Day Trader 用户界面：&lt;/p&gt;  &lt;p&gt;&lt;a name="f1"&gt;&lt;b&gt;图 1. 运行中的 Day Trader 应用程序&lt;/b&gt;&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101131520343956.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig01" border="0" alt="fig01" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101131521058654.jpg" width="478" height="804" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;文本框及其旁边的 &lt;strong&gt;Add Stock&lt;/strong&gt; 按钮允许用户输入感兴趣的每支股票的代码。用户按下 &lt;strong&gt;Download Stock Data&lt;/strong&gt; 按钮后，会从服务器请求所有这些股票的数据，在应用程序中解析并显示在屏幕上。默认情况下，获取的是 XML 数据。通过菜单，您可以在 XML、JSON 或 protocol buffers 数据格式间切换。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l1"&gt;清单 1&lt;/a&gt; 显示用于创建 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f1"&gt;图 1&lt;/a&gt; 中所示 UI 的布局 XML：&lt;/p&gt;  &lt;p&gt;&lt;a name="l1"&gt;&lt;b&gt;清单 1. Day Trader 布局 XML&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;  &lt;pre &gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&#xD;
&amp;lt;LinearLayout xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&#xD;
    android:orientation=&amp;quot;vertical&amp;quot;&#xD;
    android:layout_width=&amp;quot;fill_parent&amp;quot;&#xD;
    android:layout_height=&amp;quot;fill_parent&amp;quot;&#xD;
    &amp;gt;&#xD;
    &amp;lt;LinearLayout android:orientation=&amp;quot;horizontal&amp;quot; &#xD;
        android:layout_width=&amp;quot;fill_parent&amp;quot; &#xD;
        android:layout_height=&amp;quot;wrap_content&amp;quot;&amp;gt;&#xD;
        &amp;lt;EditText android:id=&amp;quot;@+id/symbol&amp;quot; android:layout_width=&amp;quot;wrap_content&amp;quot;&#xD;
            android:layout_height=&amp;quot;wrap_content&amp;quot; android:width=&amp;quot;120dip&amp;quot;/&amp;gt;&#xD;
        &amp;lt;Button android:id=&amp;quot;@+id/addBtn&amp;quot; android:layout_width=&amp;quot;wrap_content&amp;quot;&#xD;
            android:layout_height=&amp;quot;wrap_content&amp;quot; &#xD;
            android:text=&amp;quot;@string/addBtnLbl&amp;quot;/&amp;gt;&#xD;
    &amp;lt;/LinearLayout&amp;gt;&#xD;
    &amp;lt;LinearLayout android:orientation=&amp;quot;horizontal&amp;quot; &#xD;
        android:layout_width=&amp;quot;fill_parent&amp;quot; &#xD;
        android:layout_height=&amp;quot;wrap_content&amp;quot;&amp;gt;&#xD;
        &amp;lt;TextView android:layout_width=&amp;quot;wrap_content&amp;quot; &#xD;
            android:layout_height=&amp;quot;wrap_content&amp;quot; android:id=&amp;quot;@+id/symList&amp;quot; /&amp;gt;&#xD;
        &amp;lt;Button android:id=&amp;quot;@+id/dlBtn&amp;quot; android:layout_width=&amp;quot;wrap_content&amp;quot; &#xD;
            android:layout_height=&amp;quot;wrap_content&amp;quot; &#xD;
            android:text=&amp;quot;@string/dlBtnLbl&amp;quot;&#xD;
        /&amp;gt;&#xD;
       &amp;lt;/LinearLayout&amp;gt;&#xD;
    &amp;lt;ListView android:id=&amp;quot;@android:id/list&amp;quot; &#xD;
        android:layout_height=&amp;quot;fill_parent&amp;quot; android:layout_width=&amp;quot;fill_parent&amp;quot;&#xD;
        android:layout_weight=&amp;quot;1&amp;quot;&#xD;
        /&amp;gt;&#xD;
&amp;lt;/LinearLayout&amp;gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l1"&gt;清单 1&lt;/a&gt; 中的大部分代码都简单明了。可以看到几个小部件创建了 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f1"&gt;图 1&lt;/a&gt; 所示的输入和按钮。还会看到一个 &lt;code&gt;ListView&lt;/code&gt;，Android 小部件中真正的瑞士军刀。此 &lt;code&gt;ListView&lt;/code&gt; 将用从服务器下载的股票数据填充。&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l2"&gt;清单 2&lt;/a&gt; 显示了控制该视图的 &lt;code&gt;Activity&lt;/code&gt;：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l2"&gt;&lt;b&gt;清单 2. Day Trader 主活动&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;public class Main extends ListActivity {&#xD;
&#xD;
    private int mode = XML; // default&#xD;
    @Override&#xD;
    public void onCreate(Bundle savedInstanceState) {&#xD;
        super.onCreate(savedInstanceState);&#xD;
        setContentView(R.layout.main);&#xD;
        final EditText input = (EditText) findViewById(R.id.symbol);&#xD;
        final TextView symbolsList = (TextView) findViewById(R.id.symList);&#xD;
        final Button addButton = (Button) findViewById(R.id.addBtn);&#xD;
        final Button dlButton = (Button) findViewById(R.id.dlBtn);&#xD;
        addButton.setOnClickListener(new OnClickListener(){&#xD;
            public void onClick(View v) {&#xD;
                String newSymbol = input.getText().toString();&#xD;
                if (symbolsList.getText() == null || &#xD;
                        symbolsList.getText().length() == 0){&#xD;
                    symbolsList.setText(newSymbol);&#xD;
                } else {&#xD;
                    StringBuilder sb = &#xD;
                                  new StringBuilder(symbolsList.getText());&#xD;
                    sb.append(&amp;quot;,&amp;quot;);&#xD;
                    sb.append(newSymbol);&#xD;
                    symbolsList.setText(sb.toString());&#xD;
                }&#xD;
                input.setText(&amp;quot;&amp;quot;);&#xD;
            }&#xD;
        });&#xD;
        dlButton.setOnClickListener(new OnClickListener(){&#xD;
            public void onClick(View v) {&#xD;
                String symList = symbolsList.getText().toString();&#xD;
                String[] symbols = symList.split(&amp;quot;,&amp;quot;);&#xD;
                symbolsList.setText(&amp;quot;&amp;quot;);&#xD;
                switch (mode){&#xD;
                case JSON :&#xD;
                    new StockJsonParser().execute(symbols);&#xD;
                    break;&#xD;
                case PROTOBUF :&#xD;
                    new StockProtoBufParser().execute(symbols);&#xD;
                    break;&#xD;
                default :&#xD;
                    new StockXmlParser().execute(symbols);&#xD;
                    break;&#xD;
                }&#xD;
            }&#xD;
        });&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;此 &lt;code&gt;Activity&lt;/code&gt; 设置了 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l1"&gt;清单 1&lt;/a&gt; 中 XML 文件的布局，它将几个事件处理程序连接起来。首先，对于 &lt;strong&gt;Add Stock&lt;/strong&gt; 按钮而言，代码读取文本框中的代码并将其添加到 &lt;code&gt;symList TextView&lt;/code&gt; 中，用逗号分隔每个代码。接下来，对于 &lt;strong&gt;Download&lt;/strong&gt; 按钮而言，处理程序从 &lt;code&gt;symList TextView&lt;/code&gt; 中读取数据，然后 —基于 &lt;code&gt;mode&lt;/code&gt; 变量— 使用三个不同的类之一从服务器下载数据。菜单设置 &lt;code&gt;mode&lt;/code&gt; 变量的值；这个代码不是很重要，因此我在 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l2"&gt;清单 2&lt;/a&gt; 中省略了它。在了解各种数据下载/解析类之前，我先为您展示一下服务器如何提供此数据。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N1014B"&gt;提供股票数据&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;应用程序服务器需要能够做两件事。第一，它必须获取股票代码列表并检索它们的数据。然后，它需要接受一个格式参数并基于该格式编码数据。对于 XML 和 JSON 格式而言，该服务器将返回作为文本的串行化的股票数据。对于 protocol buffers 而言，它必须发送二进制数据。 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l3"&gt;清单 3&lt;/a&gt; 显示了处理这些步骤的 servlet：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l3"&gt;&lt;b&gt;清单 3. Stock Broker servlet&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;public class StockBrokerServlet extends HttpServlet {&#xD;
    public void doGet(HttpServletRequest request, &#xD;
          HttpServletResponse response) throws IOException {&#xD;
        String[] symbols = request.getParameterValues(&amp;quot;stock&amp;quot;);&#xD;
        List&amp;lt;Stock&amp;gt; stocks = getStocks(symbols);&#xD;
        String format = request.getParameter(&amp;quot;format&amp;quot;);&#xD;
        String data = &amp;quot;&amp;quot;;&#xD;
        if (format == null || format.equalsIgnoreCase(&amp;quot;xml&amp;quot;)){&#xD;
            data = Stock.toXml(stocks);&#xD;
            response.setContentType(&amp;quot;text/xml&amp;quot;);    &#xD;
        } else if (format.equalsIgnoreCase(&amp;quot;json&amp;quot;)){&#xD;
            data = Stock.toJson(stocks);&#xD;
            response.setContentType(&amp;quot;application/json&amp;quot;);&#xD;
        } else if (format.equalsIgnoreCase(&amp;quot;protobuf&amp;quot;)){&#xD;
            Portfolio p = Stock.toProtoBuf(stocks);&#xD;
            response.setContentType(&amp;quot;application/octet-stream&amp;quot;);&#xD;
            response.setContentLength(p.getSerializedSize());&#xD;
            p.writeTo(response.getOutputStream());&#xD;
            response.flushBuffer();&#xD;
            return;&#xD;
        }&#xD;
        response.setContentLength(data.length());&#xD;
        response.getWriter().print(data);&#xD;
        response.flushBuffer();&#xD;
        response.getWriter().close();&#xD;
    }&#xD;
&#xD;
    public List&amp;lt;Stock&amp;gt; getStocks(String... symbols) throws IOException{&#xD;
        StringBuilder sb = new StringBuilder();&#xD;
        for (String symbol : symbols){&#xD;
            sb.append(symbol);&#xD;
            sb.append('+');&#xD;
        }&#xD;
        sb.deleteCharAt(sb.length() - 1);&#xD;
        String urlStr = &#xD;
                &amp;quot;http://finance.yahoo.com/d/quotes.csv?f=sb2n&amp;amp;s=&amp;quot; + &#xD;
                    sb.toString();&#xD;
        URL url = new URL(urlStr);&#xD;
        HttpURLConnection conn = &#xD;
                (HttpURLConnection) url.openConnection();&#xD;
        BufferedReader reader = new BufferedReader(&#xD;
                new InputStreamReader(conn.getInputStream()));&#xD;
        String quote = reader.readLine();&#xD;
        List&amp;lt;Stock&amp;gt; stocks = new ArrayList&amp;lt;Stock&amp;gt;(symbols.length);&#xD;
        while (quote != null){&#xD;
            String[] values = quote.split(&amp;quot;,&amp;quot;);&#xD;
            Stock s = &#xD;
                      new Stock(values[0], values[2],&#xD;
                           Double.parseDouble(values[1]));&#xD;
            stocks.add(s);&#xD;
            quote = reader.readLine();&#xD;
        }&#xD;
        return stocks;&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;这是一个简单的 Java servlet，只支持 HTTP &lt;code&gt;GET&lt;/code&gt; 请求。它读入股票的值和格式请求参数。然后调用 &lt;code&gt;getStocks()&lt;/code&gt; 方法。该方法调用 Yahoo! Finance 获取股票数据。Yahoo! 只支持 CSV 格式的数据，因此 &lt;code&gt;getStocks()&lt;/code&gt; 方法将其解析到一个 &lt;code&gt;Stock&lt;/code&gt; 对象列表。&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l4"&gt;清单 4&lt;/a&gt; 展示了这个简单的数据结构：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l4"&gt;&lt;b&gt;清单 4. 股票数据结构&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;public class Stock {    &#xD;
    private final String symbol;&#xD;
    private final String name;&#xD;
    private final double price;&#xD;
      //getters and setters omitted&#xD;
&#xD;
    public String toXml(){&#xD;
        return &amp;quot;&amp;lt;stock&amp;gt;&amp;lt;symbol&amp;gt;&amp;quot; + symbol + &#xD;
&amp;quot;&amp;lt;/symbol&amp;gt;&amp;lt;name&amp;gt;&amp;lt;![CDATA[&amp;quot; +&#xD;
            name + &amp;quot;]]&amp;gt;&amp;lt;/name&amp;gt;&amp;lt;price&amp;gt;&amp;quot; + price + &#xD;
&amp;quot;&amp;lt;/price&amp;gt;&amp;lt;/stock&amp;gt;&amp;quot;;&#xD;
    }&#xD;
&#xD;
    public String toJson(){&#xD;
        return &amp;quot;{ 'stock' : { 'symbol' : &amp;quot; +symbol +&amp;quot;, 'name':&amp;quot; + name + &#xD;
            &amp;quot;, 'price': '&amp;quot; + price + &amp;quot;'}}&amp;quot;;&#xD;
    }&#xD;
&#xD;
    public static String toXml(List&amp;lt;Stock&amp;gt; stocks){&#xD;
        StringBuilder xml = new StringBuilder(&amp;quot;&amp;lt;stocks&amp;gt;&amp;quot;);&#xD;
        for (Stock s : stocks){&#xD;
            xml.append(s.toXml());&#xD;
        }&#xD;
        xml.append(&amp;quot;&amp;lt;/stocks&amp;gt;&amp;quot;);&#xD;
        return xml.toString();&#xD;
    }&#xD;
&#xD;
    public static String toJson(List&amp;lt;Stock&amp;gt; stocks){&#xD;
        StringBuilder json = new StringBuilder(&amp;quot;{'stocks' : [&amp;quot;);&#xD;
        for (Stock s : stocks){&#xD;
            json.append(s.toJson());&#xD;
            json.append(',');&#xD;
        }&#xD;
        json.deleteCharAt(json.length() - 1);&#xD;
        json.append(&amp;quot;]}&amp;quot;);&#xD;
        return json.toString();&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;每个 &lt;code&gt;Stock&lt;/code&gt; 都有三个属性— &lt;code&gt;symbol&lt;/code&gt;、&lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;price &lt;/code&gt;— 和几个便捷的方法，以便将其自己转换成 XML 字符串或 JSON 字符串。它提供了一个工具方法，用于将 &lt;code&gt;Stock&lt;/code&gt; 对象列表转换成 XML 或 JSON。回到 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l3"&gt;清单 3&lt;/a&gt;，根据格式请求参数，&lt;code&gt;Stock&lt;/code&gt; 对象列表被转换成 XML 或 JSON 字符串并被发送回客户端。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;XML 和 JSON 用例非常类似和直接。对于 protocol buffers，您必须生成 protocol buffers 格式的代码读写对象。为此，您需要使用 protocol buffers 规范格式定义数据结构。&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l5"&gt;清单 5&lt;/a&gt; 展示了一个示例：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l5"&gt;&lt;b&gt;清单 5. 股票的 Protocol buffers 消息&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;package stocks;&#xD;
&#xD;
option java_package = &amp;quot;org.developerworks.stocks&amp;quot;;&#xD;
&#xD;
message Quote{&#xD;
    required string symbol = 1;&#xD;
    required string name = 2;&#xD;
    required double price = 3;&#xD;
}&#xD;
&#xD;
message Portfolio{&#xD;
    repeated Quote quote = 1;&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;protocol buffers 消息格式类似于接口描述语言 (IDL)，它与语言无关，因此可以将其与各种语言一起使用。在本例中，运行 protocol buffers 编译器（&lt;code&gt;protoc&lt;/code&gt;）将 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l5"&gt;清单 5&lt;/a&gt; 中的代码编译成要用于客户端和服务器的 Java 类。有关将 protocol buffers 消息编译成 Java 类的详细信息，请参阅 Protocol Buffers Developer Guide（参见 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#resources"&gt;参考资料&lt;/a&gt;）。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;在 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l3"&gt;清单 3&lt;/a&gt; 中，一个名为 &lt;code&gt;toProtoBuf()&lt;/code&gt; 的方法将 &lt;code&gt;Stock&lt;/code&gt; 对象列表转换成一个 &lt;code&gt;Portfolio&lt;/code&gt; 消息。&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l6"&gt;清单 6&lt;/a&gt; 展示了该方法的实现：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l6"&gt;&lt;b&gt;清单 6. 创建组合消息&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;public static Stocks.Portfolio toProtoBuf(List&amp;lt;Stock&amp;gt; stocks){&#xD;
    List&amp;lt;Stocks.Quote&amp;gt; quotes = new ArrayList&amp;lt;Stocks.Quote&amp;gt;(stocks.size());&#xD;
    for (Stock s : stocks){&#xD;
        Quote q = &#xD;
            Quote.newBuilder()&#xD;
                .setName(s.name)&#xD;
                .setSymbol(s.symbol)&#xD;
                .setPrice(s.price)&#xD;
                .build();&#xD;
        quotes.add(q);&#xD;
    }&#xD;
    return Portfolio.newBuilder().addAllQuote(quotes).build();&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l6"&gt;清单 6&lt;/a&gt; 中的代码使用了从 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l5"&gt;清单 5&lt;/a&gt; 中的消息生成的代码 — &lt;code&gt;Quote&lt;/code&gt; 和 &lt;code&gt;Portfolio&lt;/code&gt; 类。只需构建来自每个 &lt;code&gt;Stock&lt;/code&gt; 对象的 &lt;code&gt;Quote&lt;/code&gt;，然后将其添加到 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l3"&gt;清单 3&lt;/a&gt; 中返回到 servlet 的 &lt;code&gt;Portfolio&lt;/code&gt; 对象即可。在 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l3"&gt;清单 3&lt;/a&gt; 中，servlet 直接打开到客户端的流并使用生成的代码编写到流的二进制协议 buffers 数据。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;现在，您了解了服务器如何创建要发送到 Android 应用程序的数据。接下来将学习应用程序如何解析此数据。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N10211"&gt;使用数据格式&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l2"&gt;清单 2&lt;/a&gt; 中的主 &lt;code&gt;Activity&lt;/code&gt; 需要使用服务器可以发送的各种格式的数据。它还需要请求适当格式的数据并且数据一旦解析，就用它来填充其 &lt;code&gt;ListView&lt;/code&gt;。因此，无论数据格式是什么，大部分功能都是通用的。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;首先，创建一个抽象的基类，封装此通用功能，如 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l7"&gt;清单 7&lt;/a&gt; 所示：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l7"&gt;&lt;b&gt;清单 7. 数据解析器基类&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;abstract class BaseStockParser extends AsyncTask&amp;lt;String, Integer, Stock[]&amp;gt;{&#xD;
    String urlStr = &amp;quot;http://protostocks.appspot.com/stockbroker?format=&amp;quot;;&#xD;
&#xD;
    protected BaseStockParser(String format){&#xD;
        urlStr += format;&#xD;
    }&#xD;
&#xD;
    private String makeUrlString(String... symbols) {&#xD;
        StringBuilder sb = new StringBuilder(urlStr);&#xD;
        for (int i=0;i&amp;lt;symbols.length;i++){&#xD;
            sb.append(&amp;quot;&amp;amp;stock=&amp;quot;);&#xD;
            sb.append(symbols[i]);&#xD;
        }&#xD;
        return sb.toString();&#xD;
    }&#xD;
&#xD;
    protected InputStream getData(String[] symbols) throws Exception{&#xD;
        HttpClient client = new DefaultHttpClient();&#xD;
        HttpGet request = new HttpGet(new URI(makeUrlString(symbols)));&#xD;
&#xD;
        HttpResponse response = client.execute(request);&#xD;
        return response.getEntity().getContent();&#xD;
    }&#xD;
&#xD;
    @Override&#xD;
    protected void onPostExecute(Stock[] stocks){&#xD;
        ArrayAdapter&amp;lt;Stock&amp;gt; adapter = &#xD;
            new ArrayAdapter&amp;lt;Stock&amp;gt;(Main.this, R.layout.stock, &#xD;
                      stocks );&#xD;
        setListAdapter(adapter);&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l7"&gt;清单 7&lt;/a&gt; 中的基类扩展了 &lt;code&gt;android.os.AsyncTask&lt;/code&gt;。这是一个常用的用于异步操作的类。它抽象出线程和处理程序的创建，用于请求主 UI 线程。它是基于其输入和输出数据类型参数化的。对于所有解析器而言，输入总是一样的：股票代码字符串。 输出也是一样的：&lt;code&gt;Stock&lt;/code&gt; 对象数组。基类获取 &lt;code&gt;format&lt;/code&gt;，这是一个指定了要使用的数据格式的字符串。然后提供一个方法，发出适当的 HTTP 请求并返回一个流响应。最后，它覆盖 &lt;code&gt;AsyncTask&lt;/code&gt; 的 &lt;code&gt;onPostExecute()&lt;/code&gt; 方法并使用从解析器返回的数据为 &lt;code&gt;Activity&lt;/code&gt; 的 &lt;code&gt;ListView&lt;/code&gt; 创建一个 &lt;code&gt;Adapter&lt;/code&gt;。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;现在看到三个解析器的功能是通用的。我将为您展示更具体的解析代码，从 XML 解析器开始。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N1025F"&gt;用 SAX 解析 XML&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;Android SDK 提供了几种使用 XML 的方式，包括标准 DOM 和 SAX。 对于一些对内存密集型情况，可以使用 SDK 的 pull-parser。大部分时候，SAX 是最快的方式。Android 包括一些便捷的 API 使得使用 SAX 更轻松。&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l8"&gt;清单 8&lt;/a&gt; 显示了 Day Trader 应用程序的 XML 解析器：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l8"&gt;&lt;b&gt;清单 8. XML 解析器实现&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;private class StockXmlParser extends BaseStockParser{&#xD;
    public StockXmlParser(){&#xD;
        super(&amp;quot;xml&amp;quot;);&#xD;
    }&#xD;
&#xD;
    @Override&#xD;
    protected Stock[] doInBackground(String... symbols) {&#xD;
        ArrayList&amp;lt;Stock&amp;gt; stocks = new ArrayList&amp;lt;Stock&amp;gt;(symbols.length);&#xD;
        try{&#xD;
            ContentHandler handler = newHandler(stocks);&#xD;
            Xml.parse(getData(symbols), Xml.Encoding.UTF_8, handler);&#xD;
        } catch (Exception e){&#xD;
            Log.e(&amp;quot;DayTrader&amp;quot;, &amp;quot;Exception getting XML data&amp;quot;, e);&#xD;
        }&#xD;
        Stock[] array = new Stock[symbols.length];&#xD;
        return stocks.toArray(array);&#xD;
    }&#xD;
&#xD;
    private ContentHandler newHandler(final ArrayList&amp;lt;Stock&amp;gt; stocks){&#xD;
        RootElement root = new RootElement(&amp;quot;stocks&amp;quot;);&#xD;
        Element stock = root.getChild(&amp;quot;stock&amp;quot;);&#xD;
        final Stock currentStock = new Stock();&#xD;
        stock.setEndElementListener(&#xD;
            new EndElementListener(){&#xD;
                public void end() {&#xD;
                    stocks.add((Stock) currentStock.clone());&#xD;
                }&#xD;
            }&#xD;
        );&#xD;
        stock.getChild(&amp;quot;name&amp;quot;).setEndTextElementListener(&#xD;
            new EndTextElementListener(){&#xD;
                public void end(String body) {&#xD;
                    currentStock.setName(body);&#xD;
                }&#xD;
            }&#xD;
        );&#xD;
        stock.getChild(&amp;quot;symbol&amp;quot;).setEndTextElementListener(&#xD;
            new EndTextElementListener(){&#xD;
                public void end(String body) {&#xD;
                    currentStock.setSymbol(body);&#xD;
                }&#xD;
            }&#xD;
        );&#xD;
        stock.getChild(&amp;quot;price&amp;quot;).setEndTextElementListener(&#xD;
            new EndTextElementListener(){&#xD;
                public void end(String body) {&#xD;
                            currentStock.setPrice(Double.parseDouble(body));&#xD;
                }&#xD;
            }&#xD;
        );&#xD;
        return root.getContentHandler();&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l8"&gt;清单 8&lt;/a&gt; 中的大部分代码都在 &lt;code&gt;newHandler()&lt;/code&gt; 方法中，该方法创建一个 &lt;code&gt;ContentHandler&lt;/code&gt;。如果熟悉 SAX 解析， 会知道 &lt;code&gt;ContentHandler&lt;/code&gt; 通过响应 SAX 解析器触发的各种事件创建解析数据。&lt;code&gt;newHandler()&lt;/code&gt; 方法使用 Android 便捷 API 指定使用事件处理程序的 &lt;code&gt;ContentHandler&lt;/code&gt;。代码只是侦听在解析器遇到各种标记时触发的事件，然后选取数据，放到 &lt;code&gt;Stock&lt;/code&gt; 对象列表中。 创建 &lt;code&gt;ContentHandler&lt;/code&gt; 后，调用 &lt;code&gt;Xml.parse()&lt;/code&gt; 方法来解析基类提供的 &lt;code&gt;InputStream&lt;/code&gt; 并返回 &lt;code&gt;Stock&lt;/code&gt; 对象数组。这是快速解析 XML 的方法，但是 —即使使用 Android 提供的便捷 API— 它也是非常冗长的。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N102A8"&gt;使用 JSON&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;XML 是 Android 上的一等公民，鉴于依赖于 XML 的 Web 服务的数量，这是个好事。很多服务还支持另一个流行格式 JSON。它通常比 XML 简洁一些，但也是人们可读的，使得它更易于使用，并且可以更轻松地将其用于调试使用它的应用程序。Android 包括一个 JSON 解析器。（您可以从 JSON.org 网站获得该解析器，只是要去除几个手机不需要的类）。 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l9"&gt;清单 9&lt;/a&gt; 显示了使用中的解析器：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l9"&gt;&lt;b&gt;清单 9. JSON 解析器实现&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;private class StockJsonParser extends BaseStockParser{&#xD;
    public StockJsonParser(){&#xD;
        super(&amp;quot;json&amp;quot;);&#xD;
    }&#xD;
    @Override&#xD;
    protected Stock[] doInBackground(String... symbols) {&#xD;
        Stock[] stocks = new Stock[symbols.length];&#xD;
        try{&#xD;
            StringBuilder json = new StringBuilder();&#xD;
            BufferedReader reader = &#xD;
                new BufferedReader(&#xD;
                            new InputStreamReader(getData(symbols)));&#xD;
            String line = reader.readLine();&#xD;
            while (line != null){&#xD;
                json.append(line);&#xD;
                line = reader.readLine();&#xD;
            }&#xD;
            JSONObject jsonObj = new JSONObject(json.toString());&#xD;
            JSONArray stockArray = jsonObj.getJSONArray(&amp;quot;stocks&amp;quot;);&#xD;
            for (int i=0;i&amp;lt;stocks.length;i++){&#xD;
                JSONObject object = &#xD;
                    stockArray.getJSONObject(i).getJSONObject(&amp;quot;stock&amp;quot;);&#xD;
                stocks[i] = new Stock(object.getString(&amp;quot;symbol&amp;quot;), &#xD;
                        object.getString(&amp;quot;name&amp;quot;), &#xD;
                        object.getDouble(&amp;quot;price&amp;quot;));&#xD;
            }&#xD;
        } catch (Exception e){&#xD;
            Log.e(&amp;quot;DayTrader&amp;quot;, &amp;quot;Exception getting JSON data&amp;quot;, e);&#xD;
        }&#xD;
        return stocks;&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;可以看到在 Android 中使用 JSON 解析器是多么简单。您将来自服务器的流转换成传递给 JSON 解析器的字符串。您遍历对象图并创建 &lt;code&gt;Stock&lt;/code&gt; 对象数组。如果使用过 XML DOM 解析，这看起来很类似，因为编程模型几乎一样。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;像 DOM 一样，JSON 解析器可以用于内存密集型应用。在 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l9"&gt;清单 9&lt;/a&gt; 中，所有来自服务器的数据都表示为字符串，然后作为 &lt;code&gt;JSONObject&lt;/code&gt;，最后作为 &lt;code&gt;Stock&lt;/code&gt; 对象数组。换句话说，同一数据通过三种不同的方式表示。可以看到，对于大量数据而言，这可能是个问题。当然，一旦到达方法末尾，这三种数据表示方式中的两种都会落在范围之外，被垃圾回收器回收。但是，只是触发更频繁的垃圾回收可能会对用户体验带来负面影响，造成处理速度下降。如果内存效率和性能很重要，使用 protocol buffers 的解析器可能是个较好的选择。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N102D5"&gt;使用 protocol buffers 处理二进制&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;em&gt;Protocol buffers&lt;/em&gt; 是一个由 Google 开发的与语言无关的数据串行化格式，旨在比 XML 更快地通过网络传送数据。它是 Google 用于服务器对服务器调用的&lt;em&gt;事实&lt;/em&gt; 标准。Google 将该格式及其用于 C++、Java 和 Python 编程语言的绑定工具以开源方式提供。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;在 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l3"&gt;清单 3&lt;/a&gt; 和 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l6"&gt;清单 6&lt;/a&gt; 中看到 protocol buffers 是二进制格式。如您所料，这使得数据很简洁。如果在客户端和服务器端启用 gzip 压缩，在使用 XML 和 JSON 时通常也可以得到类似的消息大小，但是 protocol buffers 仍然有一些大小上的优势。它还是一种可以迅速解析的格式。最后，它提供了一个相当简单的 API。 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l10"&gt;清单 10&lt;/a&gt; 显示了一个示例解析器实现：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="l10"&gt;&lt;b&gt;清单 10. Protocol buffers 解析器实现&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre &gt;private class StockProtoBufParser extends BaseStockParser{&#xD;
    public StockProtoBufParser(){&#xD;
        super(&amp;quot;protobuf&amp;quot;);&#xD;
    }&#xD;
&#xD;
    @Override&#xD;
    protected Stock[] doInBackground(String... symbols) {&#xD;
        Stock[] stocks = new Stock[symbols.length];&#xD;
        try{&#xD;
            Stocks.Portfolio portfolio = &#xD;
                Stocks.Portfolio.parseFrom(getData(symbols));&#xD;
            for (int i=0;i&amp;lt;symbols.length;i++){&#xD;
                stocks[i] = Stock.fromQuote(portfolio.getQuote(i));&#xD;
            }&#xD;
        } catch (Exception e){&#xD;
            Log.e(&amp;quot;DayTrader&amp;quot;, &amp;quot;Exception getting ProtocolBuffer data&amp;quot;, e);&#xD;
        }&#xD;
        return stocks;&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;如 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#l3"&gt;清单 3&lt;/a&gt; 所示，您可以使用 protocol buffers 编译器生成的 helper 类。这与服务器使用的 helper 类相同。可以编译它一次，然后在服务器和客户端共享它。 这样，您可以更轻松地直接从服务器的流读取数据并将其转换成 &lt;code&gt;Stock&lt;/code&gt; 对象数组。这种简单编程也具有非常出色的性能。现在看一下此性能与 XML 和 JSON 的比较。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N10307"&gt;性能比较&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;比较性能通常涉及某种微基准测试，此类基准测试很容易产生偏见或无意间得到不正确的结果。即使以公平方式设计微基准测试，很多随机因素也会对结果产生影响。尽管有这些问题，我还是要使用这样的微基准测试来比较 XML（大约 1300 ms）、JSON（大约 1150 ms）和 protocol buffers（大约 750 ms）。基准测试向服务器发送了一个关于 200 个股票的请求并测量了从发出请求到用于创建 &lt;code&gt;ListView&lt;/code&gt; 的 &lt;code&gt;Adapter&lt;/code&gt; 的数据准备就绪所需的时间量。对每个数据格式在两个设备上进行 50 次这样的操作：一个 Motorola Droid 和一个 HTC Evo，两个都通过 3G 网络。 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f2"&gt;图 2&lt;/a&gt; 显示了结果：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="f2"&gt;&lt;b&gt;图 2. 比较数据格式速度&lt;/b&gt;&lt;/a&gt;&#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101131521078362.gif"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig02" border="0" alt="fig02" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101131521088461.gif" width="572" height="350" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f2"&gt;图 2&lt;/a&gt; 显示出，在此基准测试中 protocol buffers（大约 750 ms）比 XML （大约 1300 ms）几乎快两倍。很多因素影响着数据通过网络和被手持设备处理的性能。一个明显的因素是通过网络的数据量。二进制格式的 protocol buffers 比文本格式的 XML 和 JSON 在通过网络时小得多。然而，文本格式可以使用 gzip 进行有效地压缩，这是 Web 服务器和 Android 设备都支持的标准技术。 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f3"&gt;图 3&lt;/a&gt; 显示了在打开和关闭 gzip 时通过网络的数据大小：&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="f3"&gt;&lt;b&gt;图 3. 不同格式的数据大小&lt;/b&gt;&lt;/a&gt;&#xD;
&#xD;
  &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/2011011315211336.gif"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fig03" border="0" alt="fig03" src="http://images.cnblogs.com/cnblogs_com/wangkewei/201101/201101131521143548.gif" width="576" height="386" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f3"&gt;图 3&lt;/a&gt; 应该增加了您对 XML 和 JSON 之类的文本内容的压缩效果的喜爱（更不用说 Web 格式、HTML、JavaScript 和 CSS 了）。protocol buffers 数据（大约 6KB）比原始 XML（大约 17.5KB）或 JSON（大约 13.5KB）数据小得多。但是一旦进行了压缩， JSON 和 XML（都是大约 3KB）实际上比 protocol buffers 小很多了。在本例中，它们都接近于 protocol-buffers 编码消息大小的一半了。&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;回到 &lt;a href="http://www.ibm.com/developerworks/cn/xml/x-dataAndroid/#f2"&gt;图 2&lt;/a&gt;，速度的不同显然不能由通过网络的消息大小解释。protocol-buffers 消息比 XML 或 JSON 编码的消息大，但是通过使用 protocol buffers，您仍然能够削减半秒钟的用户等待时间。这是否意味着应该在 Android 应用程序中使用 protocol buffers 呢？这样的决定很少是固定的。如果要发送的数据量很小，则三种格式间的差异也不大。对于大量数据而言，protocol buffers 可能会有所不同。但是，像这样精心设计的基准测试无法替代对您自己的应用程序的测试。&lt;/p&gt;&#xD;
&#xD;
&lt;hr /&gt;&#xD;
&#xD;
&lt;p&gt;&lt;a name="N1034F"&gt;结束语&lt;/a&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;本文介绍了如何使用 Internet 上流行的两种数据格式 XML 和 JSON 的方方面面。还讲到了第三种可能性，protocol buffers。像软件工程中的其他内容一样，选择技术主要就是权衡利弊。当您为一个局限的环境（比如 Android）开发时，这些决定的结果往往被放大了。我希望您现在拥有的关于这些后果的额外知识能够帮助您创建出色的 Android 应用程序。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wangkewei/aggbug/1934597.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wangkewei/archive/2011/01/13/1934597.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
