📜 ⬆️ ⬇️

Performing tasks in the background

On Stackoverflow, frequently encountered questions on performing background tasks on Android, incl. and repeating with a predetermined period of time. As a rule, the first that is used is Service .

This approach in some cases can lead to brakes and slow user interface response. I'll tell you when it happens and how to deal with it ...

Why can "brake" Service


Each Android application by default starts in a separate process. In each process threads are started (Thread). By default, all application components (Activity, Service, BroadcastReceiver) are launched in one “main” thread (also known as UI-thread). If we start, for example, a long network call or some kind of heavy initialization inside the service, we will get the brakes of the entire application, its interface and, most likely, a proposal to make Force close ... However, the service has the same flow as the rest its advantages - you have access to the interface elements. If the service worked in another thread, you would not have access to the UI.

What to do?


To solve this problem, it is necessary to use a separate thread for heavy tasks. In fact, you don’t even have to create it inside the service ...
')
To run a task in a separate thread, you can use the following SDK tools:

We start the flow


The flow is easy to create ...

Thread myThread = new Thread(new Runnable() { @Override pubic void run() { doLongAndComplicatedTask(); } }); myThread.start(); //  

Everything is simple, but problems start when, after doing a long assignment, we want to update the UI.

 final TextView txtResult = (TextView)findViewById(R.id.txtResult); Thread myThread = new Thread(new Runnable() { @Override public void run() { txtResult.setText(doLongAndComplicatedTask()); } }); myThread.start(); 

As a result, we get an error. "Alien" thread tried to turn to the UI! How to cure? Handler must be used. Let's refine the code ...

 final Handler myHandler = new Handler(); //     . final TextView txtResult = (TextView)findViewById(R.id.txtResult); Thread myThread = new Thread(new Runnable() { final String result = doLongAndComplicatedTask(); myHandler.post(new Runnable() { //  Handler,   UI-Thread @Override public void run() { txtResult.setText(result); //    } }); }); myThread.start(); 

AsyncTask - everything is easier


To implement such tasks in the Android SDK has a built-in tool - AsyncTask . This class allows you to not think about the thread in which your code runs, everything happens automatically. Consider the example above rewritten to AsyncTask.

 class LongAndComplicatedTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... noargs) { return doLongAndComplicatedTask(); } @Override protected void onPostExecute(String result) { txtResult.setText(result); } } LongAndComplicatedTask longTask = new LongAndComplicatedTask(); //   longTask.execute(); //  

The doInBackground method will be executed in a separate thread, the result of its execution will be passed to the onPostExecute method, which, in turn, will be executed on UI-Thread
It should be remembered that:

And what to do if the task needs to be performed regularly, at regular intervals ...

Timer. The easiest approach to the periodic launch.


Java provides a Timer to run repetitive tasks. Let's make the AsyncTask from the previous example run once a minute ...

 Timer myTimer = new Timer(); //   final Handler uiHandler = new Handler(); final TextView txtResult = (TextView)findViewById(R.id.txtResult); myTimer.schedule(new TimerTask() { //   @Override public void run() { final String result = doLongAndComplicatedTask(); uiHandler.post(new Runnable() { @Override public void run() { txtResult.setText(result); } }); }); }, 0L, 60L * 1000); //  - 60000 , 0    . 


It is worth noting that everything is simplified if the "long-running" task does not require access to the UI. In this case, neither Handlers, nor AsyncTasks are required.

By the way, the timer has another method scheduleAtFixedRate (). The differences between it and schedule () are described in the documentation.

More flexible way. ScheduledThreadPoolExecutor.


The class ScheduledThreadPoolExecutor is listed as the recommended alternative to using Timer. This class allows you to organize a pool of threads and plan tasks to be performed on it. Those. a class for queuing tasks is one, but nevertheless it can perform several tasks simultaneously if the available number of available threads allows. More details about the benefits - in the documentation .

For each scheduled task, its “descriptor” is available - the ScheduledFuture with which you can, for example, cancel the execution of one specific task without touching the rest of the pool.

Task with an asterisk. Bicycle. Thread, Looper, Handler.


And you can also collect your bike stream with a queue.

 public class LoopingThread extends Thread { private CountdownLatch syncLatch = new CountdownLatch(1); private Handler handler; public LoopingThread() { super(); start(); } @Override public void run() { try { Looper.prepare(); handler = new Handler(); syncLatch.countDown(); Looper.loop(); } catch(Exception e) { Log.d("LoopingThread", e.getMessage()); } } public Handler getHandler() { syncLatch.await(); return handler; } } Thread loopThread = new LoopingThread(); //    loopThread.getHandler().post(new Runnable() { @Override public void run() { doLongAndComplicatedTask(); } }); 

This thread, once launched, runs forever. To communicate with him (sending tasks), you can use the getHandler () method to get a handler and then send "events" to it. CountdownLatch is used for synchronization, so that the stream that wants to get a Handler does not receive it before the time when the worker thread starts and Handler is created.

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


All Articles