Android面试题总结(二)

一、Bitmap 使用时候注意什么?

对Bitmap的不小心处理,很容易引起程序内存空间耗尽而导致的程序崩溃问题。比如我们常遇到的问题:

java.lang.OutofMemoryError: bitmap size exceeds VM budget.

1.导致该问题的原因:

  1. 每个设备一般对每个应用分配的存储空间非常有限;
  2. Bitmap对象常常占用大量的内存空间,如果采用ARGB_8888的格式加载图像,内存占用将达到19MB空间;
  3. 在Anroid App中经常用到ListView,ViewPager等控件,这些控件常会包含较大数量的图片资源

2.问题及场景分析

  1. 高效地加载大图片。
    • BitmapFactory类提供了一些加载图片的方法:decodeByteArray(), decodeFile(), decodeResource(), 等等。
      为了避免占用较大内存,经常使用BitmapFactory.Options 类,设置inJustDecodeBounds属性为true。
    • 在真正decode图片之前检查它的尺寸,加载一个按比例缩小的版本到内存中,BitmapFactory.Options 中设置 inSampleSize 为true
  2. 不要在主线程处理图片。
    • 为了保证使用的资源能被回收,建议使用WeakReference, 以应用内存内存紧张时,回收部分资源,保证程序进程不被杀死。
    • 避免异步任务的长时间耗时操作,在任务执行结束后,及时释放资源。
  3. 管理Bitmap内存。
    ListView,GridView或ViewPager,这些组件的child view在不显示时会回收,并循环使用,如果没有任何对bitmap的持久引用的话,垃圾回收器会释放你加载的bitmap。这没什么问题,但当这些图片再次显示的时候,要想避免重复处理这些图片,从而达到加载流畅的效果,就要使用内存缓存本地缓存了,这些缓存可以让你快速加载处理过的图片。

    • 内存缓存,LruCache类(API Level 4之前可以使用Support Library)非常适合图片缓存任务,在一个LinkedHashMap中保存着对Bitmap的强引用,当缓存数量超过容器容量时,删除最近最少使用的成员(LRU)。
    • 本地缓存,使用磁盘缓存

      内存缓存能够加快对最近显示过的图片的访问速度,然而你不能认为缓存中的图片全是有效的。像GridView这样需要大量数据的组件是很容易填满内存缓存的。你的应用可能会被别的任务打断(比如一个来电),它可能会在后台被杀掉,其内存缓存当然也被销毁了。当用户恢复你的应用时,应用将重新处理之前缓存的每一张图片。

        在这个情形中,使用磁盘缓存可以持久的储存处理过的图片,并且缩短加载内存缓存中无效的图片的时间。当然从磁盘加载图片比从内存中加载图片要慢的多,并且由于磁盘读取的时间是不确定的,所以要在后台线程进行磁盘加载。

    • 如果以更高的频率访问图片,比如图片墙应用,使用ContentProvider可能更适合储存图片缓存。
  4. UI中展示图片。
    数量少的图片使用 PagerAdapter or FragmentPagerAdapter 更合适。

    Bitmap图片在加载到内存的时候是按照:宽像素点位数来计算的。你可以把图片看成是由width行、height列的矩阵组成,每一个矩阵元素代表一个像素点,每一个像素点都是1byte整数倍的数据,这个数据越大,表示的色彩就越丰富,图片的显示质量就越高。Bitmap中有一个枚举类Config用来配置图片的压缩格式,代表每个像素是用多大的数据来存储的,数值越大能够存储的颜色信息就越多,也就越丰富,显示效果也就越好。Config.ALPHA_8是1 byte,Config.RGB_565和Config.ARGB_4444都是2 bytes,Config.RGB_565没有Alpha值所以多用来配置没有透明度的图片,Config.ARGB_8888是4 bytes,一般图片都是按照这个来配置的。

二、Oom 是否可以try catch ?

是可以的捕获到a中的崩溃的,比如在android中加载一个大的图片文件,oom了,可以catch住,可以尝试调大sample继续加载,但是往往oom不是简单因为这里申请了内存导致oom,在这里catch,它又会在下一个地方崩溃。解决办法就是 需要dump内存,找出出内存异常的真正原因,提前做出应对措施.

三、内存泄露如何产生?

http://www.jianshu.com/p/90caf813682d

原因:当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。
如何检查和分析内存泄漏?
借助MAT、LeakCanary等工具来检测应用程序是否存在内存泄漏。
LeakCanary :https://github.com/square/leakcanary

常见的内存泄漏及解决方法

1、单例造成的内存泄漏

由于单例的静态特性使得其生命周期和应用的生命周期一样长,如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。
解决方法:Context最终使用Application的Context

2、非静态内部类创建静态实例造成的内存泄漏

非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,从而导致Activity的内存资源不能被正常回收。
解决方法:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,就使用Application的Context。

3、Handler造成的内存泄漏

当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。当主线程中实例化一个Handler对象后,它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用,所以Looper会据此回调Handle的handleMessage()方法来处理消息。当MainActivity结束时,未处理的消息持有handler的引用,而handler又持有它所属的外部类也就是MainActivity的引用。这条引用关系会一直保持直到消息得到处理,这样阻止了MainActivity被垃圾回收器回收,从而造成了内存泄漏。
解决方法:将Handler类独立出来或者使用静态内部类,这样便可以避免内存泄漏。

4、线程造成的内存泄漏

AsyncTask和Runnable:都使用了匿名内部类,那么它们将持有其所在Activity的隐式引用。如果任务在Activity销毁之前还未完成,那么将导致Activity的内存资源无法被回收,从而造成内存泄漏。
解决方法:将AsyncTask和Runnable类独立出来或者使用静态内部类,这样便可以避免内存泄漏。

5、资源未关闭造成的内存泄漏

使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。
1)比如在Activity中register了一个BraodcastReceiver,但在Activity结束后没有unregister该BraodcastReceiver。
2)资源性对象比如Cursor,Stream、File文件等往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。
3)对于资源性对象在不使用的时候,应该调用它的close()函数将其关闭掉,然后再设置为null。在我们的程序退出时一定要确保我们的资源性对象已经关闭。
4)Bitmap对象不在使用时调用recycle()释放内存。2.3以后的bitmap应该是不需要手动recycle了,内存已经在java层了。

6、使用ListView时造成的内存泄漏

初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的View对象,同时ListView会将这些View对象缓存起来。当向上滚动ListView时,原先位于最上面的Item的View对象会被回收,然后被用来构造新出现在下面的Item。这个构造过程就是由getView()方法完成的,getView()的第二个形参convertView就是被缓存起来的Item的View对象(初始化时缓存中没有View对象则convertView是null)。

构造Adapter时,没有使用缓存的convertView。
解决方法:在构造Adapter时,使用缓存的convertView。

7、集合容器中的内存泄露

把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
解决方法:在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。

8、WebView造成的泄露

当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其长期占用的内存也不能被回收,从而造成内存泄露。
解决方法:为WebView另外开启一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。
避免内存泄漏:

这里写图片描述

NO1表示Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列。而对于Dialog而言,只有在Activity中才能创建。除此之外三者都可以使用。

2、对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏。
3、对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null。
4、保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
5、对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
1)将内部类改为静态内部类
2)静态内部类中使用弱引用来引用外部类的成员变量

作者:李俊的博客
链接:http://www.jianshu.com/p/90caf813682d
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

四、适配器模式,装饰者模式,外观模式的异同?

http://blog.csdn.net/zgliang88/article/details/55803344

五、String buffer 与string builder 的区别?

StringBufferd支持并发操作,线性安全的,适 合多线程中使用。
StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用。
新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。

六、如何保证线程安全?

http://blog.csdn.net/mingtiannihao623/article/details/78017786

七、java四种引用

http://www.cnblogs.com/huajiezh/p/5835618.html

八、Jni 用过么?

九、多进程场景遇见过么?

http://blog.csdn.net/seu_calvin/article/details/53932171
http://blog.csdn.net/qq_27489007/article/details/54377655

十、关于handler,在任何地方new handler 都是什么线程下

http://www.cnblogs.com/JczmDeveloper/p/4403129.html

十一、sqlite升级,增加字段的语句

http://blog.csdn.net/lu1024188315
通过alter添加新的字段SQL语句

"ALTER TABLE 'DiHKChatMessage' ADD  'phoneNum' varchar";

我们可以在添加字段之前,对数据库进行判断看是否已经存在该字段了,方法如下:

/**
 * 方法1:检查某表列是否存在
 * @param db
 * @param tableName 表名
 * @param columnName 列名
 * @return
 */
private static boolean checkColumnExist1(SQLiteDatabase db, String tableName
        , String columnName) {
    boolean result = false ;
    Cursor cursor = null ;
    try{
        //查询一行
        cursor = db.rawQuery( "SELECT * FROM " + tableName + " LIMIT 0", null );
        result = cursor != null && cursor.getColumnIndex(columnName) != -1 ;
    }catch (Exception e){
        LogUtil.logErrorMessage("checkColumnExists1..." + e.getMessage());
    }finally{
        if(null != cursor && !cursor.isClosed()){
            cursor.close() ;
        }
    }

    return result ;
}


/**
 * 方法2:检查表中某列是否存在
 * @param db
 * @param tableName 表名
 * @param columnName 列名
 * @return
 */
private static boolean checkColumnExists2(SQLiteDatabase db, String tableName, String columnName) {
    boolean result = false ;
    Cursor cursor = null ;

    try{
        cursor = db.rawQuery( "select * from sqlite_master where name = ? and sql like ?"
                , new String[]{tableName , "%" + columnName + "%"} );
        result = null != cursor && cursor.moveToFirst() ;
    }catch (Exception e){
        LogUtil.logErrorMessage("checkColumnExists2..." + e.getMessage());
    }finally{
        if(null != cursor && !cursor.isClosed()){
            cursor.close() ;
        }
    }

    return result ;
}

十二、bitmap recycler 相关

http://www.cnblogs.com/zhucai/p/5413340.html

尽快的调用recycle是个好习惯,会释放与其相关的native分配的内存;但一般情况下其图像数据是在JVM里分配的,调用recycle并不会释放这部分内存。
http://www.cnblogs.com/blfshiye/p/4018617.html

十三、强引用置为null,会不会被回收?

1 同一个方法中
2 定义了一个大对象(小对象没有意义)
3 之后跟着一个非常耗时的操作.
4 没有满足JIT编译条件

把obj显式设置成null才是有意义的,是有助于对这个大对象的回收的.

十四、glide 使用什么缓存?Glide 内存缓存如何控制大小?

http://blog.csdn.net/shangmingchao/article/details/51125554
对缓存说明比较详细
http://www.cnblogs.com/baiqiantao/p/6808457.html

图片的毛玻璃效果:可以参考这里的BlurTransformation:https://github.com/wasabeef/glide-transformations/blob/master/transformations/src/main/java/jp/wasabeef/glide/transformations/BlurTransformation.java,或者使用Android的ScriptIntrinsicBlur自己处理Bitmap。

十五、如何保证多线程读写文件的安全?

http://blog.csdn.net/rlanffy/article/details/26622521

十六、Http 相关介绍

http://www.cnblogs.com/yin-jingyu/archive/2011/08/01/2123548.html

http://www.cnblogs.com/engeng/articles/5959335.html
这里写图片描述
1) Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号(seq = 0 就代表这是第0号包),这时候Client进入syn_sent状态,表示客户端等待服务器的回复

2) Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x+1,同时表明x为止的所有数据都已正确收到(ack=1其实是ack=0+1,也就是期望客户端的第1个包),seq = y 表示Server 自己的初始序号(seq=0就代表这是服务器这边发出的第0号包)。这时服务器进入syn_rcvd,表示服务器已经收到Client的连接请求,等待client的确认。

3) Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效(代表期望收到服务器的第1个包),Client自己的序号seq= x + 1(表示这就是我的第1个包,相对于第0个包来说的),一旦收到Client的确认之后,这个TCP连接就进入Established状态,就可以发起http请求了。

post 请求过程要了解其参数传送方式

http://www.cnblogs.com/zhangqs008/archive/2011/07/02/2341124.html

HttpClient和HttpURLConnection的区别

http://blog.csdn.net/hguang_zjh/article/details/33743249

Android中主要提供了两种方式来进行HTTP操作,HttpURLConnection和HttpClient。这两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。

HttpClient就是一个增强版的HttpURLConnection,HttpURLConnection可以做的事情HttpClient全部可以做;HttpURLConnection没有提供的有些功能,HttpClient也提供了,但它只是关注于如何发送请求、接收
响应,以及管理HTTP连接。

可使用HttpClient来登录系统,只要应用程序使用同一个HttpClient发送请求,HttpClient会自动维护与服务器之间的Session状态,也就是说程序第一次使用HttpClient登录系统后,接下来使用HttpClient即可访问被保护页而了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值