I've implemented Picasso library for loading the images. Image Caching is working perfectly until we kills the application. If application gets killed Picasso loads images again, no caching. This is a well known bug in this library as I've read it in various blogs, somewhere I read that updating the Picasso library resolves this issue, I did the same but it is still remaining. Right now I am using Picasso Version2.5.2. If anybody has a nice experience with Picasso please let me know a few things.
What is the most updated version of the Picasso?
Is this issue still remaining in Picasso in latest version?
is there tricky solution for this?
Which library i need to use for overcoming this problem?
Thanks in advance.
For loading images after killed app, you should follow instructions:
Server should return image with header Cache-Control
i.e. Cache-Control:public, max-age=604800
more information: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
You should create own picasso instance:
Cache cache = new Cache(context.getCacheDir(), 30000000);
OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
OkHttp3Downloader downloader = new OkHttp3Downloader(client);
Picasso picasso = new Picasso.Builder(appContext).downloader(downloader).build();
Load image: picasso.load(url).into(imageView);
Explaination:
Picasso doesn't have a disk cache. It delegates to whatever HTTP client you are using for that functionality (relying on HTTP cache semantics for cache control). Because of this, the behavior you seek comes for free
This is how I implemented Picasso to force it to look for cache records first before fetching the images online again..Due note that the library has a 65MB cache limit..read that somewhere in the documentation
Picasso.with(context)
.load(Link)
.placeholder(R.drawable.grad)
.error(R.drawable.grad)
.networkPolicy(NetworkPolicy.OFFLINE)
.fit()
.centerCrop()
.into(image, new Callback() {
#Override
public void onSuccess() {
//successfully loads from CACHE
image.setClickable(true);
..............
}
#Override
public void onError() {
// fetch online because cache is not there
Picasso.with(context)
.load(Link)
.fetch(new Callback() {
#Override
public void onSuccess() {
Picasso.with(context)
.load(Link)
.fit()
.centerCrop()
.into(image, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
//success..
}
#Override
public void onError() {
Toast.makeText(context, "No Image Found. Try again later", Toast.LENGTH_SHORT).show();
image.setClickable(false);
}
});
}
#Override
public void onError() {
//NO IMAGE offline or online
Toast.makeText(context, "No Image Found. Try again later", Toast.LENGTH_SHORT).show();
image.setClickable(false);
}
});
}
});
Related
I am trying to post an image to a server with Fast Android Networking and I can't figure out how.
The image gets uploaded from an emulator in Android Studio and put in an ImageView, how can I send that image to the server with a POST request?
The code under my starting point and is mostly from https://amitshekhar.me/Fast-Android-Networking/post_request.html if that helps.
AndroidNetworking.post("https://fierce-cove-29863.herokuapp.com/postFile")
.setContentType(file) // posting any type of file
.setTag("test")
.setPriority(Priority.MEDIUM)
.build()
.getAsJSONObject(new JSONObjectRequestListener() {
#Override
public void onResponse(JSONObject response) {
// do anything with response
}
#Override
public void onError(ANError error) {
// handle error
}
});
I have a method that creates targets for Picasso like this:
private void createTargets() {
Target target;
for (Id id : itemids) {
target = picassoImageTarget(getContext(), "imageDir", id.getId() + ".png");
imgTargets.add(new ImgTarget(id.getId(), target));
}
}
and then I download images to disk this way:
private void download() {
for (ImgTarget imgTarget : imgTargets) {
Picasso.with(getContext()).load("https://www.sestavsisvujsvet.cz/files/magnetky/" + imgTarget.getId() + ".png").into(imgTarget.getTarget());
}
}
it does what I want it to do, however I am unable to get a callback, because the constructor with callback exists only when using ImageView:
Can someone help me find a solution to this? I don't understand callbacks and things like that too much, so it's hard for me to figure it out.
I just need to know when the whole downloading process has finished so I can notify the user.
Thanks :)
A picasso Target is essentially a callback, if you look at the Target object, it has the following structure:
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// Notify the user
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
// Notify the user
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
In your function picassoImageTarget(...) you would be building that target, and inside the onBitmapLoaded and onBitmapFailed you can put your code in to notify the user depending on your implementation.
Also, I noticed you are using Picasso.with(context), this has been updated to Picasso.get() in the latest library, might be worthwhile updating your picasso library to the latest too.
I want to get the Download Url from uploadTask.addOnProgressListener method of Firebase. How can I get the Download Url using following code?
UploadTask uploadTask = storageRef.putBytes(data);
uploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>()
{
#Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot)
{
Log.d("aaaaasessin",""+taskSnapshot.getTask().getResult());
}
});
I used taskSnapshot.getTask().getResult() but that is not working.
Edit Aug 22th 2019:
There is a new method recently added to StorageReference's class in the Android SDK named list().
To solve this, you need to loop over the ListResult and call getDownloadUrl() to get the download URLs of each file. Remember that getDownloadUrl() method is asynchronous, so it returns a Task object. See below for details. I have even written an article regarding this topic called:
How to upload an image to Cloud Storage and save the URL in Firestore?
In order to get the download url, you need to use addOnSuccessListener, like in the following lines of code:
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
storageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
String url = uri.toString();
//Do what you need to do with url
}
});
}
});
As in the Firebase release notes on May 23, 2018 is mentioned that:
Cloud Storage version 16.0.1
Removed the deprecated StorageMetadata.getDownloadUrl() and UploadTask.TaskSnapshot.getDownloadUrl() methods. To get a current download URL, use StorageReference.getDownloadUr().
So now when calling getDownloadUrl() on a StorageReference object it returns a Task object and not an Uri object anymore.
Please also rememeber, neither the success listener nor the failure listener (if you intend to use it), will be called if your device cannot reach Firebase Storage backend. The success/failure listeners will only be called once the data is committed to, or rejected by the Firebase servers.
i have read many threads on this topic and tried to use them in my code but i am still not able to solve it.
What i am trying to do:
Like facebook or any other app which downloads feed from server which includes image, I am also doing same and trying to display feeds and images on the image view.
What is happening:
I am able to download the feeds (in JSON) which includes URL of images and i am able to show them on ImageView.
In Emulator my MAX heap size is 64MB. I am consuming around 30MB in first 10 feeds( not sure why but this is what i get from Memory tab in Android monitor of Android Studio and Even in Android Device monitor).
I have a refresh button in my app which reloads the same feeds after removing all feeds which were earlier populated. I expected that i will be consuming the same memory or some what more. But contrary to that my memory usage got increased upto 42MB. Hence after tapping on refresh for 3 to 4 times, it is causing OutOFMemory Execption. Even if i load next 10 feed or 50 feeds at a time i am getting OutOfMemory Exception.
I know that facebook instagram and many more such apps does the same thing but not sure how they implemented the code to cover this situation.
Below is my code for populating feed
private void loadFeed(List<Feed> feedList)
{
Log.v(Constant.TAG,"Loading Feed in social feed page");
for(final Feed feed:feedList) {
LinearLayout feedBox = new LinearLayout(this);
feedBox.setOrientation(LinearLayout.VERTICAL);
FrameLayout profileDetailContainer= new FrameLayout(this);
LinearLayout profileDetailContainerParent=new LinearLayout(this);
LinearLayout profileDetailContainerChild=new LinearLayout(this);
profileDetailContainerChild.setOrientation(LinearLayout.VERTICAL);
ImageView imgProfile= new ImageView(this);
TextView txtDate= new TextView(this);
TextView txtName= new TextView(this);
ImageView imgProduct= new ImageView(this);
txtName.setText(feed.getUserFullName());
TextView txtDesciption= new TextView(this);
txtName.setTypeface(null, Typeface.BOLD);
if(feed.getDescription().length()>Constant.NUMBER_OF_DESCRIPTION_CHAR_SHOW_ON_RESULT)
{
txtDesciption.setText(feed.getDescription().substring(0,Constant.NUMBER_OF_DESCRIPTION_CHAR_SHOW_ON_RESULT)+"...");
}
else
txtDesciption.setText(feed.getDescription());
if(!IOUtil.fileExists(this,feed.getProductImageName())) {
WebRequest request = new WebRequest();
request.setUrl(Constant.ROOT_APPLICATION_URL_WITH_SEPARATOR + feed.getImgPath());
request.setParam(feed.getId() + "");
new ImageDownloadTask(this, true, feed.getProductImageName(), this).execute(request);
Log.v(Constant.TAG,"URL:"+Constant.ROOT_APPLICATION_URL_WITH_SEPARATOR+feed.getImgPath());
Picasso.with(getApplicationContext()).load(R.drawable.logo).into(imgProduct);
feedBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ScreenUtility.alertException(v.getContext(),"Please wait untill product image loads");
}
});
PixyfiSession.save(feed.getId() + "", imgProduct);
PixyfiSession.save(feed.getId() + "_feed", feed);
}
else
{
ImageUtil.recycleIfPossible(imgProduct);
try {
imgProduct.setImageBitmap(ImageUtil.getLocalImage(feed.getProductImageName(),this));
FeedboxOnClickListener feedboxOnClickListener = new FeedboxOnClickListener(feed);
feedBox.setOnClickListener(feedboxOnClickListener);
} catch (FileNotFoundException e) {
Log.v(Constant.TAG,e.getMessage(),e);
}
}
if(FacebookUtil.localProfilePicExists(feed.getUserName(),this))
{
Bitmap profileImage= FacebookUtil.getLocalProfilePicture(feed.getUserName(),this);
imgProfile.setImageBitmap(profileImage);
}
else {
FacebookUtil.loadProfilePicture(feed.getUserName(),this,this);
PixyfiSession.save(feed.getUserName(),imgProfile);
imgProfile.setImageResource(R.drawable.profile);
}
try {
if(feed.getDate()==null) {
txtDate.setText(new SimpleDateFormat("dd MMM yyyy").format(new SimpleDateFormat("yyyy-MM-dd").parse(feed.getFeedDate())));
}
else {
txtDate.setText(new SimpleDateFormat("dd MMM yyyy").format(feed.getDate()));
}
} catch (ParseException e) {
Log.e(Constant.TAG,e.getMessage(),e);
}
LinearLayout.LayoutParams layoutParam= new LinearLayout.LayoutParams(140,140);
layoutParam.setMargins(5,5,0,0);
imgProfile.setLayoutParams(layoutParam);
layoutParam= new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT,Gravity.TOP|Gravity.LEFT);
layoutParam.setMargins(20,5,0,0);
txtName.setLayoutParams(layoutParam);
layoutParam= new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT,Gravity.LEFT);
layoutParam.setMargins(20,5,0,0);
txtDate.setLayoutParams(layoutParam);
profileDetailContainerParent.addView(imgProfile);
profileDetailContainerChild.addView(txtName);
profileDetailContainerChild.addView(txtDate);
profileDetailContainerParent.addView(profileDetailContainerChild);
feedBox.addView(profileDetailContainerParent);
LinearLayout.LayoutParams feedLayoutParam=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT,Gravity.CENTER_VERTICAL);
feedLayoutParam.setMargins(5,5,5,5);
imgProduct.setLayoutParams(feedLayoutParam);
txtDesciption.setLayoutParams(feedLayoutParam);
feedBox.addView(txtDesciption);
feedBox.addView(imgProduct);
feedLayoutParam=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT,Gravity.CENTER_VERTICAL);
feedLayoutParam.setMargins(0,5,0,5);
feedBox.setLayoutParams(feedLayoutParam);
feedBox.setBackgroundColor(Color.parseColor("#cccccc"));
PixyfiSession.save(feed.getId()+"_feedbox",feedBox);
imgProfile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PixyfiSession.save(Constant.SELECTED_USER_ID,feed.getUserName());
ScreenUtility.loadScreen(v.getContext(),UsersProfile.class,false);
}
});
this.feedContainer.addView(feedBox);
}
if(feedList.size()==Constant.FEED_SIZE_IN_ONE_REQUEST)
{
Button moreFeed= new Button(this);
moreFeed.setText("Load MOre Feed");
moreFeed.setOnClickListener(new MoreFeedButtonListener(this));
this.feedContainer.addView(moreFeed);
}
this.progressBar.setVisibility(View.INVISIBLE);
}
For reloading/refreshing the feed
this.currentPage=1;
this.recycleImages();
this.feedContainer.removeAllViews();
PixyfiSession.save(Constant.CURRENT_FEED_PAGE,this.currentPage);
this.progressBar.setVisibility(View.VISIBLE);
loadFeed();
recycleImages Method:
private void recycleImages()
{
for(int i=0;i<this.feedContainer.getChildCount();i++)
{
if(this.feedContainer.getChildAt(i) instanceof ImageView)
{
ImageView view=(ImageView)this.feedContainer.getChildAt(i);
ImageUtil.recycleIfPossible(view);
}
}
}
If you need further details on the code then please let me know.
Also is it possible to see memory usage of other apps like facebook in android device monitor?
UPDATE
ImageUtil.getLocalImage Method
public static Bitmap getLocalImage(String imageName, Context context) throws FileNotFoundException
{
Bitmap bitmap=null;
InputStream is=null;
if(IOUtil.fileExists(context,imageName)) {
is = context.openFileInput(imageName);
bitmap= BitmapFactory.decodeStream(is);
}
else {
throw new FileNotFoundException("Image file doesn't exists");
}
return bitmap;
}
I am adding the answer :
Instead of managing the view components yourself you should always prefer listview or better recycler view.
Because it help you avoid view leaks and provide better reuse of views that you already created.
i had same problem add this line to your manifest in application tag:
<application
android:largeHeap="true"
.
.
.
As Saber Safavi told do that first and then add
defaultConfig {
....
multiDexEnabled true
....
}
in your
app/build.gradle
file..
Look like you have problem with loading images. Please check your ImageUtils.getLocalImages, did you decode and load a scaled version of images into memory, or did you load original images into memory?
Follow this document from Android developer , i think it's the best tutorial for loading images efficiently in Android.
Image handling in Android is very very tricky. Better use a library which has been built to take care of the situation and complexities involved.
I prefer
UniversalImageLoader or Piccaso!
It is because you are keeping too many bitmap images in Run-time memory, try not to create too many images and try nullifying them.
Mobile devices typically have constrained system resources. Android devices can have as little as 16MB of memory available to a single application. Virtual Machine Compatibility gives the required minimum application memory for various screen sizes and densities. Applications should be optimized to perform under this minimum memory limit. However, keep in mind many devices are configured with higher limits.
I will not recommend you for the heapSize flag in android manifest. I hope you are not developing games :), Rather go for retrofit or any other image processing library if you want. I would suggest you to look at BitmapFun sample provided by Google. It is available on developer portal.
http://developer.android.com/training/displaying-bitmaps/index.html
Cheers!!
I'm trying to get the favicon of the loaded page after using
WebView webView = new WebView(getActivity());
webView.loadUrl("http://" + url);
I'm attaching the asynchronous WebViewClient to the WebView to get the favicon after it loads
webView.setWebViewClient(new WebViewClient()
{
#Override
public void onPageFinished(WebView view, String url)
{
String linkTitle = view.getTitle();
Bitmap favicon = view.getFavicon();
onLinkUrlFinished(url, linkTitle);
}
});
The favicon getting back is always null, even for websites such as google/facebook that has favicons for sure.
Another thread says to use WebIconDatabase but it's deprecated:
Display the Android WebView's favicon
The API on android site refers to WebViewClient.onReceivedIcon which doesnt even exist.http://developer.android.com/reference/android/webkit/WebView.html#getFavicon%28%29
What's going on here?
In order to use onReceiveIcon(), you should use setWebChromeClient.
This is what I do and it's working for me.
webView.setWebChromeClient(new WebChromeClient() {
#Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
progressBar.setProgress(newProgress);
}
#Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
webImage.setImageBitmap(icon);
}
});
WebIconDatabase is deprecated as of API 19. According to the comments in the code:
#deprecated This class is only required when running on devices up to
{#link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
So unless you don't want to support API 18 and below, you should still be using WebIconDatabase:
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
And then, regardless what API you want to support, you need to specify in a custom WebChromeClient:
public class MyCustomWebChromeClient extends WebChromeClient {
#Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
// do whatever with the arguments passed in
}
}
Remember to register your custom WebChromeClient with your WebView:
mWebView.setWebChromeClient(new MyCustomWebChromeClient());
The key is to open the WebIconDatabase so WebView has somewhere to put the icons, and override WebChromeClient.onReceivedIcon. For additional information, see this StackOverflow article.
I know its an old thread but, for those facing problems getting favicon using webview client.
Kotlin:
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
tabTitle.text = view?.title // read website title
loadImg(view) // method to load the favicon
}
private fun loadImg (view: WebView?){
// u can directly use tabImage.setBitmap instead of assigning tabImg as val
val tabImg: ImageView = findViewById(R.id.tabImage)
// creating handler object to delay the associated thread a little bit after onPageFinished is called.
val handler = Handler()
val runnable = Runnable {
if(view?.favicon != null) {
tabImg.setImageResource(0) //remove the default image
tabImg.setImageBitmap(view?.favicon) // set the favicon
}
}
handler.postDelayed(runnable, 200) // delay time 200 ms
}
It worked for me, hope it helps new readers, plz up vote if it helps u, so that u can help others!
Best regards
So in the end I didn't end up using the deprecated API, instead I found out that if you put /favicon.ico after the domain, it'll give you the ico file, which I used in the end to fetch the image. The Uri API will have a getHost() method that will give you the host without having to manually parse it
String faviconUrl = Uri.parse(url).getHost() + "/favicon.ico";
For google for example the icon url will be www.google.com/favicon.ico