In Android in order to avoid to do too work in the main thread, most of the operations should be carried out in a secondary thread or a class that extends AsyncTask; then the final results are reported in some view to be shown to the user using Activity.runOnUiThread(Runnable) or View.post(Runnable) (see Processes and Threads) or Handler.handleMessage() (see Communicating with the UI Thread).
These updates can not be too frequent otherwise you might see the error or the warning “Choreographer ([…]): Skipped […] frames! The application may be doing too much work on its main thread”
A solution is to use a thread for updates to avoid sending too many refresh requests in a short amount of time.
Here’s an example:
In the activity containing the views that receive updates, create a static class activityHandler and a private method refreshView:
public class MainActivity extends Activity { ... private final ActivityHandler activityHandler = new ActivityHandler(this); private void refreshView() { // put here the code to refresh the views } public ActivityHandler getActivityHandler() { return activityHandler; } public static class ActivityHandler extends Handler { private final WeakReference<MainActivity> mTarget; public ActivityHandler(MainActivity activity) { mTarget = new WeakReference<>(activity); } public void refresh() { post(new Runnable() { @Override public void run() { MainActivity target = mTarget.get(); target.refreshView(); } }); } } ... }
The job is done by another class, OtherClass, in a thread that is omitted here, and when this job was completed refreshRequest variable is set to true (line 17).
The constructor of OtherClass does two things:
- set activityHandler pointing to an object of class activityHandler in MainActivity
- create an object ScheduledThreadPoolExecutor that launches every second (see UI_DELAY) a thread RefresherRunnable that updates the views through activityHandler
public class OtherClass { private final static long UI_DELAY = 1L; private volatile boolean refreshRequest; private MainActivity.ActivityHandler activityHandler; private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; public OtherClass(MainActivity activity) { activityHandler = activity.getActivityHandler(); scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1); scheduledThreadPoolExecutor.scheduleAtFixedRate(new RefresherRunnable(), 0L, UI_DELAY, TimeUnit.SECONDS); } // the following lines probably are in a thread not written here, // when the job is done and ready to show to the user I set refreshRequest to true ... refreshRequest = true; ... private class RefresherRunnable implements Runnable { @Override public void run() { if (refreshRequest) { activityHandler.refresh(); refreshRequest = false; } } } }
In this way all requests for updating of the views within one second are incorporated into a single request, avoiding to perform multiple updates in a second.
Of course the arguments UI_DELAY and TimeUnit.SECONDS can be changed, for example, you can be set half a second:
... scheduledThreadPoolExecutor.scheduleAtFixedRate(new RefresherRunnable(), 0L, 500L, TimeUnit.MILLISECONDS); ...
Leave a Reply