Android: best practice for performing asynchronous operations in getView ()

Please do not close it, IMHO this is a decent and, possibly, useful programming question.


Please, I read a lot of things, and I am embarrassed because I read different opinions and different approaches.

The problem is this:

in the getView() Adapter I need to perform some asynchronous operation, for example, check the formation on the Internet and update the view based on this.

I used the following approach:

every time getView() is called, I run a Thread

but my approach caused me a lot of criticism:

stack overflow

stack overflow

stack overflow

 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 this kind of action?


Please note: I am not looking for a solution to perform network requests in getView() , but rather, how to update the view depending on the result in an asynchronous call.

+5
source share
7 answers

There are several ratings for this. Although what you do is really not suitable.

  • AsyncTask

    • The thread pool is done internally here, so you don’t have to worry about what
    • This is a cleaner approach to your problem, rather than propagating individual threads.
    • If your user changes the screen during an API call, you can also cancel the call .
    • You need to enable notifyDatasetChanged ()
    • You need to redefine very few functions to achieve the desired functionality.
  • AsyncTaskLoader

    • This gives you more control, but you lose some implicitly defined functions
    • You need more knowledge to use this, and should be well versed in classes such as LoaderManager , Loader .
    • the change is autostarted. Say, if you changed the basic dataset, the changes would automatically activate and ensure the change of your user interface.
  • Handlers and Themes

    • This is one line above your current rating, but brings more benefits.
    • You can abstract the creation of the stream and provide a handler that handles the call for all of your identifiers.
    • You can queue up threads and sent messages.
    • If the screen changes, you can delete callbacks and messages .

In conclusion, the main disadvantages of your current approach: - This is a loss of context when changes are needed. - explicit creation of multiple threads

While the latter is a serious problem, the bigger the problem with the user, the better.

There are, and may be, several other approaches based on the required control, as well as your experience with android callbacks and thread control. But these three (in my opinion) are most suitable.

PS : a common point in all of these approaches,

 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 can also examine DataSetObserver to automate your requirements again.

+2
source

This is absolutely not the best way to update information in a ListView . The getView method should simply create a view from the data that you already have. Of course, you do not need to run anything to get additional information.

The best advice I can give you is to pre-select the data. Pull the data, update the ArrayList your Adapter is connected to, then call adapter.notifyDataSetChanged() . This will redraw all your information.

Pull data at a time, not in small pieces. This is the best and most reasonable way to do this.

+5
source

EDIT

I think this is an interesting question that deserves a kind of “canonical” solution.

Google I / O 2013 : P I suggest you keep an eye on this Google input / output since 2013. They have a touchiness explaining a lot of these things there. All your questions will be answered. This is the canon.

I used the Volley library here. If you read the documents, you will see that Volley works on background threads. Therefore, there is no need to complete your async tasks. Since others have already raised issues regarding the use of Threads, I will not talk about this. Let me go directly to the code :)

The following supported me well when my listings or gridviews or any other views depend on information from the Internet:

  • Create an interface: WebInfoUpdateReceiver.java

     public interface WebInfoUpdateReceiver { public void receive(Foo [] fooItems); } 
  • Create a class to load material: 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 an implementation of the interface:

     public class Activity extends Activity implements WebInfoUpdateReceiver { public void receive(Foo [] fooItems) { // convert the array into arrayList adapter.insert(arraylist); adapter.notifyDataSetChanged(); } } 
+4
source

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; } 

In addition, to improve performance, you need to add an understanding of the interaction to your ListView adapter so that it does not start any asynchronous operation in the line after, say, fling gestures on the ListView , which means that scrolling so it makes no sense to even start the asynchronous operation . When the scroll stops or stops, you should actually start showing heavy content for each line.

A very good example can be found here: https://code.google.com/p/shelves/

+1
source

The Volley library seems to have abstracted some of the common patterns to use ...

For finer control over network requests and caching, you can see: http://developer.android.com/training/volley/simple.html#send

As in the architecture diagram, when executing a request, it searches for a cache and, in the case of omissions, tries to execute a network request and adds it to the cache for later use. Moreover, it seems that you can provide a custom request for repeated request that meets your needs and process it / cancel caching as necessary.

For in-depth help you can take a look at - https://android.googlesource.com/platform/frameworks/volley/+/master/src/main/java/com/android/volley

+1
source

IMHO, the best practice would be to not perform asynchronous operations in getView (). The Adapter class should only convert objects to views, and you should keep it as simple as possible.

You may have problems if you run an asynchronous task (or Thread) that refers to the newly created View, if the task ends after the view is no longer displayed on the screen, or if it was processed by the adapter as a result of scrolling.

You should consider performing asynchronous operations outside the adapter. Run the operation and refresh the list after it is completed. You can refresh the entire list or some items after completing the asynchronous operation.

The operations themselves can be performed in AsyncTask of your activity or in a dedicated service. Avoid creating new threads because creating threads is expensive, and if for some reason you call it too often, you might run out of memory. AsyncTasks uses a thread pool, and therefore you will not have this problem.

+1
source

So, taking a step back and looking at it in a completely abstract way, it seems that the question you are asking is how to manage some potentially long-term action (network or otherwise) that will affect the appearance of the list to view the item.

Given this assumption, you are faced with two main problems:

  • Streams are comparatively expensive to start
  • The views returned by getView () are processed and can change the "element" when the user scrolls through the list

Problem 1 can be solved with ThreadPoolExecutor (or any other mechanism that you feel). The idea is that the threads are also processed, so they do not take so much time to unwind.

Problem 2 is a bit more complicated. You can cancel the stream operation when the view has been reworked (there are several ways to do this), but you need to decide if the loss of effort is acceptable (the user can scroll your view back on the screen and you need to start again). You can save the result of your long-term task with respect to the list item (or some data owner associated with the list item), but you need to be wary of running out of memory (sometimes caching or lru caches, sometimes used here) are used. This will allow you to save your efforts and update the list only if it is still on the screen. A flag in the data of your element can be used to indicate that you already have data, and not load it again.

Sorry, I do not have time for details at the moment. This is a dream for me :-)

Good luck. I will write more if I have time. Regards, CJ

+1
source

Source: https://habr.com/ru/post/1213333/


All Articles