Android几个面试题解答

有人分享了几个面试题,个人觉得题目出得比较全面,故本人有兴趣分析解答一下,同时帮自己理顺一些思路。

    题目一:简述Android消息机制原理?
     分析:这个非常重要,但因为太常见了,所以大家都知道,肯定会想到Handler这个类,不过要说明原理,不是说完Handler怎么用就行了,要把原理说出来,而且只是简述,不能说一大堆,照本宣科,要说自己的理解。
     我的解答:在UI主线程里,有一个Looper对象与一个Message Queue(消息队列),消息队列是放Mesage(消息的),里面包含是待处理的数据,Looper的作用就是不断从消息队列里取出消息,然后交给另一个对象处理,这个对象就是Handler,其handlerMessage函数就是来处理Message的,并且在Looper所在线程里执行。
     除此之外Handler的另一个作用是将Message放到Message Queue队列中,这个放置的过程可以在其它子线程来进行;因为Hander与Looper是多对一的关系,所以能保证放到正确的队列,又由于Message中有Hadler对象的引用,所以最后处理消息的Handler就是当初放它进队列的Handler。
     子线程中需要手动调用特定的接口,就能产生本线程的消息队列与Looper,运行机制与主线程一样。
     最后归纳一下就是:产生消息--放置消息--分发消息(与产生放置过程可以并行)--处理消息

     题目二:Android中常用的三种动画是什么?
     分析:动画一般用得比较多的是用XML定义的动画放在/res/anim/文件夹内,然后使用,或者用代码来处理;有一种简单的就是用多个图片在连续变换产生动画效果;而最新也是功能最强大的是在3.0中引入的属性动画(Property Animation),该动画之所以强大,因为它不止可以应用于View,还可以应用于任何对象。三种动画分别叫:Tween Animation(补间动画)、Frame Animation(帧动画)、Property Animation(属性动画)。
     我的解答:Tween Animation(补间动画)、Frame Animation(帧动画)、Property Animation(属性动画)。
      Tween Animation:只能应用于View对象,使视图组件移动、放大、缩小以及产生透明度的变化。
      Frame Animation:通过顺序的播放排列好的图片来实现,类似电影。
      Property Animation:动画的对象除了View对象,还可以是Object对象,通过改变View对象的实际属性来实现View动画。

       题目三:Fragment使用的场景,相比Activity,使用Fragment有什么优缺点?
       分析:Fragment是3.0版引入的,当时3.0主要为了平板电脑而推出的,平板电脑屏幕大,显示的内容更多,更多的交互,故需要同一界面上实现更多数据的变化,而Fragment主要为了界面切换更方便而产生的。但Fragment不能离开Activity而单独存在,需要以Activity为载体,不用在AndroidManifest.xml中配置。
       我的解答:Fragment主要在需要频繁进行局部界面切换时使用,比如Tab菜单切换界面,或者大屏幕上左边列表来切换右边视图显示等。
       与Activity相比,
       优点:1.模块化,可以属于多个Activty,动态的加入到Activty或者从Activity移除,实现更多效果与更好的用户体验。
            2.不用在AndroidManifest.xml中配置,打包时也可以混淆,能更好的保护我们的源代码。
            3.频繁切换多个界面中能利用原有对象与界面数据,效率更高。
       缺点:1.因为受Activity生命周期的影响,自己也有单独的生命周期,故使用起来复杂一些。在复杂的变换中,一旦把握不当,容易产生bug。
            2.Fragment之间传递与返回数据没有Activity之间方便,有时还得借助Activity来实现,比如让Activity实现接口,然后Fragment调用接口来实现Fragment之间的数据通信。

         题目四:谈谈四大组件进程间通信。
         分析:一般一说到四大组件通信,肯定会想到Intent,但是题目主要讲的是进程间通信,虽然Intent能实现进程间通信,但还不够。
         我的解答:主要有如下几种进程间通信方式:
            1.Intent消息通知机制。利用Intent,可以实现 Actvity、Service互相的跨进程调用,Intent中携带了目标组件的特征与要传递的数据,只要符合特征的Activity或Service,不管是不是与打开源在同一进程中,系统会去创建目标组件,并接收传递的数据。
           2.AIDL服务。主要利用AIDL接口定义语言,来实现Activity与Service之间进行实时通信。通过AIDL,Activity中能获得Service里onBind回调函数返回的IBinder(AIDL服务对象),Activity再通过调用AIDL服务对象中的函数实现两者间的通信。
           3.Broadcast机制。BroadcastReceiver(广播接收器)作为四大组件中的一员,本身就是一种跨进程通讯方式的组成部分。在一个进程中发出广播后,另一个进程可以通过BroadcastReceiver来接收广播,并取出Broadcast中携带的数据,然后进行处理,Intent在此起到了过滤与传递数据的作用。
           4.ContentProvider。ContentProvider(内容提供者)主要用来提供进程间读取数据使用,如果在同一应用中,访问数据时虽然也能用ContentProvider,但是没用必要,直接用文件或数据库即可。ContentProvider实际是上对本地数据作了一层封装,只向外提供数据访问接口,这样可以对外控制数据的开放与隐藏。
           上面是具体的使用,不过从本质上来说,四种方式的底层都是使用的Binder机制,上面4种方式只是对Binder做了封装,方便使用。

          题目五:关于刷新ListView,如何在不使用notifyDataChanged刷新局部数据?
           分析:notifyDataChanged函数来刷新ListView时会强制ListView调用getView来刷新每个Item的内容,在某些情况下会损失效率,那能不能只想刷新某一个或几个Item呢,肯定是可以的,比如我们想改变一个TextView里的显示内容,只要用setText重新赋值即可,而Item即是一个View,其实只要想办法对View里的子View进行重新设置属性即可,关键点就是拿到子Item的根View。
            我的解答:第一步,通过位置找到想要刷新的Item的根View;第二步:对根View里的子View作数据更新。
 
//得到第一个可显示控件的位置,
  int firtvisiblePosition = listView.getFirstVisiblePosition();
  int lastVisiblePosition = listView.getLastVisiblePosition();

  if ( needFlushItemIndex >= firtvisiblePosition && needFlushItemIndex <= lastVisiblePosition  ) {
     //得到某一子项的根View
     View rootItemView = listView.getChildAt(needFlushItemIndex - firtvisiblePosition);
     //设置需要刷新子View的属性
     ......
}


         题目六:Android系统是如何确定进程优先级的高低? 
          我的解答:1.Active(活动)进程具有最高优先级,比如存在与用户正在交互的Activity;存在正在执行onReceive事件处理程序的Broadcast Receiver;存在正在执行onStart、onCreate、onDestroy事件处理程序的Service;存在正在运行、且已被标记为前台运行的Service;
          2.可见进程,例如Activity被一个对话框部分遮挡,它所在进程具有第二高的优先级;
          3.启动Service进程,已经启动Service的进程,具有第三高的优先级;
          4.后台进程,不可见,并且没有任何正在运行的Service或Activity的进程,具有第四高优先级;
          5.空进程,如果一个进程里没有包含任何app,那么这个进程的优先级是最低;

          题目七:分别说说View与ViewGroup事件分发流程。
          分析:View与ViewGroup事件分发主要指Touch事件的分发与处理,在发生触摸事件时,系统会直接或间接调用相关的回调函数,并通过函数的返回值来判断进一步的事件分发,开发者可以在函数里加入逻辑并返回true or false来进行事件分发控制。
          其主要涉及到三个回调函数,
          public boolean dispatchTouchEvent(MotionEvent ev)、
          public boolean onInterceptTouchEvent(MotionEvent ev)、
          public boolean onTouchEvent(MotionEvent ev)。其中最基本的View没有onInterceptTouchEvent函数。
          系统一般会直接调用dispatchTouchEvent函数,dispatchTouchEvent函数中再调用其它几个函数。系统最初是调用Activity的dispatchTouchEvent函数,dispatchTouchEvent函数中再一级一级调用下层的函数,如果低一级的函数返回true,则上层的函数都会直接返回true,表示此事件被低一级的View处理了。  
          
          我的解答:
          View的事件分发流程:
          1.如果View不是ENABLED或者没有注册OnTouchListener或者执行OnTouchListener.onTouch函数时返回false,则执行onTouchEvent函数,并捕捉其返回值。
          2.如果onTouchEvent函数返回false则事件交给上层继续处理。
          3.如果onTouchEvent函数返回true或者OnTouchListener.onTouch函数返回true,则表示事件已处理完毕,不需要由上层View处理。

          ViewGroup的事件分发流程:
          1.首先判断ViewGroup的onInterceptTouchEvent()函数的返回值,为true则子View的事件都被拦截;接下来由ViewGroup处理,流程同View事件分发流程。
          2.onInterceptTouchEvent()函数返回false,则遍历其子View,判断子View是不是在点击范围内,只有在点击范围内的子View才会继续进行处理。
          3.子View如果处理了该事件,则处理完成。
          4.子View如果没有处理该事件,由ViewGroup负责处理,接下来同View事件分发流程。


          题目八:涉及到数据库升级,如果想保留数据,加入数据表结果有更新(增加字段),如何实现(谈谈思路)。
          分析:数据库升级时一般情况下系统会回调SQLiteOpenHelper类的onUpgrade函数,在该函数中实现对表的操作即可。
          如果增加字段,sql语法有ALTER TABLE XX ADD COLUMN的DDL定义语言,如果在表末尾加一个字段,原有数据不会被清除,如果是想在表中间加字段,则可以采用加表转移数据的方法。
          我的解答: 
           两种情况:
           如果在表末尾加字段,直接采用ALTER TABLE XX ADD COLUMN进行增加即可,原有数据默认会保存。
           如果在表中或表首加字段,则采用数据转移的方式,首先将原表重命名,再新建一个与原表相同名称的表,再将原表的数据导入到新表,最后删除原表即可。

          题目九:Android中动态加载的原理及使用场景。
          我的解答:动态加载原理:利用DexClassLoader,可动态加载的内容包括 apk、dex、jar等,并利用反射调用插件包内的类的方法。
             使用场景:应用不想升级而达到更新目的时,可从服务器下载新内容,再动态加载达到应用功能更新;应用需要加密时,可在执行时进行解密后动态加载。

          题目十:谈谈你对Android开发的看法,如何提高用户体验(流量/电量/缓存/UI/编程规范),性能优化方面的经验。
           我的解答: 对于Android应用层开发来说主要还是对sdk的了解及java基础,除此之外关键在于有整体思维与精益求精的心思。
            性能优化可以采用一些高效的开源框架,比如Volley、gson等;多用Android提供的数据结构,比如SparseArray、Parcelable等;尽量减少View的嵌套层次;Service执行完后应stop掉;大量图片操作应采用缓存机制;没必要的对象不要产生,不用的及时释放;循环语句采用最优方式。
           

猜你喜欢

转载自danlov.iteye.com/blog/2190930