读《Android开发艺术探索》后的面试题整理

版权声明:本文为寻梦-finddreams原创文章,请关注:http://blog.csdn.net/finddreams https://blog.csdn.net/finddreams/article/details/50829769

  又有很久没有写博客了,俗话说的好:“武功要练好,一年三百六十个早。一天不练,手生脚慢;两天不练,功夫丢了一半;三天不练,成了门外汉;四天不练,只能瞪眼看”。古人说:”业精于勤,荒于嬉“,如今想想,还真那么回事。
  之前听人说《Android开发艺术探索》这本书很适合做面试的复习资料,书中深入浅出的讲解了Android开发者进阶所必需掌握的各项知识点,而这些知识点也恰巧是我们面试时面试官最喜欢问的问题,于是我又重新系统的温习了一遍《Android开发艺术探索》这本书,总结了一些书中常在面试的过程中遇到的问题。
  读完《Android开发艺术探索》之后的感想是,通过深入学习本书,不仅可以提高我们应用层的开发能力,又能够对Android系统的运行机制有一定的理解和认识。这正是我们大多数初中级Android开发者所需要的一本书,不讲过于晦涩难懂的Android底层机制,不讲入门级别的Android基础知识,全书每一章讲的都是Android开发者如何从小工到专家的干货。从四大组件的工作原理,View的工作原理和事件机制,到IPC机制,Android线程和线程池,JNI和NDK编程,以及最后的Android性能优化。因为本书有深度,读一遍两遍恐怕都不够,像西游记中白骨精那样重要的妖精都要打上三遍,《Android开发艺术探索》也需要读上三遍以上,才能慢慢理解其中的深意。所谓书读百遍,其意自见。
  话不多少,首先感谢一下作者任玉刚童鞋,感谢您为我们提供了这样一本目前Android领域难得的好书,一下内容都是来源于书中的文字,如果想要详细了解,可以买这本书来慢慢研读。
  

一、Android的四大组件

1.Android的四大组件

  Activity的主要作用是展示一个界面并和用户交互,它扮演的是一种前台界面的角色。
  Service是一种计算型组件,用于在后台执行一系列计算任务,但因为其本身还是运行在主线程中的,因此耗时的后台计算仍然需要在单独的线程中去完成。
  BroadcastReceiver是一种消息型组件,用于在不同的组件乃至不同的应用之间传递消息。广播注册有两种方式,动态注册通过Context.registerReceiver()来实现,必须要应用启动才能注册;静态注册则在AndroidManifest文件中进行,应用安装时会被系统解析,不需要启动应用就可接收广播。
  ContentProvider是一种共享型组件,用于向其他组件乃至其他应用共享数据。

2.Activity的四种启动模式:standard、singleTop、singleTask和singleInstance

  1. standard 标准模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在;
  2. singleTop 栈顶复用模式。如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,会调用Activity的onNewIntent方法,onCreate和onStart并不会被调用。(常用于应用的搜索页面)
  3. singleTask 栈内复用模式。只要该Activity在一个栈中有实例存在,就会把它上面的Activity都清除掉,让它置于栈顶,因为singleTask默认是具有clearTop的效果,最后onNewIntent会被回调。如果不存在就创建一个新的Activity,压入栈中。(常用于应用的首页MainAcitivity)
  4. singleInstance:单实例模式,这是一种加强的singleTask模式,它除了具有singleTask模式的所有特性外,还加强一点,那就是具有此种模式的Activity只能单独地位于一个任务栈中。
任务栈是一种 “后进先出”的栈结构。

3.小知识点

   1. 当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate方法,所以在onPause方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率;也就意味者,我们应当尽量在onStop方法中做操作,从而使得新Activity尽快的显示出来并切换到前台。
  2. 新Activity是透明主题时,旧Activity不会走onStop;
  3. 系统只有在Activity异常终止的时候才会调用onSavedInstanceState和onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个过程。
  4. 避免屏幕旋转时Activity重启生命周期,可以在AndroidManifest.xml中对应Activity标签声明时加上“android:configChanges=”orientation|screenSize””即可;

二、IPC机制

1、如何实现多进程?

  给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidManifest.xml中指定”android:process”属性可以在应用内实现多进程,如果进程名以”:”开头,说明该进程属于私有进程,其他应用的组件不可以和它跑在同一个进程中,如果经常名不以”:”开头,则属于全局进程,其它应用通过ShareUID方式可以和它跑在同一个进程中。

2.两个序列化类Parcelable和Serializable的区别:

  Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量的I/O操作。而Parcelable是Android中的序列化方式,因此更适合于用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高。Parceable主要用在内存序列化上,通过Parcelable将对象序列化到存储设备中或者将对象序列化后通过网络传输也都是可以的,但是这个过程会稍显复杂,此种情况建议使用Serializable。

三、View机制

1.View事件分发过程

 事件的分发过程由三个很重要的方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent:

  1. dispatchTouchEvent:用来进行事件的分发,如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和View的dispatchTouchEvent方法的影响,表示是否当消耗当前事件;

   2. onInterceptTouchEvent:用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件;
   3. onTouchEvent:在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。

  一些结论:拦截的一定是事件序列;不消耗ACTION_DOWN,则事件序列都会由其父元素处理;只消耗ACTION_DOWN事件,该事件会消失,消失的事件最终会交给Activity来处理;requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,除了ACTION_DOWN;

2.三种方式来实现View的滑动和对比

   1. 通过View本身提供的scrollTo/scrollBy方法来实现滑动;操作简单,适合对View内容的滑动;
   2. 通过动画给View施加平移效果来实现滑动;操作简单,适用于没有交互的View和实现复杂的动画效果;
  3. 通过改变View的LayoutParams使得View重新布局从而实现滑动;改变布局参数操作稍微复杂,适用于有交互的View;

3.View的工作原理

   View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽和高,layout用来确定View在父容器中的放置位置,而draw则负责将View绘制在屏幕上。

4.setContentView()方法的由来

   DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下两个部分(具体情况和Android版本及主体有关),上面的是标题栏,下面的是内容栏。在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,而内容栏的id是content,在代码中可以通过ViewGroup content = findViewById(R.android.id.content)来得到content对应的layout;
这就是为什么Acticity的Oncreate中的setContentView()方法得来的原因,而不是叫setView()。

四、Handler和MessageQueue、Looper的关系以及原理

1.MessageQueue的工作原理:主要方法为enqueueMessage和next

  1. enqueueMessag主要就是一个单链表的插入操作
  2. next方法是一个无限循环,如果消息队列中没有消息,next方法就阻塞,有新消息到来时,next方法会返回这条消息并将其从单链表中删除

2.Looper的工作原理:

  1. prepare方法,为当前没有Looper的线程创建Looper。
  2. prepareMainLooper和getMainLooper方法用于创建和获取ActivityThread的Looper。
  3. quit和quitSafely方法,前者立即退出,后者只是设定一个标记,当消息队列中的所有消息处理完毕后会才安全退出。子线程中创建的Looper建议不需要的时候都要手动终止。 d. loop方法,死循环,阻塞获取msg并丢给msg.target.dispatchMessage方法去处理,这里的target就是handler。

3.Handler的工作原理:

  1. 无论sendMessage还是post最终都是调用的sendMessageAtTime方法。
  2. 发送消息其实就是把一条消息通过MessageQueue的enqueueMessage方法加入消息队列,Looper收到消息就会调用handler的dispatchMessage方法
  3. 当我们直接Handler h = new Handler()时,本质调用的是Handler(Callback callback, Boolean async)构造方法,这个方法里会调用Looper.myLooper()方法,这个方法其实就是返回的ThreadLocal里保存的当前线程的Looper,这也就解释了为什么我们在主线程中这样new Handler没有问题,而在子线程中这样会抛出异常,因为子线程中并没有Looper,所以必须先调用Looper.prepare,但是ActivityThread会在初始化的时候创建主线程也就是UI线程的Looper,所以我们在Activity中直接new Handler是没有问题的。

五、AsyncTask和线程池

1.AsyncTask的注意事项

  AsyncTask的类必须在主线程加载,Android4.1及以上已经被系统自动完成了;
  AsyncTask对象必须在主线程创建;
  execute方法需要在UI线程调用;
  一个AsyncTask对象只能调用一次;
  Android1.6之前串行执行,Android1.6采用线程池并行处理任务,Android3.0开始,采用一个线程执行任务,但也可以通过executeOnExecutor方法来并行执行任务。

2.常见的4个线程池类型

  1. FixedThreadPool:线程数量固定的线程池,当所有线程都处于活动状态时,新任务会处于等待状态,只有核心线程并且不会回收(无超时机制),能快速的响应外界请求。
  2. CachedThreadPool:线程数量不定的线程池,最大线程数为Integer.MAX_VALUE(相当于任意大),当所有线程都处于活动状态时,会创建新线程来处理任务;线程池的空闲进程超时时长为60秒,超过就会被回收;任何任务都会被立即执行,适合执行大量的耗时较少的任务。
  3. ScheduledThreadPool:核心线程数量固定,非核心线程数量无限制,非核心线程闲置时会被立刻回收,用于执行定时任务和具有固定周期的重复任务。
  4. SingleThreadExecutor:只有一个核心线程,所有任务都在这个线程中串行执行,不需要处理线程同步问题

     《Android开发艺术探索》中的知识点还有很多很多,今天就暂且介绍到这里,如果有可能的话,还是建议你买一本书来仔细研读为好,因为毕竟书籍才是人类进步的阶梯!

猜你喜欢

转载自blog.csdn.net/finddreams/article/details/50829769