【问题标题】:Android download image from server and save to sdcard without using BitmapFactoryAndroid 从服务器下载图像并保存到 sdcard 而不使用 BitmapFactory
【发布时间】:2014-05-05 13:43:52
【问题描述】:

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

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

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

以下代码在 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();
    }
}

【问题讨论】:

    标签: java android multithreading imagedownload


    【解决方案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;
    }
    

    【讨论】:

    • 感谢您的回答,我已经通过了这个案例,但是对于某些不允许 JNI 分配更多内存的设备,它仍然会出现多线程泄漏内存。 Google IO 2012 的代码可以使它与支持所有设备的多线程一起工作,但如果图像太大,它会很慢。不知何故,用户正在快速导航活动,所以第一次加载时,它一点都不好。
    【解决方案2】:

    你可以用这个。

    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();
    }
    

    }

    【讨论】:

      【解决方案3】:

      此代码在不使用位图工厂的情况下下载图像,无法在模拟器中运行,请使用任何 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>
      

      【讨论】:

        猜你喜欢
        • 2017-08-06
        • 1970-01-01
        • 1970-01-01
        • 2014-05-08
        • 2017-01-27
        • 2015-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-10
        相关资源
        最近更新 更多