0

以下代码给出 java.lang.OutOfMemoryError : bitmap超出VM预算

我正在编写一个应用程序来浏览一系列全屏图像。我知道我将所有图像一起加载,这就是导致问题的原因,但我不知道如何解决这个问题。有人请帮忙。

  package akash.gal;

    import android.app.Activity;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.os.Bundle;
    import android.util.DisplayMetrics;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.Gallery;
    import android.widget.ImageView;

    public class GalActivity extends Activity {
    /** Called when the activity is first created. */
    Integer imageids[]={R.drawable.one,R.drawable.two,R.drawable.three};
    @Override

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Gallery gallery=(Gallery) findViewById(R.id.gallery1);
        gallery.setAdapter(new ImageAdapter(this));
    }
    public class ImageAdapter extends BaseAdapter{

    private Context context;
    private int itemBackground;
    public ImageAdapter(Context c)
    {
        context=c;
        TypedArray a=obtainStyledAttributes(R.styleable.Gallery1);
        itemBackground=a.getResourceId(R.styleable.Gallery1_android_galleryItemBackground,0);
        a.recycle();
    }
        public int getCount() {
            // TODO Auto-generated method stub
            return imageids.length;
        }

        public Object getItem(int arg0) {
            // TODO Auto-generated method stub
            return arg0;
        }

        public long getItemId(int arg0) {
            // TODO Auto-generated method stub
            return arg0;
        }

        public View getView(int arg0, View arg1, ViewGroup arg2) {
            // TODO Auto-generated method stub
            ImageView imageView=new ImageView(context);
            imageView.setImageResource(imageids[arg0]);
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            DisplayMetrics dm=new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(dm);
            imageView.setLayoutParams(new Gallery.LayoutParams(dm.widthPixels,dm.heightPixels));
            imageView.setBackgroundResource(itemBackground);
            return imageView;

        }
}
}
4

1 回答 1

1

在主要活动中:-

public class Main extends Activity {

    String mImagesPath;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mImagesPath = this.getFilesDir().getParent() + "/images/";
        createImagesDir(mImagesPath);
        copyImagesToStorage();
        loadGridView();
    }

    /**
     * Method handles the logic for setting the adapter for the gridview
     */
    private void loadGridView(){

        GridView lLazyGrid = (GridView) this.findViewById(R.id.gridview);
        try {
            LazyImageAdapter lLazyAdapter = new LazyImageAdapter(this.getApplicationContext(),
                    null,
                    mImagesPath);
            lLazyGrid.setAdapter(lLazyAdapter);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Copy images from assets to storage
     */
    private void copyImagesToStorage(){
        AssetManager lAssetManager = getAssets();
        String[] lFiles = null;
        String lTag = "copyImageFail";

        try {
            // get all of the files in the assets directory
            lFiles = lAssetManager.list("");
        } catch (IOException e) {
            Log.e(lTag, e.getMessage());
        }


        for(int i=0; i<lFiles.length; i++) {
            // We have a file to copy


            try {

                // copy the file
                copyFile(lFiles[i], mImagesPath + lFiles[i]);
            } catch(Exception e) {
                Log.e(lTag, e.getMessage());
            }       
        }
    }

    /**
     * Method copies the contents of one stream to another
     * @param aIn stream to copy from
     * @param aOut stream to copy to
     * @throws IOException
     */
    private void copyFile(String aIn, String aOut) throws IOException {
        byte[] lBuffer = new byte[1024];
        int lRead;
        final int lOffset = 0;

        // create an in and out stream
        InputStream lIn = getAssets().open(aIn);
        OutputStream lOut = new FileOutputStream(aOut);

        // Copy contents while there is data
        while((lRead = lIn.read(lBuffer)) != -1){
          lOut.write(lBuffer, lOffset, lRead);
        }

        // clean up after our streams
        lIn.close();
        lIn = null;
        lOut.flush();
        lOut.close();
        lOut = null;
    }

    /**
     * Create the directory specified at aPath if it does not exist
     * @param aPath directory to check for and create
     */
    private void createImagesDir(String aPath){
        File lDir = new File(aPath);
        if(!lDir.exists()){
            lDir.mkdir();
        }
    }
}

======================================

在 ImageLoader 类中:

public class ImageLoader extends Thread {

    public interface ImageLoadListener {

        void handleImageLoaded(ViewSwitcher aViewSwitcher, ImageView aImageView, Bitmap aBitmap);
    }

    private static final String TAG = ImageLoader.class.getSimpleName();
    ImageLoadListener mListener = null;
    private Handler handler;

    /**
     * Image loader takes an object that extends ImageLoadListener
     * @param lListener
     */
    ImageLoader(ImageLoadListener lListener){
        mListener = lListener;
    }

    @Override
    public void run() {
        try {

            // preparing a looper on current thread         
            // the current thread is being detected implicitly
            Looper.prepare();

            // Looper gets attached to the current thread by default
            handler = new Handler();

            Looper.loop();
            // Thread will start

        } catch (Throwable t) {
            Log.e(TAG, "ImageLoader halted due to a error: ", t);
        } 
    }

    /**
     * Method stops the looper and thus the thread
     */
    public synchronized void stopThread() {

        // Use the handler to schedule a quit on the looper
        handler.post(new Runnable() {

            public void run() {
                // This runs on the ImageLoader thread
                Log.i(TAG, "DownloadThread loop quitting by request");

                Looper.myLooper().quit();
            }
        });
    }

    /**
     * Method queues the image at path to load
     * Note that the actual loading takes place in the UI thread
     * the ImageView and ViewSwitcher are just references for the
     * UI thread.
     * @param aPath      - Path where the bitmap is located to load
     * @param aImageView - The ImageView the UI thread will load 
     * @param aViewSwitcher - The ViewSwitcher that needs to display the imageview
     */
    public synchronized void queueImageLoad(
            final String aPath, 
            final ImageView aImageView, 
            final ViewSwitcher aViewSwitcher) {

        // Wrap DownloadTask into another Runnable to track the statistics
        handler.post(new Runnable() {
            public void run() {
                try {

                    synchronized (aImageView){
                        // make sure this thread is the only one performing activities on
                        // this imageview
                        BitmapFactory.Options lOptions = new BitmapFactory.Options();
                        lOptions.inSampleSize = 1;
                        Bitmap lBitmap = BitmapFactory.decodeFile(aPath, lOptions);
                        //aImage.setImageBitmap(lBitmap);

                        // Load the image here
                        signalUI(aViewSwitcher, aImageView, lBitmap);
                    }
                } 
                catch(Exception e){
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Method is called when the bitmap is loaded.  The UI thread adds the bitmap to the imageview.
     * @param aViewSwitcher - The ViewSwitcher that needs to display the imageview
     * @param aImageView - The ImageView the UI thread will load 
     * @param aImage - The Bitmap that gets loaded into the ImageView
     */
    private void signalUI(
            ViewSwitcher aViewSwitcher, 
            ImageView aImageView, 
            Bitmap aImage){

        if(mListener != null){
            // we have an object that implements ImageLoadListener

            mListener.handleImageLoaded(aViewSwitcher, aImageView, aImage);
        }
    }

}

=======================================

在 LazyImageAdapter 中:

public class LazyImageAdapter extends BaseAdapter implements ImageLoadListener {

    private static final int PROGRESSBARINDEX = 0;
    private static final int IMAGEVIEWINDEX = 1;


    private Context mContext = null;
    private OnClickListener mItemClickListener;
    private Handler mHandler;
    private ImageLoader mImageLoader = null;
    private File mDirectory;

    /**
     * Lazy loading image adapter
     * @param aContext
     * @param lClickListener click listener to attach to each item
     * @param lPath the path where the images are located
     * @throws Exception when path can't be read from or is not a valid directory
     */
    public LazyImageAdapter(
            Context aContext,
            OnClickListener lClickListener,
            String lPath
        ) throws Exception {

        mContext = aContext;
        mItemClickListener = lClickListener;
        mDirectory = new File(lPath);

        // Do some error checking
        if(!mDirectory.canRead()){
            throw new Exception("Can't read this path");
        }
        else if(!mDirectory.isDirectory()){
            throw new Exception("Path is a not a directory");
        }

        mImageLoader = new ImageLoader(this);
        mImageLoader.start();
        mHandler = new Handler();

    }

    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();

        // stop the thread we started
        mImageLoader.stopThread();
    }

    public int getCount() {
        return mDirectory.listFiles().length;
    }

    public Object getItem(int aPosition) {
        String lPath = null;
        File []lFiles = mDirectory.listFiles();
        if(aPosition < lFiles.length){
            lPath = mDirectory.listFiles()[aPosition].getAbsolutePath();
        }

        return lPath;
    }

    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return 0;
    }

    public View getView(final int aPosition, View aConvertView, ViewGroup parent) {
        final ViewSwitcher lViewSwitcher;
        String lPath = (String)getItem(aPosition);

        // logic for conserving resources see google video on making your ui fast
        // and responsive
        if (null == aConvertView) {
            lViewSwitcher = new ViewSwitcher(mContext);
            lViewSwitcher.setPadding(8, 8, 8, 8);

            ProgressBar lProgress = new ProgressBar(mContext);
            lProgress.setLayoutParams(new ViewSwitcher.LayoutParams(80, 80));
            lViewSwitcher.addView(lProgress);
            ImageView lImage = new ImageView(mContext);
            lImage.setLayoutParams(new ViewSwitcher.LayoutParams(100, 100));

            lViewSwitcher.addView(lImage);

            // attach the onclick listener
            lViewSwitcher.setOnClickListener(mItemClickListener);

        } else {
            lViewSwitcher = (ViewSwitcher) aConvertView;
        }


        ViewTagInformation lTagHolder = (ViewTagInformation) lViewSwitcher
                .getTag();

        if (lTagHolder == null || 
            !lTagHolder.aImagePath.equals(lPath)) {
            // The Tagholder is null meaning this is a first time load
            // or this view is being recycled with a different image

            // Create a ViewTag to store information for later
            ViewTagInformation lNewTag = new ViewTagInformation();
            lNewTag.aImagePath = lPath;
            lViewSwitcher.setTag(lNewTag);

            // Grab the image view
            // Have the progress bar display
            // Then queue the image loading
            ImageView lImageView = (ImageView) lViewSwitcher.getChildAt(1);
            lViewSwitcher.setDisplayedChild(PROGRESSBARINDEX);
            mImageLoader.queueImageLoad(lPath, lImageView, lViewSwitcher);


        }

        return lViewSwitcher;
    }


    public void handleImageLoaded(
            final ViewSwitcher aViewSwitcher,
            final ImageView aImageView, 
            final Bitmap aBitmap) {

        // The enqueue the following in the UI thread
        mHandler.post(new Runnable() {
            public void run() {

                // set the bitmap in the ImageView
                aImageView.setImageBitmap(aBitmap);

                // explicitly tell the view switcher to show the second view
                aViewSwitcher.setDisplayedChild(IMAGEVIEWINDEX);
            }
        });

    }


}

/**
 *  View holder pattern as described in google sample code
 *  we may want to add more attributes to this if the path was
 *  say being stored in a sqlite database
 * @author bacaj
 */
class ViewTagInformation {
    String aImagePath;
}
于 2012-07-04T10:41:06.340 回答