5

我正在尝试创建一个用于从服务器下载图像并将其显示到列表视图中的应用程序。我遇到的问题是内存泄漏并使我的应用程序崩溃。我在 Android 博客中搜索,例如这个链接,它展示了一个好主意,但它仍然不足以用多线程来完成。android的某些设备可以使用它,但某些设备只能在单线程中处理,有时它根本无法工作。

我的应用程序有很多活动,每个活动都有一个 Listview 需要尽快显示图像。通过 Google IO 2012,他们使用缓冲区将原始图像保存到 SD 卡并解决了内存泄漏问题,但由于需要下载的图像太大,加载速度很慢。

我的问题是:有什么方法可以缩放图像并将图像写入 SD 卡? 我想出一些可能的解决方案是在输入流对象中使用跳过字节,并且我能够找到我需要下载的图像的每个像素的宽度和高度。

以下代码在 Google IO 2012 中使用,它适用于多线程,在我的情况下,我有 4 个线程在后台运行。

private void downloadAndWriteFile(final String url, final File file) throws OutOfMemoryError {
    BufferedOutputStream out = null;

    try {
        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setDoInput(true);
        conn.connect();

        final InputStream in = new BufferedInputStream(conn.getInputStream(), IO_BUFFER_SIZE_BYTES);    // buffer size 1KB
        out = new BufferedOutputStream(new FileOutputStream(file), IO_BUFFER_SIZE_BYTES);

        int b;
        while ((b = in.read()) != -1) {
            out.write(b);
        }
        out.close();
        conn.disconnect();
    }
    catch (Exception e) {
        Log.e(TAG, "!!downloadAndWriteFile " + e.getMessage());
        file.delete();
    }
}
4

3 回答 3

1

1)在设置图像之前使用以下代码释放与此位图关联的本机对象,并清除对像素数据的引用。如果没有其他引用,它只是允许它被垃圾收集。

BitmapDrawable drawable = (BitmapDrawable) myImage.getDrawable();
Bitmap bitmap = drawable.getBitmap();
if (bitmap != null)
{
    bitmap.recycle();
}

2)使用这种方法减少内存中位图的大小:

/**
 * decodes image and scales it to reduce memory consumption
 * 
 * @param file
 * @param requiredSize
 * @return
 */
public static Bitmap decodeFile(File file, int requiredSize) {
    try {

        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(file), null, o);

        // The new size we want to scale to

        // Find the correct scale value. It should be the power of 2.
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < requiredSize
                    || height_tmp / 2 < requiredSize)
                break;
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;

        Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(file),
                null, o2);

        return bmp;

    } catch (FileNotFoundException e) {
    } finally {
        System.gc();
    }
    return null;
}
于 2012-09-08T08:13:36.783 回答
0

此代码在不使用位图工厂的情况下下载图像,它在模拟器中不起作用,使用任何 android 手机

package com.example.filedownload;


import org.apache.http.util.ByteArrayBuffer;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.content.*;
import android.app.*;
import android.net.*;
import android.app.DownloadManager.Request;
import android.os.Environment;
public class MainActivity extends Activity {
    public  long reference;
    BroadcastReceiver receiver;
    @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        Button button=(Button)findViewById(R.id.button1);
                button.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {


                                String file = "http://tmacfitness.com/wp-content/uploads/2013/04/Beauty-of-nature-random-4884759-1280-800.jpg";
                                String serviceString = Context.DOWNLOAD_SERVICE; 
                                DownloadManager downloadManager;
                                downloadManager = (DownloadManager)getSystemService(serviceString);
                                Uri uri = Uri.parse(file);
                                DownloadManager.Request request ;
                                request =  new Request(uri);
                                request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,  "accel.jpg");   
                                reference = downloadManager.enqueue(request);



                }

        });

        IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
        long ref = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        if (reference == ref) {
        setContentView(R.layout.finalscreen);
        unregister();
        }
        }
        };
        registerReceiver(receiver, filter); 
    }
       public void unregister(){
           unregisterReceiver(receiver);

    }
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TableLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" >

        <TableRow
            android:id="@+id/tableRow1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >
        </TableRow>

        <TableRow
            android:id="@+id/tableRow2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

            <CheckedTextView
                android:id="@+id/checkedTextView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Accel" />

        </TableRow>

        <TableRow
            android:id="@+id/tableRow3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

            <Button
                android:id="@+id/button1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Button" />

        </TableRow>

        <TableRow
            android:id="@+id/tableRow4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >
        </TableRow>
    </TableLayout>

</RelativeLayout>

finalscreen.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="270dp"
        android:layout_height="wrap_content"
        android:layout_weight="2.12"
        android:text="DOWNLOAD COMPLETED" />

</LinearLayout>
于 2014-05-05T05:30:12.393 回答
0

你可以使用这个。

private void downloadImagesToSdCard(String downloadUrl,String imageName) {
try {
    URL url = new URL(downloadUrl); //you can write here any link

    File myDir =  new File("/sdcard"+"/"+Constants.imageFolder);
    //Something like ("/sdcard/file.mp3")


    if (!myDir.exists()) {
        myDir.mkdir();
        Log.v("", "inside mkdir");

    }

    Random generator = new Random();
    int n = 10000;
    n = generator.nextInt(n);
    String fname = imageName;
    File file = new File (myDir, fname);
    if (file.exists ()) file.delete (); 

         /* Open a connection to that URL. */
        URLConnection ucon = url.openConnection();
        InputStream inputStream = null;
       HttpURLConnection httpConn = (HttpURLConnection)ucon;
      httpConn.setRequestMethod("GET");
      httpConn.connect();

      if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) {
       inputStream = httpConn.getInputStream();
      }

        /*
         * Define InputStreams to read from the URLConnection.
         */
       // InputStream is = ucon.getInputStream();
        /*
         * Read bytes to the Buffer until there is nothing more to read(-1).
         */

        FileOutputStream fos = new FileOutputStream(file);
        int size = 1024*1024;
        byte[] buf = new byte[size];
        int byteRead;
        while (((byteRead = inputStream.read(buf)) != -1)) {
            fos.write(buf, 0, byteRead);
            bytesDownloaded += byteRead;
        }
        /* Convert the Bytes read to a String. */

        fos.close();

} catch(IOException io) {
    networkException = true;
    continueRestore = false;
} catch(Exception e) {   
    continueRestore = false;
    e.printStackTrace();
}

}

于 2014-05-05T04:48:30.270 回答