onBitmapLoaded do objeto Target não chamado no primeiro carregamento

126

Na minha função:

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);
}

O onBitmapLoaded () nunca é chamado na primeira vez que carrego imagens. Eu li alguns tópicos como https://github.com/square/picasso/issues/39 que recomendam usar o método fetch (Target t) (parece ser um problema de referência fraca ...), mas essa função não está disponível na última versão do picasso (2.3.2). Eu tenho apenas um método fetch (), mas não posso usar o (mytarget) ao mesmo tempo

Você poderia me explicar como usar fetch () com um Target personalizado, por favor? Obrigado.

Doc: http://square.github.io/picasso/javadoc/com/squareup/picasso/RequestCreator.html#fetch--

psv
fonte
1
Certifique-se de usar okhttp 2.0.0, eu encontro mesmo problema ao usar Picasso 2.3.2 com Okhttp 1.6.0
Hakim
github.com/square/okhttp afaik, é obrigatório se você estiver usando o Picasso 2.3.2 para incluir a biblioteca okhttp (e okio). você está usando o eclipse ou o android studio?
Hakim
Estou usando o IntelliJ. Eu vi meus dependências Gradle, eu não vi okhttp ... Picasso parece funcionar sem ele
psv
@psv como você implementou a solução abaixo com os marcadores?
Mustafa Güven

Respostas:

247

Conforme observado pelos outros entrevistados (@lukas e @mradzinski), Picasso mantém apenas uma referência fraca ao Targetobjeto. Embora você possa armazenar uma referência forte Targetem uma de suas aulas, isso ainda pode ser problemático se as Targetreferências forem de Viewalguma maneira, já que você também manterá uma forte referência a ela View(o que é uma das coisas que Picasso explicitamente ajuda a evitar).

Se você estiver nessa situação, recomendo marcar a opção Targetcomo View:

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

Essa abordagem tem o benefício de deixar o Picasso cuidar de tudo para você. Ele gerenciará os WeakReferenceobjetos para cada uma das suas visualizações - assim que uma não for mais necessária, o Targetprocessamento da imagem também será liberado, para que você não fique preso a vazamentos de memória devido a alvos de longa duração, mas seu Target durará enquanto sua visão estiver viva.

wrb
fonte
15
Salvou o meu dia. Obrigado.
cy198706
24
Não tenho uma visualização de imagem, como posso resolver esse problema? Ao lidar com este tipo de situações, o gc é o seu pior inimigo
tim687
3
Você mesmo pode armazená-lo em um ArrayList <Target> e ele vai trabalhar, lembre-se de limpar que arraylist :-)
Oliver Dixon
2
Em onBitmapLoaded e onBitmapFailed, também estou fazendo imageView.setTag (null) após processar o bitmap. Não é necessário?
Jaguar
1
Obrigado! Acabou de salvar minha vida :)
yusufiga
55

O Picasso não possui uma forte referência ao objeto Target, portanto, está sendo coletado como lixo e onBitmapLoadednão é chamado.

A solução é bastante simples, basta fazer uma forte referência ao 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);
   }
}      
lukas
fonte
2
Ou faça o seu Viewimplemento Target.
dnkoutso
nos documentos, ele diz que você deve substituir Object.equals(Object)e Object.hashCode()métodos. você tem uma amostra de trabalho?
chip
onde está escrito? Ainda tenho meu problema, mesmo fazendo uma forte referência ao meu Target ().
psv 29/07
Agora instalei o okHttp, é um pouco mais rápido para carregar, mas ainda tenho o mesmo problema no primeiro lançamento. Alguma ideia ?
psv 29/07
@psv: Você resolveu o primeiro problema de lançamento do picasso? Eu tenho o mesmo problema? Se você resolveu, como resolveu?
TheDevMan
25

Se eu tivesse o ImageView, simplesmente faria assim: imageView.setTag (target);

Eu uso a próxima solução para carregar Bitmaps nas notificações, portanto, preciso apenas de bitmap.

Então, criar Set irá armazenar objetos de destino e removê-los no final do carregamento.

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) {

        }
    }
Flinbor
fonte
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() {

            }
        });
Raghav Satyadev
fonte
1
Também resolveu o meu problema. eu queria usá-lo com notificação. às vezes a imagem estava sendo baixada com o Target e às vezes não. mas depois de usar o ImageView eu era capaz de carregar imagens sempre
Raveesh GS 31/03/19
1
no meu caso, exceto todos, essa foi a melhor solução!
Noor Hossain
4

Aqui está a solução para aqueles que não estão usando uma exibição. Este método auxiliar usa uma lista para armazenar temporariamente o objeto de destino até que um resultado seja retornado, de forma que não seja 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);
}
DroidT
fonte
3

Como @lukas disse (e citou), Picasso não possui uma forte referência ao objeto Target. Para evitar a coleta de lixo, você deve manter uma forte referência ao objeto.

Sobre o método fetch (). É bem claro na documentação que fetch () não deve ser usado com um ImageView nem com um Target, é apenas para "aquecer" o cache e nada mais, para que você não possa usá-lo da maneira que deseja. quer.

Eu recomendo que você mantenha uma referência forte como o @lukas explicou, ele deve funcionar. Caso contrário, abra um novo problema na página GitHub do projeto.

mradzinski
fonte
3

Encontrei um problema semelhante e manter a referência ao destino não ajudou em nada, então usei o seguinte código que retorna um Bitmap:


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

no lado inferior -> não há retorno de chamada e você não pode chamar esta função no encadeamento principal, você deve executar esta função em um encadeamento em segundo plano, como no exemplo a seguir:


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);
            }
        }
    }
});

Outra coisa que funciona muito melhor é apenas usar o Glide!

Eu precisava usar os dois, pois o objetivo do meu projeto era usar 2 APIs de download de imagens diferentes para mostrar uma galeria de imagens e dar ao usuário a capacidade de escolher qual API usar.

Devo dizer que fiquei impressionado com os resultados, a API de Glide funcionou perfeitamente em todos os aspectos (o alvo de Glide não tem referência fraca), enquanto Picasso me deu um inferno (essa foi a primeira vez que usei o Glide, eu costumava usar Picasso até agora, Parece que hoje vai mudar ^^).

Roee
fonte
0

Eu já havia enfrentado o mesmo problema, mas quando alterei a dependência, conforme mencionado abaixo, ele funciona corretamente agora.

 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'
khushbu
fonte