android线程队列(一)线程阻塞

我们的程序因为在子线程里面做耗时操作,内存泄漏导致了程序的崩溃,下面是崩溃的日志,从日志中,可以清晰的看出,超过程序的最大内存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版权协议,转载请附上原文出处链接和本声明。