面试中遇到的几个问题

SAX与DOM之间的区别

SAX(SimpleAPIforXML)和DOM(DocumentObjectModel)是当前两个主要的XMLAPI,几乎所有商用的xml解析器都同时实现了这两个接口。因此如果你的程序使用了SAX或者DOMAPIs,那么你的程序对xml解析器是透明。

1.DOM以一个分层的对象模型来映射xml文档。而SAX将文档中的元素转化为对象来处理。

2.DOM将文档载入到内存中处理,而SAX则相反,它可以检测一个即将到来的XML流,由此并不需要所有的XML代码同时载入到内存中。

SAX处理是如何工作的

SAX在读取XML流的同时处理它们,这很像以前的自动收报机纸带(tickertape)。请考虑下面的XML代码片断:

<?xmlversion="1.0"?>

<samples>

<server>UNIX</server>

<monitor>color</monitor>

</samples>

分析这个代码片断的SAX处理器一般情况下将产生以下事件:

Startdocument

Startelement(samples)

Characters(whitespace)

Startelement(server)

Characters(UNIX)

Endelement(server)

Characters(whitespace)

Startelement(monitor)

Characters(color)

Endelement(monitor)

Characters(whitespace)

Endelement(samples)

SAXAPI允许开发人员捕捉这些事件并对它们作出反应。

SAX处理涉及以下步骤:

1.创建一个事件处理程序。

2.创建SAX解析器。

3.向解析器分配事件处理程序。

4.解析文档,同时向事件处理程序发送每个事件。

基于事件的处理的优点和缺点

这种处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。一般来说,SAX还比它的替代者DOM快许多。

另一方面,由于应用程序没有以任何方式存储数据,使用SAX来更改数据或在数据流中往后移是不可能的。

DOM和基于树的处理

DOM是处理XML数据的传统方法。使用DOM时,数据以树状结构的形式被加载到内存中。

例如,在“SAX处理是如何工作的”中用作例子的相同文档在DOM中将表示为节点,DOM使用父子关系。

基于树的处理的优点和缺点

DOM以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改。它还可以在任何时候在树中上下导航,而不是像SAX那样是一次性的处理。DOM使用起来也要简单得多。

另一方面,在内存中构造这样的树涉及大量的开销。大型文件完全占用系统内存容量的情况并不鲜见。此外,创建一棵DOM树可能是一个缓慢的过程。

如何在SAX和DOM之间选择

选择DOM还是选择SAX,这取决于下面几个因素:

1.应用程序的目的:如果打算对数据作出更改并将它输出为XML,那么在大多数情况下,DOM是适当的选择。并不是说使用SAX就不能更改数据,但是该过程要复杂得多,因为您必须对数据的一份拷贝而不是对数据本身作出更改。

2.数据容量:对于大型文件,SAX是更好的选择。

数据将如何使用:如果只有数据中的少量部分会被使用,那么使用SAX来将该部分数据提取到应用程序中可能更好。另一方面,如果您知道自己以后会回头引用已处理过的大量信息,那么SAX也许不是恰当的选择。

3.对速度的需要:SAX实现通常要比DOM实现更快。

SAX和DOM不是相互排斥的,记住这点很重要。您可以使用DOM来创建SAX事件流,也可以使用SAX来创建DOM树。事实上,用于创建DOM树的大多数解析器实际上都使用SAX来完成这个任务!

Android中ListView.getCount()与ListView.getChildCount()区别

2010-05-0511:13:06|分类:Android|字号订阅

ListView.getCount()(实际上是AdapterView.getCount())返回的是其Adapter.getCount()返回的值。也就是“所包含的Item总个数”。

ListView.getChildCount()(ViewGroup.getChildCount)返回的是显示层面上的“所包含的子View个数”。

二者有什么不同?当ListView中的Item比较少无需滚动即可全部显示时,二者是等价的;当Item个数较多需要滚动才能浏览全部的话,getChildCount()<getCount()其中getChildCount()返回的是当前可见的Item个数。

其实Androidframework的这一设计并不难理解:当一些Item当前不显示的时候为什么还要保留它们的View呢?移动设备的资源有限,“能省则省”嘛。

View和ViewGroup介绍

资源描述:

Activity(活动)中包含views(视图)和ViewGroups(视图组)。

“视图”(View)就是显示在屏幕上的一个组件(Widget)。View的例子:按钮(Button)、标签(TextView)和文本框(EditText)。每个“视图”(View)都继承自基类android.view.View。

“视图组”(ViewGroup)可以包含一个或多个View。ViewGroup本身就是一种特殊的View,它提供了一个布局,可以使用这个布局去组织一系列的View视图。

ViewGroups的例子:LinearLayout和FrameLayout。每个“ViewGroup”都继承自基类android.view.ViewGroup。

Android提供了以下的ViewGroups:

LinearLayout

AbsoluteLayout

TableLayout

RelativeLayout

FrameLayout

ScrollView

在接下来的几节教程中,将详细地介绍每一个ViewGroup。请注意,通常情况下,在创建UI的时候,都要混合使用一种或几种不同的ViewGroup。

SurfaceView和View最本质的区别

SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。

那么在UI的主线程中更新画面可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。

当使用surfaceView由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个eventqueue的设计来保存touchevent,这会稍稍复杂一点,因为涉及到线程同步。

所以基于以上,根据游戏特点,一般分成两类。

1被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于onTouch来更新,可以直接使用invalidate。因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

2主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞mainUIthread。所以显然view不合适,需要surfaceView来控制。

在Android游戏当中充当主要的除了控制类外就是显示类,在J2ME中我们用Display和Canvas来实现这些,而GoogleAndroid中涉及到显示的为view类,Android游戏开发中比较重要和复杂的就是显示和游戏逻辑的处理。

这里我们说下android.view.View和android.view.SurfaceView。SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView,到底有哪些优势呢?SurfaceView可以控制表面的格式,比如大小,显示在屏幕中的位置,最关键是的提供了SurfaceHolder类,使用getHolder方法获取,相关的有CanvaslockCanvas()

CanvaslockCanvas(Rectdirty)、voidremoveCallback(SurfaceHolder.Callbackcallback)、voidunlockCanvasAndPost(Canvascanvas)控制图形以及绘制,而在SurfaceHolder.Callback接口回调中可以通过重写下面方法实现。

使用的SurfaceView的时候,一般情况下要对其进行创建,销毁,改变时的情况进行监视,这就要用到SurfaceHolder.Callback.

classXxxViewextendsSurfaceViewimplementsSurfaceHolder.Callback{

publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intwidth,intheight){}

//看其名知其义,在surface的大小发生改变时激发

publicvoidsurfaceCreated(SurfaceHolderholder){}

//同上,在创建时激发,一般在这里调用画图的线程。

publicvoidsurfaceDestroyed(SurfaceHolderholder){}

//同上,销毁时激发,一般在这里将画图的线程停止、释放。

}

对于Surface相关的,Android底层还提供了GPU加速功能,所以一般实时性很强的应用中主要使用SurfaceView而不是直接从View构建,同时Android123未来后面说到的OpenGL中的GLSurfaceView也是从该类实现。

Activity,Window,View的关系:

1,一个Activity构造的时候会构造一个window(PhoneWindow),并且只有一个。

2,这个窗户有一个ViewRoot(view或viewgroup);

3,addView();

4,WindowManager接受的消息,并且回调Activity函数,如onkeydown();

2、Service与Thread的区别

很多时候,你可能会问,为什么要用Service,而不用Thread呢,因为用Thread是很方便的,比起Service也方便多了,下面我详细的来解释一下。

1).Thread:Thread是程序执行的最小单元,它是分配CPU的基本单位。可以用Thread来执行一些异步的操作。

2).Service:Service是android的一种机制,当它运行的时候如果是LocalService,那么对应的Service是运行在主进程的main线程上的。如:onCreate,onStart这些函数在被系统调用的时候都是在主进程的main线程上运行的。如果是RemoteService,那么对应的Service则是运行在独立进程的main线程上。因此请不要把Service理解成线程,它跟线程半毛钱的关系都没有!

既然这样,那么我们为什么要用Service呢?其实这跟android的系统机制有关,我们先拿Thread来说。Thread的运行是独立于Activity的,也就是说当一个Activity被finish之后,如果你没有主动停止Thread或者Thread里的run方法没有执行完毕的话,Thread也会一直执行。因此这里会出现一个问题:当Activity被finish之后,你不再持有该Thread的引用。另一方面,你没有办法在不同的Activity中对同一Thread进行控制。

举个例子:如果你的Thread需要不停地隔一段时间就要连接服务器做某种同步的话,该Thread需要在Activity没有start的时候也在运行。这个时候当你start一个Activity就没有办法在该Activity里面控制之前创建的Thread。因此你便需要创建并启动一个Service,在Service里面创建、运行并控制该Thread,这样便解决了该问题(因为任何Activity都可以控制同一Service,而系统也只会创建一个对应Service的实例)。

因此你可以把Service想象成一种消息服务,而你可以在任何有Context的地方调用Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在Service里注册BroadcastReceiver,在其他地方通过发送broadcast来控制它,当然这些都是Thread做不到的。

3、Service的生命周期

onCreate  onStart  onDestroy  onBind

1).被启动的服务的生命周期:如果一个Service被某个Activity调用Context.startService方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

2).被绑定的服务的生命周期:如果一个Service被某个Activity调用Context.bindService方法绑定启动,不管调用bindService调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService断开连接或者之前调用bindService的Context不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

3).被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用stopService或Service的stopSelf来停止服务。

4).当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

特别注意:

1、你应当知道在调用bindService绑定到Service的时候,你就应当保证在某处调用unbindService解除绑定(尽管Activity被finish的时候绑定会自      动解除,并且Service会自动停止);

2、你应当注意使用startService启动服务之后,一定要使用stopService停止服务,不管你是否使用bindService;

3、同时使用startService与bindService要注意到,Service的终止,需要unbindService与stopService同时调用,才能终止Service,不管startService与bindService的调用顺序,如果先调用unbindService此时服务不会自动终止,再调用stopService之后服务才会停止,如果先调用stopService此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后服务才会自动停止;

4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的Activity如果会自动旋转的话,旋转其实是Activity的重新创建,因此旋转之前的使用bindService建立的连接便会断开(Context不存在了),对应服务的生命周期与上述相同。

5、在sdk2.0及其以后的版本中,对应的onStart已经被否决变为了onStartCommand,不过之前的onStart任然有效。这意味着,如果你开发的应用程序用的sdk为2.0及其以后的版本,那么你应当使用onStartCommand而不是onStart。

4、startService启动服务

想要用startService启动服务,不管Local还是Remote我们需要做的工作都是一样简单。当然要记得在Androidmanifest.xml中注册service。

相关推荐