面试题十一:异常与性能优化1(anr、oom、Bitmap)

目录

一、anr

1、什么是anr

2、造成anr的主要原因

3、Android中哪些操作是在主线程呢

4、如何解决anr

二、oom

1、什么是oom

2、一下容易混淆的概念

3、如何解决oom

3.1有关bitmap

3.2其他方面

三、Bitmap

1、recycle

2、LRU

3、计算inSampleSize

4、缩略图

5、三级缓存

6、BitmapUtils.java


一、anr

1、什么是anr

Application Not Responding 一个应用程序无响应的对话框。
默认情况下,Activity中一个操作最长的执行时间为5秒,如果超过5秒没有响应,就会出现anr。而在BroadcastReceiver中,最长的执行时间是10秒。

2、造成anr的主要原因

应用程序的响应性是由Activity Manager和WindowManager系统服务监视的,如果出现上述的超过5秒或者10秒的情况,就会出现anr。哪些操作会导致出现anr:

  • a、主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞
  • b、主线程中存在耗时操作(下载、IO流的读取、耗时计算等)

3、Android中哪些操作是在主线程呢

  • Activity的所有生命周期回调都是执行在主线程的;
  • Service默认是执行在主线程的。(如果有耗时操作,会报异常,解决办法:用IntentService);
  • BroadcastReceiver的onReceive回调是执行在主线程的;
  • 没有使用子线程的Looper的Handler的handleMessage,post(Runnable)是执行在主线程的;
  • AsyncTask的回调中除了doInBackground,其他都是执行在主线程。

4、如何解决anr

  • 使用AsyncTask处理耗时IO操作。
  • 使用Thread或者HandlerThread提高优先级。
  • 使用Handler来处理工作线程的耗时任务。
  • Activity的onCreate和onResume回调中尽量避免耗时的代码。

二、oom

1、什么是oom

当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出Out of memory异常。

2、一下容易混淆的概念

内存溢出、内存抖动、内存泄漏
内存溢出:当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制。
内存抖动:短时间内大量对象被创建,然后又被释放,这种情况如果达到内存的阈值会触发GC
内存泄漏:一些不再使用的垃圾对象,由于引用了还在使用的对象,导致不能被回收,这些垃圾对象多了,就会导致oom。
以上三者,内存溢出最需要注意、内存泄漏次之,内存抖动最后。

3、如何解决oom

3.1有关bitmap

图片显示:加载合适大小的图片,不要总是请求网络大图,有时可以使用缩略图。
及时释放内存
图片压缩
捕获异常

3.2其他方面

listview:convertview/lru
避免在onDraw方法里面执行对象的创建(如果在onDraw方法里面频繁地进行对象的创建,会造成内存突然上升,而在释放这些内存的时候,又会造成频繁地GC,这样就导致内存抖动)
谨慎使用多进程

三、Bitmap

1、recycle

public final class Bitmap implements Parcelable {
     /**
     * Free the native object associated with this bitmap, and clear the
     * reference to the pixel data. This will not free the pixel data synchronously;
     * it simply allows it to be garbage collected if there are no other references.
     * The bitmap is marked as "dead", meaning it will throw an exception if
     * getPixels() or setPixels() is called, and will draw nothing. This operation
     * cannot be reversed, so it should only be called if you are sure there are no
     * further uses for the bitmap. This is an advanced call, and normally need
     * not be called, since the normal GC process will free up this memory when
     * there are no more references to this bitmap.
     */
    public void recycle() {
        if (!mRecycled && mNativePtr != 0) {
            if (nativeRecycle(mNativePtr)) {
                // return value indicates whether native pixel object was actually recycled.
                // false indicates that it is still in use at the native level and these
                // objects should not be collected now. They will be collected later when the
                // Bitmap itself is collected.
                mNinePatchChunk = null;
            }
            mRecycled = true;
        }
    }

}

 在源码中的注释:

该方法用于释放跟这个bitmap(位图)有关的native(本地)对象,并清除跟数据对象有关的reference(引用)。
但是这个方法并不会同步立即清除数据对象,系统会给垃圾回收机制(GC发送指令),如果GC发现这些数据没有
其他的引用的话,就会回收这些数据对象。
调用该方法后,该bitmap会被标记为"dead",因此任何调用它的方法都会报异常。
这个操作不可逆,所以一定要确保该位图没有被调用时,再谨慎地调用该方法。
该方法,一般不建议调用,因为改位图如果没有引用的情况下,会自动被GC回收。  

2、LRU

package android.support.v4.util;

public class LruCache<K, V> {

    private final LinkedHashMap<K, V> map;
    
    public final V get(K key) {...}

    public final V put(K key, V value) {...}

    public void trimToSize(int maxSize) {...}

}

 LruCache算法(Least Recently Used),也叫近期最少使用算法。它的主要算法原理就是把最近使用的对象用强引用存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。

相关文章:Android高效加载大图、多图解决方案,有效避免程序OOM

面试题:LRU算法是怎么实现的?

它的内部是用一个LinkedHashMap来实现的,里面提供了一个get、put方法来完成缓存的获取和添加。当缓存满的时候,它会提供一个trimToSize方法,把已添加的时间最长的缓存清除,添加新的缓存对象。

接下来我们看这个trimToSize方法:

/**
     * Remove the eldest entries until the total of remaining entries is at or
     * below the requested size.
     *
     * @param maxSize the maximum size of the cache before returning. May be -1
     *            to evict even 0-sized elements.
     */
public void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }

                if (size <= maxSize || map.isEmpty()) {
                    break;
                }

                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

3、计算inSampleSize

// 根据maxWidth, maxHeight计算最合适的inSampleSize
    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            if (width > height) {
                inSampleSize = Math.round((float)height / (float)reqHeight);
            } else {
                inSampleSize = Math.round((float)width / (float)reqWidth);
            }
        }
        return inSampleSize;
    }

4、缩略图

//缩略图
    public static Bitmap thumbnail(String path,
                                   int maxWidth, int maxHeight, boolean autoRotate) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        // 获取这个图片的宽和高信息到options中, 此时返回bm为空
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);
        options.inJustDecodeBounds = false;
        // 计算缩放比
        int sampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inPurgeable = true;
        options.inInputShareable = true;
        if (bitmap != null && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
        bitmap = BitmapFactory.decodeFile(path, options);
        return bitmap;
    }

5、三级缓存

网络缓存、本地缓存、内存缓存

当我们的APP首次请求一张图片时,会从网络中获取,当这张图片加载成功之后,这张bitmap会被保存在内存和本地文件中各一份,当我们再次请求这张同样地址的图片时,就直接从内存或者本地文件中获取,避免再次从网络中获取,减少流量消耗。

6、BitmapUtils.java

public class BitmapUtils {
    // 根据maxWidth, maxHeight计算最合适的inSampleSize
    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            if (width > height) {
                inSampleSize = Math.round((float)height / (float)reqHeight);
            } else {
                inSampleSize = Math.round((float)width / (float)reqWidth);
            }
        }
        return inSampleSize;
    }

    //缩略图
    public static Bitmap thumbnail(String path,
                                   int maxWidth, int maxHeight, boolean autoRotate) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        // 获取这个图片的宽和高信息到options中, 此时返回bm为空
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);
        options.inJustDecodeBounds = false;
        // 计算缩放比
        int sampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inPurgeable = true;
        options.inInputShareable = true;
        if (bitmap != null && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
        bitmap = BitmapFactory.decodeFile(path, options);
        return bitmap;
    }

    //保存bitmap
    public static String save(Bitmap bitmap,
                               Bitmap.CompressFormat format, int quality, File destFile) {
        try {
            FileOutputStream out = new FileOutputStream(destFile);
            if (bitmap.compress(format, quality, out)) {
                out.flush();
                out.close();
            }
            if (bitmap != null && !bitmap.isRecycled()) {
                bitmap.recycle();
            }
            return destFile.getAbsolutePath();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 保存到sd卡
    public static String save(Bitmap bitmap,
                               Bitmap.CompressFormat format, int quality, Context context) {
        if (!Environment.getExternalStorageState()
                .equals(Environment.MEDIA_MOUNTED)) {
            return null;
        }
        File dir = new File(Environment.getExternalStorageDirectory()
                + "/" + context.getPackageName() + "/save/");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File destFile = new File(dir, UUID.randomUUID().toString());
        return save(bitmap, format, quality, destFile);
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值