面试系列,推荐先读我的心得:
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错误