我们的程序因为在子线程里面做耗时操作,内存泄漏导致了程序的崩溃,下面是崩溃的日志,从日志中,可以清晰的看出,超过程序的最大内存4M,导致程序内存泄漏崩溃
下载小图片(6张1M以下)的效果图(2M的图程序会崩溃):
运行出来大致是这种效果
FATAL EXCEPTION: Thread-2
Process: com.example.administrator.testz, PID: 8942
java.lang.OutOfMemoryError: Failed to allocate a 26401228 byte allocation with 4194304
free bytes and 4MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeByteArray(Native Method)
at android.graphics.BitmapFactory.decodeByteArray(BitmapFactory.java:561)
at android.graphics.BitmapFactory.decodeByteArray(BitmapFactory.java:591)
at com.handler.datatimepickerdemo.HandlerPostActivity2$MyThread.run(HandlerPostActivity2.java:87)
at java.lang.Thread.run(Thread.java:761)造成这样结果的原因是什么呢
下面是我的程序代码,大家请看,我特地去找的大图片,中国地图和世界地图,我们这个demo图片才2M不到,就导致了程序的崩溃,大家如果是学生的话,自然可以忽略不计,但是如果是做商业项目,经常需要网络下载显示高清大图,如何保证程序不崩溃呢。当然,市场上的okhttp,volley等开源框架可以实现效果,我们尝试自己用原生的请求,去下载高清大图,我们使用thread+run开辟子线程的方式,很明显无法实现我们的效果
当然,如果要解决这个问题的话很简单,因为我们的问题出现的原因是程序分配的内存太小,我们去清单文件多分点内存就可以了
// android:largeHeap="true" 在清单文件的application中添加这个,会增大应用程序分配的内存,不容易崩溃
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true" //添加这一句,可以多申请内存
android:supportsRtl="true"而且这种单线程的下载图片的方式有一个弊端,必须一个一个加载图片,上一张显示完毕,加载下一张,影响效率,图片大的话,会更加明显
下一篇博客讲解,消息队列请求下载图片
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import com.example.administrator.testz.R;
public class HandlerPostActivity2 extends Activity {
private Button btnDown;
private final static int MAX = 6;
private ImageView[] imageViews = new ImageView[MAX];
//线程阻塞,导致程序崩溃
private static String image_path[] =
{ "http://www.onegreen.net/maps/m/a/world1.jpg",
"http://www.onegreen.net/maps/Upload_maps/201711/2017111201285735.jpg",
"http://www.onegreen.net/maps/m/a/world1.jpg",
"http://www.onegreen.net/maps/Upload_maps/201711/2017111201285735.jpg",
"http://www.onegreen.net/maps/m/a/world1.jpg",
"http://www.onegreen.net/maps/Upload_maps/201711/2017111201285735.jpg"};
private ProgressDialog dialog;
// 一个静态的Handler,Handler建议声明为静态的
private static Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.asynctask_activity);
btnDown = (Button) findViewById(R.id.btnDown);
imageViews[0] = (ImageView)findViewById(R.id.iv1);
imageViews[1] = (ImageView)findViewById(R.id.iv2);
imageViews[2] = (ImageView)findViewById(R.id.iv3);
imageViews[3] = (ImageView)findViewById(R.id.iv4);
imageViews[4] = (ImageView)findViewById(R.id.iv5);
imageViews[5] = (ImageView)findViewById(R.id.iv6);
dialog = new ProgressDialog(this);
dialog.setTitle("提示");
dialog.setMessage("正在下载,请稍后...");
dialog.setCancelable(false);
btnDown.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 开启一个子线程,用于下载图片
new Thread(new MyThread()).start();
// 显示对话框
dialog.show();
}
});
}
public class MyThread implements Runnable {
@Override
public void run() {
for(int i=0;i<MAX;i++){
// 下载一个图片
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(image_path[i]);
HttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
byte[] data = EntityUtils.toByteArray(httpResponse
.getEntity());
// 得到一个Bitmap对象,并且为了使其在post内部可以访问,必须声明为final
final Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);
final int finalI = i;
handler.post(new Runnable() {
@Override
public void run() {
// 在Post中操作UI组件ImageView
imageViews[finalI].setImageBitmap(bmp);
}
});
// 隐藏对话框
dialog.dismiss();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}那么如果我们改成thread+handler会不会好一些呢
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import com.example.administrator.testz.R;
// android:largeHeap="true" 在清单文件的application中添加这个,会增大应用程序分配的内存,不容易崩溃
public class HandlerMessageActivity1 extends Activity {
private Button btnDown;
private final static int MAX = 6;
private ImageView[] imageViews = new ImageView[MAX];
private static String image_path[] = { "http://www.onegreen.net/maps/m/a/world1.jpg",
"http://www.onegreen.net/maps/Upload_maps/201711/2017111201285735.jpg",
"http://www.onegreen.net/maps/m/a/world1.jpg",
"http://www.onegreen.net/maps/Upload_maps/201711/2017111201285735.jpg",
"http://www.onegreen.net/maps/m/a/world1.jpg",
"http://www.onegreen.net/maps/Upload_maps/201711/2017111201285735.jpg"};
private ProgressDialog dialog;
private static int IS_FINISH = 1;
private byte[] data;
private Bitmap bmp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.asynctask_activity);
btnDown = (Button) findViewById(R.id.btnDown);
imageViews[0] = (ImageView)findViewById(R.id.iv1);
imageViews[1] = (ImageView)findViewById(R.id.iv2);
imageViews[2] = (ImageView)findViewById(R.id.iv3);
imageViews[3] = (ImageView)findViewById(R.id.iv4);
imageViews[4] = (ImageView)findViewById(R.id.iv5);
imageViews[5] = (ImageView)findViewById(R.id.iv6);
dialog = new ProgressDialog(this);
dialog.setTitle("提示信息");
dialog.setMessage("正在下载,请稍后...");
dialog.setCancelable(false);
btnDown.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new MyThread()).start();
dialog.show();
}
});
}
private Handler handler = new Handler() {
// 在Handler中获取消息,重写handleMessage()方法
@Override
public void handleMessage(Message msg) {
// 判断消息码是否为1
for(int i=0;i<MAX;i++){
if(msg.what==i){
data=(byte[])msg.obj;
bmp=BitmapFactory.decodeByteArray(data, 0, data.length);
imageViews[i].setImageBitmap(bmp);
dialog.dismiss();
}
}
}
};
public class MyThread implements Runnable {
@Override
public void run() {
for(int i=0;i<MAX;i++){
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(image_path[i]);
HttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
byte[] data = EntityUtils.toByteArray(httpResponse
.getEntity());
// 获取一个Message对象,设置what为1
Message msg = Message.obtain();
msg.obj = data;
msg.what = i;
// 发送这个消息到消息队列中
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}通过消息机制,我们在主线程中接收显示图片,对于图片的实时显示刷新,效果会更好,但是还是会出现oom问题,导致程序崩溃,原因就是多余的bitmap占用了过多的内存,并且没有及时的进行清理,我下一篇博客会尝试使用loop消息队列的方式来解决这个问题
版权声明:本文为cf8833原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。