Android开发之图片详解(4)

接上一篇。

主要研究下bitmap和drawable的使用,以及两者的区别。

先看测试代码:

package com.example.imagetext;

import java.util.ArrayList;
import java.util.List;

import com.example.imagetext.R.drawable;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import android.widget.ImageView;

public class MainActivity extends Activity {
    private Drawable drawable1,drawable2,drawable4,drawable3;
    private Bitmap bitmap1,bitmap2,bitmap3,bitmap4,bitmap6,bitmap5,bitmap7;
    private ImageView iv1,iv2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        drawable1 = getResources().getDrawable(R.drawable.bg_1);
        drawable2 = getResources().getDrawable(R.drawable.bg_1);

        bitmap1 = ((BitmapDrawable)drawable1).getBitmap();
        bitmap2 = ((BitmapDrawable)drawable2).getBitmap();

        iv1= (ImageView) findViewById(R.id.iv1);
        iv2= (ImageView) findViewById(R.id.iv2);

        iv1.setImageResource(R.drawable.bg_1);
        iv2.setImageResource(R.drawable.bg_1);

        drawable3 = iv1.getDrawable();
        drawable4 = iv2.getDrawable();

        bitmap3 = ((BitmapDrawable)drawable3).getBitmap();
        bitmap4 = ((BitmapDrawable)drawable4).getBitmap();

        bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);
        bitmap6=BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);
        bitmap7 = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.bg_1));
        if(drawable1.equals(drawable2)){
            Log.e("tttext","drawble1==drawable2");
        }else{
            Log.e("tttext","drawble1!=drawable2");
        }

        if(drawable3.equals(drawable4)){
            Log.e("tttext","drawble3==drawable4");
        }else{
            Log.e("tttext","drawble3!=drawable4");
        }

        if(bitmap1.equals(bitmap2)){
            Log.e("tttext","bitmap1==bitmap2");
        }else{
            Log.e("tttext","bitmap1!=bitmap2");
        }

        if(bitmap3.equals(bitmap4)){
            Log.e("tttext","bitmap3==bitmap4");
        }else{
            Log.e("tttext","bitmap3!=bitmap4");
        }

        if(bitmap3.equals(bitmap1)){
            Log.e("tttext","bitmap3==bitmap1");
        }else{
            Log.e("tttext","bitmap3!=bitmap1");
        }

        if(bitmap5.equals(bitmap6)){
            Log.e("tttext","bitmap5==bitmap6");
        }else{
            Log.e("tttext","bitmap5!=bitmap6");
        }
        Log.e("tttext","bitmap1占用内存:"+getBitmapsize(bitmap1)/1024/1024+"M");
        Log.e("tttext","bitmap3占用内存:"+getBitmapsize(bitmap3)/1024/1024+"M");
        Log.e("tttext","bitmap5占用内存:"+getBitmapsize(bitmap5)/1024/1024+"M");
        Log.e("tttext","bitmap7占用内存:"+getBitmapsize(bitmap7)/1024/1024+"M");
    }

    @SuppressLint("NewApi")
    public long getBitmapsize(Bitmap bitmap) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
            return bitmap.getByteCount();
        }
        // Pre HC-MR1
        return bitmap.getRowBytes() * bitmap.getHeight();

    }

}

打印结果如下:

好的,现在我们逐一进行分析。

1。 drawable1 = getResources().getDrawable(R.drawable.bg_1);

drawable2 = getResources().getDrawable(R.drawable.bg_1);

bitmap1 = ((BitmapDrawable)drawable1).getBitmap();

bitmap2 = ((BitmapDrawable)drawable2).getBitmap();

从打印结果可以看出,drawable1和drawable2并不是同一个对象,但是它们所持有的bitmap却是同一个对象。上篇文章我已经说过了,当drawable引用的是一个资源图片时,它指向的是一个位图对象bitmapDrawable并且它会去缓存里面寻找是否已经存在这个bitmap,如果有直接复用,否则新建。

2。 iv1.setImageResource(R.drawable.bg_1);

iv2.setImageResource(R.drawable.bg_1);

    drawable3 = iv1.getDrawable();
    drawable4 = iv2.getDrawable();

    bitmap3 = ((BitmapDrawable)drawable3).getBitmap();
    bitmap4 = ((BitmapDrawable)drawable4).getBitmap();

这里看到,尽管我们使用.setImageResource方法时,打印出来的结果也一样,但是,不同的是,setImageResource与setImageDrawable所引用的drawble以及bitmap并不是同一个,也就是说setImageResource只会复用setImageResource的位图,setImageDrawable只会复用setImageDrawable的位图,这里要注意。

3。bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);

bitmap6=BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);

bitmap7 = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.bg_1));

这里我是直接从资源文件夹里加载的bitmap

,分别用了两种方法,从打印的结果可知,三者均不为同一个bitmap对象,并没有进行复用。

但是,从占用内存大小可知:BitmapFactory.decodeResource得到的bitmap依然是放大了4倍的,(为什么放大的是4倍,可以看看我上篇文章所说的,测试机是320dpi,而这个图片是放在mdpi文件件下的),而BitmapFactory.decodeStream得到的则是真实的没有经过缩放的图片,这也正是两个方法之间的区别。

4。

setImageResource(直接在xml里src引用也是调用的这个方法)

setImageDrawable

setImageBitmap

这几个方法的区别是什么呢?

首先从源码可以看到,setImageDrawable和setImageBitmap这两个方法是一样的,都是调用的setImageDrawable,之所以要区分出一个setImageBitmap是为了方便我们在引用网络图片或者是本地非资源文件的图片时转化成bitmap的缩放。另外这两个方法是异步去加载的。

而setImageResource或者是src这个从源码也可以看到,它们是在主线程中执行的,可能会造成activity的启动的延迟。

5.到底有没有必要调用bitmap.recycle()这个方法去手动回收图片呢,这里应该分场景:

如果是从资源文件夹里加载的图片,最好不要自己去手动释放,因为它会复用缓存里的bitmap但是并不会去判断它是否被回收了,否则很容易出现异常。

如果是从网络上或者是从本地相册之类的文件加载的图片,因为我们本身就算用的流去转成bitmap,就算回收了也不会去复用,这个时候是可以去手动回收的,并且可以和软引用联合使用。

那可能有的人要问了,如果我非要使用资源文件夹里的图片,又非要手动去回收,但是又要保证不出现异常该怎么办呢?比如说一个banner页,这个时候,我建议不要用自带的setImageResource(直接在xml里src引用也是调用的这个方法)

setImageDrawable这两个方法。

应该用bitmap=BitmapFactory.decodeResource和setImageBitmap联合使用,这里并不会复用,并且系统也会根据频幕密度去缩放,避免造成内存的浪费,同时你也可以自己手动去回收bitmap而不会报异常,因为上面我讲过了,用这个方法是系统是不会去复用bitmap的。

6.bitmap和Drawble的具体区别,我再总结下,bitmap表示的是一个位图,可以理解为一个对象,而Drawble表示的是一个图画,即可以是一个位图,也可以是个图形。

bitmap和Drawble都可以进行缩放,但是只有bitmap可以对像素进行精确的操作。

所以当你要精确的压缩放大像素时,就必须用bitmap。比如网络图片的传输和加载。但是这里值得注意的是,我们可以先从流中获取到bitmap的宽高信息,并且计算出你具体想要缩放到的宽高。然后再加载到bitmap,可以一开始就将整个大图加载到bitmap里造成内存溢出,实际上很多时候都是因为这个原因出现的OOM,比如拍照。

下一篇,还有最后一篇,主要针对bitmap说下怎么避免内存溢出和提高效率的情况下的缩放。

时间: 2024-11-04 17:38:04

Android开发之图片详解(4)的相关文章

Android开发之图片详解(3)

在Android中,Bitmap普遍是造成OOM的罪魁祸首,而且普遍都认为bitmap所占用的内存远大于Drawable,而且网上也提供了很多内存回收的方法以及建议,比如在activity销毁后主动去调用bitmap.recyle()方法等,那么到底是不是真的呢?我专门针对bitmap和drawble做了以下测试: 为了方便测出OOM,我专门将一张比较大的图片1270*1920放在了mdpi的文件夹下,而我的测试机是320dpi的,那么根据我上篇文章所讲到的,如果通过资源去加载bitmap,那么

Android开发之异步详解(一)Thread+Handler

请尊重他人的劳动成果,转载请注明出处:  Android开发之异步详解(一)Thread+Handler http://blog.csdn.net/fengyuzhengfan/article/details/40211589 在Android实际开发工程中经常会进行一些诸如:文件读写.访问网络等耗时的操作,这些耗时的操作是不建议放到UI线程里的.所以我们会新开一个线程,在子线程中进行这些耗时的操作,耗时操作过程中,UI经常需要更新,但Android是不允许在子线程中修改UI的.所以就出现了Th

Android 开发 之 Fragment 详解

作者 : 韩曙亮 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38064191 1. Fragement 概述 Fragement 与 Activity 生命周期关系 : Fragement 嵌入到 Activity 组件中才可以使用, 其生命周期与 Activity 生命周期相关. -- stop 与 destroy 状态 : Activity 暂停 或者 销毁的时候, 其内部嵌入的所有的 Fragement 也会执行

Android开发之异步详解(二)之AsyncTask

请尊重他人的劳动成果,转载请注明出处:Android开发之异步详解(二)之AsyncTask http://blog.csdn.net/fengyuzhengfan/article/details/40212745 我曾在<Android开发之异步详解(一)之Thread+Handler>一文中介绍过通过Thread+Handler实现异步操作.感兴趣的朋友可以看一下. 虽然Thread+Handler可以实现更新主线程的UI并实现异步,但Thread+Handler模式需要为每一个任务创建一

搭建Android开发环境附图详解+模拟器安装(JDK+Eclipse+SDK+ADT)

——搭建android开发环境的方式有多种,比如:JDK+Eclipse+SDK+ADT或者JDK+Eclipse+捆绑好的AndroidSDK或者Android Studio. Google 决定将重点建设 Android Studio编译工具.Google 在去年年底终止支持其 Eclipse包括插件ADT.(Android Studio会是一个趋势). 这篇将仅作为一个笔记吧,因为之前一直使用Word,记事本记录一些笔记之类.(大神勿喷,如有错误不当的地方,还望指出.谢谢!) 搭建开发环境

【Android开发】InstanceState详解

首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Android开发过程中很少用到,但在有时候掌握其用法会帮我们起到比较好的效果.尤其是在应用程序在不知道的情况下退出后,如何实现其数据保存的功能.先来让我们看下这两个方法的有什么样的作用. 1. 基本作用:  Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 o

Android 开发之动画详解

一.动画类型 Android的animation由四种类型组成:alpha.scale.translate.rotate XML配置文件中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 Java Code代码中 AlphaAnimation 渐变透明度动画效果 ScaleAnimation 渐变尺寸伸缩动画效果 TranslateAnimation 画面转换位置移动动画效果 RotateAnim

Android开发 DialogFragment对话框详解

前言 在聊DialogFragment之前,我们看看以往我们在Android里实现一个对话框一般有这几种方式: Dialog 继承重写Dialog实现一个自定义的Dialog AlertDialog Android原生提供的对话框(底层是继承Dialog实现) PopupWindow 用弹出悬浮框,实现对话框.这种对话框可以用在指定位置显示,一般用于一些非常小的按键弹窗.怎么实现可以参考我的博客:https://www.cnblogs.com/guanxinjing/p/10156153.htm

Android:ViewPager扩展详解——带有导航的ViewPagerIndicator(附带图片缓存,异步加载图片)

大家都用过viewpager了, github上有对viewpager进行扩展,导航风格更加丰富,这个开源项目是ViewPagerIndicator,很好用,但是例子比较简单,实际用起来要进行很多扩展,比如在fragment里进行图片缓存和图片异步加载. 下面是ViewPagerIndicator源码运行后的效果,大家也都看过了,我多此一举截几张图: 下载源码请点击这里 ===========================================华丽的分割线==============