As you have may have noticed, we are already using the AsyncTask class in our application. Now, we will go one step forward with it--we will run it on the executor. Why would we do that?
First of all, by default, all AsyncTasks are executed in sequence by Android. To execute it in parallel, we need to execute it on the executor.
Wait! There is more. Now, when we execute tasks in parallel, imagine you executed a few of them. Let's say we start with two. That's fine. They will perform their operations and report us when completed. Then, imagine we run four of them. They will work too, in most cases, if the operations they execute are not too heavy. However, at some point, we run fifty AsyncTasks in parallel.
Then, your application is slowing down! Everything will slow down because there is no control over the execution of tasks. We must manage tasks so the performance is preserved. So, let's do that! We will continue on the same class we were updating so far. Change your NoteActivity as follows:
class NoteActivity : ItemActivity() { ... private val threadPoolExecutor = ThreadPoolExecutor( 3, 3, 1, TimeUnit.SECONDS, LinkedBlockingQueue<Runnable>() ) private class TryAsync(val identifier: String) : AsyncTask<Unit,
Int, Unit>() { private val tag = "TryAsync" override fun onPreExecute() { Log.i(tag, "onPreExecute [ $identifier ]") super.onPreExecute() } override fun doInBackground(vararg p0: Unit?): Unit { Log.i(tag, "doInBackground [ $identifier ][ START ]") Thread.sleep(5000) Log.i(tag, "doInBackground [ $identifier ][ END ]") return Unit } override fun onCancelled(result: Unit?) { Log.i(tag, "onCancelled [ $identifier ][ END ]") super.onCancelled(result) } override fun onProgressUpdate(vararg values: Int?) { val progress = values.first() progress?.let { Log.i(tag, "onProgressUpdate [ $identifier ][ $progress ]") } super.onProgressUpdate(*values) } override fun onPostExecute(result: Unit?) { Log.i(tag, "onPostExecute [ $identifier ]") super.onPostExecute(result) } } ... private val textWatcher = object : TextWatcher { override fun afterTextChanged(p0: Editable?) { ... } override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2:
Int, p3: Int) {} override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int,
p3: Int) { p0?.let { tryAsync(p0.toString()) } } } ... private fun tryAsync(identifier: String) { val tryAsync = TryAsync(identifier) tryAsync.executeOnExecutor(threadPoolExecutor) } }
Since this is not actually something we will keep in the Journaler application, do not commit this code. Create it as a separate branch if you wish. We created a new instance of ThreadPoolExecutor. The constructor takes several arguments, as shown here:
- corePoolSize: This represents a minimal number of threads to keep in the pool.
- maximumPoolSize: This represents a maximal number of threads allowed in the pool.
- keepAliveTime: If the number of threads is greater than the core, the noncore threads will wait for a new tasks, and if they don't get one within the time defined by this parameter, they will terminate.
- Unit: This represents the time unit for keepAliveTime.
- WorkQueue: This represents the queue instance that will be used to hold the tasks.
- We will run our tasks on this executor. AsyncTask concretization will log all events during its life cycle. In the main method, we will wait for 5 seconds. Run the application and try to add a new note with Android as the title. Observe your Logcat output:
08-04 14:56:59.283 21953-21953 ... I/TryAsync: onPreExecute [ A ] 08-04 14:56:59.284 21953-23233 ... I/TryAsync: doInBackground [ A ][ START ] 08-04 14:57:00.202 21953-21953 ... I/TryAsync: onPreExecute [ An ] 08-04 14:57:00.204 21953-23250 ... I/TryAsync: doInBackground [ An ][ START ] 08-04 14:57:00.783 21953-21953 ... I/TryAsync: onPreExecute [ And ] 08-04 14:57:00.784 21953-23281 ... I/TryAsync: doInBackground [ And ][ START ] 08-04 14:57:01.001 21953-21953 ... I/TryAsync: onPreExecute [ Andr ] 08-04 14:57:01.669 21953-21953 ... I/TryAsync: onPreExecute [ Andro ] 08-04 14:57:01.934 21953-21953 ... I/TryAsync: onPreExecute [ Androi ] 08-04 14:57:02.314 21953-2195 ... I/TryAsync: onPreExecute [ Android ] 08-04 14:57:04.285 21953-23233 ... I/TryAsync: doInBackground [ A ][ END ] 08-04 14:57:04.286 21953-23233 ... I/TryAsync: doInBackground [ Andr ][ START ] 08-04 14:57:04.286 21953-21953 ... I/TryAsync: onPostExecute [ A ] 08-04 14:57:05.204 21953-23250 ... I/TryAsync: doInBackground [ An ][ END ] 08-04 14:57:05.204 21953-21953 ... I/TryAsync: onPostExecute [ An ] 08-04 14:57:05.205 21953-23250 ... I/TryAsync: doInBackground [ Andro ][ START ] 08-04 14:57:05.784 21953-23281 ... I/TryAsync: doInBackground [ And ][ END ] 08-04 14:57:05.785 21953-23281 ... I/TryAsync: doInBackground [ Androi ][ START ] 08-04 14:57:05.786 21953-21953 ... I/TryAsync: onPostExecute [ And ] 08-04 14:57:09.286 21953-23233 ... I/TryAsync: doInBackground [ Andr ][ END ] 08-04 14:57:09.287 21953-21953 ... I/TryAsync: onPostExecute [ Andr ] 08-04 14:57:09.287 21953-23233 ... I/TryAsync: doInBackground [ Android ][ START ] 08-04 14:57:10.205 21953-23250 ... I/TryAsync: doInBackground [ Andro ][ END ] 08-04 14:57:10.206 21953-21953 ... I/TryAsync: onPostExecute [ Andro ] 08-04 14:57:10.786 21953-23281 ... I/TryAsync: doInBackground [ Androi ][ END ] 08-04 14:57:10.787 21953-2195 ... I/TryAsync: onPostExecute [ Androi ] 08-04 14:57:14.288 21953-23233 ... I/TryAsync: doInBackground [ Android ][ END ] 08-04 14:57:14.290 21953-2195 ... I/TryAsync: onPostExecute [ Android ]
Let's filter logs by the methods we execute in our tasks. Let's look at the filter for the onPreExecute method first:
08-04 14:56:59.283 21953-21953 ... I/TryAsync: onPreExecute [ A ] 08-04 14:57:00.202 21953-21953 ... I/TryAsync: onPreExecute [ An ] 08-04 14:57:00.783 21953-21953 ... I/TryAsync: onPreExecute [ And ] 08-04 14:57:01.001 21953-21953 ... I/TryAsync: onPreExecute [ Andr ] 08-04 14:57:01.669 21953-21953 ... I/TryAsync: onPreExecute [ Andro ] 08-04 14:57:01.934 21953-21953 ... I/TryAsync: onPreExecute [ Androi ] 08-04 14:57:02.314 21953-21953 ... I/TryAsync: onPreExecute [ Android ]
Do the same for each method and focus on the times when the methods were executed. To give more challenge to your code, change the doInBackground() method implementation to do some more serious and intensive work. Then, fire more tasks by typing a longer title, for example, the entire sentence. Filter and analyze your log.