android面试题2

一。UI线程和非UI线程的交互方式,写出几种各自的耗时

1、handler

2、Activity.runOnUIThread(Runnable)

3、View.Post(Runnable)

4、View.PostDelayed(Runnabe,long)

5、AsyncTask

http://www.cr173.com/html/19165_1.html

二。requestLayout, invalidate和postInvalidate的异同

Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
http://www.cnblogs.com/tt_mc/archive/2012/01/30/2332023.html

三。写出三种常用layout布局并进行解释

http://www.360doc.com/content/12/0410/13/7656232_202460915.shtml

四。view的绘制过程,讲讲draw/onDraw和drawChild这个可以口

 

绘制VIew本身的内容,通过调用View.onDraw(canvas)函数实现

绘制自己的孩子通过dispatchDraw(canvas)实现


View组件的绘制会调用draw(Canvas canvas)方法,draw过程中主要是先画Drawable背景,对 drawable调用setBounds()然后是draw(Canvas c)方法.有点注意的是背景drawable的实际大小会影响view组件的大小,drawable的实际大小通过getIntrinsicWidth()和getIntrinsicHeight()获取,当背景比较大时view组件大小等于背景drawable的大小。


画完背景后,draw过程会调用onDraw(Canvas canvas)方法,然后就是dispatchDraw(Canvas canvas)方法, dispatchDraw()主要是分发给子组件进行绘制,我们通常定制组件的时候重写的是onDraw()方法。值得注意的是ViewGroup容器组件的绘制,当它没有背景时直接调用的是dispatchDraw()方法, 而绕过了draw()方法,当它有背景的时候就调用draw()方法,而draw()方法里包含了dispatchDraw()方法的调用。因此要在ViewGroup上绘制东西的时候往往重写的是dispatchDraw()方法而不是onDraw()方法,或者自定制一个Drawable,重写它的draw(Canvas c)和 getIntrinsicWidth(),。

getIntrinsicHeight()方法,然后设为背景。

http://blog.csdn.net/fener10289/article/details/8231712

 

五。View和ViewGroup的关系

Android系统中的所有UI类都是建立在View和ViewGroup这两个类的基础上的。所有View的子类成为”Widget”,所有ViewGroup的子类成为”Layout”。View和ViewGroup之间采用了组合设计模式,可以使得“部分-整体”同等对待。ViewGroup作为布局容器类的最上层,布局容器里面又可以有View和ViewGroup

。Activity、Window、View之间的关系

 

而当我们运行程序的时候,有一个setContentView()方法,Activity其实不是显示视图(直观上感觉是它),实际上Activity调用了PhoneWindow的setContentView()方法,然后加载视图,将视图放到这个Window上,而Activity其实构造的时候初始化的是Window(PhoneWindow),Activity其实是个控制单元,即可视的人机交互界面。

setContentView(R.layout.main)其实就是下面内容。(注释掉本行执行下面的代码可以更直观)

getWindow().setContentView(LayoutInflater.from(this).inflate(R.layout.main,null))

即运行程序后,Activity会调用PhoneWindow的setContentView()来生成一个Window,而此时的setContentView就是那个最底层的View。然后通过LayoutInflater.infalte()方法加载布局生成View对象并通过addView()方法添加到Window上,(一层一层的叠加到Window上)

所以,Activity其实不是显示视图,View才是真正的显示视图

注:一个Activity构造的时候只能初始化一个Window(PhoneWindow),另外这个PhoneWindow有一个”ViewRoot”,这个”ViewRoot”是一个ViewViewGroup,是最初始的跟视图,然后通过addView方法将View一个个层叠到ViewRoot上,这些层叠的View最终放在Window这个载体上面

 

七。什么是dpi,ps,sp

px (pixels)像素 -- 是像素,就是屏幕上实际的像素点单位。
dip或dp (device independent pixels)设备独立像素, 与设备屏幕有关。
sp (scaled pixels — best for text size)放大像素-- 主要处理字体的大小。
dpi:屏幕像素密度。

至于根源。
android最早是没有考虑到这么多屏幕分辨率的。最早的机器是g1,他的分辨率是480*320。但是,由于android是开放的平台,各种各样分辨率的设备都可以运行。为了兼容这些平台,android从1.6开始,加入了设备独立像素,dip或者dp。标准屏幕480*320上,px与dp是1比1的。分辨率高的,比如800*480,就要按比例兑换成480*320。
开发者在开发的时候,ui设计时最好用dp,系统会自动按比例计算为px,从而适配视图。
sp我一直以为是跟dp一样的,今天一查,估计是专门配置字体大小的。不过,这个问题不是很大,dp也是可以的。

dpi是屏幕像素密度。就是1英寸上像素点的个数。对于屏幕来说,dpi越大,屏幕的精细度越高,屏幕看起来就越清楚

 

 

八。怎样的类可以作为HashMap的键

应该尽量避免使用“可变”的类作为HashMap的键

Hashmap实际上是一个数组和链表的结合体,利用数组来模拟一个个桶(类似于Bucket Sort)以快速存取不同hashCode的key,对于相同hashCode的不同key,再调用其equals方法从List中提取出和key所相对应的value。

Java中hashMap的初始化主要是为initialCapacity和loadFactor这两个属性赋值。前者表示hashMap中用来区分不同hash值的key空间长度,后者是指定了当hashMap中的元素超过多少的时候,开始自动扩容,。默认情况下initialCapacity为16,loadFactor为0.75,它表示一开始hashMap可以存放16个不同的hashCode,当填充到第12个的时候,hashMap会自动将其key空间的长度扩容到32,以此类推

LinkedHashMap的实现与 HashMap 的不同之处在于,前者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是集合中元素的插入顺序。该类定义了header、before与after三个属性来表示该集合类的头与前后“指针”,其具体用法类似于数据结构中的双链表,

 九。

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

 

List Set,区别在于List是有序的Collection,且其中允许重复的元素,比如我们常用的Vector,ArrayList,LinkedList,都是实现了List接口的类;而Set是一种不包含重复的元素的Collection。

Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。HashMap是实现了Map接口的具体类。
Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的

HashMap允许将null作为一个entry的key或者value,而Hashtable不允许

HashTable使用Enumeration,HashMap使用Iterator。 以上只是表面的不同,它们的实现也有很大的不同

HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey

HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

哈希值的使用不同,HashTable直接使用对象的hashCode;而HashMap重新计算hash值,而且用与代替求模

十。Jni调用机制是什么

Java Native Interface(JNI)是java本地接口,所谓的本地(native)一般是指c/c++语言。当使用java进行程序设计的时候,一般主要有三种情况需要c的协助。

1)调用驱动。由于操作系统提供的驱动接口一般都是c接口,java语言本身不具备操作这些驱动的能力

2)对于某些大量数据处理的模块,java效率可能远低于c,因此,程序员希望使用c完成

3)对于某些功能模块,可能java和c的效率差不多,但是这些模块已经有现成的c代码,程序员不想用java重写,而是复用已有的c代码。

十一。通过Intent传递一些二进制数据的方法有哪些?

1). 使用Serializable接口实现序列化,这是Java常用的方法。
 
2). 实现Parcelable接口,这里Android的部分类比如Bitmap类就已经实现了,同时Parcelable在Android AIDL中交换数据也很常见的。

十二。sim卡的EF 文件有何作用
sim卡的文件系统有自己规范,主要是为了和手机通讯,sim本
身可以有自己的操作系统,EF就是作存储并和手机通讯用的

十三。什么是嵌入式实时操作系统, Android 操作系统属于实时操作系统吗?
嵌入式实时操作系统是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统作出快速响应,并控制所有实时任务协调一致运行的嵌入式操作系统。主要用于工业控制、军事设备、
航空航天等领域对系统的响应时间有苛刻的要求,这就需要使用实时系统。又可分为软实时和硬实时两种,而android是基于linux内核的,因此属于软实时。

十四。 Android中Task任务栈的分配。

假如有三个Activity A B C,A跳到B,然后B跳到C,现在需要从C跳到A如何传递数据而且使效率最高呢?

首先我们来看下Task的定义,Google是这样定义Task的:a task is what the user experiences as an "application."It's a group of related activities, arranged in a stack. A task is a stack ofactivities, not a class or an element in the manifest file. 这意思就是说Task实际上是一个Activity栈,通常用户感受的一个Application就是一个Task。从这个定义来看,Task跟Service或者其他Components是没有任何联系的,它只是针对Activity而言的。

Activity有不同的启动模式, 可以影响到task的分配

Task,简单的说,就是一组以栈的模式聚集在一起的Activity组件集合。它们有潜在的前后驱关联,新加入的Activity组件,位于栈顶,并仅有在栈顶的Activity,才会有机会与用户进行交互。而当栈顶的Activity完成使命退出的时候,Task会将其退栈,并让下一个将跑到栈顶的Activity来于用户面对面,直至栈中再无更多Activity,Task结束。

事件 Task栈(粗体为栈顶组件)

点开Email应用,进入收件箱(Activity A) A

选中一封邮件,点击查看详情(Activity B) AB

点击回复,开始写新邮件(Activity C) ABC

写了几行字,点击选择联系人,进入选择联系人界面(Activity D) ABCD

选择好了联系人,继续写邮件 ABC

写好邮件,发送完成,回到原始邮件 AB

点击返回,回到收件箱 A

退出Email程序 null

如上表所示,是一个实例。从用户从进入邮箱开始,到回复完成,退出应用整个过程的Task栈变化。这是一个标准的栈模式,对于大部分的状况,这样的Task模型,足以应付,但是,涉及到实际的性能、开销等问题,就会变得残酷许多。

比如,启动一个浏览器,在Android中是一个比较沉重的过程,它需要做很多初始化的工作,并且会有不小的内存开销。但与此同时,用浏览器打开一些内容,又是一般应用都会有的一个需求。设想一下,如果同时有十个运行着的应用(就会对应着是多个Task),都需要启动浏览器,这将是一个多么残酷的场面,十个Task栈都堆积着很雷同的浏览器Activity,

是多么华丽的一种浪费啊。

于是你会有这样一种设想,浏览器Activity,可不可以作为一个单独的Task而存在,不管是来自那个Task的请求,浏览器的Task,都不会归并过去。这样,虽然浏览器Activity本身需要维系的状态更多了,但整体的开销将大大的减少,这种舍小家为大家的行为,还是很值得歌颂的

standard", "singleTop","singleTask", "singleInstance"。

standard模式,是默认的也是标准的Task模式,在没有其他因素的影响下,使用此模式的Activity,会构造一个Activity的实例,加入到调用者的Task栈中去,对于使用频度一般开销一般什么都一般的Activity而言,standard模式无疑是最合适的,因为它逻辑简单条理清晰,所以是默认的选择。

而singleTop模式,基本上于standard一致,仅在请求的Activity正好位于栈顶时,有所区别。此时,配置成singleTop的Activity,不再会构造新的实例加入到Task栈中,而是将新来的Intent发送到栈顶Activity中,栈顶的Activity可以通过重载onNewIntent来处理新的Intent(当然,也可以无视...)。这个模式,降低了位于栈顶时的一些重复开销,更避免了一些奇异的行为(想象一下,如果在栈顶连续几个都是同样的Activity,再一级级退出的时候,这是怎么样的用户体验...),很适合一些会有更新的列表Activity展示。一个活生生的实例是,在Android默认提供的应用中,浏览器(Browser)的书签Activity(BrowserBookmarkPage),就用的是singleTop。

singleTask,和singleInstance,则都采取的另辟Task的蹊径。

标志为singleTask的Activity,最多仅有一个实例存在,并且,位于以它为根的Task中。所有对该Activity的请求,都会跳到该Activity的Task中展开进行。singleTask,很象概念中的单件模式,所有的修改都是基于一个实例,这通常用在构造成本很大,但切换成本较小的Activity中。最典型的例子,还是浏览器应用的主Activity(名为Browser...),它是展示当前tab,当前页面内容的窗口。它的构造成本大,但页面的切换还是较快的,于singleTask相配,还是挺天作之合的。

singleInstance显得更为极端一些。在大部分时候singleInstance与singleTask完全一致,唯一的不同在于,singleInstance的Activity,是它所在栈中仅有的一个Activity,如果涉及到的其他Activity,都移交到其他Task中进行。这使得singleInstance的Activity,像一座孤岛,彻底的黑盒,它不关注请求来自何方,也不计较后续由谁执行。在Android默认的各个应用中,很少有这样的Activity,在我个人的工程实践中,曾尝试在有道词典的快速取词Activity中采用过,

是因为我觉得快速取词入口足够方便(从notification中点选进入),并且会在各个场合使用,应该做得完全独立。

大的apk 拆成很多小的apk

●Activity的android:taskAffinity=""属性姻缘关系

1.配置后当启动这个activity时就先去找有没有activity的亲和力属性相同有就加入这个

activity所在的任务中没有就新开任务

2.affinity起作用需要的条件而者具备一个:

1.intent包含FLAG_ACTIVITY_NEW_TASK标记

2.activity元素启用了allowTaskReparenting属性.

 十五。对android虚拟机的理解,包括内存管理机制垃圾回收机制

dalvik基于寄存器,二地址或三地址指令;而JVM基于stack ,零地址指令

Dalvik执行的是特有的DEX文件格式,而JVM运行的是*.class文件格式。

优势:1、在编译时提前优化代码而不是等到运行时

2、 虚拟机很小,使用的空间也小;被设计来满足可高效运行多种虚拟机实例。

3、常量池已被修改为只使用32位的索引,以 简化解释器

十六。

android的service的生命周期?哪个方法可以多次被调用:
答:1)与采用Context.startService()方法启动服务有关的生命周期方法
onCreate() -> onStart() -> onDestroy()
onCreate()该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。
onDestroy()该方法在服务被终止时调用。

2)与采用Context.bindService()方法启动服务有关的生命周期方法
onCreate() -> onBind() -> onUnbind() -> onDestroy()
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
如果先采用startService()方法启动服务,然后调用bindService()方法绑定到服务,再调用unbindService()方法解除绑定,最后调用bindService()方法再次绑定到服务,触发的生命周期方法如下:
onCreate() ->onStart() ->onBind() ->onUnbind()[重载后的方法需返回true] ->onRebind()
十七.android的broadcast的生命周期:
答:1)Broadcast receiver生命周期中仅有一个回调方法:
void onReceive(Context curContext, Intent broadcastMsg)
当接收器接收到一条broadcast消息,Android就会调用onReceiver(),并传递给它一个Intent对象,这个对象携带着那条broadcast消息。我们认为仅当执行这个方式时,Broadcast receiver是活动的;这个方法返回时,它就终止了。这就是Broadcast receiver的生命周期。

2)由于Broadcast receiver的生命周期很短,一个带有活动的Broadcast receiver的进程是受保护的,以避免被干掉;但是别忘了有一点,Android会在任意时刻干掉那些携带不再活动的组件的进程,所以很可能会造成这个问题。

3)解决上述问题的方案采用一个Service来完成这项工作,Android会认为那个进程中(Service所在的进程)仍然有在活动的组件。
十八。.android view,surfaceview,glsurfaceview的区别:
答:SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。
那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。
所以基于以上,根据游戏特点,一般分成两类。
1)被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
2)主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。


 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haorantiangang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值