onBitmapLoaded của đối tượng Target không được gọi trong lần tải đầu tiên


126

Trong chức năng của tôi:

public void getPointMarkerFromUrl(final String url, final OnBitmapDescriptorRetrievedListener listener) {
final int maxSize = context.getResources().getDimensionPixelSize(R.dimen.icon_max_size);
Target t = new Target() {
  @Override
  public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
    if (bitmap != null)
      listener.bitmapRetrieved(getBitmapDescriptorInCache(url, bitmap));
    else
      loadDefaultMarker(listener);
  }

  @Override
  public void onBitmapFailed(Drawable errorDrawable) {
    loadDefaultMarker(listener);
  }

  @Override
  public void onPrepareLoad(Drawable placeHolderDrawable) {
  }
};

Picasso.with(context)
    .load(url)
    .resize(maxSize, maxSize)
    .into(t);
}

OnBitmapLoaded () không bao giờ được gọi lần đầu tiên tôi tải ảnh. Tôi đã đọc một số chủ đề như https://github.com/sapes/picasso/issues/39 trong đó đề xuất và sử dụng phương pháp tìm nạp (Target t) (có vẻ như đó là vấn đề về tham chiếu yếu ...), nhưng chức năng này không có sẵn trong bản phát hành cuối cùng của picasso (2.3.2). Tôi chỉ có một phương thức tìm nạp (), nhưng tôi không thể sử dụng vào (mytarget) cùng một lúc

Bạn có thể giải thích cho tôi cách sử dụng fetch () với Target tùy chỉnh không? Cảm ơn bạn.

Tài liệu: http://sapes.github.io/picasso/javadoc/com/sapesup/picasso/RequestCreator.html#fetch--


1
hãy chắc chắn để sử dụng okhttp 2.0.0, tôi gặp phải cùng một vấn đề khi sử dụng Picasso 2.3.2 với Okhttp 1.6.0
Hakim

github.com/sapes/okhttp afaik, bắt buộc nếu bạn đang sử dụng Picasso 2.3.2 để bao gồm thư viện okhttp (và okio). Bạn đang sử dụng nhật thực hay studio android?
hakim

Tôi đang sử dụng IntelliJ. Tôi đã thấy sự phụ thuộc cấp độ của mình, tôi đã không thấy okhttp ... Picasso dường như hoạt động mà không có nó
psv

@psv làm thế nào bạn thực hiện giải pháp dưới đây với các điểm đánh dấu?
Mustafa Güven

Câu trả lời:


247

Theo ghi nhận của những người trả lời khác (@lukas và @mradzinski), Picasso chỉ giữ một tham chiếu yếu đến Targetđối tượng. Mặc dù bạn có thể lưu trữ một tài liệu tham khảo mạnh mẽ Targettrong một trong các lớp học của mình, nhưng điều này vẫn có thể gây ra vấn đề nếu các Targettài liệu tham khảo Viewtheo bất kỳ cách nào, vì bạn cũng sẽ giữ một tài liệu tham khảo mạnh mẽ về điều đó View(đó là một trong những điều mà Picasso rõ ràng giúp bạn tránh).

Nếu bạn ở trong tình huống này, tôi khuyên bạn nên gắn thẻ Targetvào View:

final ImageView imageView = ... // The view Picasso is loading an image into
final Target target = new Target{...};
imageView.setTag(target);

Cách tiếp cận này có lợi ích là để Picasso xử lý mọi thứ cho bạn. Nó sẽ quản lý các WeakReferenceđối tượng cho từng chế độ xem của bạn - ngay khi không còn cần thiết nữa, mọi Targethình ảnh xử lý cũng sẽ được phát hành, do đó bạn không bị kẹt với rò rỉ bộ nhớ do các mục tiêu tồn tại lâu, nhưng Target của bạn sẽ tồn tại miễn là quan điểm của nó là còn sống.


15
Cứu ngày của tôi. Cảm ơn.
cy198706

24
Tôi không có chế độ xem hình ảnh, làm thế nào tôi có thể giải quyết vấn đề này? Khi xử lý các tình huống này, gc là kẻ thù tồi tệ nhất của bạn
tim687

3
Bạn thậm chí có thể lưu trữ nó trong một ArrayList <Target> và nó sẽ hoạt động, chỉ cần nhớ xóa danh sách mảng đó :-)
Oliver Dixon

2
Trong onBitmapLoaded và onBitmapFails, tôi cũng đang thực hiện imageView.setTag (null) sau khi xử lý bitmap. Có cần thiết không?
Jaguar

1
Cảm ơn bạn! Chỉ cứu mạng tôi :)
yusufiga

55

Picasso không có tham chiếu mạnh đến đối tượng Target, do đó, nó là rác được thu thập và onBitmapLoadedkhông được gọi.

Các giải pháp khá đơn giản, chỉ cần làm cho một tài liệu tham khảo mạnh mẽ đến Target.

public class MyClass {
   private Target mTarget = new Target() {...};

   public void getPointMarkerFromUrl(final String url, final OnBitmapDescriptorRetrievedListener listener) {

         Picasso.with(context)
         .load(url)
         .resize(maxSize, maxSize)
         .into(mTarget);
   }
}      

2
Hoặc làm cho bạn Viewthực hiện Target.
dnkoutso

trong các tài liệu, nó nói rằng bạn phải ghi đè Object.equals(Object)Object.hashCode()phương thức. bạn có một mẫu làm việc?
chip

nó được viết ở đâu Tôi vẫn gặp vấn đề của mình ngay cả khi tham chiếu mạnh đến Target ().
psv

Bây giờ tôi đã cài đặt okHttp, tải nhanh hơn một chút nhưng tôi vẫn gặp vấn đề tương tự ở lần khởi chạy đầu tiên. Có ý kiến ​​gì không?
psv

@psv: Bạn đã có thể giải quyết vấn đề khởi động đầu tiên của picasso chưa? Tôi có cùng một vấn đề? Nếu bạn đã giải quyết làm thế nào bạn giải quyết nó?
TheDevMan

25

Nếu tôi có ImageView, tôi sẽ đơn giản thực hiện như thế này: imageView.setTag (target);

Tôi sử dụng giải pháp tiếp theo để tải Bitmap vào thông báo, vì vậy tôi chỉ cần bitmap.

Vì vậy, tạo Set witch sẽ lưu trữ các đối tượng Target và loại bỏ chúng khi tải xong.

final Set<Target> protectedFromGarbageCollectorTargets = new HashSet<>();

private void loadBitmap(String url) {
   Target bitmapTarget = new BitmapTarget(nEvent);
   protectedFromGarbageCollectorTargets.add(bitmapTarget);
   Picasso.with(context).load(url).into(bitmapTarget);
}

class BitmapTarget implements Target {

        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {

                    //handle bitmap
                    protectedFromGarbageCollectorTargets.remove(this);
                }
            }
        }

        @Override
        public void onBitmapFailed(Drawable drawable) {
            protectedFromGarbageCollectorTargets.remove(this);
        }

        @Override
        public void onPrepareLoad(Drawable drawable) {

        }
    }

13
ImageView profile = new ImageView(context);
        Picasso.with(context).load(URL).into(profile, new Callback() {
            @Override
            public void onSuccess() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {//You will get your bitmap here

                        Bitmap innerBitmap = ((BitmapDrawable) profile.getDrawable()).getBitmap();
                    }
                }, 100);
            }

            @Override
            public void onError() {

            }
        });

1
Nó cũng giải quyết vấn đề của tôi. tôi muốn sử dụng nó với Thông báo. đôi khi hình ảnh được tải xuống với Target và đôi khi không. nhưng sau khi sử dụng ImageView tôi đã có thể tải hình ảnh mọi lúc
Raveesh GS

1
trong trường hợp của tôi, ngoại trừ tất cả, đây là giải pháp tốt nhất!
Noor Hossain

4

Đây là giải pháp cho những người không sử dụng chế độ xem. Phương thức trợ giúp này sử dụng danh sách để tạm thời lưu trữ đối tượng đích cho đến khi kết quả được trả về để nó không phải là gc'd:

private List<Target> targets = new ArrayList<>();

public void downloadBitmap(final Context context, final String url, final MyCallback callback) {
    Target target = new Target() {

        @Override
        public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
            targets.clear();
            callback.onSuccess(bitmap);
        }

        @Override
        public void onBitmapFailed(Exception e, Drawable errorDrawable) {
            targets.clear();
            callback.onFailure(null);
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
        }
    };
    targets.add(target);
    Picasso.with(context).load(url).into(target);
}

3

Giống như @lukas đã nói (và trích dẫn), Picasso không giữ một tham chiếu mạnh mẽ đến đối tượng Target. Để tránh thu gom rác, bạn phải giữ một tham chiếu mạnh đến đối tượng.

Về phương thức fetch (). Trong tài liệu khá rõ ràng rằng fetch () sẽ không được sử dụng với ImageView hay Target, nó chỉ để "làm nóng" bộ đệm và không có gì khác, vì vậy bạn sẽ không thể sử dụng nó theo cách của bạn muốn

Tôi khuyên bạn nên giữ một tài liệu tham khảo mạnh mẽ như @lukas giải thích, nó sẽ hoạt động. Nếu không, vui lòng mở một vấn đề mới trên trang GitHub của dự án.


3

Tôi đã gặp phải vấn đề tương tự và giữ tham chiếu đến mục tiêu không giúp ích gì cả nên tôi đã sử dụng đoạn mã sau trả về Bitmap:


Bitmap bitmap = picasso.with(appContext).load(url).get();

ở phía bên dưới -> không có cuộc gọi lại và bạn không thể gọi hàm này trên luồng chính, bạn phải chạy hàm này trên luồng nền như trong ví dụ sau:


handlerThread = new HandlerThread(HANDLER_THREAD_NAME);
handlerThread.start();

Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
    @Override
    public void run() {
        Bitmap bitmap = null;
        try {
            bitmap = picasso.with(appContext).load(url).get();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (bitmap != null) {
                //do whatever you wanna do with the picture.
                //for me it was using my own cache
                imageCaching.cacheImage(imageId, bitmap);
            }
        }
    }
});

Một thứ khác hoạt động tốt hơn rất nhiều chỉ là sử dụng Glide!

Tôi cần sử dụng cả hai vì mục đích của dự án của tôi là sử dụng 2 api tải hình ảnh khác nhau để hiển thị một bộ sưu tập hình ảnh và cung cấp cho người dùng khả năng chọn sử dụng api nào.

Tôi phải nói rằng, tôi đã rất ngạc nhiên với kết quả, api của Glide hoạt động hoàn hảo ở mọi khía cạnh (mục tiêu của Glide không có tài liệu tham khảo yếu), Picasso đã cho tôi địa ngục (đó là lần đầu tiên tôi sử dụng Glide, tôi thường sử dụng Picasso cho đến nay, có vẻ như hôm nay nó sẽ thay đổi ^^).


0

Tôi đã phải đối mặt với cùng một vấn đề nhưng khi tôi thay đổi sự phụ thuộc như được đề cập dưới đây, bây giờ nó hoạt động đúng.

 implementation 'com.squareup.picasso:picasso:2.5.2'
 implementation 'com.squareup.okhttp:okhttp:2.3.0'
 implementation 'com.squareup.okhttp:okhttp-urlconnection:2.3.0'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.