1. 程式人生 > >android執行緒佇列(一)執行緒阻塞

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訊息佇列的方式來解決這個問題