2018年Android面试题汇总四(持续更新中)

面试系列,推荐先读我的心得:

2018年Android面试题汇总一

十二、ThreadLocal

12.1、四大方法:set、get、remove和initialValue。

   1、initialValue在第一次调用get或set时执行,只执行一次,初始化内部类Values中Oject数组。

   2、JDK5.0开始支持泛型

   3、内部ThreadLocal.ThreadLocalMap用来存储key键ThreadLocal和Value值变量副本

12.2、如何为每个线程维护一个变量副本:

  a、ThreadLocal有个静态类ThreadLocalMap,键key为当前ThreadLocal对象、值value为对应线程的变量副本。一个线程可能含有多个ThreadLocal

  b、ThreadLocal为不同的线程创建对应的ThreadLocalMap。在initialValue中createMap(t, value)

 c、使用ThreadLocal作为Key的原因,ThreadLocal相比Thread要少,提供性能

12.3、与线程同步机制比较

  1、同步机制仅提供有一个变量,不同线程排队访问;ThreadLocal为每个线程提供一个变量副本,可以同时访问。

  2、ThreadLocal不是解决多线程变量问题,

  3、ThreadLocal一般声明为static变量

12.4、存在的内存泄漏问题

   1、使用线程池,线程结束后不会销毁而会被再次使用,就可能出现内存泄漏

   2、如果ThreadLocal置为null,则存在ThreadLocalMap

Object>,内存泄漏。线程被gc,线程中的ThreadMap也被销毁,不会内存泄漏

12.5、适用场景

   数据库链接、Session管理。多线程需要获得初始状态


十二、Android版ThreadLocal

  1、ThreadLocal的内部类Values中Oject数组偶数位存ThreadLocal的弱引用,下一位存值

  2、采用斐波拉契散列寻址

  3、Thread自身有个ThreadLocal.Values成员变量,在ThreadLocal中初始化


6、典型应用

           一个线程只允许一个Looper。为保证只有一个Looper采用ThreadLocal原理,在Looper的prepare函数中

if sThreadLocal.get() != null

     throw一个线程只能有一个Looper

sThreadLocal.set(new Looper(quitAllowed));


十四、内存泄漏

1、静态变量(比如单例类)持有Activity的引用,例如Context或者接口。

   解决方案:接口在OnDestroy时remove掉,Context传递ApplicationContext

2、a、Activity内部类Handler、Runnable和AsyncTask对Activity的隐式引用,Activity销毁之后,任务没完成。

           解决方案:a、在onDestroy中用Handler.removeAllCallbackAndMessage

                                b、Handler定义为静态内部类,静态内部类不会持有外部类的引用。配合使用WeakReference解决

                                  c、典型的:Volley在Activity中使用,Response回调Listener采用静态内部类和WeakReference使用Activity

3、资源未关闭:BroadcastReceiver、ContentObserver、File、Cursor、Stream和Bitmap;Bitmap.recylce()并置为null

4、静态内部类中引用外部类变量(如Context、View)时采用弱引用

5、不需要的对象,赋值为null

6、持久化Drawable,定义成static,4.0之前持有View,View持有Activity

十五、AsyncTask

           15.1、使用准则:

                     1、必须在UI线程创建,execute必须在UI线程中执行

                     2、不能手动调用onPreExecute、doInBackground、onPostExecute、onProgressUpdate

                     3、只能执行一次;因为多个子线程同时运行造成线程不同步,状态不一致

           15.2、存在的问题

             1、AsyncTask所在的Activity终止,AsyncTask不会终止;

                    2、在没有不可中断操作比如BitmapFactory.decodeStream的,重写onCancel对socket、file进行关闭。并在Activity的onDestroy可调用AsyncTask.cancel终止

             3、AsyncTask如果是非静态内部类,依照非静态内部类会持有外部类的引用,由于声明周期问题,可能会造成泄漏;

             4、1.6之前串行;1.6到2.3并行;2.3以后execute串行,executeOnExecutor执行并行自定义的执行器

             5、AsyncTask内部用弱引用持有Activity

             6、横竖屏切换,Activity重建,Activity引用失效,onPostExecute刷新界面会失效。解决方法:在Activity的onRetainNonConfigurationInstance中重新传一个对象给AsyncTask

   15.3、Android 3.0之后

   1、execute只有一个线程执行排队任务,对应线程池Executors.newSingleThreadPool。也就是AsyncTask预定义的SERIAL_EXECUTOR

2、executeOnExecutor可以自定义线程池执行任务。执行无数个就用java线程池的Executors.newCachedThreadPool。AsyncTask默认定义了THREAD_POOL_EXECUTOR,就是Executors.newFixedThreadPool(5),最多只能5个线程。

十四、性能优化

14.1、布局优化

  1、使用ViewStub、merge和include

  2、overdraw,GPU选项观察overdraw的情况

  3、ondraw不要new对象,不能耗时,60fps, 16ms绘制帧,GPU加速

  4、避免内存泄漏

  5、ListView:a、使用Holder;b、分页加载;c、滑动停止后在加载图片、

  6、Bitmap加载:用LRUBitmap,内存加硬盘缓存

  7、必要的时候使用SurfaceView

 14.2、性能优化

           1、上述布局优化

           2、使用ArrayMap和SparseArray替代HashMap

           3、少用枚举,多用static访问块

十四、ListView异步加载图片错位原理与解决方法

14.1、  异步加载图片错位原因

1、 同步加载不会出现图片错位

2、异步加载图片出现错位

ListView一整屏显示7个item,当向下滑动时,显示出item8.由于RecycleBin机制,item8重用item1。如果异步网络请求item8的图片比item1慢,item8会显示item1的图片,当item8图片下载完成,此时向上滑动显示item1,item1会显示item8的图片。

14.2、 解决方案:对ImageView设置tag为图片URL,并设置默认图。等异步请求加载完比较Image的tag与请求的URL是否匹配

十四、ListView和RecyclerView

  14.1、使用的对比

     1、ListView使用时:

      a、要继承重写BaseAdapter

      b、自定义类ViewHolder,配合convertView一起完成复用优化

     2、RecyclerView使用时

      a、要继承RecyclerView.Adapter

      b、继承RecyclerView.ViewHolder

      c、设置布局管理器

        LinearLayoutManager linearLayoutManager= new LinearLayout(activity);

        mRecyclerView.setLayoutManager(linearLayoutManager);

   14.2、局部刷新

      1、ListView调用Adapter.notifyDataSetChanged会重绘每个item

      2、RecyclerView.Adapter提供notifyItemChanged(int

postion)刷新单个item

   14.3、HeaderView和FooterView对position的影响

      1、ListView会将HeaderView和FooterView计算到position中,对setOnItemClickListener会有影响

    2、RecyclerView只有addOnItemTouchListener

十五、OOM情景和解决方案

1、原因:a、加载对象过大;b、相应资源过多,来不及释放;c、Adapter没有使用缓存的convertView。e、Android虚拟机Dalvik,最大堆大小为16M,有的机器为24M。

2、解决方案:关闭资源;引用方面处理;加载图片预压缩;堆内存自定义大小和优化Dalvik虚拟机对内存分配

PS:Dalvik虚拟机堆内存分配优化setTargetHeapUtilization;自定义堆内存大小setMinimumHeapSize

十五、性能分析新方法

利用Looper的setMessageLogging方法,Looper.getMainLooper

十六、AndroidManifest中对Android:process属性设置的两种形式:

1、以冒号开头,android:process

= ":remote",该进程是私有进程,其他应用的组件不可以和它跑在同一个进程中。

2、以.或者以包名.remote开头,全局进程,其他应用可以设置相同的ShareUID和它泡在同一个进程

十七、多进程面临4个问题

a、Application多次重建;b、静态成员失效;c、文件共享问题;d、断点调试问题。

十八、java序列化

18.1、序列化:将对象转换为字节流序列,目的:a、永久保存到硬盘上;b、网络上传送

18.2、序列化的要求:实现Serializable或Externalizable接口的对象;

18.3、用到的api:ObjectOutputStream、ObjecInputStream;writeObject、readObject;

18.4、序列化的使用原则:对象的序列化

           a、序列化不会保存静态变量,因为序列化保存的是对象的状态,静态变量属于类的状态

           b、父类没有实现序列化接口Serializable,子类实现了。序列化时基类对象不会序列化,基类的属性不会序列化,反序列化时通过无参构造函数构建基类对象

          c、父类实现序列化,子类没有。基类改变,序列化ID不会改变

           e、外部类不序列化,非静态内部类、匿名内部类、本地类不能序列化,因为这些内部类中有个变量,这个变量指向外部类对象this,序列化要求对象中所有的对象属性也要序列化。

           f、外部类序列化,内部类必须序列化

           g、静态内部类不管外部类有没有实现序列化,都可以序列化

           h、List、Map容器中的泛型类型必须实现序列化接口Serializable,否则会报NotSerializableException错误

展开阅读全文

没有更多推荐了,返回首页