我正在尝试创建一个用于从服务器下载图像并将其显示到listview的应用程序。我做的问题是内存泄漏并使我的应用程序崩溃。我在Android博客中搜索了这个link,它显示了一个好主意,但仍然不足以用多线程来做。 android的某些设备可以使用它,但有些设备只能在单个线程中处理,有时它根本无法工作。
我的应用程序有很多活动,每个活动都有一个Listview需要尽可能快地显示图像。通过Google IO 2012,他们使用缓冲区将原始图像保存到SD卡,它解决了泄漏内存的问题,但由于需要下载的图像太大,导致加载速度变慢。
我的问题是:有没有办法将图像一起缩放到SD卡? 我想出一些可能的解决方案是在输入流对象中使用Skip字节,我能够找到Width和Height以及我需要下载的图像的每像素位数。
以下代码在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();
}
}
答案 0 :(得分: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;
}
答案 1 :(得分: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();
}
}
答案 2 :(得分: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>