Android中包含多个Activity的应用退出按钮实现

Android中application 全局变量

android
编程中,application这样的名词似乎变得那样的不常见,而让大家更为熟悉的是activity、intent、
provider、broadcast和service。但其实android中的application也有着它自身的用处。打开manifest文
件,会看到有一个application配置标签,这就是有关application的使用了。

就是说application是用来保存全局变量的,并且是在package创建的时候就跟着存在了。所以当我们需要创建全局变量的时候,不需要再像j2se那样需要创建public权限的static变量,而直接在application中去实现。只需要调用Context的getApplicationContext
或者Activity的getApplication
方法来获得一个application对象,再做出相应的处理。

退出按钮实现

首先,创建Application来存储所有打开的Activity的list。代码如下:

package com.deaboway.view;  
import java.util.ArrayList;  
import java.util.List;  
import android.app.Activity;  
import android.app.Application;  
public class Deaboway extends Application  

    private List<Activity> mainActivity = new ArrayList<Activity>();  

    public List<Activity> MainActivity()  

        return mainActivity;  

     

    public void addActivity(Activity act)  

        mainActivity.add(act);  

     

    public void finishAll()  

        for (Activity act mainActivity)  

            if (!act.isFinishing())  

                act.finish();  

             

         

        mainActivity = null 

     

}

其次,在所有Activity的  
@Override   
public void onCreate(Bundle savedInstanceState) {}方法中加上:

      public void onCreate(Bundle savedInstanceState)  

          super.onCreate(savedInstanceState);  

    Deaboway appState = (Deaboway)this.getApplication();  

    appState.addActivity(this);  

          setContentView(R.layout.main);  

         。。。。。。。  

      } 

 

第三,在退出按钮的地方调用:

    OnClickListener()  

                public void onClick(View v)  

                    Deaboway appState = (Deaboway)getApplicationContext();  

                    appState.finishAll();  

                 

            

 

最后,要记得在manifest中的application标签中添加 android:name=”.Deaboway”

Android Market排名算法及规则

(Just one blogger _guess_ how Google rates apps on Android
market. For reference only)



http://www.mobile20.com.cn/android-market-ranking-rules/

众所周知,做搜索出身的Google,旗下的Market的排名肯定是依据一个形同( A×a% + B×b% +
C×c%)的公式计算出来的数值,进行排名的。开发者可根据其排名规则,对自己的产品设计和研发以及推广进行指导。


   1. 排名公式里有哪些指标?

  指标A、B、C到底是什么?权重a、b、c究竟是多少?这些问题的答案,应该是每个App开发者和运营者都渴望了解的。知道了这个答案之后,有些“聪明”的人就会像当年SP的从业者一样,去刷那些权重大的指标、从而拉升产品的排名了。网上关于此问题的讨论很多都是不了了之、无疾而终。相对较深入的一篇文章《Android
Market App
Rankings
》,也只是在猜想是基于“安装量”的增长和留存。可惜,该作者离正确答案就差一步了……

也许你已经等得不耐烦了,哈哈。现在就说说我的结论吧。指标A=“总安装/总下载”,即下载转安装的转换率;B=“评分/5”,即产品得分比上Market的满分;C=“留存安装/总安装”,即安装的留存率。不要急着问我a、b、c等于多少,准确数值只有Google知道、而且可以调,我只能告诉你它们加起来等于100,还有就是b>max(a,c)(为什么?下面耐心看)。

  依据这个推断公式,就是说如果每个下载的用户都安装了、而且都给了5分、并且一直都没有卸载——OK.不论a、b、c是多少,你的产品得了满分,它就是No.1!


  2. 为什么是它们?

  在告诉你为什么是A、B、C之前,我先告诉你为什么不是D、E、F……

  先看个数据,Android Market-通信-免费-TOP25:

Android <wbr>Market排名算法及规则Market排名算法及规则” TITLE=”Android Market排名算法及规则” />

  去掉了产品名称,我们可以看到更纯的数据。


  首先,
我们可以肯定的排除排名是按“下载次数”排序的,下载次数1万+的产品(第9位)居然排到了下载次数500万+(第11位)的前面。试想第11位的产品在喊:“这是为什么?它哪点比我好???”——答案:请看“评分”。啊?它是5分!没错,第9位的产品评分得了满分。哦,排除了D的同时,我们收获了B。


  其次,
我们可以排除“评分人数”。原因就是,在“下载次数”和“评分”都同等量级的情况下,比对了若干样本,都无章可循——实在跟“评分人数”不相干。E被干掉了。


  再次,
要排除的是“加速度”。很多人在猜测会有一个“加速度”指标,或者新增下载的、或是新增安装的。大家的这种猜测是源自于“鼓励新产品”,但是这是一种过于激进的想法。Market这么大盘子是要鼓励推陈出新的,但“保护”新产品的同时也得“爱护”那些确实有实力的老产品。过大的用户基数,肯定会拖慢老牛产品的“加速度”,如果这么做的话每天的TOP10都是面目全非的(App
Store的排名规则里就有“加速度”,所以TOP榜天天易容)。所以,“加速度”也不是真正的公平。

  排除了D、E、F,收获了B。那A和C是从哪来的?

  休息一下,问个题外话?我们一般是怎么评判一个物品的“好”“坏”的?是不是有以下部分组成?有一样东西摆在你面前,勾起了你试用的欲望。试用之后,你可能爱不释手,继续用吧。路遥知马力,又用了一段时间,你又有了新的评判。如果这个东西实在是太好了,那你就会与它形影不离……回归到我们判断一个物品的标准的本质,无非就是是否“接受它”、是否“肯定它”、是否“留着它”。

言归正传,用户是否“接受”一个App——下载只是“抬头”,安装才是“点头”。如果单以“安装”为指标,又会让老产品滚雪球,有失公平。所以,A(下载转安装的转化率)浮出水面了。“安装”做分母,“下载”做分子。一方面,排名在前的产品下载量越高、安装量就有可能越高的同时,如果产品不够好“下载量”越高就可能成了拖后腿的那个分母,上面举例的第11位产品就是个例子(庞大的下载量下,安装量无法保持同步增长,所以让其难堪重负,排名下落——剧透下,第11位是曾经“通信”下排名第1的产品KakaoTalk)。另一方面,只要A(还有B、C)足够好,也让一些新产品可以冒出头来。

  B(评分)刚才已经分析到了,这里需要补充一点的是:评分不是一次性的。我们会发现在Market里“评分”是可以多次修改的,这样的目的就是鞭策产品要持续的“好”,用户一直掌握着产品的“生杀大权”。另外,就是为什么前文说到b大于a和c,原因就是纵观TOP100,评分低于4分的只有两个;TOP25,评分低于4的是零个;TOP10,评分低于4.5的只有一个(避免出现App
Store里《小姐日记》这样低分却长期占据高排位的现象)——一言以蔽之:要想在前排就坐,产品首先得让用户觉得“好”。

  该说说C(安装留存率)的出处。说白了就是控制流失率,如果“总安装”很高、但是“留存安装”很少,那也不能称之为“好”产品。所以,安装留存率就是另一个全面衡量产品优劣的指标。


  3. 它们到底是多少?

  前文已经说到b比a和c都大,那a和c孰大孰小呢?这个取决于Android
Market的策略,如果“a比c大”意味着“更看重新增”(任何一个店铺先开张的时候,都会最看重这个)、如果“c比a大”意味着“更看重留存”(对于一个已经把客流吃透的老店,会更看重这个)——所以,这个真是可以调的,取决于Android
Market发展到哪个阶段和运营管理者的自我认识。


  4. 感言

  Android Market如果真是我想象的这样排名,可谓新老兼顾,攻防兼备。

  说它新老兼顾,是因为它让新鲜的好的新产品既能快速冒出来、也能让它经得起时间的推敲,或昙花一现或源远流长。说它攻守兼备,是因为这样会把盘子越做越大,而且也防止了“刷”(能刷上去,但是刷上去之后如果产品不够好,会让产品跌得更惨),把盘子越做越牢。这样公平的规则,也为Android
Market的应用量新增速度已超过App Store提供了另一个有力的支持。高!实在是高!

Android Market排名规则这个隐形的裁判告诉我们:持续地把产品做好,才是正道!

【贪吃蛇—Java程序员写Android游戏】系列5.Android新浪微博客户端实现——准备篇

大家想必都使用过微博,或者是每天牢骚不断的强迫症用户,或者是随便注册个帐户的酱油党。毋容置疑,从2010年开始到现在,微博在中国又重新焕发出第二春(第一春是饭否、叽歪。。。2009年被封杀过,不过最近貌似饭否又活过来了。。。),而新浪微博在其中居功至伟。

其实,中国的微博大同小异,学习的都是鼻祖:Twitter;就是好像中国的SNS都是学习的Facebook,而且学得非常像!(笑*^-^*)就拿新浪微博为例,其API到现在为止还有一些直接用Twitter
API的例子,本博跟他们反映过,得到的回复是:没有人在维护它。而其它微博的API也基本都是一个模式。

不管怎么说,新浪微博目前都是国内微博的执牛耳者,(因为它叫“面包牌面包”嘛,再笑*^-^*),而本博之前也写过几个新浪微博的应用,还算熟悉;因此,从本次开始,我们来一起把贪吃蛇游戏跟新浪微博结合起来,做一个新浪微博头像的贪吃蛇。

一、 资料

1. 新浪微博API

要开发新浪微博的Android客户端,首先,要对其开放的API及相关规范比较熟悉,因此需要参考http://open.t.sina.com.cn/wiki/

2. 相关教程

我们不是第一个吃螃蟹的人,我们要站在巨人的肩膀上。现在网上流传比较广的教程是一位叫“水的右边”的朋友写的《android开发我的新浪微博客户端》,据他自己说,他开始本系列文章写作的时候,接触Android三个星期的时间。所以,如果朋友你也是初学者,要有信心经过一段时间的努力熟练掌握Android开发。这里,为了方便大家,我把eoe一位斑竹整理好的全部文章放在如下地址,大家可以自行下载:

http://ishare.iask.sina.com.cn/f/14857560.html

二、 开发准备

要使用新浪微博开放API,需要有新浪微博,并创建新浪微博应用,获取App Key和App
Secret。因此,我们需要登陆新浪微博,并进入“我的应用”页面,你可以直接访问http://open.t.sina.com.cn/apps
。在这里创建一个新的应用,本博这里创建如下:

clip_image002

然后,我们就进入了微博开发阶段。通过这里列出的Key可以正常调用新浪微博API。

此外,我们还要准备些图片素材,本博基本就是从互联网上捞一些,然后PS下。可能不是那么美观,大家将就下啦。

三、 创建项目

在Eclipse中创建一个叫SnakeSina的项目,并实现基本的Splash页面和新浪微博Oauth认证页面。这里就不做详细的展开,各位可以自行参考前面推荐的资料。

1. 登陆应用时的Splash界面:

clip_image004

2. 第一次登陆,询问用户是否进行登录授权:

clip_image006

3. 使用Sqlite保存授权成功后的useid、key、secret等信息。

下次,我们将通过新浪微博API获取资源并保存。

【贪吃蛇—Java程序员写Android游戏】系列 4.用Google SVN管理开源的Android项目

最近在写一个新浪微博团购分享的手机客户端(感兴趣的朋友可以到这里下载http://sharetuan.sinaapp.com/
,是J2ME版本的,以后我基本就不会进行J2ME版本的开发,注意精力放在Android上了),因此博客更新慢了点。不过,我会尽量保证一周至少更新一次。

本次讲讲如何使用Google的SVN来管理我们的Android开源项目。

一、创建Project

1. 访问http://code.google.com/hosting/

2. 用Google帐户登录登录,并点击左下角的Create a new
project
,你会看到如下界面:

clip_image002

3. 填入相应的信息如下:

clip_image004

4. 这里简单介绍下Google SVN上列出的各种开源协议:

Apache License 2

Apache
License是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。需要满足的条件也和BSD类似:

· 需要给代码的用户一份Apache License

· 如果你修改了代码,需要在被修改的文件中说明。

·
在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明。

· 如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache
License。你可以在Notice中增加自己的许可,但不可以表现为对Apache License构成更改。

Apache
License也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。

英文原文:http://www.apache.org/licenses/LICENSE-2.0.html

Artistic License/GPL

使作者保持对进一步开发的控制。

英文原文:

http://www.perlfoundation.org/artistic_license_1_0

http://www.perlfoundation.org/artistic_license_2_0

Eclipse Public License 1.0

英文原文:www.eclipse.org/legal/epl-v10.html

GNU GPL v2

我们很熟悉的Linux就是采用了GPL。GPL协议和BSD, Apache
License等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代
码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux,包括商业公司的linux和linux上各种各样的由个人,组织,以及商
业软件公司开发的免费软件了。

GPL协议的主要内容是只要在一个软件中使用(”使用”指类库引用,修改后的代码或者衍生代码)GPL
协议的产品,则该软件产品必须也采用GPL协议,既必须也是开源和免费。这就是所谓的”传染性”。GPL协议的产品作为一个单独的产品使用没有任何问题,还可以享受免费的优势。

由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议,对于使用GPL协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。

其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似。

英文原文:www.gnu.org/licenses/gpl-2.0.html

GNU GPL v3

与GNU GPL v2的区别如下:

a、对用户的专利保护

新版本GPL最重要的特性,就是明确了专利许可。任何分发GPLv3软件的人必须自动为软件用户提供许可证,让他们可以使用所有相关的专利。
从理论上来讲,这样做可以保护所有GPL专利享有者的诉讼权利,防止Linux经销商与专利持有者,如微软公司进行独家经营。

b、不包含任何网络服务条款

GPLv3的早期草案中包含了一个可选择条款,即要求所有的网络应用程序为远程用户提供源代码。如今,这个条款已被取消。幸亏如此,否则对于网络开发商和用户来说都会是极大的负担。尽管这样做的初衷是为了保证拥有此软件服务的用户享有的服务与二进制文件代表的服务完全相同,而这种做法却可能会
潜在地产生更为广泛的影响。

但是,对SaaS用户提供源代码的要求并没有被放弃。FSF仍然要求进行Affero许可,而这个许可在GPL的基础上还增加了一项特别条
款。这或许是在告诫采取双重许可策略的经销商们,因为他们强烈激励服务供应商购买商业许可,而不是使用开放源代码版本。

c、对DRM的限制

绝大多数对GPLv3有争议的条款都涉及到对数字版权的管理。新版本的GPL并没有要求禁用DRM,但是通过两种非常重要的途径对它进行了限
制。

首先是项声明,即基于GPL代码为基础的DRM不会构成一种“有效的技术手段”。这项声明旨在保证在DMCA和惩戒逆向开发DRM系统的法律
条款下,打破基于GPL代码为基础的DRM为合法。这项声明主要是针对用户的应用,但是如果它能够阻止使用DRM时对互操作性的限制,那么对企业用户来说
很有利。

其次,要求应用GPL软件的家用设备必须允许用户执行他们自己修改的版本。这样做是为了阻止那些使用GPL代码、但需要由硬件供应商认可的设
备,如
TiVO等。但是,这只能影响到家用设备:供应商仍然能够利用数字签名来锁定GPL代码。这种差异都是缘于签署代码的开发者们拥有合法的安全应用程序,而
这恰恰是企业所需要的。

英文原文:www.gnu.org/licenses/gpl.html

GNU Lesser GPL

LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生之GPL类库的的软件必须采用GPL协议不同。LGPL
允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并
发布和销售。

但是如果修改LGPL协议的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生的代码都必须采用LGPL协议。因此LGPL协议的开源代码很适合作为第三方类库被商业软件引用,但不适合希望以LGPL协议代码为基础,通过修改和衍生的方式做二次开发的商业软件采用。

GPL/LGPL都保障原作者的知识产权,避免有人利用开源代码复制并开发类似的产品

英文原文:http://www.gnu.org/copyleft/lesser.html

MIT License

MIT是和BSD一样宽范的许可协议,作者只想保留版权,而无任何其他了限制.也就是说,你必须在你的发行版里包含原许可协议的声明,无论你是以二进制发布的还是以源代码发布的.

英文原文:http://www.opensource.org/licenses/mit-license.php

Mozilla Public License 1.1

允许免费重发布、免费修改,但要求修改后的代码版权归软件的发起者。这种授权维护了商业软件的利益,它要求基于这种软件的修改无偿贡献版权给该软件。

英文原文:http://www.mozilla.org/MPL/MPL-1.1.html

New BSD License

BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。

但”为所欲为”的前提当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:

· 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。

· 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。

· 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。

BSD
代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对
商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发。

英文原文:http://www.opensource.org/licenses/bsd-license.php

Other Open Source

Common Development and Distribution License

2005年,SUN公司宣布将开放操作系统Solaris的源代码,并推出CDDL(Common Development and
Distribution License)作为Open Solaris的许可证。CDDL许可证是MPL许可证(Mozilla
Public License,用来管理Mozilla网页浏览器及相关软件)的“升级版”。

英文原文:http://www.sun.com/cddl/cddl.html

Common Public License – v 1.0

英文原文:http://www.eclipse.org/legal/cpl-v10.html

还有微软的一些开源协议,这里就不列了,用的人不多,反正好像只有微软在用吧,哈哈

大家可以根据自己的情况进行选择。

5. 点击Create project按钮之后,就完成了Project的创建,如下:

clip_image006

6. 你可以访问http://code.google.com/p/support/wiki/GettingStarted,来学习如何管理一个SVN上的Project,当然,我们接下来也会进行相关介绍。

注意: Google的SVN是强制开源的!

二、提交Project

1. 现在就可以直接通过http://code.google.com/p/snake-book/访问刚刚创建的项目了,点击source可以看到如下图所示。待会咱们就可以使用Eclipse提交文件到https://snake-book.googlecode.com/svn/trunk/地址,并通过帐户密码进行update,需要注意的是,以后管理该项目的密码就是这里“googlecode.com
password”帮我们生成的比较复杂安全性比较高的密码。另外一个地址:http://snake-book.googlecode.com/svn/trunk/给匿名用户checkout代码出来使用,只读权限。

clip_image008

2. 前面的文章中介绍过Eclipse环境的搭建,现在我们要访问http://subclipse.tigris.org/来安装Eclipse的svn插件,以方便我们以后代码的提交和管理。安装的地址如下图,输入地址后,根据向导安装后重启Eclipse即可。

clip_image010

3.
现在我们就可以把源代码提交到svn服务器上,打开要上传的项目,右键->team->share
Project->svn,写入https路径。下一步,输入Google账号和上传密码,起个名,finish。如下图:

clip_image012

clip_image014

clip_image016

注意:提交的时候使用Googlecode.com
帮我们生成的密码;只提交源文件而不要包含生成的文件。

4. 现在我们可以直接在浏览器中通过http://snake-book.googlecode.com/svn/trunk/
访问刚刚提交的代码了,也可以直接在Eclipse中使用svn插件,或者使用其它svn客户端进行代码的访问。

三、使用Project

1.
现在,Eclipse看到的项目会多出svn的图标,如下图,这表示本项目已经纳入了版本控制。以后做的修改可以通过提交到Google的code进行版本控制了。

clip_image018

2.
在我们进行了代码或者文件的更新之后,就可以通过:右键->team->commit来进行代码的提交,如下图:

clip_image020

3. 更多的操作会在以后用到的时候讲解;也请自行参考相关文档。今天就到这里吧,谢谢大家。

预告:下次将为大家带来新浪微博的头像贪吃蛇实现(准备篇)。

【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详

本次会详细讲解将Android的Snake Sample移植到J2ME上,从而比较二者的区别和联系。

在《1.Android
SDK
Sample-Snake详解
》中,我们已经详细介绍了Android实现的Snake项目结构,现在我们要将这个项目用J2ME实现。

一、 J2ME vs. Android

Android的UI实用、方便,而且很美观,基本无需改动且定制方便。而J2ME的高级用户界面比较鸡肋,在现在大多数的应用里都看不到,多数稍微复杂点的界面都是手工画,或是用一些开源的高级UI库。接下来我们简单比较下二者的区别,为Snake项目从Android到J2ME的移植做准备。

1. 平台

J2ME

开发平台

Android

操作系统

2. 工程结构

J2ME

res:资源文件

src:源代码

Android

src:源代码

res\drawable:图片

res\raw:声音

res\values:字符串

assets:数据文件

3. 安装包

J2ME

jad,jar

Android

apk

4. 代码结构

J2ME

MIDlet,Canvas,采用继承的方式,只有一个MIDlet,一般只有一个Canvas

Android

Activity,View,采用继承的方式,只有一个Activity,一般只有一个View

5. 入口程序

J2ME

MIDlet类

Android

Activity类

6. 主程序结构

J2ME

package com.deaboway.j2me;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

public class MyMidlet extends MIDlet {

protected void destroyApp(boolean arg0) throws
MIDletStateChangeException {

// TODO Auto-generated method stub

}

protected void pauseApp() {

// TODO Auto-generated method stub

}

protected void startApp() throws MIDletStateChangeException
{

// TODO Auto-generated method stub

}

}

Android

package com.deaboway.android;

import android.app.Activity;

import android.os.Bundle;

public class myActivity extends Activity {

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

}

}

7. 生命周期-开始

J2ME

startApp(),活动状态,启动时调用,初始化。

Android

onCreate(),返回时也会调用此方法。

onCreate()后调用onStart(),onStart()后调用onResume(),此时Activity进入运行状态。

8. 生命周期-暂停

J2ME

pauseApp(),暂停状态,如来电时,调用该接口。

Android

onPause()。

9. 生命周期-销毁

J2ME

destroyApp(),销毁状态,退出时调用。

Android

onStop(),程序不可见时调用onDestroy(),程序销毁时调用。

10. 刷新

J2ME

高级UI组件由内部刷新实现。低级UI,canvas中通过调用线程结合repaint()来刷新,让线程不断循环。低级UI架构可以用MVC方式来实现,建议使用二级缓存。

Android

高级UIHandler类通过消息的机制刷新。onDraw()刷新接口,低级UI开发者用线程控制更新,在lockCanvas()和unlockCanvasAndPost()方法之间绘制。

如果去读API,我们可以发现J2ME中Canvas的repaint()与Android中View的invalidate()/postInvalidate()方法实现了相同的功能(连说明文字几乎都一样…),但是invalidate()/postInvalidate()两者却有着区别:invalidate()
只能在UI这个线程里通过调用onDraw(Canvas
canvas)来update屏幕显示,而postInvalidate()是要在non-UI线程里做同样的事情的。这就要求我们做判断,哪个调用是本
线程的,哪个不是,这在做多线程callback的时候尤为重要。而在J2ME中,不管怎样直接调用repaint()就好了。

11. 绘画

J2ME

Displayable类。J2me中所有可显示的组件都是直接或间接的继承了Displayable,直接的是Canvas和Screen。不同的继承导致了低级
UI和高级UI的区别。J2me中现成的UI组件都是直接或者间接继承了Screen。只要调用Display.getDisplay(MIDLet
instan).setCurrrent(Displayable
disp),就可以把组件显示到手机界面上。切换界面的时候也可以使用该接口。

Android

View类。可见的组件直接或者间接继承了android.view.View。通过
Activity.setContentView(View
view)就可以显示在android手机界面上,切换界面的时候也可以使用该接口。如果是直接继承了View而不是Android自带的UI组件,那么
还要自己去实现它的刷新,类似J2me的低级UI组件。

12. 画笔

J2ME

高级UI组件由内部刷新实现。低级UI,canvas中通过调用线程结合repaint()来刷新,让线程不断循环。低级UI架构可以用MVC方式来实现,建议使用二级缓存。

Android

Canvas类,Android绘
制的时候会传入一个参数Paint。该对象表示绘制的风格,比如颜色,字体大小,字体格式等。Android的Canvas不同于J2ME的Canvas,它更像于J2ME的Graphics,用来绘制。

13. 全屏

J2ME

Canvas中SetFullScreenMode()。

Android

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);requestWindowFeature(Window.FEATURE_NO_TITLE);

14. 获得屏幕尺寸

J2ME

Canvas类的getHeight()和getWidth()

Android

Display d = getWindowManager().getDefaultDisplay();

screenWidth = d.getWidth();

screenHeight = d.getHeight();

15. 可绘区域

J2ME

int clipX = g.getClipX();

int clipY = g.getClipY();

int clipWidth = g.getClipWidth();

int clipHeight = g.getClipHeight();

g.clipRect(x, y, width, height);

g.setClip(clipX, clipY, clipWidth, clipHeight);//释放当前状态

Android

canvas.save();//保存当前状态

canvas.clipRect(x,y, x+width, y+height)

cavnas.resave();//释放当前状态

16. 清屏操作

J2ME

g.setColor(Color.WHITE);

g.fillRect(0,0,getWidth(),getHeight());

Android

// 首先定义paint

Paint paint = new Paint();

// 绘制矩形区域-实心矩形

// 设置颜色

paint.setColor(Color.WHITE);

// 设置样式-填充

paint.setStyle(Style.FILL);

// 绘制一个矩形

canvas.drawRect(new Rect(0, 0, getWidth(), getHeight()),
paint);

17. 双缓冲

J2ME

Image bufImage=Image.createImage(bufWidth, bufHeight);

Graphics bufGraphics=bufImage.getGraphics();

Android

Bitmap carBuffer = Bitmap.createBitmap(bufWidth, bufHeight,
Bitmap.Config.ARGB_4444);

Canvas carGp = new Canvas(carBuffer);

18. 图片类

J2ME

Image类,Image.createImage(path);

Android

BitMap类,BitmapFactory.decodeResource(getResources(),R.drawable.map0);

19. 绘制矩形

J2ME

drawRect的后两个参数为宽度和高度

Android

drawRect的后两个参数为结束点的坐标

20. 按键

J2ME

keyPressed()

keyRepeated()

keyReleased()

Android

onKeyDown()

onKeyUp()

onTracKballEvent()

21. 键值

J2ME

Canvas.LEFT…

Android

KeyEvent.KEYCODE_DPAD_LEFT…

22. 触笔

J2ME

pointerPressed(),pointerReleased(),pointerDragged()

Android

onTouchEvent()

23. 数据存储

J2ME

Record Management System (RMS)

Android

SQLite数据库,SharedPreferences类

24. 连接

J2ME

从Connector打开,可以直接在Connector.Open时设置连接是否可读写,以及超时设置

Android

从URL对象打开,必须使用setDoInput(boolean)和setDoOutput(boolean)方法设置,使用setConnectTimeout(int)不仅可以对连接超时进行设置,还能设置超时时间,参数为0时忽略连接超时

25. 游戏开发包

J2ME

javax.microedition.lcdui.game.*;

GameCanvas/Layer/LayerManager/ Sprite/TiledLayer

Android

无专门针对游戏的开发包。

26. 音效

J2ME

Player s=Manager.createPlayer(InputStream);

s.prepare();//创建

s.start();//播放

s.stop();//暂停

s.stop();//关闭

s.release();//释放

Android

MediaPlayer类处理背景音乐

SoundPool类处理一些简单的音效

27. 显示文本

J2ME

String

Android

TextView类

28. 打印信息

J2ME

System.out.println()

Android

Log类

二、 迁移关键点

1. 基础类和结构

J2ME程序的主体从Activity改变为MIDlet,TileView从View改变为Canvas,相关的方法也需要进行调整,但是主体结构和逻辑还是一致的。此外,有些J2ME不支持的类,需要做对应处理。

资源获取,从xml改为自行获取:

private void initSnakeView() {

// 获取图片资源

try {

imageRED_STAR =
Image.createImage(“/redstar.png”);

imageRED_STAR =
Utils.zoomImage(imageRED_STAR,mTileSize,mTileSize);

imageYELLOW_STAR =
Image.createImage(“/yellowstar.png”);

imageYELLOW_STAR =
Utils.zoomImage(imageYELLOW_STAR,mTileSize,mTileSize);

imageGREEN_STAR =
Image.createImage(“/greenstar.png”);

imageGREEN_STAR =
Utils.zoomImage(imageGREEN_STAR,mTileSize,mTileSize);

} catch(Exception e) {

Log.info(“Create Images: “+e);

}

// 设置贴片图片数组

resetTiles(4);

// 把三种图片存到Bitmap对象数组

loadTile(RED_STAR, imageRED_STAR);

loadTile(YELLOW_STAR, imageYELLOW_STAR);

loadTile(GREEN_STAR, imageGREEN_STAR);

}

ArrayList,用Vector替换掉:

// 坐标数组转整数数组,把Coordinate对象的x y放到一个int数组中——用来保存状态

private String coordVectorToString(Vector cvec) {

int count = cvec.size();

StringBuffer rawArray = new StringBuffer();

for (int index = 0; index < count;
index++) {

Coordinate c = (Coordinate) cvec.elementAt(index);

rawArray.append(c.x+”,”);

rawArray.append(c.y+”,”);

Log.info(“coordVectorToString(),
c.x=”+c.x+”,c.y=”+c.y);

}

Log.info(“coordVectorToString(),
rawArray.toString=”+rawArray);

return rawArray.toString();

}

// 整数数组转坐标数组,把一个int数组中的x y放到Coordinate对象数组中——用来恢复状态

// @J2ME 还是用Vector替换ArrayList

private Vector coordStringToVector(String raw) {

Vector coordArrayList = new Vector();

Log.info(“coordStringToVector(), raw=”+raw);

String[] rawArray = Utils.splitUtil(raw,”,”);

Log.info(“coordStringToVector(),
rawArray.length=”+rawArray.length);

int coordCount = rawArray.length;

for (int index = 0; index <
coordCount; index += 2) {

Coordinate c = new
Coordinate(Integer.parseInt(rawArray[index]),
Integer.parseInt(rawArray[index + 1]));

coordArrayList.addElement(c);

}

return coordArrayList;

}

Bundle,用RMS实现:

package com.deaboway.snake.util;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import javax.microedition.rms.RecordStore;

public class Bundle extends BaseRMS {

private static String[] SECTION = {

“”AL”:”,””DT”:”,

“”ND”:”,””MD”:”,

“”SC”:”,””ST”:”};

private static int LEN = SECTION.length;

private static boolean inited = false;

private static Bundle INSTANCE;

public static void INIT(){

if(inited)return;

inited = true;

INSTANCE = new Bundle();

INSTANCE.loadBundles();

}

public static Bundle getInstance(){

return INSTANCE;

}

private String[] CONTENT = new String[LEN];

private Bundle() {

super(“snake-view”);

}

public void loadBundles() {

try {

this.open();

this.close();

} catch (Exception e) {

try {

this.close();

RecordStore.deleteRecordStore(this.getRMSName());

this.open();

this.close();

} catch (Exception ex) {

}

}

}

public void resetBundles() {

try {

this.close();

RecordStore.deleteRecordStore(this.getRMSName());

this.open();

this.close();

} catch (Exception ex) {

}

}

public void updateBundles() throws Exception {

try {

this.openonly();

updateData();

if (this.getRecordStore() != null)

this.close();

} catch (Exception e) {

throw new Exception(this.getRMSName() + “::updateBundles::” +
e);

}

}

protected void loadData() throws Exception {

try {

byte[] record = this.getRecordStore().getRecord(1);

DataInputStream istream = new DataInputStream(

new ByteArrayInputStream(record, 0, record.length));

String content = istream.readUTF();

int[] start = new int[LEN+1];

for(int i =0;i<LEN;i++){

start[i] = content.indexOf(SECTION[i]);

}

start[LEN]=content.length();

for(int i =0;i<LEN;i++){

CONTENT[i] = content.substring(start[i]+5,start[i+1]);

Log.info(“CONTENT[“+i+”]=”+CONTENT[i]);

}

} catch (Exception e) {

throw new Exception(this.getRMSName() + “::loadData::” + e);

}

}

protected void createDefaultData() throws Exception {

try {

ByteArrayOutputStream bstream = new
ByteArrayOutputStream(12);

DataOutputStream ostream = new DataOutputStream(bstream);

CONTENT[0] = “9,20,9,7”;

CONTENT[1] = “1”;

CONTENT[2] = “1”;

CONTENT[3] = “600”;

CONTENT[4] = “0”;

CONTENT[5] = “7,7,6,7,5,7,4,7,3,7,2,7”;

StringBuffer sb = new StringBuffer();

for(int i=0;i<LEN;i++){

sb.append(SECTION[i]);

sb.append(CONTENT[i]);

}

ostream.writeUTF( sb.toString());

ostream.flush();

ostream.close();

byte[] record = bstream.toByteArray();

this.getRecordStore().addRecord(record, 0, record.length);

} catch (Exception e) {

throw new Exception(this.getRMSName() + “::createDefaultData::”
+ e);

}

}

protected void updateData() throws Exception {

try {

ByteArrayOutputStream bstream = new
ByteArrayOutputStream(12);

DataOutputStream ostream = new DataOutputStream(bstream);

StringBuffer sb = new StringBuffer();

for(int i=0;i<LEN;i++){

sb.append(SECTION[i]);

sb.append(CONTENT[i]);

}

ostream.writeUTF(sb.toString());

ostream.flush();

ostream.close();

byte[] record = bstream.toByteArray();

this.getRecordStore().setRecord(1, record, 0,
record.length);

} catch (Exception e) {

throw new Exception(this.getRMSName() + “::updateData::” +
e);

}

}

public String getValue(int key) {

return CONTENT[key];

}

public void setValue(int key, String value) {

CONTENT[key] = value;

}

}

Log,自己实现Log系统:

package com.deaboway.snake.util;

public class Log{

private static final int FATAL = 0;

private static final int ERROR = 1;

private static final int WARN = 2;

private static final int INFO = 3;

private static final int DEBUG = 4;

private static int LOG_LEVEL = INFO;

public static void info(String string){

if(LOG_LEVEL >= INFO){

System.out.println(“[Deaboway][ INFO] ” + string);

}

}

public static void debug(String string){

if(LOG_LEVEL >= DEBUG){

System.out.println(“[Deaboway][DEBUG] ” + string);

}

}

public static void warn(String string){

if(LOG_LEVEL >= WARN){

System.out.println(“[Deaboway][ WARN] ” + string);

}

}

public static void error(String string){

if(LOG_LEVEL >= ERROR){

System.out.println(“[Deaboway][ERROR] ” + string);

}

}

public static void fatal(String string){

if(LOG_LEVEL >= FATAL){

System.out.println(“[Deaboway][FATAL] ” + string);

}

}

}

2. TileView

(1)用Image替换BitMap,“private Image[] mTileArray;”

(2)private final Paint mPaint = new
Paint();不再需要了。直接在Graphics中drawImage即可

(3)onSizeChanged()
不会被自动调用,需要在构造函数中主动调用,以实现对应功能。onSizeChanged(this.getWidth(),this.getHeight());

(4)最重要的,用paint替换onDraw,呵呵,Canvas的核心啊!失去它你伤不起!!!咱也试试咆哮体!!!!!!

3. SnakeView

(1)J2ME 没有Handler,直接用Thread定期repaint()就OK。这里要啰嗦几句。

熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。实际上Android中也实现了类似Windows的消息循环机制,它通过Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。

Android系统中Looper负责管理线程的消息队列和消息循环。Handler的作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。

一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在主线程中处理了。因为主线程一般负责界面的更新操作,并且Android系统中的weget不是线程安全的,所以这种方式可以很好的实现Android界面更新。在Android系统中这种方式有着广泛的运用。

如果另外一个线程要把消息放入主线程的消息队列,就需要通过Handle对象,只要Handler对象以主线程的Looper创建,那么调用Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程的消息队列。并且将会在Handler主线程中调用该handler的handleMessage接口来处理消息。

之所以Android有这些处理,是因为Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新。而对于J2ME来说,Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者,也可以从Runnable接口继承。实现如下:

class RefreshHandler extends Thread {

public void run() {

while (true) {

try {

//delay一个延迟时间单位

Thread.sleep(mMoveDelay);

} catch (Exception e) {

e.printStackTrace();

}

// 更新View对象

SnakeView.this.update();

// 强制重绘

SnakeView.this.repaint();

}

}

};

(2)直接使用String代替TextView类,在Canvas的paint()中直接绘制各种提示信息。

(3)在一些地方需要主动调用repaint()进行强制重绘。

其它具体参考源代码。

4. Snake

本类就比较简单了,直接把源代码贴出来如下:

package com.deaboway.snake;

import javax.microedition.lcdui.Display;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

import com.deaboway.snake.util.BaseRMS;

import com.deaboway.snake.util.Bundle;

import com.deaboway.snake.util.Log;

public class Snake extends MIDlet {

public static Display display;

public static SnakeView mSnakeView;

public static MIDlet SNAKE;

public Snake() {

Bundle.INIT();

display=Display.getDisplay(this);

SNAKE = this;

mSnakeView = new SnakeView();

mSnakeView.setTextView(“”);

mSnakeView.setMode(SnakeView.READY);

display.setCurrent(mSnakeView);

}

protected void destroyApp(boolean arg0) throws
MIDletStateChangeException {

mSnakeView.saveState();

}

protected void pauseApp() {

mSnakeView.setMode(SnakeView.PAUSE);

}

protected void startApp() throws MIDletStateChangeException
{

// 检查存贮状态以确定是重新开始还是恢复状态

Log.debug(“startApp(), BaseRMS.FIRST=”+BaseRMS.FIRST);

if (BaseRMS.FIRST) {

// 存储状态为空,说明刚启动可以切换到准备状态

mSnakeView.setMode(SnakeView.READY);

} else {

// 已经保存过,那么就去恢复原有状态

// 恢复状态

if (!mSnakeView.restoreState()) {

// 恢复状态不成功,设置状态为暂停

mSnakeView.setMode(SnakeView.PAUSE);

}

}

}

}

本次就大概介绍这么多,源代码将在下次放出。下次主要讲解源代码的存储和维护,敬请期待。

【贪吃蛇—Java程序员写Android游戏】系列 2. 用J2ME实现Android的Snake Sample预览

为了让大家更好的理解J2ME和Android编程的差别,我用J2ME重新实现了Android的Snake Sample。

下次,我会详细介绍在将Snake从Android移植到J2ME上时,需要特别注意的问题,并对Android和J2ME的区别和联系进行粗略的比较。

本次,暂时把J2ME实现的运行画面列出如下。JAVA的好处就是一次编写多处运行,:-),我的PC上各种模拟器都有,因此在WTK、黑莓、Nokia
N97上都跑了跑。

【贪吃蛇—Java程序员写Android游戏】系列 <wbr>2. <wbr>用J2ME实现Android的Snake <wbr>Sample预览2. 用J2ME实现Android的Snake Sample预览” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 2. 用J2ME实现Android的Snake Sample预览” />

【贪吃蛇—Java程序员写Android游戏】系列 <wbr>2. <wbr>用J2ME实现Android的Snake <wbr>Sample预览2. 用J2ME实现Android的Snake Sample预览” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 2. 用J2ME实现Android的Snake Sample预览” />

【贪吃蛇—Java程序员写Android游戏】系列 <wbr>2. <wbr>用J2ME实现Android的Snake <wbr>Sample预览2. 用J2ME实现Android的Snake Sample预览” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 2. 用J2ME实现Android的Snake Sample预览” />

可运行的包也给大家一下:

http://ishare.iask.sina.com.cn/f/14364755.html

注意:向上是开始,确定键(中键、Fire键)是暂停或者退出。

【贪吃蛇—Java程序员写Android游戏】系列 0. 前言几句话

这段时间以来,本博陆续介绍了Android系统的开发和调试环境、模拟器,并详细解析了Android的一个实例SoftKeyboard,这里具体列出如下:

基础已经有了,因此后续准备写名为《贪吃蛇—Java程序员写Android游戏》的系列文章。

之所以选择以《贪吃蛇》为例和切人点,有如下几个原因:

  1. 贪吃蛇是本人在手机上玩到的第一个有趣的游戏,想必对很多朋友来说也是这样,实现简单却又极具可玩性。
  2. 贪吃蛇可以有很多版本,很多变种,可以做得很简单,也可以做得很复杂。可以2D,也可以3D。
  3. 贪吃蛇,最早的版本是蛇吃苹果,让我们想到伊甸园的蛇,吃了智慧之树的苹果,而我们也要像亚当和夏娃那样,不断的吃苹果,获得智慧,充实自己。

闲话少叙,本系列文章初步的计划是,由Android SDK
Sample的贪吃蛇游戏(Snake)为切入点,通过跟J2ME手机游戏和J2SE桌面游戏开发的比较,逐步介绍Android游戏开发的相关知识和流行框架。

本系列要求读者具有Java编程基础,最好学过基础的Java语法。如果进行过J2ME程序的开发就为适合。当然,如果你做过J2SE的开发也不错,不过,这十年Java应用最多的想必是JEE吧。(^-^)

让我们一起做“贪吃蛇”,不停地吸收养分,不断地壮大自己吧!
欢迎大家的批评指正。

Android模拟器简介—更新到最新的3.0 Honeycomb

前面介绍了Windows环境下,基于Android SDK(2.3) 和
Eclipse(helios)的Android开发环境的搭建,并创建了第一个应用程序Hello Android
World,之后用Honeycomb Preview模拟器深入解析了Eclipse中Android工程的结构以及调试。

具体请参考小生的blog:http://blog.sina.com.cn/deaboway
或者http://blog.csdn.net/deaboway

一、 环境变量

当前最新版本的Android
SDK中,adb已经被从./tools/目录移动到./platform-tools/,因此为了方便,在windows环境中,需要把以上两个目录添加到PATH中,如下:

clip_image001

二、 常用命令

这样,我们就可以直接在cmd中直接运行adb/android/emulator等命令,简单介绍如下:

1. 命令行输入adb相关命令:

clip_image003

adb命令具体的参数可以通过在命令行输入adb后回车,或者adb
help后回车的方式查看,这里简单介绍几个比较常用的命令。

adb devices

列出所有装载的设置。

adb shell

进入设备根目录/,取得对设备的控制权,如输入ls命令等。

adb install <apk文件路径>

将指定的apk文件安装到设备上。如果有多个设备同时存在,则需要指定目标设备,如:adb install -s
emulator-5554 D:/name.apk。

adb uninstall [-k] <软件名>

卸载指定软件,如果加 -k 参数,为卸载软件但是保留配置和缓存文件。

adb push <本地路径>
<远程路径>

把本机电脑上的文件或者文件夹复制到设备(手机)。比如,我们可以用如下命令,将sdcard外的东西存放进去:adb push
gavin.txt /sdcard/gavin.txt。

adb pull <远程路径>
<本地路径>

把设备(手机)上的文件或者文件夹复制到本机电脑。

2. 命令行输入Android相关命令:

clip_image005

android命令具体的参数可以通过在命令行输入android后回车,或者android
-help后回车的方式查看,这里简单介绍几个比较常用的命令。

3. 命令行输入emulator相关命令:

clip_image007

emulator命令具体的参数可以通过在命令行输入emulator
-help后回车的方式查看,这里简单介绍几个比较常用的命令。

emulator -avd <name>

启动指定模拟器。

emulator -avd <name> -sdcard
<name>

启动一个带有SD卡的指定模拟器。

emulator -skin <name>

启动一个使用指定skin的模拟器。

emulator -netdelay <delay>

模拟多种网络延迟等级,因此你可以在更接近真实情况的环境下测试你的应用程序。可以在模拟器启动时设置延迟等级或范围,也可以在模拟器运行期间通过控制台动态修改延迟。(关于控制台的操作,后面会介绍)

emulator -netspeed <speed>

模拟多种网络传输速度。可以在模拟器启动时指定传输速度或范围,也可以在模拟器启动后通过控制台动态修改传输速度。

4. mksdcard [-l label]
<size>
<file>

创建sdcard,如:mksdcard 256M C:\gavin.img

5. 模拟器控制台

每一个运行中的模拟器实例都包括一个控制台,我们可以利用控制台动态的查询和控制模拟设备的环境。如:我们可以利用控制台动态的管理端口映射和网络特性,还可以模拟电话事件。想要进入控制台输入命令,需要使用telnet连接到控制台的端口号:

telnet localhost <port>

其中,port是emulator的port号,如果第一个是5554端口,下一个实例使用的端口号会加2:5556、5558等。最多可以有16个模拟器实例同时运行控制台。

获取模拟器的方法有:

l 在启动模拟器时使用-verbose选项来检测该模拟器实例使用的端口号;

l 在调试输出中找“emulator console running on port number”这一行;

l 在命令行中使用adb devices来查看模拟器实例和他们的端口列表。

clip_image008

连接上控制台之后,可以输入help [command]来查看命令列表和指定命令的教程。要离开控制台会话,使用quit 或
exit 命令。下面,简单介绍几个控制台下常用命令。

redir <list|add|del> redir

在模拟器运行期间添加和删除端口重定向。连接上控制台后,可以通过它来管理端口重定向:列出、增加、删除。

network status

检测网络状况和当前延迟、加速特性。

network delay gprs <delay>

同emulator -netdelay <delay>

network speed <speed>

同emulator -netspeed <speed>

gsm <call|data|voice>

在模拟器上模拟电话功能。可以模拟打入电话和建
/终止数据连接。Android系统模拟电话呼叫跟真实情况一样。这个版本暂不支持电话录音。

三、 Android模拟器快捷键

 


temp

这里特别介绍下最新版本的更适合平板电脑使用、专门为Android平板电脑进行优化的系统版本Honeycomb
Preview上,Ctrl-F11的使用。

在Honeycomb Preview上,默认是如下图所示显示的:

clip_image010

如果想要换成在电脑屏幕上正常显示,则需要:在“Menu”-“Settings”-“Screen”-“Auto-rotate
screen”,取消选中即可。如果此时在Honeycomb
Preview横向的时候,屏幕仍然翻转,则连续按两次Ctrl-F11——先切成竖屏,再切回横屏——这时,就会发现屏幕变成如下显示:

clip_image012

另外,可以在“Menu”-“Settings”-“Language&input”-“Select
language”中选择“中文(简体)”,则可以使用中文界面,如下:

clip_image014

更多Setting请自行摸索,有不清楚的在小生博客留言吧。

四、 模拟器的限制

模拟器基本上可以模拟绝大多数Android设备的功能,但是,迄今为止,模拟器仍存在如下限制:

  • 不支持呼叫和接听实际来电;但可以通过控制台模拟电话呼叫(呼入和呼出)
  • 不支持USB连接
  • 不支持相机/视频捕捉
  • 不支持音频输入(捕捉);但支持输出(重放)
  • 不支持扩展耳机
  • 不能确定连接状态
  • 不能确定电池电量水平和交流充电状态
  • 不能确定SD卡的插入/弹出
  • 不支持蓝牙

五、 运行多个模拟器实例

如果必要的话,可以同时运行多个模拟器实例。每个模拟器实例使用独立的用户数据内存和不同的
控制台端口。这样可以独立的管理每一个模拟器实例。但是,每个实例存储跨会话的持久用户数据的能力—用户设置和安装的应用程序—会受限制。具体如下:

l
只有第一个模拟器实例能根据会话保存用户数据。默认情况下它把用户数据保存在开发设备的~/.android/userdata.img
(Linux 和 Mac) 或 C:\Documents and
Settings\<user>\Local
Settings\Android\userdata.img
(Windows)文件里。在启动模拟器时使用-data选项来控制用户数据的存储和加载位置。

l
在第一个实例后启动的模拟器实例(并行的)在会话过程中也保存用户数据;但它们不会为下一个会话保存它。这些实例将数据保存在临时文件中,当实例退出时,相应的临时文件会被删除。

六、 模拟器上网问题

1、将网络连接代理设置写入配置数据库

① 通过命令行或者通过双击emulatoer可执行文件,打开模拟器

② 在命令行执行adb shell打开android的控制台

③ 执行 ls -l
/data/data/com.android.providers.settings/databases/settings.db
,查看系统设置数据库文件是否存在

④ 如果数据库文件存在,执行 sqlite3
/data/data/com.android.providers.settings/databases/settings.db,以settings.db为当前scheme启动SQLITE3

⑤ 执行 INSERT INTO system VALUES(99,’http_proxy’,
‘192.168.0.245:8080′);
语句插入数据,其中192.168.0.245是我们要使用的代理服务器IP,8080是端口,请根据具体情况替换

⑥ 执行 SELECT * FROM system; 来查看刚才插入的数据

⑦ 重启模拟器,以使代理服务器设置生效

⑧ 可以使用 DELETE FROM system WHERE _id=99; 语句来删除代理设置,或者使用 UPDATE
system SET value = ‘192.168.0.245:8070’ WHERE _id=99;
来修改代理服务器设置

2、可能是DNS的问题

① 用系统的命令进入Android开发包的tools目录

cd E:\android-sdk-windows\tools

② 使用adb的shell,确认系统的各项属性

adb shell

getprop

getprop会列出系统当前的各项属性

③ 得到模拟器的DNS地址

在结果里可以看到:

[net.dns1]: [10.0.2.3]

[net.dns2]: [10.0.2.4]

④ 把dns改成我们自己的DNS

setprop net.dns1 192.168.1.1

注:一般TP-LINK及D-LINK的DNS都是192.168.1.1,如果不是请使用ipcnofig
/all命令确认。

3、直接带参数启动模拟器

emulator @default -http-proxy 10.16.xx.xx:8080

其中default是模拟器的名称

4、在模拟器中设置

启动模拟器,然后进入 Settings->Wireless &
networks->Mobile networks->Access
Point Names

然后打开出现在列表中的access point;

然后下面这样设置:

– Proxy : your proxy address

– Port : your proxy port

– Username : your username if needed, or <Not
set>

– Password : your password if needed, or <Not
set>

更多相关文章,请访问:

http://blog.sina.com.cn/deaboway

http://blog.csdn.net/deaboway

以上两个blog同步更新。

用Eclipse开发和调试Android应用程序(二)

二、 创建Android Eclipse工程

请参考小生的《Android SDK
2.3与Eclipse最新版开发环境搭建(五)》,博文地址:http://blog.sina.com.cn/s/blog_573860a90100ownd.html
or
http://blog.csdn.net/deaboway/archive/2011/01/30/6170449.aspx

三、 工程结构详细解析

1. 工程结构

在Eclipse的Java透视图中,可以看到工程的Package Explorer如下图:

clip_image002

具体包括:

src 文件夹 – 包含示例应用程序的包,即org.deaboway.test。

R.java – Android Developer Tools 自动创建这个文件,它提供访问 Android
应用程序的各种资源所需的常量。后面会详细讨论 R 类与资源之间的关系。

HelloAndroidWorld.java – 应用程序的主活动类的实现。

Referenced libraries – 包含 android.jar,这是 Android SDK 中的
Android 运行时类的 jar 文件。

res 文件夹– 包含应用程序的资源,包括:

· drawable – 图标:这个文件夹包含图形文件,比如图标和位图。

· layout – 布局文件:这个文件夹包含表示应用程序布局和视图的 XML 文件。

· values – 字符串:这个文件夹包含 strings.xml 文件。这是为应用程序实现字符串本地化的主要方法。

AndriodManifest.xml – 示例应用程序的部署描述符。

default.properties 和proguard.cfg
从2.3版本以后,android sdk
本身包含proguard,可以通过正常的编译方式也能实现代码混淆,我们可以看到android-sdk-windows\tools\下面多了一个proguard文件夹。防止反编译工具进行反编译,保证代码安全。

2. 主类分析

这个示例应用程序由一个Activity(活动)组成,即HelloAndroidWorld。正如前面提到的,HelloAndroidWorld类是在HelloAndroidWorld.java
文件中实现的。

package org.deaboway.test;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TextView;

public class HelloAndroidWorld extends
Activity {

@Override

public void onCreate(Bundle savedInstanceState)
{

super.onCreate(savedInstanceState);

// TextView textView = new TextView(this);

// textView.setText(“Hello Android World!”);

// setContentView(textView);

setContentView(R.layout.main);

}

}

在这个源代码片段中,要注意几点:

  • HelloAndroidWorld 是一个普通的 Java 类,包含包和导入语句。
  • HelloAndroidWorld 扩展 android.app 包中的 Android 基类 Activity。
  • onCreate() 方法是这个活动的入口点,它接受一个 Bundle 类型的参数。Bundle 类本质上是 map 或
    hashmap 的包装器。在这个参数中传递构造活动所需的元素。这里暂时不讨论这个参数。
  • setContentView() 负责用 R.layout.main 参数创建主 UI。R.layout.main
    是应用程序资源中主布局的标识符。

3. R.java文件

R.java 文件是在构建时自动创建的,所以不要手工修改它,因为所有修改都会丢失。本实例的R.java文件内容如下:

package org.deaboway.test;

public final class R {

public static final class attr {

}

public static final class drawable
{

public static final int
icon=0x7f020000;

}

public static final class layout
{

public static final int
main=0x7f030000;

}

public static final class string
{

public static final int
app_name=0x7f040001;

public static final int
hello=0x7f040000;

}

}

R 类包含一些匿名子类,每个子类包含前面描述的各种资源的标识符。注意,这些类都是静态的。

请注意 R.layout.main 代表的元素。这个标识符代表由 main.xml 定义的布局。在活动的 onCreate
方法中可以使用这个值:setContentView(R.layout.main)。这就是在运行时把特定的活动(这里是HelloAndroidWorld)和特定的布局(主布局)联系在一起的方法。

4. main.xml文件

本示例应用程序包含一个活动和一个视图。应用程序包含一个名为 main.xml 的文件,它代表活动的主 UI 的视觉方面。

注意,在 main.xml 中没有指定在哪里使用这个布局。这意味着,如果需要的话,可以在多个活动中使用它。

<?xml version=“1.0”
encoding=“utf-8”?>

<LinearLayout
xmlns:android=“http://schemas.android.com/apk/res/android”

android:orientation=“vertical”

android:layout_width=“fill_parent”

android:layout_height=“fill_parent”

>

<TextView

android:layout_width=“fill_parent”

android:layout_height=“wrap_content”

android:text=“@string/hello”

/>

</LinearLayout>

这是最简单的布局,其中只有一个垂直的线性布局,这意味着所有元素排成一列。这里有一个 TextView
元素,它与其他开发环境中的标签类似。TextView 代表不可编辑的静态文本。

注意,每个视图元素(比如这个示例中的 LinearLayout 和 TextView)都有属于 Android
名称空间的属性。一些属性是所有视图元素都有的,比如 android:layout_width
和android:layout_height。这些属性可以采用的值是:

fill_parent – 使视图元素占满可用的空间。也可以认为这就是 “拉伸”。

wrap_content – 这个值让 Android 把元素一个接一个地排列,不进行拉伸。

5. AndroidManifest.xml文件

AndroidManifest.xml 文件是 Android
应用程序的部署描述符。这个文件列出应用程序中包含的所有Activity(活动)、ContentProvider(内容提供程序)、BroadcastReceiver(广播接收器)与Service(服务),以及应用程序支持的
IntentFilter。下面是这个示例应用程序的完整的 AndroidManifest.xml 文件。

<?xml version=“1.0”
encoding=“utf-8”?>

<manifest
xmlns:android=“http://schemas.android.com/apk/res/android”

package=“org.deaboway.test”

android:versionCode=“1”

android:versionName=“1.0”>

<application android:icon=“@drawable/icon”
android:label=“@string/app_name”>

<activity
android:name=“.HelloAndroidWorld”

android:label=“@string/app_name”>

<intent-filter>

<action
android:name=“android.intent.action.MAIN”
/>

<category
android:name=“android.intent.category.LAUNCHER”
/>

</intent-filter>

</activity>

</application>

</manifest>

  • 这里指定了源文件中的包名。采用与 Java
    源文件和导入语句相似的模式。<manifest> 标记的实际作用是
    “导入” 这个包中的类。在这个文件中,所有非完全限定的类都属于 package 属性指定的包。
  • <application>
    标记的一个属性引用了应用程序的一个资源。请注意 drawable 标识符前面的 @ 符号。这里的意思是,在应用程序资源的
    drawable 文件夹中寻找名为 “icon” 的资源。
  • <activity> 标记包含以下属性和值:
    • android:name表示实现这个活动的 Java 类。
    • android:label 是应用程序的名称。注意,它来自一个字符串资源。string.xml
      文件包含应用程序的本地化字符串。
    • <intent-filter> 表示应用程序中可用的
      IntentFilter。这是 Android 应用程序中最常见的 IntentFilter。这个过滤器的实际意思是,它实现 “主”
      操作(也就是入口点),而且它位于 OS 的启动器中。这意味着可以在 Android
      设备上像启动其他应用程序一样,从应用程序主列表中启动它。

四、 应用程序调试

要想检查正在运行的应用程序中发生了什么情况,就需要查看正在运行的 Dalvik VM。在 Eclipse 中,选择 Window
> Open Perspective > Other。在出现的对话框中选择
DDMS。这会在 Eclipse 中打开一个新的透视图,其中有许多有趣的窗口。

DDMS 为IDE和Emultor(or
GPhone)架起来了一座桥梁。Developer可以通过DDMS看到目标机器上运行的进程/线程状态:可以让Eclipse程序连接到开发机上运行;可以看进程的heap信息、logcat信息、进程分配内存情况;可以像目标机发送短信、发送地理位置信息以及打电话;可以像gdb一样attach
某一个进程调试。

下面简要介绍一下 DDMS 透视图中提供的资源:

1. Devices

Device 中罗列了Emulator中所有的进程,选项卡右上角那一排按钮分别为:Debug the selected
process调试进程、Update Heap更新进程堆栈信息、Dump HPROF file检查HPROF(Heap/CPU
Profiling Tool)文件、Cause GC调用垃圾回收、更新进程Update Threads、Start Method
Profiling开始方法分析、Stop Process停止某个进程,和Screen Capture抓取
Emulator目前的屏幕。当你选中某个进程,并按下调试进程按钮时,如果eclipse中有这个进程的代码,那就可以进行源代码级别的调试。有点像
gdb attach。图片抓取按钮可以把当前android的显示桌面抓到你的机器上,也是非常有用。

clip_image004

这里可以查看到所有与DDMS连接的终端的详细信息,以及每个终端正在运行的APP进程,每个进程最右边相对应的是与调试器链接的端口。因为Android是基于Linux内核开发的操作平台,同时也保留了Linux中特有的进程ID,它介于进程名和端口号之间。

DDMS监听第一个终端App进程的端口为8600,APP进程将分配8601,如果有更多终端或者更多APP进程将按照这个顺序依次类推。DDMS通过8700端口(”base
port”)接收所有终端的指令。

2. Emulator Control

通过这个面板的一些功能可以非常容易的使测试终端模拟真实手机所具备的一些交互功能,比如:接听电话,根据选项模拟各种不同网络情况,模拟接受SMS消息和发送虚拟地址坐标用于测试GPS功能等。

clip_image006

Telephony Status: 通过选项模拟语音质量以及信号连接模式。

Telephony Actions: 模拟电话接听和发送SMS到测试终端。

Location Control: 模拟地理坐标或者模拟动态的路线坐标变化并显示预设的地理标识,可以通过以下3种方式:

· Manual: 手动为终端发送二维经纬坐标。

· GPX: 通过GPX文件导入序列动态变化地理坐标,从而模拟行进中GPS变化的数值。

· KML: 通过KML文件导入独特的地理标识,并以动态形式根据变化的地理坐标显示在测试终端。

如:在Emulator Control\Telephony Actions 中输入如上图内容“Android
中文”,单击发送后,在Android模拟器中打开Messaging,看到下面的短信:

clip_image008

3. 文件管理器

显示文件系统信息。File
Explorer非常有用:它可以把文件上传到Android、或者从Android上下载文件下来、也可以进行文件删除操作。选项卡右上角有上传、下载、删除三个按钮。一般情况下,File
Explorer会有如下三个目录:data、sdcard、system。

· data对应手机的RAM,会存放Android OS
运行时的Cache等临时数据(/data/dalvik-cache目录);没有root权限时apk程序安装在/data/app中(只是存放apk
文件本身);/data/data中存放Android中所有程序(系统apk+第三方apk)的详细目录信息。

· mnt是mount,挂装的意思,mnt目录下的 sdcard对应sd卡。

· system对应手机的ROM,OS以及系统自带apk程序等存放在这里。

用户应用程序部署在 /data/app 目录中,而 Android 内置的应用程序部署在 /system/app
目录中。

clip_image010

4. Threads、Heap、Allocation Tracker

Threads视图列出当前进程的所有线程。

Heap视图展示一些堆的状态,在垃圾回收其间更新。当选定一个虚拟机时, VM
Heap视图不能显示数据,可以点击右边面包上的带有绿色的”Show heap updates”按钮,然后在点击”Cause GC
“实施垃圾回收更新堆的状态。

在Allocation Tracker视图里,我们可以跟踪每个选中的虚拟机的内存分配情况。点击”Start
Tracking”后点击”Get Allocations “就可以看到。

例如,为了查看Garbage Collection信息:点击 [Start Tracking]按钮
-> 保持程序运行一段时间后 ->点击[Get Allocations]
按钮,凭借得到的Allocations信息可以通过Line Number准确找到哪里的代码出现了问题。

clip_image012

5. LogCat

LogCat 是一个日志文件,它记录 VM 中发生的活动。应用程序可以通过 Log.i(tag,message);
在这个日志文件中添加自己的日志项,其中的 tag 和 message 都是 Java 字符串。Log
类属于android.util.Log 包。

clip_image014

6. Console

Android输出的信息,加载程序等信息。

clip_image016

用Eclipse开发和调试Android应用程序(一)

前面介绍了Windows环境下,基于Android SDK(2.3) 和
Eclipse(helios)的Android开发环境的搭建,并创建了第一个应用程序Hello Android
World。具体挺参考小生的blog:http://blog.sina.com.cn/deaboway
或者http://blog.csdn.net/deaboway

现在,我们已经可以使用Eclipse来创建和开发Android应用程序,本文将仍以Hello Android
World工程来深入解析Eclipse中Android工程的结构以及调试。

写上篇的时候,刚好Android SDK Platform Honeycomb Preview, revision
1(android-3.0_pre_r01-linux.zip)已经加入http://dl-ssl.google.com/android/repository/repository.xml,却无法下载,因此我们的第一个Android应用程序是用的Android
SDK Platform 2.3.1,即Android 9 AVD进行演示。现在Android SDK Platform
Honeycomb
Preview已经放到http://dl-ssl.google.com/android/repository/android-3.0_pre_r01-linux.zip,敢为天下先是我等求知若渴的程序员的优秀品质,因此,本次我们使用最新版本的Android
SDK Platform Honeycomb Preview来进行我们本次教程。

先看看最新的Honeycomb
Preview的样子吧(由于是Preview版本,启动确实不敢恭维,根据传闻,前几天之所以该版本一度无法下载安装是因为google发现这个Preview版本太烂,面子上挂不住,所以又撤掉了,呵呵):

clip_image002

相比手机上目前使用的最高Android 2.3版本而言,Android 3.0
Honeycomb更适合平板电脑使用,是专门为Android平板电脑进行优化的系统版本。随着SDK的发布,更加有利于开发者和厂商针对
Android 3.0 Honeycomb平板电脑进行开发,包括Android平板电脑应用和匹配。

一、 Android 应用程序概述

1. Android的嫡系组件

Android有四项一等公民(或称为嫡系组件),包括:Activity(活动)、ContentProvider(内容提供程序)、BroadcastReceiver(广播接收器)与Service(服务)。它们都必须宣告于AndroidManifest.xml档案里。

Activity活动

活动是最常用的 Android 应用程序形式。活动在一个称为视图(后文将介绍)的类的帮助下,为应用程序提供 UI。视图类实现各种
UI 元素,比如文本框、标签、按钮和计算平台上常见的其他 UI 元素。

一个应用程序可以包含一个或多个活动。这些活动通常与应用程序中的屏幕形成一对一关系。

应用程序通过调用 startActivity() 或 startSubActivity()
方法从一个活动转移到另一个活动。如果应用程序只需“切换”到新的活动,就应该使用前一个方法。如果需要异步的调用/响应模式,就使用后一个方法。在这两种情况下,都需要通过方法的参数传递一个
intent。

由操作系统负责决定哪个活动最适合满足指定的 intent(后文将介绍)。

对于Activity,关键是其生命周期的把握(后文将介绍),其次就是状态的保存和恢复(onSaveInstanceState
onRestoreInstanceState),以及Activity之间的跳转和数据传输(intent)。

Activity几乎承接着用户对应用程序(Application)的所有操作,Activity应该有一个窗口(Window),这个窗口是可以通过不用的主题(Theme)改变样子的。Activity应该要注意它的生命周期(Lifecycle)、设备状态(Configuration)改变时的影响以及运行状态和数据的保存,这个在一个应用程序是否可靠和人性化上至关重要。Activity里还应该要申明一些许可(Permissions),以便使用Android的一些软硬件功能,这些申明可以由代码或者Manifest.xml给出。最后,每个Activity(的入口)一定要在Manifest当中申明。

Service服务

与其他多任务计算环境一样,“在后台”运行着一些应用程序,它们执行各种任务。Android
把这种应用程序称为“Service服务”。

Service是没有界面的程序,它是所谓的服务,也叫后台程序。应该要非常注意Service的启动(startService)和绑定(bindService)这两种开启Service的方法之间的关系以及Service对应的生命周期,两种开户Service的方法对Service
的生命周期效果是不同的。还有就是申明许可以及申明Service,也是在代码内或者Manifest内申明。

BroadcastReceiver广播接收器

广播接收器是一个应用程序组件,它接收请求并处理 intent。与服务一样,接收器在一般情况下也没有 UI 元素。广播接收器通常在
AndroidManifest.xml 文件中注册。广播接收器的类属性是负责实现这个接收器的 Java 类。

广播接收并不是通常所说的无线电广播,而是指由sendBroadcast()所发送出来的意图(Intent),即广播在这里的意思是意图,BroadcastReceiver在注册(Registe)之后可以自动监听符合预先给定的条件的意图,如果有则会通知此
BroadcastReceiver的持有程序。

ContentProvider内容提供程序——数据管理

内容提供程序是 Android
的数据存储抽象机制。我们以移动设备上常见的一种数据为例:地址簿或联系人数据库。地址簿包含所有联系人及其电话号码,用户在使用手机时可能需要使用这些数据。内容提供程序对数据存储的访问方法进行抽象。内容提供程序在许多方面起到数据库服务器的作用。对数据存储中数据的读写操作应该通过适当的内容提供程序传递,而不是直接访问文件或数据库。可能还有内容提供程序
的 “客户机” 和 “实现”。

ContentProvider是作保存应用程序数据和建立维持数据库之用,以便程序重新启动时回到以前的状态或者保存信息。应该注意应用程序的使用权限以及SQL语言的使用,Android用的是一个轻量级的数据库系统SQLite。

2. Android生命周期

Android
程序的生命周期是由系统控制而非程序自身直接控制,这与桌面应用程序在程序自身收到关闭请求后执行一个特定的动作(比如从 main 函数中
return)而导致进程结束的思维不同。

在Android系统中,当某个activity调用startActivity(myIntent)时,系统会在所有已经安装的程序中寻找其intentfilter和myIntent最匹配的一个activity,启动这个进程,并把这个intent通知给这个activity。这就是一个程序的“生”。在Android中,所有的应用程序“生来就是平等的”,所以不光Android的核心程序甚至第三方程序也可以发出一个intent来启动另外一个程序中的一个activity。Android的这种设计非常有利于“程序部件”的重用。

Android根据其重要性在内存不足的时候移去重要性最低的进程。重要性由高到低为:

1.前台进程。这样的进程拥有一个在屏幕上显示并和用户交互的activity或者它的一个IntentReciver正在运行。这样的程序重要性最高,只有在系统内存非常低,万不得已时才会被结束。

2.可见进程。在屏幕上显示,但是不在前台的程序。比如一个前台进程以对话框的形式显示在该进程前面。这样的进程也很重要,它们只有在系统没有足够内存运行所有前台进程时,才会被结束。

3.服务进程。这样的进程在后台持续运行,比如后台音乐播放、后台数据上传下载等。这样的进程对用户来说一般很有用,所以只有当系统没有足够内存来维持所有的前台和可见进程时,才会被结束。

4.后台进程。这样的程序拥有一个用户不可见的activity。这样的程序在系统内存不足时,按照LRU的顺序被结束。

5.空进程。这样的进程不包含任何活动的程序部件。系统可能随时关闭这类进程。

从某种意义上讲,垃圾收集机制把程序员从“内存管理噩梦”中解放出来,而Android的进程生命周期管理机制把用户从“任务管理噩梦”中解放出来。Android使用Java作为应用程序API,并且结合其独特的生命周期管理机制同时为开发者和使用者提供最大程度的便利。

Activity生命周期

Activity中常用的函数有SetContentView() findViewById() finish()
startActivity(),其生命周期涉及的函数有:

void onCreate(Bundle savedInstanceState)

void onStart()

void onRestart()

void onResume()

void onPause()

void onStop()

void onDestroy()

clip_image004

注意的是,Activity的使用需要在Manifest文件中添加相应的<Activity>,并设置其属性和intent-filter。

Service生命周期

Service可以通过Context.startService()或Context.bindService()创建,通过Context.stopService()、Service.stopSelf()、Service.stopSelfResult()或Context.unbindService()来关闭。其生命周期涉及的函数有:

void onCreate()

void onStart(Intent intent)

void onDestroy()

其中onCreate()和onDestroy()可以被所有服务调用,无论是由Context.startService()还是Context.bindService()发起的Service。但是,onStart()只能被由startService()发起的Service调用。

如果一个Service运行其它对象绑定它,需要扩展如下callback方法:

IBinder onBind(Intent intent)

boolean onUnbind(Intent intent)

void onRebind(Intent intent)

clip_image006

BroadcastReceiver生命周期

只包含一个方法:void onReceive(Context curContext, Intent
broadcastMsg)

包含活动的组件的BroadcastReceiver将不会被系统关闭,但是仅包含不活动的组件的进程将随时会背系统关闭(当其它组建需要内存时)。

3.
Intent简介——Android的创新导航与触发机制

前文介绍了Android的四项一等公民:Activity(活动)、ContentProvider(内容提供程序)、BroadcastReceiver(广播接收器)与Service(服务)。这四种组件是独立的,它们之间可以互相调用,协调工作,最终组成一个真正的Android应用。

在这些组件之间的通讯中,主要是由Intent协助完成的。Intent是Android应用开发里很重要的一个元件,通过Intent可以从一个Activity来启动另一个任意的Activity,不管是自己定义的还是系统定义的。在ActivityGroup(extends
Activity)里面,Intent的flag设置对于子Activity的启动方式至关重要。

Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将
Intent传递给调用的组件,并完成组件的调用。

因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。

Android应用程序框架的强大之处在于它将Web习惯引入到移动应用程序中。这并不意味着该平台提供了一个强大的浏览器,也不仅限于使用
JavaScript和服务器端资源,而是涉及Android平台的工作原理以及该平台的用户如何与移动设备交互这一核心问题。互联网的强大之处一言以蔽之,在于一切事物通过一次单击即可获得。这些单击的内容对于用户来说就是URL(UniformResource
Locator,统一资源定位符)或URI(Uniform Resource
Identifier,统一资源标识符)。有效使用URI可帮助用户方便快捷地访问所需的日常信息。“把链接发给我”就说明了一切。

在移动设备上复制桌面设备体验的平台只能吸引一小部分忠实的用户。多级菜单、多次单击在移动市场中通常都不被人所接受。移动应用程序对直观易用的要求比其他任何市场中的应用程序都要高。Intent和IntentFilter将“单击”范例引入到了Android平台移动应用程序使用和开发的核心中。

Intent结构

Intent是执行某操作的一个抽象描述,它描述了如下内容:

首先,要执行的动作(action)的一个简要描述,如VIEW_ACTION(查看)、EDIT_ACTION(修改)等,Android为我们定义了一套标准动作:

MAIN_ACTION

VIEW_ACTION

EDIT_ACTION

PICK_ACTION

GET_CONTENT_ACTION

DIAL_ACTION

CALL_ACTION

SENDTO_ACTION

ANSWER_ACTION

INSERT_ACTION

DELETE_ACTION

RUN_ACTION

LOGIN_ACTION

CLEAR_CREDENTIALS_ACTION

SYNC_ACTION

PICK_ACTIVITY_ACTION

WEB_SEARCH_ACTION

此外,我们还可以根据应用的需要,定义我们自己的动作,并可定义相应的Activity来处理我们的自定义动作。

其次,执行动作要操作的数据(data),Android中采用指向数据的一个URI来表示,如在联系人应用中,一个指向某联系人的URI可能为:content://contacts/1。这种URI表示,通过
ContentURI这个类来描述,具体可以参考android.net.ContentURI类的文档。

此外,除了action和data这两个重要属性外,还有一些附加属性:

category(类别),被执行动作的附加信息。例如 LAUNCHER_CATEGORY 表示Intent
的接受者应该在Launcher中作为顶级应用出现;而ALTERNATIVE_CATEGORY表示当前的Intent是一系列的可选动作中的一个,这些动作可以在同一块数据上执行。

type(数据类型),显式指定Intent的数据类型(MIME)。一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。

component(组件),指定Intent的的目标组件的类名称。通常 Android会根据Intent
中包含的其它属性的信息,比如action、data/type、category进行查找,最终找到一个与之匹配的目标组件。但是,如果
component这个属性有指定的话,将直接使用它指定的组件,而不再执行上述查找过程。指定了这个属性以后,Intent的其它所有属性都是可选的。

extras(附加信息),是其它所有附加信息的集合。使用extras可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。

总之,action、 data/type、category和extras
一起形成了一种语言。这种语言使系统能够理解诸如“查看某联系人的详细信息”之类的短语。随着应用不断的加入到系统中,它们可以添加新的action、
data/type、category来扩展这种语言。应用也可以提供自己的Activity来处理已经存在的这样的“短语”,从而改变这些“短语”的行为。

解析Intent

在应用中,我们可以以两种形式来使用Intent:

·
显式(直接)Intent:指定了component属性的Intent(调用setComponent(ComponentName)或者setClass(Context,Class)来指定)。通过指定具体的组件类,通知应用启动对应的组件。

·
隐式(间接)Intent:没有指定comonent属性的Intent。这些Intent需要包含足够的信息,这样系统才能根据这些信息,在在所有的可用组件中,确定满足此Intent的组件。

对于显式(直接)Intent,Android不需要去做解析,因为目标组件已经很明确,Android需要解析的是那些隐式(间接)Intent,通过解析,将Intent映射给可以处理此Intent的Activity、BroadcastReceiver或Service。

Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行判断的,判断方法如下:

·
如果Intent指明定了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配;

·
如果Intent没有提供type,系统将从data中得到数据类型。和action一样,目标组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。

·
如果Intent中的数据不是content:类型的URI,而且Intent也没有明确指定它的type,将根据Intent中数据的scheme(比如http:或者mailto:)进行匹配。同上,Intent的scheme必须出现在目标组件的scheme列表中。

·
如果Intent指定了一个或多个category,这些类别必须全部出现在组建的类别列表中。比如Intent中包含了两个类别:LAUNCHER_CATEGORY和ALTERNATIVE_CATEGORY,解析得到的目标组件必须至少包含这两个类别。

IntentIntentFilter

Intent是对需要的声明。

IntentFilter是在需要时有能力和有兴趣提供协助的声明。

Intent由一系列描述所需动作或服务的信息构成。本节将介绍所请求的动作以及与之相关的数据。

IntentFilter可以是通用的,也可以特定于向某些Intent提供服务。

Intent的动作属性通常为动词,例如VIEW、PICK或EDIT。很多内置的Intent动作都是作为Intent类的成员定义的。应用程序开发人员也可以创建新动作。要查看信息,应用程序可以采用以下Intent动作:Intent的数据部分采用URI的形式表示,并且可以是信息的任何部分,例如联系人记录、网站位置或对媒体剪辑的引用。

IntentFilter定义Intent与应用程序之间的关系。IntentFilter可以与Intent的数据部分或动作部分相关,或同时与两者相关。IntentFilter还包含一个category(类别)字段。类别可以帮助对动作进行分类。例如,CATEGORY_LAUNCHER类别指示Android包含此Intent-Filter的Activity在主应用程序启动器或主界面上应该处于可见状态。

分发Intent后,系统会计算可用的Activity、Service,以及已注册的BroadcastReceiver,并将
Intent分发给大多数适当的接收者。

clip_image007

IntentFilter通常在应用程序的AndroidManifest.xml中使用<intent-filter>标记进行定义。从本质上说,AndroidManifest.xml文件就是一个应用程序描述符文件。

下一节介绍,这是 Android 在移动设备屏幕上显示 UI 元素的机制。

4. Android
视图——显示用户界面(UI)元素

Android 活动通过视图显示UI元素。视图采用以下布局设计之一:

· LinearVertical – 后续的每个元素都排在前一个元素下面,形成一个单一列。

· LinearHorizontal – 后续的每个元素都排在前一个元素右边,形成一个单一行。

· Relative – 后续的每个元素相对于前一个元素有一定的偏移量。

· Table – 与 HTML 表相似的一系列行和列。每个单元格可以包含一个视图元素。

选择一种布局(或布局的组合)之后,就可以用各个视图显示 UI。

视图元素由大家熟悉的 UI 元素组成,包括:

· Button

· ImageButton

· EditText

· TextView(与标签相似)

· CheckBox

· Radio Button

· Gallery 和 ImageSwitcher(用来显示多个图像)

· List

· Grid

· DatePicker

· TimePicker

· Spinner(与组合框相似)

· AutoComplete(具有文本自动补全特性的 EditText)

视图是在一个 XML 文件中定义的。每个元素都有一个或多个属于 Android 名称空间的属性。