วิธีขี้เกียจโหลดรูปภาพใน ListView ใน Android


1931

ฉันใช้ a ListViewเพื่อแสดงภาพและคำอธิบายภาพที่เกี่ยวข้องกับภาพเหล่านั้น ฉันได้รับภาพจากอินเทอร์เน็ต มีวิธีการโหลดภาพที่ขี้เกียจหรือไม่ในขณะที่ข้อความปรากฏขึ้น UI จะไม่ถูกบล็อกและรูปภาพจะถูกแสดงขณะดาวน์โหลด

จำนวนภาพทั้งหมดไม่คงที่


10
คุณสามารถใช้AsyncImageView GreenDroid ของ setUrlโทรเพียง
Pascal Dimassimo

7
ฉันได้ใช้มัน มันเป็นการใช้งานที่ยอดเยี่ยม ข่าวร้ายที่ AsyncImageView เป็นส่วนหนึ่งของโครงการ GreenDroid ขนาดใหญ่ซึ่งทำให้แอปพลิเคชันของคุณใหญ่ขึ้นแม้ในกรณีที่คุณต้องการทั้งหมดคือ AsyncImageView ดูเหมือนว่าโครงการ GreenDroid จะไม่ได้รับการปรับปรุงตั้งแต่ปี 2011
borisstr

5
คุณสามารถลองห้องสมุดนี้: Android-http-image-managerซึ่งในความคิดของฉันดีที่สุดสำหรับการโหลดรูปภาพแบบอะซิงโครนัส
Ritesh Kumar Dubey

34
เพียงใช้ Picasso มันจะทำเองทั้งหมด 'Picasso.with (yourContext) .load (img src / path / drawable ที่นี่) .into (imageView คือเป้าหมายของคุณ);' แค่นั้นแหละ!
Anuj Sharma

6
ลองใช้: github.com/nostra13/Android-Universal-Image-Loaderห้องสมุดนี้เร็วและมีประสิทธิภาพมากสำหรับการโหลดที่ขี้เกียจและการแคชรูปภาพ
krunal patel

คำตอบ:


1087

นี่คือสิ่งที่ฉันสร้างขึ้นเพื่อเก็บภาพที่แอปของฉันกำลังแสดงอยู่ โปรดทราบว่าวัตถุ "บันทึก" ที่ใช้งานที่นี่เป็นเสื้อคลุมที่กำหนดเองของฉันเกี่ยวกับระดับการบันทึกขั้นสุดท้ายใน Android

package com.wilson.android.library;

/*
 Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
    private final Map<String, Drawable> drawableMap;

    public DrawableManager() {
        drawableMap = new HashMap<String, Drawable>();
    }

    public Drawable fetchDrawable(String urlString) {
        if (drawableMap.containsKey(urlString)) {
            return drawableMap.get(urlString);
        }

        Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
        try {
            InputStream is = fetch(urlString);
            Drawable drawable = Drawable.createFromStream(is, "src");


            if (drawable != null) {
                drawableMap.put(urlString, drawable);
                Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                        + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                        + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
            } else {
              Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
            }

            return drawable;
        } catch (MalformedURLException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        } catch (IOException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        }
    }

    public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
        if (drawableMap.containsKey(urlString)) {
            imageView.setImageDrawable(drawableMap.get(urlString));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                //TODO : set imageView to a "pending" image
                Drawable drawable = fetchDrawable(urlString);
                Message message = handler.obtainMessage(1, drawable);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }

    private InputStream fetch(String urlString) throws MalformedURLException, IOException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet request = new HttpGet(urlString);
        HttpResponse response = httpClient.execute(request);
        return response.getEntity().getContent();
    }
}

104
ฉันคิดว่าคุณควรใช้ SoftReferences เพื่อที่ว่าโปรแกรมของคุณจะไม่ก่อให้เกิด OutOfMemoryException เนื่องจาก GC สามารถล้าง softreferences เมื่อขนาดฮีพเพิ่มขึ้น ... คุณสามารถจัดการรุ่นของคุณเองหลังจากผ่านไปไม่กี่วินาทีคุณสามารถใส่รูปภาพของคุณลงในรายการนั้นและก่อนที่จะโหลดคุณควรตรวจสอบว่าภาพนั้นมีอยู่หรือไม่ จากรายการนั้นและนำกลับไปยังรายการ softref ของคุณและหลังจากนั้นบางครั้งคุณสามารถล้างรายการของคุณ :)
AZ_

36
โครงการ Google Shelves เป็นตัวอย่างที่ยอดเยี่ยมดูว่าพวกเขาทำcode.google.com/p/shelves อย่างไร
AZ_

12
คุณไม่พลาดการส่งคืนเมื่อ drawableMap มีรูปภาพ ... โดยไม่ต้องเริ่มการดึงข้อมูลเธรด
Karussell

5
รหัสนี้มีปัญหาหลายประการ ประการแรกคุณควรแคช Drawables ที่จะก่อให้เกิดการรั่วไหลของหน่วยความจำ: stackoverflow.com/questions/7648740/... ประการที่สองตัวแคชไม่เคยถูกล้างออกดังนั้นมันจะเติบโตไปตลอดกาลนั่นคือการรั่วไหลของหน่วยความจำอื่น
satur9nine

10
ยังไม่มีใครเคยได้ยินเกี่ยวกับLRU Cache developer.android.com/training/displaying-bitmaps/ …
Muhammad Babar

1025

ฉันทำตัวอย่างง่ายๆของรายการที่ขี้เกียจ (อยู่ที่ GitHub) พร้อมรูปภาพ

การใช้งานพื้นฐาน

ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView); 

อย่าลืมเพิ่มสิทธิ์ต่อไปนี้ใน AndroidManifest.xml ของคุณ:

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please

สร้าง ImageLoader เพียงอินสแตนซ์เดียวและนำมาใช้ใหม่ในแอปพลิเคชันของคุณ วิธีนี้การแคชรูปภาพจะมีประสิทธิภาพมากขึ้น

มันอาจจะเป็นประโยชน์กับใครบางคน มันดาวน์โหลดภาพในเธรดพื้นหลัง รูปภาพถูกแคชในการ์ด SD และในหน่วยความจำ การใช้แคชนั้นง่ายมากและเพียงพอสำหรับการสาธิต ฉันถอดรหัสภาพด้วย inSampleSize เพื่อลดการใช้หน่วยความจำ ฉันพยายามจัดการกับมุมมองการรีไซเคิลอย่างถูกต้อง

ข้อความแทน


7
ขอบคุณผมใช้รุ่นที่ปรับเปลี่ยนเล็กน้อยของรหัสของคุณเกือบทุกที่ในโครงการของฉัน: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/...
shaman.sir

3
มีใครดูที่โค้ดโดย Gilles Debunne เป็นทางเลือก ความแตกต่างใหญ่สองอย่างที่ฉันเห็นคือ 1) ในการแคชหน่วยความจำกับการ์ด SD และ 2) การใช้ AsyncTasks แทนเธรดพูล android-developers.blogspot.com/2010/07/…
Richard

4
มีข้อผิดพลาดบางครั้งมันก็ยิง: 10-13 09: 58: 46.738: ข้อผิดพลาด / AndroidRuntime (24250): จัดการไม่ได้จับ: ด้ายหลักออกเนื่องจากข้อยกเว้นที่ไม่ได้ตรวจสอบ 10-13 09: 58: 46.768: ข้อผิดพลาด / AndroidRuntime (24250) : java.lang.ArrayIndexOutOfBoundsException: ดัชนีอาร์เรย์อยู่นอกช่วง: 0 10-13 09: 58: 46.768: ข้อผิดพลาด / AndroidRuntime (24250): ที่ java.util.Vector.elementAt (Vector.java:331) 10-13 09: 58: 46.768: ข้อผิดพลาด / AndroidRuntime (24250): ที่ java.util.Vector.get (Vector.java:445) 10-13 09: 58: 46.768: ข้อผิดพลาด / AndroidRuntime (24250): ที่ com.my.app.image .ImageLoader $ PhotosQueue.Clean (ImageLoader.java:91)
Mikooos

64
ฉันต้องการเพิ่มสำหรับมือใหม่อย่างฉันว่าถ้าคุณต้องการเพิ่มรหัสนี้ในโครงการของคุณคุณต้องเพิ่มสิทธิ์การใช้งานแคชภายนอกในรายการ ขอบคุณ
Adam

2
@BruceHill ไม่มี photosQueue ในซอร์สโค้ดล่าสุด คุณดูเหมือนจะใช้เวอร์ชั่นเก่ามาก
Fedor

552

ผมขอแนะนำแหล่งเครื่องมือเปิดLoader สากลภาพ เดิมนั้นใช้LazyListของโครงการ Fedor Vlasov และได้รับการปรับปรุงอย่างมากมายตั้งแต่นั้นมา

  • การโหลดภาพแบบมัลติเธรด
  • ความเป็นไปได้ของการปรับแต่งอย่างกว้างการกำหนดค่าของ ImageLoader (ตัวจัดการเธรด, downlaoder, ตัวถอดรหัส, หน่วยความจำและแคชดิสก์ตัวเลือกรูปภาพที่แสดงและอื่น ๆ )
  • ความเป็นไปได้ของการแคชรูปภาพในหน่วยความจำและ / หรือไฟล์ sysytem ของอุปกรณ์ (หรือการ์ด SD)
  • ความเป็นไปได้ในการโหลด "ฟัง"
  • ความเป็นไปได้ในการปรับแต่งทุกการโทรภาพที่แสดงด้วยตัวเลือกแยก
  • รองรับวิดเจ็ต
  • รองรับ Android 2.0+


16
หากคุณกำลังมองหาโค้ด "โหลดภาพ" ที่ยอดเยี่ยมที่ผู้สร้างใช้ปฏิบัติและรักษาไว้ในฐานะลูกที่มีค่าของเขา (ไม่ใช่เพียงแค่โซลูชั่นเดียว) คุณก็พบว่ามันเป็นเช่นนั้น big ups Nostra
AndroidGecko

ผลงานยอดเยี่ยมจริง ๆ แต่มันไม่ค่อยดีนัก หากคุณสามารถบอกได้ว่าประสิทธิภาพที่ดีที่สุด 'ImageLoader' build ... หรืออาจเป็นเพราะ 'DisplayImageOptions'
dinigo

ฉันรัก gridview นี้มาก แต่ฉันไม่สามารถใช้สำหรับการกวดวิชาของฉัน ฉันเป็นผู้เริ่มต้นของ Android ฉันสร้างแอปพลิเคชันเกี่ยวกับ gridview แต่แอปพลิเคชันของฉันคือ targetSdkVersion 15 ดังนั้นฉันต้องใช้ Asynctask สำหรับกระบวนการในเธรดพื้นหลัง ฉันเคยใช้ gridview นี้ แต่มันไม่ทำงาน ฉันจะใช้มันใน targetSdkVersion 15 ได้อย่างไร
kongkea

คุณสามารถสร้างคำถามแยกต่างหากพร้อมแท็ก[universal-image-loader]
nostra13

3
หลังจากอ่านเอกสาร Android อย่างเป็นทางการและพยายามทำสิ่งนี้ด้วยตัวเองฉันค่อนข้างมั่นใจว่าห้องสมุดนี้เป็นจุดสิ้นสุดของการโหลดรูปภาพทั้งหมด ไม่สามารถลงคะแนนได้มากพอ
หลัก

159

Multithreading For Performanceบทช่วยสอนโดย Gilles Debunne

นี่คือจากบล็อกนักพัฒนา Android รหัสที่แนะนำใช้:

  • AsyncTasks.
  • ฮาร์ดขนาด FIFO cacheจำกัด
  • garbage collectแคชที่นุ่มนวลและง่ายดาย
  • ยึด Drawableในขณะที่คุณดาวน์โหลด

ป้อนคำอธิบายรูปภาพที่นี่


10
มันทำงานได้ดีใน 2.1 เช่นกัน อย่าใช้ AndroidHttpClient
โทมัส Ahle

2
@ thomas-ahle ขอบคุณฉันเห็นว่า AndroidHttpClient กำลังให้ข้อผิดพลาดใน 2.1 เนื่องจากมีการใช้งานจาก 2.2 แต่ไม่ได้พยายามหาสิ่งอื่นมาแทนที่จริง ๆ
Adinia

4
@Adina คุณถูกต้องฉันลืม อย่างไรก็ตามไม่มีอะไรในสูตรที่ไม่สามารถทำได้ด้วย HttpClient ปกติ
โทมัส Ahle

ฉันเคยได้ยินมาหลายที่แล้วที่ Google ไม่แนะนำการอ้างอิงแบบนุ่มเนื่องจากเคอร์เนล Android มีความกระตือรือร้นที่จะรวบรวมการอ้างอิงเหล่านี้เมื่อเทียบกับรุ่นก่อนหน้าของระบบ
มูฮัมหมัดอาเหม็ดอาบูทาลิบ

109

อัปเดต: โปรดทราบว่าคำตอบนี้ไม่ได้ผลในขณะนี้ ตัวรวบรวมข้อมูลขยะทำงานอย่างหนักหน่วงใน SoftReference และ WeakReference ดังนั้นรหัสนี้ไม่เหมาะสำหรับแอปใหม่ (ให้ลองใช้ไลบรารี่เช่นUniversal Image Loader แทนในคำตอบอื่น ๆ )

ขอบคุณ James สำหรับโค้ดและ Bao-Long สำหรับคำแนะนำในการใช้ SoftReference ฉันใช้การเปลี่ยนแปลง SoftReference กับรหัสของ James น่าเสียดายที่ SoftReferences ทำให้รูปภาพของฉันเป็นขยะที่รวบรวมได้เร็วเกินไป ในกรณีของฉันมันใช้ได้ดีโดยไม่มีสิ่ง SoftReference เนื่องจากขนาดรายการของฉันมี จำกัด และภาพของฉันมีขนาดเล็ก

มีการอภิปรายจากปีที่ผ่านมาเกี่ยวกับการ SoftReferences กลุ่ม Google ที่เป็น: การเชื่อมโยงไปด้าย ในฐานะที่เป็นวิธีแก้ปัญหาการรวบรวมขยะที่เร็วเกินไปพวกเขาแนะนำความเป็นไปได้ของการตั้งค่าขนาดฮีพ VM ด้วยตนเองโดยใช้ dalvik.system.VMRuntime.setMinimumHeapSize () ซึ่งไม่ค่อยน่าสนใจสำหรับฉัน

public DrawableManager() {
    drawableMap = new HashMap<String, SoftReference<Drawable>>();
}

public Drawable fetchDrawable(String urlString) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null)
            return drawable;
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

    if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
    try {
        InputStream is = fetch(urlString);
        Drawable drawable = Drawable.createFromStream(is, "src");
        drawableRef = new SoftReference<Drawable>(drawable);
        drawableMap.put(urlString, drawableRef);
        if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
        return drawableRef.get();
    } catch (MalformedURLException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    } catch (IOException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    }
}

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null) {
            imageView.setImageDrawable(drawableRef.get());
            return;
        }
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            imageView.setImageDrawable((Drawable) message.obj);
        }
    };

    Thread thread = new Thread() {
        @Override
        public void run() {
            //TODO : set imageView to a "pending" image
            Drawable drawable = fetchDrawable(urlString);
            Message message = handler.obtainMessage(1, drawable);
            handler.sendMessage(message);
        }
    };
    thread.start();
}

3
คุณสามารถสร้างรุ่นเช่นรุ่นยากและรุ่นนุ่ม คุณสามารถแก้ไขเวลาล้างแคชจะล้างรูปภาพทั้งหมดที่ไม่ได้เข้าถึงใน 3 วินาที .. คุณสามารถดูโครงการ google ชั้นวาง
AZ_

developer.android.com/reference/java/lang/ref/…เอกสาร SoftReference มีหมายเหตุเกี่ยวกับการแคชให้ดูที่ส่วน "หลีกเลี่ยงการอ้างอิงที่นุ่มนวลสำหรับการแคช" แอปพลิเคชันส่วนใหญ่ควรใช้ android.util.LruCache แทนการอ้างอิงแบบนุ่มนวล
vokilam

ฉันชื่นชมรหัสของคุณ แต่ตอนนี้ใน Android O / S ใหม่มีการเก็บขยะ 'ก้าวร้าว' การถือการอ้างอิงที่อ่อนแอนั้นไม่สมเหตุสมผลเลยสำหรับฉัน
j2emanue

@ j2emanue คุณพูดถูกในขณะที่ฉันพยายามระบุที่ด้านบนของคำตอบ SoftReferences จะถูกเก็บรวบรวมขยะเร็วเกินไป ฉันจะพยายามแก้ไขคำตอบนี้เพื่อให้ชัดเจนยิ่งขึ้น
TalkLittle

97

ปิกัสโซ

ใช้ห้องสมุด Picasso ของ Jake Wharton (Perfect ImageLoading Library จากผู้พัฒนา ActionBarSherlock)

การดาวน์โหลดรูปภาพและไลบรารีแคชที่มีประสิทธิภาพสำหรับ Android

รูปภาพเพิ่มบริบทและไหวพริบที่จำเป็นสำหรับแอปพลิเคชัน Android Picasso ช่วยให้การโหลดภาพที่ไม่ยุ่งยากในแอปพลิเคชันของคุณ - มักจะอยู่ในโค้ดเดียว

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

ข้อผิดพลาดทั่วไปของการโหลดรูปภาพบน Android ได้รับการจัดการโดยอัตโนมัติโดย Picasso:

การจัดการ ImageView การรีไซเคิลและการยกเลิกการดาวน์โหลดในอะแดปเตอร์ การแปลงภาพที่ซับซ้อนด้วยการใช้หน่วยความจำน้อยที่สุด หน่วยความจำอัตโนมัติและแคชดิสก์

ห้องสมุด Picasso Jake Wharton

เหิน

Glide เป็นเฟรมเวิร์กการจัดการมีเดียแบบโอเพ่นซอร์สที่รวดเร็วและมีประสิทธิภาพสำหรับ Android ที่ล้อมรอบการถอดรหัสสื่อหน่วยความจำและการแคชดิสก์และการรวมทรัพยากรในอินเตอร์เฟซที่เรียบง่ายและใช้งานง่าย

รองรับการดึงถอดรหัสและการแสดงภาพนิ่งวิดีโอรูปภาพและ GIF เคลื่อนไหว ร่อนประกอบด้วย API ที่ยืดหยุ่นที่ช่วยให้นักพัฒนาสามารถเชื่อมต่อกับเครือข่ายสแต็กเกือบทุกชนิด โดยปกติแล้ว Glide จะใช้ HttpUrlConnection ตามสแต็คที่กำหนดเอง แต่ยังรวมถึงยูทิลิตี้ไลบรารีที่เชื่อมต่อกับโครงการวอลเลย์ของ Google หรือไลบรารี OkHttp ของ Square แทน

Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);

โฟกัสหลักของ Glide คือการทำการเลื่อนรายการภาพทุกชนิดให้ราบรื่นและรวดเร็วที่สุดเท่าที่จะเป็นไปได้ แต่ Glide ยังมีประสิทธิภาพสำหรับเกือบทุกกรณีที่คุณต้องการดึงปรับขนาดและแสดงภาพระยะไกล

Glide Image Loading Library

จิตรกรรมฝาผนังโดย Facebook

Fresco เป็นระบบที่ทรงพลังสำหรับการแสดงภาพในแอพพลิเคชั่น Android

ภาพเฟรสโกดูแลการโหลดภาพและการแสดงผลดังนั้นคุณไม่จำเป็นต้อง มันจะโหลดภาพจากเครือข่ายที่จัดเก็บในท้องถิ่นหรือทรัพยากรในท้องถิ่นและแสดงตัวยึดตำแหน่งจนกว่าภาพจะมาถึง มีแคชสองระดับ หนึ่งในหน่วยความจำและอีกหนึ่งในการจัดเก็บข้อมูลภายใน

ปูนเปียก Github

ใน Android 4.x หรือต่ำกว่า Fresco จะวางรูปภาพในพื้นที่พิเศษของหน่วยความจำ Android สิ่งนี้ช่วยให้แอปพลิเคชันของคุณทำงานได้เร็วขึ้นและประสบปัญหากับ OutOfMemoryError ที่น่ากลัวน้อยกว่า

เอกสาร Fresco


Picasso เป็นห้องสมุดที่พัฒนาโดย Square
LordRaydenMK

83

ตัวโหลดประสิทธิภาพสูง - หลังจากตรวจสอบวิธีการที่แนะนำที่นี่ฉันใช้โซลูชันของ Benกับการเปลี่ยนแปลงบางอย่าง -

  1. ฉันรู้ว่าการทำงานกับ drawable นั้นเร็วกว่าด้วยบิตแมปดังนั้นฉันจึงใช้ drawable แทน

  2. การใช้ SoftReference นั้นยอดเยี่ยม แต่มันทำให้ภาพแคชถูกลบบ่อยเกินไปดังนั้นฉันจึงเพิ่มรายการที่เชื่อมโยงที่เก็บการอ้างอิงรูปภาพป้องกันไม่ให้ภาพถูกลบจนกว่าจะถึงขนาดที่กำหนดไว้ล่วงหน้า

  3. หากต้องการเปิด InputStream ฉันใช้ java.net.URLConnection ซึ่งอนุญาตให้ฉันใช้เว็บแคช (คุณต้องตั้งค่าแคชการตอบกลับก่อน แต่นั่นเป็นอีกเรื่องหนึ่ง)

รหัสของฉัน:

import java.util.Map; 
import java.util.HashMap; 
import java.util.LinkedList; 
import java.util.Collections; 
import java.util.WeakHashMap; 
import java.lang.ref.SoftReference; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ExecutorService; 
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.net.MalformedURLException; 
import java.io.IOException; 
import java.net.URL;
import java.net.URLConnection;

public class DrawableBackgroundDownloader {    

private final Map<String, SoftReference<Drawable>> mCache = new HashMap<String, SoftReference<Drawable>>();   
private final LinkedList <Drawable> mChacheController = new LinkedList <Drawable> ();
private ExecutorService mThreadPool;  
private final Map<ImageView, String> mImageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());  

public static int MAX_CACHE_SIZE = 80; 
public int THREAD_POOL_SIZE = 3;

/**
 * Constructor
 */
public DrawableBackgroundDownloader() {  
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);  
}  


/**
 * Clears all instance data and stops running threads
 */
public void Reset() {
    ExecutorService oldThreadPool = mThreadPool;
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
    oldThreadPool.shutdownNow();

    mChacheController.clear();
    mCache.clear();
    mImageViews.clear();
}  

public void loadDrawable(final String url, final ImageView imageView,Drawable placeholder) {  
    mImageViews.put(imageView, url);  
    Drawable drawable = getDrawableFromCache(url);  

    // check in UI thread, so no concurrency issues  
    if (drawable != null) {  
        //Log.d(null, "Item loaded from mCache: " + url);  
        imageView.setImageDrawable(drawable);  
    } else {  
        imageView.setImageDrawable(placeholder);  
        queueJob(url, imageView, placeholder);  
    }  
} 


private Drawable getDrawableFromCache(String url) {  
    if (mCache.containsKey(url)) {  
        return mCache.get(url).get();  
    }  

    return null;  
}

private synchronized void putDrawableInCache(String url,Drawable drawable) {  
    int chacheControllerSize = mChacheController.size();
    if (chacheControllerSize > MAX_CACHE_SIZE) 
        mChacheController.subList(0, MAX_CACHE_SIZE/2).clear();

    mChacheController.addLast(drawable);
    mCache.put(url, new SoftReference<Drawable>(drawable));

}  

private void queueJob(final String url, final ImageView imageView,final Drawable placeholder) {  
    /* Create handler in UI thread. */  
    final Handler handler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
            String tag = mImageViews.get(imageView);  
            if (tag != null && tag.equals(url)) {
                if (imageView.isShown())
                    if (msg.obj != null) {
                        imageView.setImageDrawable((Drawable) msg.obj);  
                    } else {  
                        imageView.setImageDrawable(placeholder);  
                        //Log.d(null, "fail " + url);  
                    } 
            }  
        }  
    };  

    mThreadPool.submit(new Runnable() {  
        @Override  
        public void run() {  
            final Drawable bmp = downloadDrawable(url);
            // if the view is not visible anymore, the image will be ready for next time in cache
            if (imageView.isShown())
            {
                Message message = Message.obtain();  
                message.obj = bmp;
                //Log.d(null, "Item downloaded: " + url);  

                handler.sendMessage(message);
            }
        }  
    });  
}  



private Drawable downloadDrawable(String url) {  
    try {  
        InputStream is = getInputStream(url);

        Drawable drawable = Drawable.createFromStream(is, url);
        putDrawableInCache(url,drawable);  
        return drawable;  

    } catch (MalformedURLException e) {  
        e.printStackTrace();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  

    return null;  
}  


private InputStream getInputStream(String urlString) throws MalformedURLException, IOException {
    URL url = new URL(urlString);
    URLConnection connection;
    connection = url.openConnection();
    connection.setUseCaches(true); 
    connection.connect();
    InputStream response = connection.getInputStream();

    return response;
}
}

ใช้งานได้ดี! BTW มีการพิมพ์ผิดในชื่อคลาส
Mullins

5
ในกรณีที่ช่วยคนอื่นเวลา:import java.util.Map; import java.util.HashMap; import java.util.LinkedList; import java.util.Collections; import java.util.WeakHashMap; import java.lang.ref.SoftReference; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import android.graphics.drawable.Drawable; import android.widget.ImageView; import android.os.Handler; import android.os.Message; import java.io.InputStream; import java.net.MalformedURLException; import java.io.IOException; import java.net.URL; import java.net.URLConnection;
Michael Reed

ขอบคุณมากนี่เป็นการนำไปปฏิบัติที่ดี ฉันยังใส่ตัวยึดตำแหน่งที่แตกต่างกันเมื่อโหลด drawable เพื่อให้ผู้ใช้สามารถรับข้อเสนอแนะบางอย่าง
Juan Hernandez

นอกจากนี้ฉันคิดว่าดีกว่าที่จะใช้คิว LIFO ใน executorService (mThreadPool) แทนค่าเริ่มต้น FIFO ดังนั้นภาพสุดท้ายที่ร้องขอ (ซึ่งอาจเป็นภาพที่มองเห็นได้) ถูกโหลดก่อน ดูstackoverflow.com/questions/4620061/how-to-create-lifo-executor
Juan Hernandez

9
@MichaelReed ในกรณีที่คุณเป็นผู้ใช้ Eclipse ฉันแนะนำให้ใช้ Ctrl-Shift-O (นั่นคือตัวอักษร O ไม่ใช่ตัวเลข 0) โดยอัตโนมัติกระบวนการเพิ่มการนำเข้าและจัดระเบียบให้คุณ หากคุณใช้ Mac ให้ใช้ Command-Shift-O แทน
SilithCrowe

78

ฉันได้ติดตามการฝึกอบรม Android นี้แล้วและฉันคิดว่ามันทำงานได้ดีมากในการดาวน์โหลดภาพโดยไม่ปิดกั้น UI หลัก นอกจากนี้ยังจัดการแคชและจัดการกับการเลื่อนดูภาพหลายภาพ: การโหลดบิตแมปขนาดใหญ่อย่างมีประสิทธิภาพ


ฉันขอโทษฉันชี้ไปที่คลาสเดียวสำหรับแอป Google IO (และฉันสายเกินไปที่จะแก้ไข) คุณควรศึกษาทั้งหมดโหลดภาพของพวกเขาและยูทิลิตี้แคชชั้นเรียนที่คุณสามารถหาได้ในแพคเกจเดียวกับระดับแคช
mkuech

ทุกคนจะแนะนำการคว้าไฟล์ DiskLruCache, Image * .java จากโฟลเดอร์ util ของแอป iosched เพื่อช่วยจัดการการโหลด / แคชภาพสำหรับมุมมองรายการหรือไม่ ฉันหมายความว่ามันคุ้มค่าแน่นอนที่จะต้องทำตามคำแนะนำของนักพัฒนาซอฟต์แวร์ออนไลน์ในหัวข้อ แต่คลาสเหล่านี้ (จาก iosched) มีรูปแบบเพิ่มขึ้นเล็กน้อย
Gautam

65

1. Picassoช่วยให้การโหลดภาพที่ไม่ยุ่งยากในแอปพลิเคชันของคุณ - มักจะอยู่ในโค้ดหนึ่งบรรทัด!

ใช้ Gradle:

implementation 'com.squareup.picasso:picasso:2.71828'

เพียงหนึ่งบรรทัดของรหัส!

Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);

2. Glideการโหลดรูปภาพและไลบรารีแคชสำหรับ Android ที่เน้นการเลื่อนที่ราบรื่น

ใช้ Gradle:

repositories {
  mavenCentral() 
  google()
}

dependencies {
   implementation 'com.github.bumptech.glide:glide:4.7.1'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}

// สำหรับมุมมองง่ายๆ:

  Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);

3. fresco เป็นระบบที่มีประสิทธิภาพสำหรับการแสดงภาพในแอปพลิเคชั่น Android Frescoดูแลการโหลดและการแสดงภาพดังนั้นคุณไม่จำเป็นต้องทำอะไร

เริ่มต้นกับ Fresco


บทช่วยสอนนี้อาจช่วยคุณได้มากขึ้นสำหรับ PICASOO: - androidtutorialshub.com/และร่อน: - androidtutorialshub.com/ ...
lalit vasan

52

ฉันเขียนบทช่วยสอนที่อธิบายวิธีการโหลดภาพในมุมมองรายการแบบสันหลังยาว ฉันเข้าไปดูรายละเอียดเกี่ยวกับปัญหาการรีไซเคิลและการเห็นพ้องด้วย ฉันยังใช้กลุ่มเธรดถาวรเพื่อป้องกันการวางไข่ของเธรดจำนวนมาก

โหลดรูปภาพในสันโดษ Listview ขี้เกียจ


41

วิธีที่ฉันทำได้คือการเปิดตัวเธรดเพื่อดาวน์โหลดรูปภาพในพื้นหลังและส่งกลับสำหรับรายการแต่ละรายการ เมื่อดาวน์โหลดภาพเสร็จแล้วจะโทรติดต่อกลับซึ่งจะอัพเดตมุมมองสำหรับรายการ

วิธีนี้ใช้ไม่ได้ผลเมื่อคุณรีไซเคิลมุมมอง


การใช้เธรดสำหรับแต่ละภาพเป็นวิธีที่ฉันใช้เช่นกัน หากคุณแยกโมเดลของคุณออกจากมุมมองของคุณคุณสามารถคงโมเดลไว้ข้างนอกกิจกรรม (เช่นในคลาส 'แอ็พพลิเคชัน' ของคุณ) เพื่อเก็บแคชไว้ ระวังทรัพยากรหมดหากคุณมีภาพมากมาย
เจมส์วิลสัน

คุณช่วยอธิบายได้ไหม ฉันยังใหม่ต่อการพัฒนา Android ขอบคุณสำหรับเคล็ดลับแม้ว่า
lostInTransit

14
การเริ่มเธรดใหม่สำหรับแต่ละรูปภาพไม่ใช่โซลูชันที่มีประสิทธิภาพ คุณสามารถจบด้วยเธรดจำนวนมากในหน่วยความจำและการแช่แข็ง UI
Fedor

Fedor เห็นด้วยฉันมักจะใช้คิวและกลุ่มเธรดซึ่งเป็นวิธีที่ดีที่สุดในการไปที่ IMO
jasonhudgins

32

ผมแค่อยากจะเพิ่มอีกหนึ่งตัวอย่างที่ดีมากขึ้นอะแดปเตอร์ XML Google ใช้มันและฉันก็ใช้ตรรกะเดียวกันเพื่อหลีกเลี่ยงข้อผิดพลาด OutOfMemory

โดยทั่วไปImageDownloader นี้เป็นคำตอบของคุณ (เพราะครอบคลุมความต้องการส่วนใหญ่ของคุณ) บางอย่างที่คุณสามารถใช้ในการที่


2
คลาส ImageDownloader ไม่ได้รับการปฏิบัติตาม: ดูวิธีแก้ไขด้านล่าง code.google.com/p/parleys-android-nextgen/issues/detail?id=1
Sam

29

นี่เป็นปัญหาที่พบบ่อยใน Android ที่ได้รับการแก้ไขในหลาย ๆ ทางโดยคนจำนวนมาก ในความคิดของทางออกที่ดีที่สุดที่ผมเคยเห็นเป็นห้องสมุดที่ค่อนข้างใหม่ที่เรียกว่าปิกัสโซ นี่คือไฮไลท์:

  • โอเพ่นซอร์ส แต่มุ่งไปที่ชื่อเสียงJake WhartonของActionBarSherlock
  • โหลดรูปภาพจากเครือข่ายหรือทรัพยากรแอพแบบอะซิงโครนัสด้วยโค้ดหนึ่งบรรทัด
  • ListViewตรวจจับอัตโนมัติ
  • ดิสก์อัตโนมัติและแคชหน่วยความจำ
  • สามารถทำการแปลงแบบกำหนดเองได้
  • ตัวเลือกที่กำหนดค่าได้มากมาย
  • API ที่ง่ายที่สุด
  • อัปเดตบ่อยครั้ง

29

ฉันใช้ NetworkImageView จาก Android Volley Library com.android.volley.toolbox.NetworkImageViewใหม่และดูเหมือนว่าจะใช้งานได้ดี เห็นได้ชัดว่านี่เป็นมุมมองเดียวกับที่ใช้ในGoogle Playและแอปพลิเคชันใหม่ของ Google คุ้มค่าที่จะเช็คเอาท์


1
ฉันคิดว่านี่เป็นทางออกที่ดีที่สุด - คำตอบอื่น ๆ เก่ามาก - วอลเลย์เร็วจริงๆและรวมกับ jake warthons disklrucache มันเป็นทางออกที่สมบูรณ์แบบ - ฉันลองคนอื่น ๆ เยอะ แต่ไม่มีใครเสถียรและเร็วเหมือนวอลเลย์
Alexander Sidikov Pfeif

26

เวลาในการโหลดรูปภาพจากอินเทอร์เน็ตมีวิธีแก้ปัญหามากมาย นอกจากนี้คุณยังอาจจะใช้ห้องสมุดAndroid แบบสอบถาม มันจะให้กิจกรรมที่จำเป็นทั้งหมดแก่คุณ ตรวจสอบให้แน่ใจว่าคุณต้องการทำอะไรและอ่านหน้าห้องสมุด wiki และแก้ไขข้อ จำกัด ในการโหลดภาพ

นี่คือรหัสของฉัน:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if (v == null) {
        LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = vi.inflate(R.layout.row, null);
    }

    ImageView imageview = (ImageView) v.findViewById(R.id.icon);
    AQuery aq = new AQuery(convertView);

    String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";

    aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
        @Override
        public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
            iv.setImageBitmap(bm);
        }
    ));

    return v;
}

มันควรจะแก้ปัญหาการโหลดขี้เกียจของคุณ


ทำได้ดีสำหรับฉัน แต่ต้องการไฟล์ Jar เพื่อรวมไว้ในโครงการของคุณ คุณสามารถดาวน์โหลดไฟล์ JAR ได้จากที่นี่ AQuery androidAQuery = new AQuery (this); ลิงก์คือ: code.google.com/archive/p/android-query/downloads
Selim Raza

25

ฉันคิดว่าปัญหานี้ได้รับความนิยมมากในหมู่นักพัฒนา Android และมีห้องสมุดมากมายที่อ้างว่าแก้ไขปัญหานี้ แต่ดูเหมือนมีเพียงไม่กี่คนเท่านั้นที่ทำเครื่องหมายได้ AQueryเป็นหนึ่งในห้องสมุดดังกล่าว แต่มันก็ยังดีกว่าห้องสมุดส่วนใหญ่ในทุกด้านและคุ้มค่าที่จะลอง


22

คุณต้องลองใช้ Universal Loader ที่ดีที่สุด ฉันใช้สิ่งนี้หลังจากทำ RnD จำนวนมากในการโหลดแบบขี้เกียจ

Universal Image Loader

คุณสมบัติ

  • การโหลดภาพแบบมัลติเธรด (async หรือซิงค์)
  • ปรับแต่งการกำหนดค่าของ ImageLoader ได้หลากหลาย (ตัวจัดการเธรด, ตัวดาวน์โหลด, ตัวถอดรหัส, หน่วยความจำและดิสก์แคช, ตัวเลือกรูปภาพที่แสดง ฯลฯ )
  • ตัวเลือกการปรับแต่งมากมายสำหรับทุกการโทรภาพที่แสดง (ภาพต้นขั้วสวิตช์แคชตัวเลือกการถอดรหัสการประมวลผลบิตแมปและการแสดง ฯลฯ )
  • การแคชรูปภาพในหน่วยความจำและ / หรือบนดิสก์ (ระบบไฟล์ของอุปกรณ์หรือการ์ด SD)
  • ฟังกระบวนการโหลด (รวมถึงความคืบหน้าการดาวน์โหลด)

รองรับ Android 2.0+

ป้อนคำอธิบายรูปภาพที่นี่


20

ดูShutterbugซึ่งเป็นพอร์ต SDWebImage (ไลบรารีที่ดีบน iOS) ของ Applidium ที่มีน้ำหนักเบาไปยัง Android รองรับการแคชแบบอะซิงโครนัสการจัดเก็บ URL ที่ล้มเหลวจัดการการทำงานพร้อมกันได้ดีและมีการรวมคลาสย่อยที่เป็นประโยชน์

คำขอดึง (และรายงานข้อผิดพลาด) ก็ยินดีเช่นกัน!


16

DroidPartsมีImageFetcherที่ต้องมีการกำหนดค่าเริ่มต้น

  • ใช้แคช & แคชในหน่วยความจำที่ใช้น้อยที่สุด (LRU) แคช
  • ถอดรหัสภาพได้อย่างมีประสิทธิภาพ
  • รองรับการปรับเปลี่ยนบิตแมปในเธรดพื้นหลัง
  • ข้ามง่ายจาง
  • มีการโทรกลับความคืบหน้าการโหลดภาพ

Clone DroidPartsGramสำหรับตัวอย่าง:

ป้อนคำอธิบายภาพที่นี่


สวัสดีฉันเคยดูตัวอย่างรหัสแล้ว แต่ฉันมีปัญหาในการใช้ ImageFetcher กับ ArrayAdapter คุณคิดจะดูคำถามของฉันหรือไม่ stackoverflow.com/questions/21089147/…ขอบคุณ =]
masha

16

เพียงคำแนะนำสั้น ๆ สำหรับคนที่ไม่แน่ใจเกี่ยวกับไลบรารีที่จะใช้สำหรับภาพที่กำลังโหลดขี้เกียจ:

มีสี่วิธีพื้นฐาน

  1. DIY => ไม่ใช่ทางออกที่ดีที่สุด แต่สำหรับรูปภาพสองสามภาพและหากคุณต้องการไปโดยไม่ต้องวุ่นวายกับการใช้ห้องสมุดอื่น

  2. ห้องสมุดกำลังโหลดขี้เกียจของ Volley => จากพวกที่ Android มันเป็นสิ่งที่ดีและทุกอย่าง แต่มีเอกสารไม่ดีและเป็นปัญหาในการใช้

  3. Picasso: วิธีแก้ปัญหาง่าย ๆ ที่ใช้งานได้คุณสามารถระบุขนาดภาพที่แน่นอนที่คุณต้องการนำเข้าได้มันใช้งานง่ายมาก แต่อาจไม่ใช่ "นักแสดง" สำหรับแอปที่ต้องจัดการกับภาพจำนวนมาก

  4. UIL: วิธีที่ดีที่สุดในการโหลดภาพที่ขี้เกียจ คุณสามารถแคชภาพ (คุณต้องได้รับอนุญาตจากหลักสูตร) ​​เริ่มต้นตัวโหลดหนึ่งครั้งจากนั้นทำงานให้เสร็จ ไลบรารีโหลดรูปภาพแบบอะซิงโครนัสที่โตที่สุดที่ฉันเคยเห็นมา


15

Novoda ยังมีห้องสมุดโหลดภาพที่ขี้เกียจและแอพมากมายเช่น Songkick, Podio, SecretDJ และ ImageSearch ใช้ห้องสมุดของพวกเขา

ห้องสมุดของพวกเขาโฮสต์ที่นี่ใน Github และพวกเขามีตัวติดตามปัญหาที่ค่อนข้างใช้งานเช่นกัน ดูเหมือนว่าโครงการของพวกเขาจะใช้งานได้ค่อนข้างดีเช่นกันด้วยความมุ่งมั่นกว่า 300 ครั้งในขณะที่เขียนคำตอบนี้


1
จริงๆแล้ว Novoda เป็นห้องสมุดที่ยอดเยี่ยม แต่ ... บางครั้งคุณไม่จำเป็นต้องมีห้องสมุดขนาดใหญ่และมีวิธีแก้ปัญหาง่ายๆ นั่นคือเหตุผลที่ LazyList ใน Github นั้นดีมากหากแอปของคุณแสดงเฉพาะภาพใน listView และไม่ใช่คุณสมบัติหลักของแอปของคุณเพียงแค่กิจกรรมอื่นที่ฉันต้องการใช้บางอย่างที่เบากว่า มิฉะนั้นถ้าคุณรู้ว่าคุณจะต้องใช้บ่อยและเป็นส่วนหนึ่งของแกนลองโนโวดา
Nicolas Jafelle

13

ตรวจสอบส้อมของฉันLazyList โดยพื้นฐานแล้วฉันปรับปรุง LazyList โดยชะลอการเรียก ImageView และสร้างสองวิธี:

  1. เมื่อคุณต้องการใส่บางสิ่งเช่น "กำลังโหลดภาพ ... "
  2. เมื่อคุณต้องการแสดงภาพที่ดาวน์โหลด

ฉันยังปรับปรุง ImageLoader โดยการใช้ซิงเกิลตันในวัตถุนี้


12

หากคุณต้องการแสดงเค้าโครง Shimmer เช่น Facebook จะมีห้องสมุด Facebook อย่างเป็นทางการสำหรับสิ่งนั้น FaceBook Shimmer Android

มันดูแลทุกอย่างคุณเพียงแค่ใส่รหัสการออกแบบที่คุณต้องการในรูปแบบซ้อนในกรอบชิมเมอร์ นี่คือตัวอย่างรหัส

<com.facebook.shimmer.ShimmerFrameLayout
     android:id=“@+id/shimmer_view_container”
     android:layout_width=“wrap_content”
     android:layout_height="wrap_content"
     shimmer:duration="1000">

 <here will be your content to display />

</com.facebook.shimmer.ShimmerFrameLayout>

และนี่คือรหัส java สำหรับมัน

ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();

เพิ่มการพึ่งพานี้ในไฟล์ gradle ของคุณ

implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'

นี่คือลักษณะที่ปรากฏภาพหน้าจอของ Shimmer Android


11

รหัสข้างต้นทั้งหมดมีมูลค่าของตัวเอง แต่ด้วยประสบการณ์ส่วนตัวของฉันเพียงแค่ลองใช้ Picasso

Picasso เป็นห้องสมุดเฉพาะสำหรับวัตถุประสงค์นี้โดยจริงแล้วมันจะจัดการแคชและการดำเนินการเครือข่ายอื่น ๆ โดยอัตโนมัติคุณจะต้องเพิ่มไลบรารีในโครงการของคุณและเพียงแค่เขียนโค้ดบรรทัดเดียวเพื่อโหลดอิมเมจจาก URL ระยะไกล

กรุณาเยี่ยมชมที่นี่: http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149


9

ใช้ไลบรารีร่อน มันใช้งานได้สำหรับฉันและจะทำงานให้กับรหัสของคุณเช่นกันมันใช้ได้ทั้งกับภาพและ gif ด้วย

ImageView imageView = (ImageView) findViewById(R.id.test_image); 
    GlideDrawableImageViewTarget imagePreview = new GlideDrawableImageViewTarget(imageView);
    Glide
            .with(this)
            .load(url)
            .listener(new RequestListener<String, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {                       
                    return false;
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    return false;
                }
            })
            .into(imagePreview);
}

7

ฉันสามารถแนะนำวิธีอื่นที่ใช้งานได้ดีเช่น Android Query

คุณสามารถดาวน์โหลดไฟล์JAR ได้จากที่นี่

AQuery androidAQuery = new AQuery(this);

ตัวอย่างเช่น:

androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);

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




4
public class ImageDownloader {

Map<String, Bitmap> imageCache;

public ImageDownloader() {
    imageCache = new HashMap<String, Bitmap>();

}

// download function
public void download(String url, ImageView imageView) {
    if (cancelPotentialDownload(url, imageView)) {

        // Caching code right here
        String filename = String.valueOf(url.hashCode());
        File f = new File(getCacheDirectory(imageView.getContext()),
                filename);

        // Is the bitmap in our memory cache?
        Bitmap bitmap = null;

        bitmap = (Bitmap) imageCache.get(f.getPath());

        if (bitmap == null) {

            bitmap = BitmapFactory.decodeFile(f.getPath());

            if (bitmap != null) {
                imageCache.put(f.getPath(), bitmap);
            }

        }
        // No? download it
        if (bitmap == null) {
            try {
                BitmapDownloaderTask task = new BitmapDownloaderTask(
                        imageView);
                DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
                        task);
                imageView.setImageDrawable(downloadedDrawable);
                task.execute(url);
            } catch (Exception e) {
                Log.e("Error==>", e.toString());
            }

        } else {
            // Yes? set the image
            imageView.setImageBitmap(bitmap);
        }
    }
}

// cancel a download (internal only)
private static boolean cancelPotentialDownload(String url,
        ImageView imageView) {
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

    if (bitmapDownloaderTask != null) {
        String bitmapUrl = bitmapDownloaderTask.url;
        if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
            bitmapDownloaderTask.cancel(true);
        } else {
            // The same URL is already being downloaded.
            return false;
        }
    }
    return true;
}

// gets an existing download if one exists for the imageview
private static BitmapDownloaderTask getBitmapDownloaderTask(
        ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
            return downloadedDrawable.getBitmapDownloaderTask();
        }
    }
    return null;
}

// our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context) {
    String sdState = android.os.Environment.getExternalStorageState();
    File cacheDir;

    if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
        File sdDir = android.os.Environment.getExternalStorageDirectory();

        // TODO : Change your diretcory here
        cacheDir = new File(sdDir, "data/ToDo/images");
    } else
        cacheDir = context.getCacheDir();

    if (!cacheDir.exists())
        cacheDir.mkdirs();
    return cacheDir;
}

private void writeFile(Bitmap bmp, File f) {
    FileOutputStream out = null;

    try {
        out = new FileOutputStream(f);
        bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (out != null)
                out.close();
        } catch (Exception ex) {
        }
    }
}

// download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private String url;
    private final WeakReference<ImageView> imageViewReference;

    public BitmapDownloaderTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    // Actual download method, run in the task thread
    protected Bitmap doInBackground(String... params) {
        // params comes from the execute() call: params[0] is the url.
        url = (String) params[0];
        return downloadBitmap(params[0]);
    }

    @Override
    // Once the image is downloaded, associates it to the imageView
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
            // Change bitmap only if this process is still associated with
            // it
            if (this == bitmapDownloaderTask) {
                imageView.setImageBitmap(bitmap);

                // cache the image

                String filename = String.valueOf(url.hashCode());
                File f = new File(
                        getCacheDirectory(imageView.getContext()), filename);

                imageCache.put(f.getPath(), bitmap);

                writeFile(bitmap, f);
            }
        }
    }

}

static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
        super(Color.WHITE);
        bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
                bitmapDownloaderTask);
    }

    public BitmapDownloaderTask getBitmapDownloaderTask() {
        return bitmapDownloaderTaskReference.get();
    }
}

// the actual download code
static Bitmap downloadBitmap(String url) {
    HttpParams params = new BasicHttpParams();
    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
            HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    final HttpGet getRequest = new HttpGet(url);

    try {
        HttpResponse response = client.execute(getRequest);
        final int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            Log.w("ImageDownloader", "Error " + statusCode
                    + " while retrieving bitmap from " + url);
            return null;
        }

        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                final Bitmap bitmap = BitmapFactory
                        .decodeStream(inputStream);
                return bitmap;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                entity.consumeContent();
            }
        }
    } catch (Exception e) {
        // Could provide a more explicit error message for IOException or
        // IllegalStateException
        getRequest.abort();
        Log.w("ImageDownloader", "Error while retrieving bitmap from "
                + url + e.toString());
    } finally {
        if (client != null) {
            // client.close();
        }
    }
    return null;
 }
}

4

ฉันมีปัญหานี้และนำไปใช้ lruCache ฉันเชื่อว่าคุณต้องการ API 12 ขึ้นไปหรือใช้ไลบรารี compatiblity v4 lurCache เป็นหน่วยความจำอย่างรวดเร็ว แต่ก็ยังมีงบประมาณดังนั้นหากคุณกำลังกังวลเกี่ยวกับที่คุณสามารถใช้ diskcache ... มันอธิบายทั้งหมดในแคชบิตแมป

ตอนนี้ฉันจะให้การใช้งานของฉันซึ่งเป็นซิงเกิลตันที่ฉันโทรจากที่ใดก็ได้เช่นนี้:

//Where the first is a string and the other is a imageview to load.

DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);

ต่อไปนี้เป็นโค้ดที่เหมาะสำหรับการแคชจากนั้นเรียกใช้ด้านบนใน getView ของอะแดปเตอร์เมื่อดึงภาพเว็บ:

public class DownloadImageTask {

    private LruCache<String, Bitmap> mMemoryCache;

    /* Create a singleton class to call this from multiple classes */

    private static DownloadImageTask instance = null;

    public static DownloadImageTask getInstance() {
        if (instance == null) {
            instance = new DownloadImageTask();
        }
        return instance;
    }

    //Lock the constructor from public instances
    private DownloadImageTask() {

        // Get max available VM memory, exceeding this amount will throw an
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an
        // int in its constructor.
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        // Use 1/8th of the available memory for this memory cache.
        final int cacheSize = maxMemory / 8;

        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // The cache size will be measured in kilobytes rather than
                // number of items.
                return bitmap.getByteCount() / 1024;
            }
        };
    }

    public void loadBitmap(String avatarURL, ImageView imageView) {
        final String imageKey = String.valueOf(avatarURL);

        final Bitmap bitmap = getBitmapFromMemCache(imageKey);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            imageView.setImageResource(R.drawable.ic_launcher);

            new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
        }
    }

    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    private Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }

    /* A background process that opens a http stream and decodes a web image. */

    class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> {
        ImageView bmImage;

        public DownloadImageTaskViaWeb(ImageView bmImage) {
            this.bmImage = bmImage;
        }

        protected Bitmap doInBackground(String... urls) {

            String urldisplay = urls[0];
            Bitmap mIcon = null;
            try {
                InputStream in = new java.net.URL(urldisplay).openStream();
                mIcon = BitmapFactory.decodeStream(in);

            } 
            catch (Exception e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }

            addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);

            return mIcon;
        }

        /* After decoding we update the view on the main UI. */
        protected void onPostExecute(Bitmap result) {
            bmImage.setImageBitmap(result);
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.