preface
Yesterday when I was nervous, I did 60 push-ups at a stretch. If you have the chance, you can also try, the tension is much relieved.
AsyncTask principles? How to understand AsyncTask? Will there be the thought that wants to express and oneself express wrong? Ineffective communication? That is sorry, GG, the bad place that this article says still hopes everybody points out.
This article extends from routine operations to FutureTask, which is associated with AsyncTask
directory
I. Asynchronous data synchronization
Problem: The timeliness of data acquisition in asynchronous threads is not a security of data manipulation in multithreading. For example, you initiate A request through A, how to get data through A, for example, A.et.
public class Demo { private static String result = "1"; public static void main(String[] args){ new Thread(new Runnable() { @Override public void run() { result = "2"; } }).start(); System.out.println(result); }}Copy the code
The problem with the code above is obvious to everyone. We want the result, but it is not satisfactory. We think of a thread as a network request, and result is the result it returns, so what do we need to do to get the correct return value later?
The while loop? Yes, but it’s not very user-friendly, CPU intensive, multiple thread operations have thread safety concerns, and so on
While (result==null){// here represents the data is empty wait delay sleep} result // get dataCopy the code
Is there a good way? You can add a callback with a return value, which is usually done in development. If the network request result is available, just call me back
System.out.println(System.currentTimeMillis()); new MyThread<String>(data -> { System.out.println(System.currentTimeMillis()); System.out.println(data); }).start(); interface Callback{ void callback(String data); } static class MyThread<T> extends Thread{ private Callback callback; private T result; public MyThread(Callback callback){ this.callback = callback; } @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } callback. Callback (" network request "); }}Copy the code
Yes, yes, but you need to set a callback, can I just get the data? Is there a way? There are
public static void main(String[] args){ RunnerThread thread = new RunnerThread<>(() -> { try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } return "network request "; }); thread.start(); System.out.println(System.currentTimeMillis()); System.out.println(thread.get()); System.out.println(System.currentTimeMillis()); } interface RunnerTask<T> { T run(); } static class RunnerThread<T> extends Thread{ private T result; private RunnerTask<T> task; private volatile boolean finished = false; public RunnerThread(RunnerTask<T> task){ this.task = task; } @Override public void run() { synchronized (this){ result = task.run(); finished = true; notifyAll(); }} public T get() {synchronized (this){ finished) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } return result; //output 1610550574299 Network request 1610550578299Copy the code
That is, the producer-consumer model can be used to solve such problems.
The final version is actually a shortened version of FutureTask. FutureTask is much more powerful, with internal CAS operations to verify states, including new, completed, cancelled, interrupted, and so on. Block the thread with locksupport. part, save data, and unblock the thread with locksupport. unpark
FutureTask<String> Future = new FutureTask<>(() -> {return "network request, download picture "; }); new Thread(future).start(); System.out.println(future.get()); // block here until your data comes backCopy the code
FutureTask set
FutureTask programming ideas
Basic use and source code analysis
FutureTask can block fetching results, so if you perform a time-consuming operation, can you block in multiple steps, for example
preCall(); Int result = futureTask.get(); if(result == 1){ doInBackground(); } void doInBackground(){int result = futuretask.get (); if(result == 2){ onPostExecute(); // Update progress}}Copy the code
And then I wonder if AsyncTask does something like this, does it do that? After all, I forgot about AsyncTask. Although behind slap slap face, but still need to continue to learn to understand.
Second, the AsyncTask
1,
Multithreaded task, time-consuming operation, the main thread will be notified of the result by the worker thread
2. Main methods
Internal thread pools
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
Copy the code
The number of core threads is 1, the maximum number of threads is 20, the timeout time of non-core threads is 3 seconds, there is no blocking queue for elements, and the rejection policy will be executed using another thread pool
sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
sBackupExecutor = new ThreadPoolExecutor(
BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
sBackupExecutor.allowCoreThreadTimeOut(true);
Copy the code
The number of core threads is 5, the maximum number of threads is 5, the timeout time of core threads is 3 seconds, the bounded blocking queue, the size is not specified.
4. Brief analysis of source code
The execution method calls the following method
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { if (mStatus ! = Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("Cannot execute task:" + " the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)"); } } mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this; }Copy the code
When a new task is created, its state changes to RUNNING, FINISHED, and PENDING. Therefore, an AsyncTask can be invoked only once.
And then onPreExecute() is called before the task starts
Both mWorker and mFuture are initialized in AsyncTask constructors.
MFuture is also implemented through the following class
private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); }}}); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) ! = null) { THREAD_POOL_EXECUTOR.execute(mActive); }}}Copy the code
The thread pool executes the mFuture, so the focus shifts to the mFuture,
Let’s take a look at the internal creation logic of mWorker and mFuture
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; }}; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { try { postResultIfNotInvoked(get()); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); }}};Copy the code
The thread pool executes the mFuture, which is an object of type FutureTask that implements the RunnableFuture interface, which inherits the Runnable class and Future, so calling the run method calls the Run method of RunnableFuture, Trigger mWorker’s call method.
So we call the doInBackground method, we do the time-consuming operation here, and finally, we do postResult(result)
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
Copy the code
Here, we switch to the main thread and call the AsyncTask method to terminate the task
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
Copy the code
When it’s done, the onPostExecute method is called.
The onProgressUpdate() method is triggered using the publishProgress method
protected final void publishProgress(Progress... values) { if (! isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } } case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); // main thread break;Copy the code
The whole process is done in a simplified version, but you can also go into details about exceptions, such as callbacks to final results, guaranteed by AtomicBoolean
5. AsyncTask Handler
The AsyncTask class must be loaded in the main thread. The AsyncTask class must be loaded in the main thread. The AsyncTask class must be loaded in the main thread. Only its onPreExecute(), onProgressUpdate(), and onPostExecute() are executed on the thread calling it. For business purposes, AsyncTask is usually used in the main thread. It is not appropriate to say that AsyncTask must be used in the main thread.
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
Copy the code
6. Cancel method
AsyncTask cancel method call timing problem
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
Copy the code
Let’s look at the publishProgress method again
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
Copy the code
So, calling the cancel method, no matter how publishProgress is called, it calls the onCancel method, which is also the main thread or the called thread, right
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
Copy the code
7. Serial execution
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); }}}); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) ! = null) { THREAD_POOL_EXECUTOR.execute(mActive); }}}Copy the code
The first AsyncTask is executed, and then it will fetch data from the mTask, causing the mActive to be empty. Therefore, it can be seen that AsyncTask is processed in serial. API version 29. Both serial and parallel tasks are based on performance considerations. Too many parallel tasks may lead to thread overflow or other abnormal problems. Serial tasks are not efficient but have low memory pollution.
Third, to reflect on
The details of AsyncTask are not analyzed in detail. On the whole, we already know how she does it. If you are interested, you can go into details by yourself.
Thread switch with Handler, asynchronous execution through the thread pool, thread safety with API atomic class, these technologies we all know, but mixed up with, this idea may be unexpected, want to advance, experience its design mode, packaging method routine, performance considerations, usually still have to ask why.
Read the source code in three steps
- Understanding thoughts
- Abandon the code and feel the author’s original intention and purpose in designing the framework
- To grasp the design
- Abandon the details and experience the code’s interfaces and abstract classes as well as the macro design
- Understand the details
- The code is gradually expanded based on the top-level abstract design