ผมลองเขียนโปรแกรม 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) เกี่ยวไหม หรือว่าผมคิดมากไปเองครับ?
on
เอ.. ลองดูว่า getImage(String
mek Tue, 01/01/2013 - 21:00
เอ.. ลองดูว่า getImage(String url) ถูกเรียกถี่ด้วยไหมครับ หรือมีส่วนไหนอีกไหมที่ใช้ BitmapFactory?
ภาพมันเป็น .png, .gif กับ
mementototem Wed, 02/01/2013 - 15:59
In reply to เอ.. ลองดูว่า getImage(String by mek
ภาพมันเป็น .png, .gif กับ .jpg คิดว่าคงจะดาวโหลดผ่านฟังชั่นนี้ไม่ได้ครับ
01-02 14:57:29.805: I/System.out(3176): resolveUri failed on bad bitmap uri: http://example.com/image.gifอ่อ ครับ
mek Fri, 04/01/2013 - 23:58
In reply to ภาพมันเป็น .png, .gif กับ by mementototem
อ่อ ครับ แต่ที่ตั้งใจจะถามจริงๆ คือ
bmp = BitmapFactory.decodeStream(stream);
นี่รันเท่ากับจำนวนภาพที่ต้องการจะ download มาจริงๆรึปล่าวเท่านั้นเองครับ เช่น ต้องการโหลดภาพเพียง 2 ภาพ แต่ใน log มี report เกี่ยวกับ BitmapFactory หลายครั้ง แสดงว่า code บรรทัดนี้ถูกเรียกหลายครั้งโดยไม่ได้ตั้งใจ หรืออาจเป็น code ส่วนอื่น (ที่มีการใช้ BitmapFactory ทั้งที่เราเขียนเอง หรือมากับ android) ที่เป็นต้นเหตุครับ
อ๋อครับ BitmapFactory
mementototem Sun, 06/01/2013 - 14:06
In reply to อ่อ ครับ by mek
อ๋อครับ BitmapFactory ที่ระบบเรียกผมไม่รู้จะดูยังไง แต่ที่ผมเรียกใช้เอง ลองให้มันแสดง log หลังจากเรียกจบ ก็เท่ากับรูปที่โหลดมาครับ
ไม่ทราบว่าภาพที่โหลดมามีไซส์เ
mr_tawan Wed, 02/01/2013 - 07:56
ไม่ทราบว่าภาพที่โหลดมามีไซส์เท่าไหร่ครับ ?
200k - 300k ครับ
mementototem Wed, 02/01/2013 - 15:28
In reply to ไม่ทราบว่าภาพที่โหลดมามีไซส์เ by mr_tawan
200k - 300k ครับ (รูปขนาดกว้างยาวไม่เกิน 640px ครับ)
ตรงนี้ผมว่าต้องเอาขนาดภาพที่เ
mr_tawan Thu, 03/01/2013 - 11:57
In reply to 200k - 300k ครับ by mementototem
ตรงนี้ผมว่าต้องเอาขนาดภาพที่เป็น uncompressed มาคิดนะครับ อย่าง ภาพขนาด 640 * 480 ที่ 32 บิทสี (สี่ไบท์) จะกินที่ในหน่วยความจำประมาณ 1.2MB น่ะ
จะว่าไปผมก็ทำ App ดูรูปอยู่เหมือนกัน (แต่เป็นอ่านจาก Samba Share) ทั้ง App ผมใช้ Memory ราว ๆ 45-50MB ครับ ก็ขึ้น OutOfMemory อยู่บ่อย ๆ แหละ แต่ว่ายังทำไม่ถึงขั้นที่จะลงไป profile ขนาดนั้น ฮะๆๆ ของผมส่วนที่กินเยอะที่สุดคือส่วน LRUCache ๕รับ
อันนี้ทดสอบบน Android version
PaPaSEK Wed, 02/01/2013 - 09:45
อันนี้ทดสอบบน Android version ไหนครับ แล้วตัวอุปกรณ์มีแรมเหลือเท่าไหร่
HD2 กับ MIUI 2.10.12 บน
mementototem Wed, 02/01/2013 - 15:30
In reply to อันนี้ทดสอบบน Android version by PaPaSEK
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 เหมือนกัน
เอ่อ ...
PaPaSEK Wed, 02/01/2013 - 17:35
In reply to HD2 กับ MIUI 2.10.12 บน by mementototem
เอ่อ ... ไปไม่ถูกเลยทีเดียว
เพราะ Android ก่อน HC จะมีปัญหาเรื่อง GC ขึ้นถี่ครับ อีกปัญหาที่ GC ขึ้นถี่เกินก็คือแรมเหลือน้อย แต่เท่าที่ดูก็ค่อนข้างอยู่ในเกณฑ์ที่ GC ไม่น่าจะถี่ขนาดนั้น
อ้อ ... 300K เกือบเข้าข่ายภาพที่จัดอยู่ในข่าย "ใหญ่" นะครับ
ลองดูอันนี้เผื่อช่วยได้
ถ้าเป็น 640*640px ถอดแบบ
mementototem Wed, 02/01/2013 - 19:27
In reply to เอ่อ ... by PaPaSEK
ถ้าเป็น 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
คงเป็นเพราะ heap
soginal Wed, 02/01/2013 - 18:21
คงเป็นเพราะ heap ไม่พอละมั้งครับ (max heap size มันเป็น 16 MB รึเปล่าครับ) gc เลยรันบ่อยๆ ลองเซ็ต android:largeHeap="true" ดูรึยังครับ
รึไม่ก็ลอง decode ภาพออกมาให้เป็นภาพเล็กๆตาม link นี้ดูครับ (คงเคยอ่านแล้วมั้ง)
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
แต่จริงๆภาพของคุณก็เล็กอยู่แล้วล่ะนะ :) แต่ลองทำให้เล็กกว่านี้เพื่อเทสดูก็ได้นะครับ ว่าถ้าเหลือแต่ภาพจิ๋วๆแล้วยังมีปัญหาอยู่ไหม
ลอง decode เป็นภาพเล็ก (ผ่าน
mementototem Wed, 02/01/2013 - 19:40
In reply to คงเป็นเพราะ heap by soginal
ลอง decode เป็นภาพเล็ก (ผ่าน inSimpleSize) allocation size ของ BitmapFactory เล็กลง แต่ heap ยังคงอยู่ที่ 13M กว่า ๆ เหมือนเดิม แล้วก็ยังมี GC_CONCURRENT ขึ้นอยู่ ไม่มี GC_ALLOC ไม่ขึ้น grow heap (largeHeap เก็บพอ?)
เผื่อเกี่ยว: ตอนนี้ผมสั่งให้มันโหลดภาพมาเก็บเพิ่มเป็น 3 ภาพครับ