这几天在作图记的时候遇第一次遇到了OOM,好激动~~java
追究缘由,是由于在ListView中加载的图片太大形成的,由于我使用的都是手机相机直接拍摄的照片,图片都比较大,因此在加载的时候会出现内存溢出,那么咱们就须要将图片压缩显示了。android
首先,咱们能够经过Bitmap.getWidth和 Bitmap.getHeight来获取一张图片的实际宽和高app
MainActivity.javaide
package com.example.test3; import java.io.IOException; import java.io.InputStream; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.Display; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends Activity { private ImageView iv; private Button bt; private int screenWidth, screenHeigh; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt = (Button) findViewById(R.id.bt); iv = (ImageView) findViewById(R.id.img); // 获取屏幕信息 WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); screenWidth = display.getWidth(); screenHeigh = display.getHeight(); bt.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { InputStream is = getAssets().open("pic.jpg"); Bitmap bitmap = BitmapFactory.decodeStream(is); iv.setImageBitmap(bitmap); int realWidth = bitmap.getWidth(); int realHeight = bitmap.getHeight(); Toast.makeText(MainActivity.this, "真实图片的宽:" + realWidth + ",真实图片的高:" + realHeight + "\n 屏幕宽度:" + screenWidth + ",屏幕高度:" + screenHeigh, Toast.LENGTH_SHORT).show(); is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } }
运行结果:优化
当咱们使用ListView来加载这些大图的时候,每每会出现内存溢出的状况,譬以下面:this
MainActivity.javaspa
package com.example.test3; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; public class MainActivity extends Activity { private ListView lv; private List list = new ArrayList(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); addItem(); lv = (ListView) findViewById(R.id.lv); MyAdapter adapter = new MyAdapter(list, MainActivity.this); lv.setAdapter(adapter); } private void addItem() { list.add(""); list.add(""); list.add(""); list.add(""); } class MyAdapter extends BaseAdapter { private List mList; private Context mContext; public MyAdapter(List list, Context context) { this.mList = list; this.mContext = context; } @Override public int getCount() { // TODO Auto-generated method stub return mList.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return mList.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(mContext).inflate(R.layout.item, null); ImageView iv = (ImageView) view.findViewById(R.id.image); try { InputStream is = getAssets().open("pic.jpg"); Bitmap bitmap = BitmapFactory.decodeStream(is); is.close(); iv.setImageBitmap(bitmap); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return view; } } }
由于咱们没有对adapter作任何优化,因此咱们每次滑动界面的时候,都会调用getView方法而后加载图片,这个时候就会出现OOM异常:code
虽然出现OOM异常的缘由有N多种,但就上个例子中,咱们只须要将图片按照必定比例去缩小,而后在ListView中加载缩略图,就能够解决掉。blog
在Android的BitmapFactory.Options类,能够帮助咱们对图片进行一系列的配置(不知道这个表述是否正确)图片
该类有一个成员变量inJustDecodeBounds,源码中对该变量的描述以下
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
若是将其设置为true,那么在BitmapFactory.decodeXXX的时候将返回为null(不返回bitmap),可是容许调用者在不给这些bitmap分配内存的状况下查询该bitmap的信息。
那么咱们就能够在不将其加载到内存的状况下,获取到该图片的宽高,咱们修改getView方法中的代码:
public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(mContext).inflate(R.layout.item, null); ImageView iv = (ImageView) view.findViewById(R.id.image); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; try { InputStream is = getAssets().open("pic.jpg"); Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); int width = options.outWidth; int height = options.outHeight; Log.d("TTTT", "width=" + width + ",height=" + height); is.close(); iv.setImageBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); } return view; }
输出结果为:
而且在程序界面并无显示图片,由于decodeStream返回值为null
继续修改getView方法,此次咱们加载一张5000*5000的大图
@Override public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(mContext).inflate(R.layout.item, null); ImageView iv = (ImageView) view.findViewById(R.id.image); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; try { inputStream = getAssets().open("big_pic.png"); BitmapFactory.decodeStream(inputStream, null, options); // 图片真实宽高 int picWidth = options.outWidth; int picHeight = options.outHeight; Log.d("TTTT", "图片的真实width:" + picWidth + ",图片的真实height:" + picHeight); // 手机屏幕宽高 WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); int screenWidth = display.getWidth(); int screenHeight = display.getHeight(); Log.d("TTTT", "屏幕的width:" + screenWidth + ",屏幕的height:" + screenHeight); // 获取缩放比例 int scaleX = picWidth / screenWidth; int scaleY = picHeight / screenHeight; // 设置默认缩放比例为1 int scale = 1; if (scaleX >= scaleY & scaleY >= 1) { scale = scaleX; } else if (scaleY >= scaleX & scaleX >= 1) { scale = scaleY; } Log.d("TTTT", "缩放比例为:" + scale); options.inJustDecodeBounds = false; options.inSampleSize = scale; inputStream = getAssets().open("big_pic.png"); Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); Log.d("TTTT", "缩放后的width:" + bitmap.getWidth() + ",缩放后的height:" + bitmap.getHeight()); inputStream.close(); iv.setImageBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); } return view; }
再次运行:
系统提示:
图片的真实width:5000,图片的真实height:5000 屏幕的width:1080,屏幕的height:1776 缩放比例为:4 缩放后的width:1250,缩放后的height:1250
在上面的代码中咱们能够看到,咱们讲injust inJustDecodeBounds设置为了false,表示咱们要将bitmap加载到内存中去了,而且咱们使用inSampleSize来设置了缩放比例
至此,图片缩放完成