Please don't close this, IMHO it is decent and possibly useful programming question.
Please I am reading a lot of stuff, and I am getting confused because I read different opinions and different approaches.
The problem is the following:
in the getView()
of an Adapter
I need to perform some asynchronous operation, like checking an formation on the web, and update the view based on that.
I used the following approach:
every time getView()
is called I start a Thread
but my approach as earned me lots of criticism:
https://stackoverflow.com/a/28484345/1815311
https://stackoverflow.com/a/28484335/1815311
https://stackoverflow.com/a/28484351/1815311
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
//...
}
else {
//...
}
Thread th= new Thread(new Runnable() {
@Override
public void run() {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
CheckSomeInfoOverTheInternet(url, new myCallback {
@Override
public void onSuccess() {
holder.textview.setText("OK");
}
@Override
public void onFailre() {
holder.textview.setText("NOT OK!!!!");
}
});
}
});
}
});
th.start();
return convertView;
}
Please what would be the best practice for doing such a thing?
Please note, I am not looking for a solution to execute the network requests in getView()
, but rather, how to updated the view depending on the result on the asynchronous call.
This is absolutely not a good way to go about updating information in a ListView
. The getView
method should simply create the view from data that you already have. It certainly shouldn't be running anything to get more information.
The best advice that I could give you is to fetch the data beforehand. Pull the data, update the ArrayList
that your Adapter
is connected to, then call adapter.notifyDataSetChanged()
. This will redraw all of your information.
Pull the data all at once - not in small parts. This is the best and most reasonable way to do this.
EDIT
I think this is an interesting question, worth some kind of "canonical" solution
Google I/O 2013 :P I suggest you to watch this Google I/O from 2013. They have clealy explained a lot of these stuff there. All of your questions will be answered there. It's canon.
I have used Volley library here. If you read the docs then you'll see that Volley runs on background threads. So no need to implement your async tasks. Since others have already covered the issues in using Threads I will not talk about those. Let me go into code directly :)
The following has served me well whenever my listviews or gridviews or any other views depend on information from the web:
Create an interface: WebInfoUpdateReceiver.java
public interface WebInfoUpdateReceiver {
public void receive(Foo [] fooItems);
}
Create a class to download stuff: Downloader.java
public class Downloader {
private Context mContext;
private WebInfoUpdateReceiver mReceiver;
public Downloader(WebInfoUpdateReceiver receiver) {
mReceiver = receiver;
}
public void downloadStuff() {
MyStringRequest request = new MyStringRequest(Request.Method.GET,requestUrl, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// parse the response into Foo[]
mReceiver.update(foo);
}
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
RequestQueue queue = Volley.newRequestQueue(mContext);
queue.add(request);
}
}
Now make your activity implement the interface:
public class Activity extends Activity implements WebInfoUpdateReceiver {
public void receive(Foo [] fooItems) {
// convert the array into arrayList
adapter.insert(arraylist);
adapter.notifyDataSetChanged();
}
}
There are several appraoches for this. Although what you are doing is really not appropriate.
AsyncTask
AsyncTaskLoader
Handlers and Threads
In conclusion,the major drawbacks of your current approach: - is loss of context when the changes need to be made. - the explicit creation of multiple threads
While the latter is a major issue,the more "user-noticeable" problem would the first.
There are and could be several other approaches based on the control you require and the your expertise with android callbacks and thread management.But these three are(according to me) most suitable.
PS:the common point in all these approaches is,
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
//...
}
else {
//...
}
//execute a task for your given id where task could be:
//1. AsyncTask
//2. AsyncTaskLoader
//3. Handlers and thread
//call notifyDataSetChanged() in all the cases,
return convertView;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
//do any tasks which you feel are required
}
PPS:You could also look into DataSetObserver to again automate your requirements.
You can use something like this:
public View getView(int position, View convertView,
ViewGroup parent) {
ViewHolder holder;
...
holder.position = position;
new ThumbnailTask(position, holder)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
return convertView;
}
private static class ThumbnailTask extends AsyncTask {
private int mPosition;
private ViewHolder mHolder;
public ThumbnailTask(int position, ViewHolder holder) {
mPosition = position;
mHolder = holder;
}
@Override
protected Cursor doInBackground(Void... arg0) {
// Download bitmap here
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (mHolder.position == mPosition) {
mHolder.thumbnail.setImageBitmap(bitmap);
}
}
}
private static class ViewHolder {
public ImageView thumbnail;
public int position;
}
Also, for even better performance you need to add interaction awareness to your ListView
adapter so that it doesn’t trigger any asynchronous operation per row after, say, a fling gesture on the ListView
—which means that the scrolling is so fast that it doesn’t make sense to even start any asynchronous operation. Once scrolling stops, or is about to stop, is when you want to start actually showing the heavy content for each row.
A very good example can be found here: https://code.google.com/p/shelves/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With