I'm having a loading screen where I fetch data belonging to the logged in user. Among this data is their profile images (three of them).
What I want to do is to cache the images when I download them with their URL in Loading screen. Then in the next activity I want to access the cached images to display them in that activity. So basically load all images in one activity to be used in another one. This way (I figured) I won't have to make a http request everytime user enters the activity where their profile image is shown. Which gives a better user experience.
So it's the matter of getting the images from cache to screen that I can't work out, cause I think I've loaded them into cache correctly, seen below.
Here's my current method in Loading Activity:
Backendless.Data.of(UserFileMapping.class).find(new AsyncCallback<BackendlessCollection<UserFileMapping>>() {
#Override
public void handleResponse(BackendlessCollection<UserFileMapping> response) {
Iterator<UserFileMapping> iterator = response.getCurrentPage().iterator();
while(iterator.hasNext()){
UserFileMapping fileMapping = iterator.next();
String profile1 = fileMapping.profile_url;
String profile2 = fileMapping.profile_url_2;
String profile3 = fileMapping.profile_url_3;
Picasso.with(getApplicationContext())
.load(profile1)
.fetch(new Callback() {
#Override
public void onSuccess() {
continue_button.setVisibility(View.VISIBLE);
loading_text.setText("Done!");
}
#Override
public void onError() {
//Make toast
}
});
}
}
#Override
public void handleFault(BackendlessFault fault) {
System.out.println("FAULT:" + fault.getCode());
}
});
It's the issue with downloading all three images at once as well, but that isn't as important as the caching question. If you have a good idea on this in addition to the cache issue I would gladly like to hear it too.
Related
I am making an app using Java in android studio, and in it I want users to have the option to set a custom profile picture. I'm using firebase storage to store the images for the PFPs, and taking them from there every time I load it, but it always takes around half a second or so to load the profile picture: gif of the problem
Here's my code for loading the pfp in the homepage (I am using de.hdodenhof.circleimageview.CircleImageView to make the ImageView circular):
public class HomePage extends AppCompatActivity {
de.hdodenhof.circleimageview.CircleImageView pfpImage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_page);
pfpImage = findViewById(R.id.pfpImg);
Intent intent = getIntent();
if (intent != null && intent.hasExtra("UserEmail")) {
String userEmail = intent.getExtras().getString("UserEmail");
StorageReference userPfp = DBRef.refStorage.child("ProfileImages/Users/" + userEmail.replace(".",",")); //DBRef is a class I created to reference all Firebase related objects.
userPfp.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Picasso.with(HomePage.this).load(uri).into(pfpImage); // I am using com.squareup.picasso:picasso:2.5.2 to load the image from the Uri.
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(HomePage.this, "Profile Picture Loading Failed", Toast.LENGTH_SHORT).show();
}
});
}
else
pfpImage.setImageResource(R.drawable.doggo);
}
}
Is there some better way to load/store the pictures so they appear when the page loads?
EDIT: I managed to negate this by not loading from Firebase every time the activity is opened, but only the first time and saving it, using an idea from this post
There is other library named glide that can load picture faster when the page loads.
Here is the link for the reference.
https://guides.codepath.com/android/Displaying-Images-with-the-Glide-Library
I'm loading smaller quality version of image with Picasso, and if user clicks on that image in a list, then new Activity opens where I'm showing full quality version of image also with Picasso.
Problem is that full quality version of image is large so it takes some time to load it.
I would like that during that period previously loaded smaller quality version of image is shown. Picasso would show it instantly because it was already loaded on previous Activity.
I tried to implement it by call Picasso method twice like this:
Picasso.get().load(url_small_quality).into(imageView)
Picasso.get().load(url_full_quality).into(imageView)
but I think it skips load(url_small_quality) because imageView is empty for some time until url_full_quality is loaded.
I tried to run only load(url_small_quality) and then image is loaded instantly as it should, because it was previously loaded and stored.
Is there a way to somehow set previously loaded smaller quality image as a placeholder until full quality image is loaded?
Most probably picasso is cancelling your previous request , you can try this
Picasso.get().load(url_small_quality).into(imageView,object:Callback{
override fun onSuccess() {
Picasso.get().load(url_full_quality).into(imageView)
}
override fun onError(e: Exception?) {
}
})
You can use a drawable as placeholder but that would be a default image
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
You can load your high quality image after successfully loading your low quality image like below(Since there's no direct way to achieve this or other can be to use disk caching but that would require more efforts) :-
Picasso.with(context)
.load(thumb) // small quality url goes here
.into(imageView, new Callback() {
#Override
public void onSuccess() {
Picasso.with(context)
.load(url) // full quality url goes here
.placeholder(imageView.getDrawable())
.into(imageView);
}
#Override
public void onError() {
}
});
As Astha Garg said in comments this can't be done:
Picasso.get().load(url_small_quality).into(imageView)
Picasso.get().load(url_full_quality).into(imageView)
Simply create one more imageview and place it above previous one with thumb and load your high resolution there.
Java:
Picasso.get().load(url_small_quality).into(IVThumb, new Callback() {
#Override
public void onSuccess() {
Picasso.get().load(url_full_quality).into(IVFull, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError(Exception e) {
}
});
}
#Override
public void onError(Exception e) {
}
});
xml:
inside LinearLayout create:
<ImageView
android:id="#+id/IVThumb"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/IVFull"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In an android app I have made, the home page shows a list a games in a listview, each row being custom made to include a textview and an imageview and some buttons. The imageview's src comes from downloading th image from my google cloud firebase, and the functionality of this works well, but when the listview is scrolled through there is an issue. The images seem to be unloaded when scrolled away from, which causes a bit of lag when they are reloaded once scrolled back to. I imagine this is built in to prevent a listview from loading many high resolution images and keeping them loaded, but for my list, keeping the images loaded won't be a problem. Is there a way I can turn this off and just keep the images loaded? Here is a video of the problem:
https://www.youtube.com/watch?v=FYYJWZcvBy4&feature=youtu.be
here is the code of the listview and getting the image from firebase:
public void showData(DataSnapshot dataSnapshot){
TextView toolbarTitle = findViewById(R.id.toolbarTitle);
toolbarTitle.setText("Popular Games");
ArrayList<GameInformation> PopularGames = new ArrayList<>();
Iterator<DataSnapshot> items = dataSnapshot.child("Games").getChildren().iterator();
PopularGames.clear();
while(items.hasNext()){
GameInformation game = new GameInformation();
DataSnapshot item = items.next();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
Iterator<DataSnapshot> itemsDeep1 = item.child("consoles").getChildren().iterator();
while(itemsDeep1.hasNext()){
DataSnapshot itemDeep = itemsDeep1.next();
game.setConsoles(itemDeep.getValue(String.class));
}
Iterator<DataSnapshot> itemsDeep2 = item.child("genres").getChildren().iterator();
while(itemsDeep2.hasNext()){
DataSnapshot itemDeep = itemsDeep2.next();
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());
ArrayAdapter adapter = new CustomListAdapter(this, PopularGames);
mListView.setAdapter(adapter);
}
getting the image:
storageRef.child("Games/"+singleGame.getPicturePath()+".jpg").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Glide.with(getContext()).load(uri).into(gameImageID);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});
The delay you're facing is because there are 2 operations (each one takes a few secs to complete) being performed when you get each image:
The image is read from Firebase Storage using getDownloadUrl()
The image is displayed with Glide
I recommend that when you store the images, save their Download URL to the database instead of saving their path. This way, you'd no longer need to call for getDownloadURL(). You'd simply load the image with a single operation:
Glide.with(getContext()).load(singleGame.getPicturePath()).into(gameImageID);
Not related to the question:
You can simplify your code by using a forEach loop (instead of while) in your Iterators:
PopularGames.clear();
for(DataSnapshot item : dataSnapshot.child("Games").getChildren()){
GameInformation game = new GameInformation();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
for(DataSnapshot itemDeep: item.child("consoles").getChildren()){
game.setConsoles(itemDeep.getValue(String.class));
}
for(DataSnapshot itemDeep: item.child("genres").getChildren()){
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());
I've noticed that occasionally images won't load in my app through picasso and that picasso is in fact erring. I am using two images per list item in a list view. Here's the picasso code:
Picasso.with(DashboardActivity.this).load(status).into(iv_customer_status_pic, new Callback() {
#Override public void onSuccess() {
Log.d("Debug", "Picasso Success");
}
#Override public void onError() {
Log.d("Debug", "Picasso Errored");
}
});
How can I ensure that the images are loaded, I don't want them to error and then make them disappear. Also why does it error? Is there a timeout? I noticed on more powerful devices it happens less.
The reasons why it fails might because of no Internet connection and Invalid Image URL.
With regard to the error handling refer to nPn's answer.
The reason the onError() callback for Picasso.with().load().into(target, callback) exists is because there is no 100% guarantee the load will be successful. For example if you are trying to load from a uri and you don't have an internet connection, the load will not be successful.
You can somehow attempt a re-try (which I think is already built into Picasso), but ultimately, you need to handle the case were the load fails (for whatever reason). One option would be to load a "default" image, like a generic "profile picture" if you were trying to load a specific users profile picture.
If you move the implementation of the callbacks to a separate class , or even the containing class you should be able to retry from the onError() call back. Here is what I am thinking:
class ContainingClass implements Callback.EmptyCallback
private int mRetryAttempts = 0;
#Override
public void onError() {
if (mRetryAttempts < 2) {
mRetryAttempts++;
// try again
} else {
mRetryAttempts = 0;
}
}
#Override
public void onSuccess() {
mRetryAttempts = 0;
}
I want to download the following image downloading code with Picasso image cache.
DownloadImage downloadImage = new DownloadImage();
downloadImage.execute(advert.getImgUrl());
private class DownloadImage extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... arg) {
Bitmap bmp = null;
try {
URL url = new URL(arg[0]);
bmp = BitmapFactory.decodeStream(url.openConnection()
.getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return bmp;
}
#Override
protected void onPostExecute(Bitmap result) {
if (result == null) {
Intent intent = new Intent(AdvertisingActivity.this,
AdvertisingErrorActivity.class);
intent.putExtra("ad-error", "Error downloading image");
}
adImg.setImageBitmap(result);
super.onPostExecute(result);
}
}
I have several questions regarding this.
I want to download more than one image in parallel. If I make repeated calls of Picasso.with(getActivity()).load(url); with different url values, does this get done?
I want to download images in one activity and use it in another activity. Is this possible? How can this be done?
If I call Picasso.with(getActivity()).load(url); more than once with the same url value, does this load the cached images for subsequent calls after the image has been downloaded?
If the image download process does not succeed for some reasons, can you make Picasso report you of the failure?
I've researched some more into your questions and decided that I should publish this as an answer rather than a comment.
Yes - Picasso loads images asynchronously so making repeated calls will cause images to be downloaded in parallel.
Yes - just make the call as normal and Picasso will handle the re-use of downloaded images e.g. in Activity1, call Picasso.with(this).load("image1"); and, later, make a call to the same URL in Activity2. The image will already be cached (either in memory or on device storage) and Picasso will re-use it, rather than downloading it again.
Yes - see above (Picasso will automatically use cached images where available)
This does not seem to have such a clear-cut answer. One thing you can do is provide an image to display if an error occurs while fetching the real image:
Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);
The 'placeholder' will be displayed whilst the attempt is being made to fetch the image from the web; the 'error' image will be displayed, for instance, if the URL is not valid or if there is no Internet connection.
Update, 17/03/2014:
Picasso supports the use of a callback to report you of a failure. Modify your usual call (e.g. the above example) like so:
.into(imageView, new Callback() {
#Override
public void onSuccess() {
// TODO Auto-generated method stub
}
#Override
public void onError() {
// TODO Auto-generated method stub
}
});
In conclusion, it sounds like Picasso would be a great choice of library for you. It definitely makes image downloading very quick and very easy, so I like it a lot.