Tags:

ผมลองเขียนโปรแกรม Android ให้โหลดรูปจากอินเทอร์เน็ต แล้วก็นำมาแสดงใน ImageView แต่ทุกครั้งที่โหลดรูปใหม่มาแสดง จะมี garbage collector (GC_CONCURRENT กับ GC_FOR_ALLOC) มา free memory ทุกครั้ง (ดูจาก Log Cat)

ใน DDMS มี heap size 10.937/16.133 MB และ allocation tracker แจ้ง size ที่ใหญ่ที่สุดมาจาก BitmapFactory ผมจะลดตรงนี้ได้วิธีไหนบ้างครับ

(รูปภาพที่ดาวโหลดส่วนใหญ่เป็น .jpg ภาพไม่ใหญ่มากนัก กว้างยาวไม่เกิน 640px ครับ)

ฟังชั่นดาวโหลดภาพ

{syntaxhighlighter brush:java}
public static Bitmap getImage(String url) {

AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
HttpGet http = new HttpGet(url);
InputStream stream = null;
Bitmap bmp = null;

// try to connect to server
try {
    HttpResponse response = client.execute(http);
    int statusCode = response.getStatusLine().getStatusCode();

    // connected, everything OK
    HttpEntity entity = response.getEntity();
    if (statusCode != HttpStatus.SC_OK) {
        Log.e(TAG, "Server error: " + statusCode);
        return null;
    }

    stream = entity.getContent();
    if (stream == null) {
        return null;
    }

    bmp = BitmapFactory.decodeStream(stream);
    stream.close();
    entity.consumeContent();
}
catch (Exception e) {
    Log.e(TAG, "Unknown Error: " + e.toString());
}
finally {
    client.close();
}

return bmp;

}
{/syntaxhighlighter}

ฟังชั่นแสดงภาพ ผมเก็บรูปภาพลงตัวแปรก่อน เพราะจะได้นำมาแสดงอีกครั้งตอนหมุนจอครับ
{syntaxhighlighter brush:java}
public class StartFragment extends Fragment {
private Bitmap bmp = null;

// --skip--

public void displayImage() {
    ImageView view = (ImageView) getActivity().findViewById(R.id.image);
    view.setImageBitmap(bmp);
}

public void getImage() {
    // normally run in AsyncTask
    bmp = getImage("http://example.com/photo.jpg");
}

}
{/syntaxhighlighter}

แบบนี้จะมีปัญหาอะไรไหม เพราะลองหาอ่านจากหลาย ๆ เว็บ เขาบอกว่าการที่ gc ขึ้นบ่อย ๆ อาจเกิดจาก memory leak แต่เท่าที่ผมดูผ่าน Setting แอพที่เขียนกินแรมประมาณ 20 MB (แล้วแต่ขนาดภาพที่แสดงอยู่)

มือถือผมแรมเหลือค่อนข้างน้อย (แรมแค่ 512 แต่ลง JB) เกี่ยวไหม หรือว่าผมคิดมากไปเองครับ?

Get latest news from Blognone
By: mek
Android
on 1 January 2013 - 21:00 #523921

เอ.. ลองดูว่า getImage(String url) ถูกเรียกถี่ด้วยไหมครับ หรือมีส่วนไหนอีกไหมที่ใช้ BitmapFactory?

By: mementototem
ContributorJusci's WriterAndroidWindows
on 2 January 2013 - 15:59 #524209 Reply to:523921
mementototem's picture

ภาพมันเป็น .png, .gif กับ .jpg คิดว่าคงจะดาวโหลดผ่านฟังชั่นนี้ไม่ได้ครับ

01-02 14:57:29.805: I/System.out(3176): resolveUri failed on bad bitmap uri: http://example.com/image.gif


Jusci - Google Plus - Twitter

By: mek
Android
on 4 January 2013 - 23:58 #525268 Reply to:524209

อ่อ ครับ แต่ที่ตั้งใจจะถามจริงๆ คือ

bmp = BitmapFactory.decodeStream(stream);

นี่รันเท่ากับจำนวนภาพที่ต้องการจะ download มาจริงๆรึปล่าวเท่านั้นเองครับ เช่น ต้องการโหลดภาพเพียง 2 ภาพ แต่ใน log มี report เกี่ยวกับ BitmapFactory หลายครั้ง แสดงว่า code บรรทัดนี้ถูกเรียกหลายครั้งโดยไม่ได้ตั้งใจ หรืออาจเป็น code ส่วนอื่น (ที่มีการใช้ BitmapFactory ทั้งที่เราเขียนเอง หรือมากับ android) ที่เป็นต้นเหตุครับ

By: mementototem
ContributorJusci's WriterAndroidWindows
on 6 January 2013 - 14:06 #525652 Reply to:525268
mementototem's picture

อ๋อครับ BitmapFactory ที่ระบบเรียกผมไม่รู้จะดูยังไง แต่ที่ผมเรียกใช้เอง ลองให้มันแสดง log หลังจากเรียกจบ ก็เท่ากับรูปที่โหลดมาครับ


Jusci - Google Plus - Twitter

By: mr_tawan
ContributoriPhoneAndroidWindows
on 2 January 2013 - 07:56 #524053
mr_tawan's picture

ไม่ทราบว่าภาพที่โหลดมามีไซส์เท่าไหร่ครับ ?


  • 9tawan.net บล็อกส่วนตัวฮับ
By: mementototem
ContributorJusci's WriterAndroidWindows
on 2 January 2013 - 15:28 #524189 Reply to:524053
mementototem's picture

200k - 300k ครับ (รูปขนาดกว้างยาวไม่เกิน 640px ครับ)


Jusci - Google Plus - Twitter

By: mr_tawan
ContributoriPhoneAndroidWindows
on 3 January 2013 - 11:57 #524508 Reply to:524189
mr_tawan's picture

ตรงนี้ผมว่าต้องเอาขนาดภาพที่เป็น uncompressed มาคิดนะครับ อย่าง ภาพขนาด 640 * 480 ที่ 32 บิทสี (สี่ไบท์) จะกินที่ในหน่วยความจำประมาณ 1.2MB น่ะ

จะว่าไปผมก็ทำ App ดูรูปอยู่เหมือนกัน (แต่เป็นอ่านจาก Samba Share) ทั้ง App ผมใช้ Memory ราว ๆ 45-50MB ครับ ก็ขึ้น OutOfMemory อยู่บ่อย ๆ แหละ แต่ว่ายังทำไม่ถึงขั้นที่จะลงไป profile ขนาดนั้น ฮะๆๆ ของผมส่วนที่กินเยอะที่สุดคือส่วน LRUCache ๕รับ


  • 9tawan.net บล็อกส่วนตัวฮับ
By: PaPaSEK
ContributorAndroidWindowsIn Love
on 2 January 2013 - 09:45 #524072
PaPaSEK's picture

อันนี้ทดสอบบน Android version ไหนครับ แล้วตัวอุปกรณ์มีแรมเหลือเท่าไหร่

By: mementototem
ContributorJusci's WriterAndroidWindows
on 2 January 2013 - 15:30 #524195 Reply to:524072
mementototem's picture

HD2 กับ MIUI 2.10.12 บน Android 4.1.2 แรมก่อนเปิดโปรแกรม 135 MB (หน้า Setting/App)

หลังจากผมกด clear แรมไปใน task manager ของ MIUI จะแจ้ง 336/512MB ก่อนเปิดโปรแกรม พอเปิดแล้วจะเป็น 395/512MB (ซอฟแวร์ใช้ได้แค่ 448 ครับ) (อันนี้น่าจะเพราะ service ต่างเริ่มกลับทำงานด้วยมั้งครับ เพราะ cache process ของแอพมันแค่ 15 - 20 MB เอง)

Xperia Sola กับ Stock Android 4.0.4 แรมก่อนเปิดโปรแกรมก็ 133 MB (Setting/App) ก็ขึ้น GC เหมือนกัน


Jusci - Google Plus - Twitter

By: PaPaSEK
ContributorAndroidWindowsIn Love
on 2 January 2013 - 17:35 #524247 Reply to:524195
PaPaSEK's picture

เอ่อ ... ไปไม่ถูกเลยทีเดียว

เพราะ Android ก่อน HC จะมีปัญหาเรื่อง GC ขึ้นถี่ครับ อีกปัญหาที่ GC ขึ้นถี่เกินก็คือแรมเหลือน้อย แต่เท่าที่ดูก็ค่อนข้างอยู่ในเกณฑ์ที่ GC ไม่น่าจะถี่ขนาดนั้น

อ้อ ... 300K เกือบเข้าข่ายภาพที่จัดอยู่ในข่าย "ใหญ่" นะครับ

ลองดูอันนี้เผื่อช่วยได้

By: mementototem
ContributorJusci's WriterAndroidWindows
on 2 January 2013 - 19:27 #524281 Reply to:524247
mementototem's picture

ถ้าเป็น 640*640px ถอดแบบ ARGB_8888 (4b/px) ขนาดมันในแรมจะเป็น 1.56MB ครับ แต่ภาพมันเป็น jpg ผมเลยถอดด้วย RGB_565 ครับ (แต่ไม่รู้สึกว่าขนาดมันจะต่างกันสักเท่าไร่เลย)

พอดีผม decodestream มันเลยสั่ง decode 2 ครั้งไม่ได้ กำลังมองหาวิธีอื่นในการสร้างค่า inSampleSize อยู่เหมือนกัน แต่ผมอยากได้ภาพชัด ๆ มากกว่า เผื่อหมุนมือถือ ภาพที่ได้จะได้ชัดตลอด

ผมกังวลเรื่อง memory leak มากกว่า ตามความเข้าใจผม สิ่งที่ GC ลบทิ้งไปคือตัวแปร bmp ในฟังชั่น getImage() ถ้ามันไม่ leak ก็คงไม่เป็นไร (แต่รู้สึกว่า ผมจะเก็บ reference ของ view ต่าง ๆ ไว้เป็น global var ไม่ได้ คงโดน GC เก็บทิ้ง)

ป.ล. ลองแอบดู QuickPic มันก็ขึ้น GC บ่อย ๆ สบายใจขึ้นนิดนึง :D


Jusci - Google Plus - Twitter

By: soginal
AndroidIn Love
on 2 January 2013 - 18:21 #524263
soginal's picture

คงเป็นเพราะ heap ไม่พอละมั้งครับ (max heap size มันเป็น 16 MB รึเปล่าครับ) gc เลยรันบ่อยๆ ลองเซ็ต android:largeHeap="true" ดูรึยังครับ

รึไม่ก็ลอง decode ภาพออกมาให้เป็นภาพเล็กๆตาม link นี้ดูครับ (คงเคยอ่านแล้วมั้ง)
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html

แต่จริงๆภาพของคุณก็เล็กอยู่แล้วล่ะนะ :) แต่ลองทำให้เล็กกว่านี้เพื่อเทสดูก็ได้นะครับ ว่าถ้าเหลือแต่ภาพจิ๋วๆแล้วยังมีปัญหาอยู่ไหม

By: mementototem
ContributorJusci's WriterAndroidWindows
on 2 January 2013 - 19:40 #524285 Reply to:524263
mementototem's picture

ลอง decode เป็นภาพเล็ก (ผ่าน inSimpleSize) allocation size ของ BitmapFactory เล็กลง แต่ heap ยังคงอยู่ที่ 13M กว่า ๆ เหมือนเดิม แล้วก็ยังมี GC_CONCURRENT ขึ้นอยู่ ไม่มี GC_ALLOC ไม่ขึ้น grow heap (largeHeap เก็บพอ?)

เผื่อเกี่ยว: ตอนนี้ผมสั่งให้มันโหลดภาพมาเก็บเพิ่มเป็น 3 ภาพครับ


Jusci - Google Plus - Twitter