I am using a recyclerView to show images using asysnc task. I am getting the images from a web server. My code is
from onBindViewHolder methode I call
new ImageLoadTask(url, holder.imageView).execute();
My ImageLoader asysnc task is
public class ImageLoadTask extends AsyncTask<Void, Void, Bitmap> {
private String url;
private ImageView imageView;
ProgressDialog pDialog;
public ImageLoadTask(String url, ImageView imageView) {
this.url = url;
this.imageView = imageView;
}
#Override
protected Bitmap doInBackground(Void... params) {
try {
URL urlConnection = new URL(url);
HttpURLConnection connection = (HttpURLConnection) urlConnection
.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
imageView.setImageBitmap(result);
}
}
The problem is when I scroll and skip one or more views before the image is downloaded and that view is recycled the image donwnload reqest is not canceled on those intermediate view resulting in a flash of that/those image(s) before the actual image is loaded in that view.
I tried passing the HttpURLConnection from the adapter and checking if its not null and then calling disconnect on this as shown before from onBindViewHolder methode, but still it happens. I am using the
if (holder.urlConnection != null)
{
holder.urlConnection.disconnect();
try {
holder.urlConnection.getInputStream().close();
}
catch (Exception e) {
e.printStackTrace();
}
holder.urlConnection = null;
}
new ImageLoadTask(url, holder.imageView,holder.viewHolderActivity, holder.urlConnection).execute();
What can I do to cancel the image requests ?
Save ImageLoadTask link in holder
if (holder.urlConnection != null)
{
holder.urlConnection.disconnect();
try {
holder.urlConnection.getInputStream().close();
}
catch (Exception e) {
e.printStackTrace();
}
holder.urlConnection = null;
}
holder.imageTask = new ImageLoadTask(url, holder.imageView,holder.viewHolderActivity, holder.urlConnection);
holder.imageTask.execute();
and cancel it on
//Called when a view created by this adapter has been recycled.
public void onViewRecycled(VH holder){
holder.imageTask.cancel();
}
On your ViewHolder keep a reference to your ImageLoadTask.
in the onBindViewHolder method cancel the existing ImageLoadTask by calling its cancel method and create a new task for the new image. Note that when a AsyncTask is cancelled it will not call the onPostExecute method instead it will call onCancelled.
Try to cancel asynctask using cancel() method in order to cancel the image request:
ImageLoadTask imageLoadTask=new ImageLoadTask(url, holder.imageView);
imageLoadTask.cancel(true);
Use glide:
https://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
It's a fast and responsive framework for loading images from any given back-end. You can do stuff like setTimeout, cancelRequests, fadeEffects and some other stuff. You don't even have to worry about how to handle the network request. Glide will simply do that for you.
Usage:
Glide.with(context)
.load(imageLoadPath)
.placeholder(imgToShowWhileLoading)
.centerCrop()
.into(imageView);
And then you can set the GlideModule configuration like this:
(Original code snippet: glide image loading timeout increase). Make a custom class to implement GlideModule. In one of it's overriding methods:
#Override
public void registerComponents(Context context, Glide glide) {
final int retryPolicy = 10000;
RequestQueue queue = new RequestQueue(
new DiskBasedCache(new File(context.getCacheDir(), "volley")),
new BasicNetwork(new HurlStack())) {
#Override public <T> Request<T> add(Request<T> request) {
request.setRetryPolicy(new DefaultRetryPolicy(retryPolicy, 1, 1);
return super.add(request);
}
};
queue.start();
glide.register(GlideUrl.class, InputStream.class, new VolleyUrlLoader.Factory(queue));
}
Play with timeout limit all you want, and see how it can meet your needs.
Related
I'm trying to load an image from an URL to a Bitmap but I am getting a NetworkOnMainThreadException error but I don't know why.
This is the method I am using:
public Bitmap getBitmapFromURL(String src) {
try {
java.net.URL url = new java.net.URL(src);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
I also tried to load the image to a Target using Picasso library, because in the end, I want to get the dominant color from this image using Palette. This is the code I have using Picasso:
Picasso.with(MovieDetails.this)
.load("https://image.tmdb.org/t/p/w500" + backdrop)
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Palette palette = Palette.from(bitmap).generate();
System.out.println(palette.getVibrantSwatch().toString().substring(16, Math.min(palette.getVibrantSwatch().toString().length(), 22)));
LinearLayout lLayout = (LinearLayout) findViewById(R.id.layout_bg);
lLayout.setBackgroundColor(Color.parseColor("#"+ palette.getVibrantSwatch().toString().substring(16, Math.min(palette.getVibrantSwatch().toString().length(), 22))));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
I put this inside my onCreate method, but I am getting "method does not override method from its superclass" errors in all the three #Override methods.
I managed to solve my problem by using an AsyncTask as it follows:
I put this in the end of my Activity:
public class MyAsync extends AsyncTask<Void, Void, Bitmap>{
#Override
protected Bitmap doInBackground(Void... params) {
try {
URL url = new URL("https://image.tmdb.org/t/p/w500" + backdrop);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
Please note that inside this method I put the URL for the image.
To access the Bitmap inside my Activity and get the dominant color using Palette I did this:
MyAsync obj = new MyAsync(){
#Override
protected void onPostExecute(Bitmap bmp) {
super.onPostExecute(bmp);
Bitmap bm = bmp;
if (bm != null && !bm.isRecycled()) {
Palette palette = Palette.from(bm).generate();
System.out.println(palette.getVibrantSwatch().toString().substring(16, Math.min(palette.getVibrantSwatch().toString().length(), 22)));
LinearLayout lLayout = (LinearLayout) findViewById(R.id.layout_bg);
lLayout.setBackgroundColor(Color.parseColor("#"+ palette.getVibrantSwatch().toString().substring(16, Math.min(palette.getVibrantSwatch().toString().length(), 22))));
}
}
};
obj.execute();
This code is inside my onCreate method.
The way to correct the first issue is to perform that action in a Thread or Runnable, There is a flag you can set something like StrictModeEnabled or something like that, but that's bad, don't resort to that.
As far as picasso, i dont know about the overriding methods from superclass thing, i'd try to do it in onViewCreated instead of onCreate
public class MainActivity extends Activity {
ImageView downloadedimg;
public class ImageDownloader extends AsyncTask<String,void,Bitmap> {
#Override
protected Bitmap doInBackground(String... urls) {
try{
URL url=new URL(urls[0]);
HttpURLConnection connection=(HttpURLConnection) url.openConnection();
connection.connect();
InputStream inputStream=connection.getInputStream();
Bitmap mybitmap= BitmapFactory.decodeStream(inputStream);
return mybitmap;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}
public void downloadImage(View view) {
ImageDownloader task = new ImageDownloader;
Bitmap myImage;
try {
myImage = task.execute("https://www.google.co.in/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&cad=rja&uact=8&ved=0ahUKEwiOw5HvydXOAhVKqo8KHYHfAtIQjRwIBw&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FBart_Simpson&psig=AFQjCNFZSwEG2tjp15Km14uuzEsmZUZ_MQ&ust=1471974330385306");
downloadedimg.setImageBitmap(myImage);
}
catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
downloadedimg=(ImageView)findViewById(R.id.imageView);
}
in the line
myImage = task.execute("some url to image address online");
the complier shows error that
incompatible type
required: android.graphics.Bitmap
found: android.os.AsyncTask "<"java.lang.String,void,android.graphics.Bitmap>
double code infront of java's bracket are not part of statement
#edit Thank you for previous advice i searched and found .get() method
i used task("").get() to return the bitmap image and code compiled without errors but now nothings happens when i click the download image button
AsyncTask.execute does not return the object downloaded. It can't, the entire idea of an AsyncTask is that its asynchronous. Any code that needs to use the result should either be put in onPostExecute (for UI changes) or at the end of doInBackground (for processing data).
I am working on making a music player and what i have done so far is that I created a song list in listview with their thumbnail images using metadata files.
Now when i scroll my list it lags, that might be because of the image loading.
I referred to Google Play Music App and what they have done is that they fetch album art at the time of scrolling so scrolling is smooth.
Is there any way to implement that kind of functionality?
You have to use lazy loading of images, so images will be downloaded in background.
public class DownloadImage extends AsyncTask<ImageView, Void, Bitmap> {
ImageView imageView = null;
#Override
protected Bitmap doInBackground(ImageView... imageViews) {
this.imageView = imageViews[0];
return download_Image((String) imageView.getTag());
}
#Override
protected void onPostExecute(Bitmap result) {
imageView.setImageBitmap(result);
}
private Bitmap download_Image(String url) {
try {
URL ulrn = new URL(url);
HttpURLConnection con = (HttpURLConnection) ulrn.openConnection();
InputStream iS = con.getInputStream();
Bitmap bmp = BitmapFactory.decodeStream(iS);
return bmp;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
}
Make a call to this class in your adapter getview
imgThumbnail.setTag(<your image url>);
new DownloadImage().execute(imgThumbnail);
I'm trying to download an image and show it as an ImageView in my infoWindow , working with InfoWindowsAdapter , i put all the necessary code in the getInfoContents() method, but i can't get the image , in fact i can get it but still can't show it .
As i read in the documentation,i have to recall the treatment to set the downloaded image , i have to use showInfoWindow() method because it's the only way to do it . I tried to use it in getInfoContents() just before the return but , it blocks my app , need help !
This is my getInfoContents() :
public View getInfoContents(Marker marker) {
// set the view
View v = (View) getLayoutInflater().inflate(R.layout.info_window,null);
//assign values to the view field
ImageView imagePlace =(ImageView) v.findViewById(R.id.imageview1);
TextView tvLocality = (TextView) v.findViewById(R.id.tv_place);
//setting values with the parameter
Picasso.with(getApplicationContext()).load(url_image_place).into(imagePlace);
tvLocality.setText(marker.getTitle());
//marker.showInfoWindow();
return v;
}
This my downloadIcon() :
private Bitmap downloadIcon(String iconURL) {
Bitmap bmImg = null;
try {
URL url = new URL(iconURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bmImg = BitmapFactory.decodeStream(is);
} catch (IOException e) {
e.printStackTrace();
bmImg = null;
}
return bmImg;
}
EDIT :
I used picasso to download my image , but the image is shown only after the second click , and any marker's infowindow that use the same image get the image in the first time , it means that the image have to be downloaded first !! i edited my getInfoContents() and i'm not using downloadIcon() anymore !
Using Picasso, this is extremely easy. Simply write:
String url = ...;//Your url
ImageView imageView; //Your ImageView
Picasso.with(context).load(url).into(imageView);
Or, If you want to do it yourself, you can implement an AsyncTask:
Definition:
private class ImageDownloader extends AsyncTask {
#Override
protected Bitmap doInBackground(String... param) {
return downloadIcon(param[0]);
}
#Override
protected void onPostExecute(Bitmap result) {
//Handle your result, i.e. your bitmap
}
}
Execution:
new ImageDownloader().execute(iconURL);
You should not dowmload image by using main UI thread, so you need another thread to do it, something like:
public void setImage(final ImageView img, final String iconURL)
{
Thread thread = new Thread(new Runnable() {
#Override
public void run()
{
Bitmap bmImg;
try {
URL url = new URL(iconURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bmImg = BitmapFactory.decodeStream(is);
}
catch (IOException e) {
e.printStackTrace();
bmImg = null;
}
//after downloading, then talk to main ui
runOnUiThread(new Runnable()
{
#Override
public void run()
{
img.setImageBitmap(bmImg);
}
}
}}).start();
}
Hope this help!
I am continuing my efforts in making an app for my school newspaper. My newest challenge is to get every club and organization logo to appear in a ImageView that is in an Activity with other details about the club.
For obvious reasons I don't want to store all of these images within the app, I would rather fetch them from a URL. I think a way to do this would be the following snippet:
Intent intent = getIntent();
Bitmap bm = null;
try {
bm = BitmapFactory.decodeStream((InputStream)new URL(intent.getStringExtra("keyLogo")).getContent());
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
detailsLogo.setImageBitmap(bm);
I am getting a URL from an XML file that I created which has all of the clubs on campus and their information. This way I only have to change the XML when I want to change something within the app.
The problem here is that it throws a NetworkOnMainThread exception. Could this be put into an AsyncTask within my activity and run that way? Any advice would be appreciated.
EDIT: The linked question does not answer my needs. One of the answers to it is close, but my questions specifically needs an Async task which that questions accepted answer does not include.
Here is the finished activity using the accepted answer and other pieces:
public class OrgDetails extends Activity {
/*******************************************************************
* Async Task
******************************************************************/
private class GetImageFromServer extends AsyncTask<String, Void, Bitmap> {
String url;
Context context;
private Bitmap image;
ImageView detailsLogo = (ImageView)findViewById(R.id.detailsLogo);
public GetImageFromServer(String url, Context context){
this.url = url;
this.context = context;
}
#Override
protected Bitmap doInBackground(String... params){
try{
URL urli = new URL(this.url);
URLConnection ucon = urli.openConnection();
image = BitmapFactory.decodeStream(ucon.getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return image; //<<< return Bitmap
}
#Override
protected void onPostExecute(Bitmap result){
detailsLogo.setImageBitmap(result);
}
}
/*******************************************************************
* End Async Task
******************************************************************/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.orgdetails);
TextView detailsSname = (TextView)findViewById(R.id.detailsSname);
TextView detailsLname = (TextView)findViewById(R.id.detailsLname);
TextView detailsDescription = (TextView)findViewById(R.id.detailsDescription);
Intent intent = getIntent();
detailsLname.setText(intent.getStringExtra("keyLname"));
detailsSname.setText(intent.getStringExtra("keySname"));
detailsDescription.setText(intent.getStringExtra("keyDescription"));
String str_url = intent.getStringExtra("keyLogo");
GetImageFromServer asyObj = new GetImageFromServer(str_url,OrgDetails.this);
asyObj.execute("");
}
}
Change your code using AsyncTask for getting image from sever instead of doing Network Operation on Main UI thread. Create an inner class of Activity by extending AsyncTask as:
private class GetImageFromServer extends AsyncTask<String, Void, Bitmap> {
String url;
Context context;
public GetImageFromServer(URL url,Context context){
this.url=url;
this.context=context;
}
#Override
protected Bitmap doInBackground(String... params) {
bm = BitmapFactory.decodeStream((InputStream)new URL(url).getContent());
return bm; //<<< return Bitmap
}
#Override
protected void onPostExecute(Bitmap result) {
detailsLogo.setImageBitmap(result);
}
}
and execute AsyncTask as from Activity:
String str_url=intent.getStringExtra("keyLogo");
GetImageFromServer asyObj=new GetImageFromServer(str_url,Your_Activity.this);
asyObj.execute("");
You get a NetworkOnMainThread exception as you are attempting to perform network operations in the UI thread.
You should put this into an AsyncTask as you suggested, and your download code should retrieve the InputStream rather than cast the content like so:
URL url = new URL(path);
URLConnection ucon = url.openConnection();
Bitmap image = BitmapFactory.decodeStream(ucon.getInputStream());
If you also want more flexible control over your images, consider using Image adapters for your Bitmap objects. These are containers that extend the BaseAdapter and can provide you extra controls to manipulate your images e.g. scaling.
Yes you definitely want to do your grabbing of images over the network on a separate thread. You could set up your doInBackground to get the image and then in the onpostexecute set the bitmap to the imageview.