android執行緒佇列(一)執行緒阻塞
阿新 • • 發佈:2019-02-14
我們的程式因為在子執行緒裡面做耗時操作,記憶體洩漏導致了程式的崩潰,下面是崩潰的日誌,從日誌中,可以清晰的看出,超過程式的最大記憶體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訊息佇列的方式來解決這個問題