Best method of implementing Picasso.Builder and onImageLoadFailed function - java

For the past few days I have been implementing Picasso in my Android project. Initial implementation was easy enough, but now I have run into an issue I cannot seem to resolve and have not found a clear answer to in either the Picasso documentation or other tuturials/stackoverflow questions.
My knowledge of java is sufficient, but Android and more advanced OOP concepts are quite new to me so please bear this in mind.
Initially I implemented Picasso like this in my RecyclerViewAdapter:
public void onBindViewHolder(MyViewHolder viewHolder, int i) {
Picasso.with(viewHolder.image.getContext()).setIndicatorsEnabled(true);
Picasso.with(viewHolder.image.getContext())
.load(new File("imagePath"))
.fit()
.centerCrop()
.into(viewHolder.image);
}
This worked fine, but due to unrelated errors I wished to implement debugging options and looked towards the onError() and onImageLoadFailed() functions. Resulting in the following implementation:
Picasso.Builder builder = new Picasso.Builder(viewHolder.image.getContext());
builder.indicatorsEnabled(true);
builder.loggingEnabled(true);
builder.listener(new Picasso.Listener() {
#Override
public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
Log.d("TAG", " onImageLoadFailed message");
exception.printStackTrace();
}
});
builder.build().load(new File("imagePath"))
.fit()
.centerCrop()
.into(viewHolder.image, new com.squareup.picasso.Callback() {
#Override
public void onError() {
Log.d("TAG", " onError message");
}
});
However, with this implementation Picasso seemed to never reload images from memory when I scrolled through my Recyclerviewer. Therefore I tried the following:
Picasso picasso = new Picasso.Builder(viewHolder.image.getContext()).listener(new Picasso.Listener() {
#Override
public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
Log.d("TAG", " onImageLoadFailed message");
exception.printStackTrace();
}
}).build();
picasso.with(viewHolder.image.getContext())
.setIndicatorsEnabled(true);
picasso.with(viewHolder.image.getContext())
.load(new File("imagePath"))
.fit()
.centerCrop()
.into(viewHolder.image, new com.squareup.picasso.Callback() {
#Override
public void onError() {
Log.d("TAG", " onError message");
}
});
This implementation seems to work nicely (although it does seem that some images are loaded in memory much quicker than others, perhaps due to .fit(), my next objective will be to optimize file loading speed).
However I am uncertain if this is the best implementation. I have tried to find the answer in the Picasso documentation and it seems to be related to the creation of new Picasso instances / Picasso being a singelton. However, I am afraid I am too inexperienced to fully understand this and there seem to be no tutorials on this topic.
So my main question is as follows: What is the best way of implementing Picasso in a project/throughout multiple activities? Should I use Picasso.Builder to access the onImageLoadFailed function? And if so, should I use it for every activity or not?
Thank you in advance for your help!

Related

Force retrofit to load new data

I'm using retrofit to get my json data in my recycleview. It used to work fine couple of weeks ago but I ran code now then It only loads data one time and then as many times as I make change any text or add new value in json data, It always load the same initial data on loading. I haven't used any cache property and strange thing is once it loads first time, then if i delete my json then still it loads the data instead of throwing exception and giving error of json not found.
What am I missing. I changed the version of retrofit but it dosesn't seem to work. Here is my code of Mainactivity is
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
Call<List<RetroPhoto>> call = service.getAllPhotos();
call.enqueue(new Callback<List<RetroPhoto>>() {
#Override
public void onResponse(Call<List<RetroPhoto>> call, Response<List<RetroPhoto>> response) {
progressDoalog.dismiss();
generateDataList(response.body());
}
#Override
public void onFailure(Call<List<RetroPhoto>> call, Throwable t) {
progressDoalog.dismiss();
Toast.makeText(NewsActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
}
/*Method to generate List of data using RecyclerView with custom adapter*/
private void generateDataList(List<RetroPhoto> photoList) {
recyclerView = findViewById(R.id.customRecyclerView);
adapter = new CustomAdapter(this,photoList);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(NewsActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
and code of GetDataService is
public interface GetDataService {
#GET("b.json")
Call<List<RetroPhoto>> getAllPhotos();
}
and my gradle is
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.okhttp:okhttp:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.7.0'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
Thanks in advance for any help. I am banging my heads on floor over 3 hours on figuring out what excatly gone through in couple of weeks where I even didn't touch the code.
I realized problem was from server side as I was using ipage hosing, the i moved json to other hosing and immediately changes were shown in app on request but ipage didn't. So code is fine.

Get Bitmap using a Target in Picasso

I'm working on movies Android app which get list of movies from an API which provides a poster path for all movies.
I want to get the image as Bitmap from the image's URL to save it as a Bitmap variable in the model class. I want to save the image as blob in the DB to retrieve it directly without redownloading it each time the user opens the app. Is that possible?
I want to do something like this, but it always returns null.
private Bitmap posterBitmap;
public void setPosterBitmap () {
Picasso.get().load(POSTERS_URL).into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
posterBitmap = bitmap; // I don't want to set it to an Image view here
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {}
});
}
Thanks in advance.
new Thread(() -> {
try {
Bitmap mBitmap = Picasso.get().load(link).get();
} catch (Exception e) {
Log.e(""+e);
}
}).start();
By using
implementation 'com.squareup.picasso:picasso:2.71828'
This code is working for me:
...
private static final String TAG = MainActivity.class.getName();
private Target mTarget;
...
mTarget = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
if (bitmap == null) {
Log.w(TAG, "Null");
} else {
Log.i(TAG, "Worked");
}
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
Log.w(TAG, "failed");
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.i(TAG, "Prepare");
}
};
// Small image loads without resize
// Picasso.get().load("http://www.theretirementmanifesto.com/wp-content/uploads/2016/08/Yoda-free-clip-art-680x410.jpg").into(mTarget);
// Mega high res out of memory image
Picasso.get().load("https://upload.wikimedia.org/wikipedia/commons" +
"/5/5e/M104_ngc4594_sombrero_galaxy_hi-res.jpg").
resize(100, 100).into(mTarget);
Also I'm assuming that the following line is in the manifest:
<uses-permission android:name="android.permission.INTERNET" />
Using this version of Picasso:
implementation 'com.squareup.picasso:picasso:2.71828'
Also it may be worth declaring the Target as a member variable due to issues Picaso has arising from 'weak references'. You will have to research this, but I believe it may be unsafe to declare the Target as an anonymous inner class.
Also it may be necessary to call resize(x, y) to prevent an out of memory situation depending on the image sizes and whether you control their source.
UPDATE:
The project won't work as written because it is using an synchronous solution, but you're making an asynchronous call:
holder.moviePosterIV.setImageBitmap(movie.getPosterBitmap());
The code:
public Bitmap getPosterBitmap() {
target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
posterBitmap = bitmap;
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {}
};
return posterBitmap;
}
It cannot be done like this. In a nut shell, the Target is going to be called in the future when the image has been downloaded. But the code is being written as if the image is ready immediately.
It is a synchronous solution to an asynchronous problem. In synchronous programming we write 1 line after the other then return the result when the data is ready.
If we wrote it synchronously:
f() {
image = getImage(url) // the program could be blocked here for minutes
return image
}
So instead we do it asynchronously:
f() {
getImageInTheFuture(url, imageReadyFunc); // call imageReadyFunc when it is finally downloaded
}
imageReadyFunc(image) {
setTheImage();
}
The asynchronous solution prevents the app from blocking, but it is also a real pain because we can no longer use a 'return' statement. Instead we have to break the code up into 2 sections. Code that we can run before the image is available. Code that we can run after the image is available.
But under the hood Picasso is doing all of this work for you. I'd really advise against trying to manage the bitmaps directly. In the bad old days of Android before Picasso, Glide, etc. apps used to routinely crash and run out of memory trying to manage their own bitmaps. It is technically difficult to do without causing memory leaks and running out of memory.
Hope that makes sense...

How can i make parallel or concurent API calls?

I am building an Android app (my first app actually) and it's about getting similar tracks to the one you searched for. I am using retrofit2 with rxJava and gson for my calls.
For each track i found i add the corresponding image provided by the response in an imageview, but this image is not the actual album image, it's just an image of the band. I want to have the album image which i can get from the API if i do an album search.
So is there a way to make a API call that returns the album info for each track without losing to much time loading? I want these calls to happen in parallel with each other so as to be less visible to the "user" (me).
This is the code that i use to search for the similar tracks:
private void loadSimilarTracks() {
String mbid = selectedTrack.getMbid();
String artist = selectedTrack.getmArtist();
String track = selectedTrack.getName();
searchService = new LastFMSearchService();
Flowable<List<TrackSimilar>> fetchDataObservable = null;
if(!mbid.equals("")) {
fetchDataObservable = searchService.getSimilarTracks(mbid);
}
else{
fetchDataObservable = searchService.getSimilarTracks(artist, track);
}
mCompositeSubscription.add(fetchDataObservable
.timeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSubscriber<List<TrackSimilar>>() {
#Override
public void onNext(List<TrackSimilar> tracks) {
mTracks = tracks;
similarTrackAdapter.setTrackList(tracks);
}
#Override
public void onError(Throwable e) {
Toast.makeText(getActivity(), "API CALL ERROR", Toast.LENGTH_SHORT).show();
}
#Override
public void onComplete() {
resultsView.setVisibility(View.VISIBLE);
}
})
);
}
P.S i am using the lastFM api for my info.
Thanks in advance for any response.
Your code is good, one thing that you can do is that you can segment your code into multiple functions instead of a single one and run them as separate threads.
One function that makes api calls to search for similar tracks and other to get the image and trust me that significantly improves the response. Hope it helps..

Picasso doesn't load image on first call ever

I have come across a very interisting issue. I am using the bellow code to load bitmaps using picasso:
final Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// loaded bitmap is here (bitmap)
Log.i(TAG, "bitmapLoaded");
imageView.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.i(TAG, "bitmapFailed");
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
imageView.setTag(target);
Picasso.with(this)
.load(photoUrl)
.into(target);
I know a lot of questions have being asked about picasso not loading images due to weak reference but I don't think that's the case, since I have followed the solutions suggested in many topics to reference target like above.
In my program, I use this same code in 3 different classes and in 3 distinct moments. What I have noticed is that whenever I call this method for the first time it doesn't work, but for the next times it works, doesn't matter which of the 3 calls is being used. I can say that because I print different messages to the log from this 3 different methods.
Any thoughts about what is going on or have I missed something?
Thank you in advance.
Try to achieve this with using async approach.
Picasso.with(context).load(URL).into(profile, new Callback() {
#Override
public void onSuccess() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {//Use your "bitmap" here
Bitmap innerBitmap = ((BitmapDrawable) profile.getDrawable()).getBitmap();
}
}, 100);
}
Also you may try to use Glide https://github.com/bumptech/glide
Problem: The problem is Picasso holds a weak reference to the target class and it got GARBAGE COLLECTED.
Solution: Convert it to class field instead of using it as local reference.

Google Fit Listen for Data Updates not working

I'm trying to implement a Google Fit Listener when data is updated into Google Fit services.
In this link of Google Fit documentation there is a simple example, however, it is not 100% clear. For that reason, I have two problems:
I don't know how to implement mResultCallback variable (there aren't any examples in this documentation).
When I define a simple ResultCallback (it seems to work but I'm not sure) and I launch the application, it gives me a result error code: java.lang.SecurityException: Signature check failed
The code within the HistortyApi lists one of android.permission.ACCESS_FINE_LOCATION or android.permission.BODY_SENSORS as being required.
Adding those permissions to my code hasn't resolved the same problem though.
Confirmed bug in Google Fit services. See discussion in https://plus.google.com/110141422948118561903/posts/Lqri4LVR7cD
mResultCallback is a ResultCallback<Status> so you need to implement a class of that type. Documentation is here, but there's only one method you need to implement:
public abstract void onResult (Status result)
The standard way is to do this using an anonymous class either when you declare mResultCallback or when you're using it as a parameter. Below is an example from Google's BasicRecordingAPI example:
Fitness.RecordingApi.subscribe(mClient, DataType.TYPE_ACTIVITY_SAMPLE)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
if (status.getStatusCode()
== FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED) {
Log.i(TAG, "Existing subscription for activity detected.");
} else {
Log.i(TAG, "Successfully subscribed!");
}
} else {
Log.i(TAG, "There was a problem subscribing.");
}
}
});
If you want to use a member variable you can simply make an assignment instead:
ResultCallback<Status> mResultCallback = new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
...
}
});
Of course you can define a non-anonymous class, but if you did that for every callback you had you would end up creating a LOT of classes.

Categories