Android面试总结

来源:互联网 发布:可以打电话的软件 编辑:程序博客网 时间:2024/06/10 14:21

   前段在找工作,现在将面试过程中最常见的问题总结一下,希望对大家有帮助。如果有不足或者错误的地方,请指正!


1.事件分发机制


事件都是从Activity.dispatchTouchEvent()开始传递
事件由父View传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件拦截,停止其向子view传递,由自身的onTouchEvent()进行处理
如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()(用于事件处理)函数。
如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN必须返回true,之后的事件才会传递进来
OnTouchListener优先于onTouchEvent()对事件进行消费。


2.Android性能优化

1.布局优化
   --1.减少布局层级,重用布局layout merge
   --2.仅在需要的时候才加载布局,viewstub,没有绘制功能,不参与布局
   --3.在不影响布局层级的情况下,尽量使用LinearLayout布局,因为RelativeLayout会绘制两次
2.代码优化
   --1.常量声明成 static final,编译器首次创建,之后访问时只会去查找,不会重新创建
   --2.避免创建不必要的对象 比如:StringBuffer替代String 
   --3.使用增强for循环
   --4.避免使用float类型,建议用int类型替换
   --5.避免在ondraw或for循环中创建对象

3.内存泄漏分析及解决办法

内存溢出通俗理解就是软件(应用)运行需要的内存,超出了它可用的最大内存。
内存泄漏就是我们对某一内存空间的使用,使用完成后没有释放。

1,资源对象使用完没有关闭,如file,cursor
2.构建adapter时,没有复用convertview,解决:使用viewHolder
Context使用不当造成的内存泄漏,比如在单例中引用的Context,当Activity 结束了,单例对象还是存在,这将使得该对象继续持有 Context 的引用,造成内存泄漏。解决:使用Application 的 Context。
4.监听器没取消造成的内存泄漏,比如广播,ContentObserver
5.集合中对象没及时清理
6.Handler造成的内存泄漏,比如Activity被finish,延时任务还存在,它会持有该activity的引用,所以该activity就不会被回收从未造成内存泄漏,解决方法:Static + WeakReference的方式来达到断开Handler与Activity之间存在引用关系的目的。其存活时间就与activity无关了

4.怎样避免OOM

1.尽量使用轻量级的数据结构,解决:ArrayMap/SparseArray代替HashMap等传统数据结构。
2.避免在Android中使用枚举
3.减少bitmap对象内存的占用,解决:1.按比例缩放 2.选择解码格式
4.内存对象的重复利用,如:利用线程池技术,LRU
5.不要在类似ondraw这样频繁调用的方法中创建对象,这样会引起频繁的GC,甚至内存的抖动。
6.内存对象的泄漏,会导致一些不再使用的对象无法及时释放,这样一方面占用了宝贵的内存空间,很容易导致后续需要分配内存的时候,空闲空间不足而出现OOM。
7.优化布局层次,减少内存消耗

5.MVP模式

View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity)
Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)
Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试

6.Handler机制

说到这个问题了,,不得不吐槽一下了,,我两年前找工作就问这个问题,,现在还在问。。
看这个链接吧,,写的挺详细的 点击打开链接

7.Activity启动模式及应用场景

standard,创建一个新的Activity。
singleTop,栈顶不是该类型的Activity,创建一个新的Activity。否则,onNewIntent。
singleTask,回退栈中没有该类型的Activity,创建Activity,否则,onNewIntent+ClearTop。
singleInstance,回退栈中,只有这一个Activity,没有其他Activity。

singleTop适合接收通知启动的内容显示页面。
例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。

singleTask适合作为程序入口点。
例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。

singleInstance应用场景:
闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。

8.Service两种启动方式区别

  1 通过startService
   Service会经历 onCreate 到onStart,然后处于运行状态,stopService的时候调用onDestroy方法。如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。
  2 通过bindService   
    Service会运行onCreate,然后是调用onBind, 这个时候调用者和Service绑定在一起。调用者退出了,Srevice就会调用onUnbind->onDestroyed方法。
   所谓绑定在一起就共存亡了。调用者也可以通过调用unbindService方法来停止服务,这时候Srevice就会调用onUnbind->onDestroyed方法。

9.手写单例模式

public class Singleton {      private static Singleton instance;      private Singleton (){}        public static Singleton getInstance() {      if (instance == null) {          instance = new Singleton();      }      return instance;      }  } 

以上单例有什么问题,怎么改进?
  上面单例是个线程不安全的懒汉式单例,解决方法是 双重加锁
public class Singleton {      private volatile static Singleton singleton;      private Singleton (){}      public static Singleton getSingleton() {      if (singleton == null) {          synchronized (Singleton.class) {          if (singleton == null) {              singleton = new Singleton();          }          }      }      return singleton;      }  } 

10.多线程访问ArrayList会有什么问题,怎么解决?

 因为ArrayList是线程不安全的,所以多个线程同时同时操作ArrayList会出现数据混乱问题
 解决方式有以下三种:
   1.线程同步
   2.使用Collections.synchronizedList();使用方法如下:

    假如你创建的代码如下:List<Map<String,Object>> data=new ArrayList<Map<String,Object>>();

    那么为了解决这个线程安全问题你可以这么使用Collections.synchronizedList(),如:

    List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>());

   其他的都没变,使用的方法也几乎与ArrayList一样,大家可以参考下api文档;

额外说下 ArrayList与LinkedList;这两个都是接口List下的一个实现,用法都一样,但用的场所的有点不同,ArrayList适合于进行大量的随机访问的情况下使用,LinkedList适合在表中进行插入、删除时使用,二者都是非线程安全,解决方法同上(为了避免线程安全,以上采取的方法,特别是第二种,其实是非常损耗性能的)。

   3.CopyOnWriteArrayList

    原理:在对其实例进行修改时,会创建一个临时数据修改,修改完毕后,再将原来的引用指向新的数组。

11.描述一次完整的网络请求

 -1.建立TCP/IP连接,客户端通过与服务器通过Socket三次握手进行连接。

 -2.客户端向服务器发送请求

 -3.客户端发送请求信息,请求内容,最后会发送一个空行,表示客户端请求完毕

 -4.服务器会做出应答,并向客户端发送应答头消息

 -5.服务器在向客户端发送完应答头消息后,也会发送一个空行表示消息发送完毕,接着就以content-typey要求的数据格式发送数据给客户端

 -6.发送数据完毕后,服务器会关闭TCP连接

12.View的刷新机制

由ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。


调用流程 :

mView.draw()开始绘制,draw()方法实现的功能如下:

绘制该View的背景
为显示渐变框做一些准备操作(见5,大多数情况下,不需要改渐变框)          
调用onDraw()方法绘制视图本身(每个View都需要重载该方法,ViewGroup不需要实现该方法)调用dispatchDraw ()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

13.自定义View

这个问题几乎是面试中必问的问题,,所以大家对这个多看看吧,网上教程太多了,,不重复造轮子了。