In Android, threads are divided into main threads and sub-threads. The main interface is used to interact with users and perform UI-related operations, while sub-threads are responsible for time-consuming operations. If you do time-consuming operations on the main thread, the program will not respond in a timely manner. Therefore, time-consuming operations must be carried out in child threads.
1. Main thread and child thread
The main thread is all the threads used by the process. In Android, the main thread is the thread that performs UI interaction with the user. Therefore, in the Development of Android, it is necessary to put time-consuming operations, network request access operations, database reading operations and other operations in the sub-thread, to avoid the main thread in a long-term occupation state to reduce user experience. System requirements for network access must be conducted in a child thread, otherwise you will be thrown NetworkOnMainThreadException anomalies.
2. Thread form
There are traditional threads, AsyncTask, HandlerThread and IntentService in Android.
2.1, AsyncTask
AsyncTask encapsulates Thread and Handler and must be called from the main Thread. It can execute tasks in child threads and then pass the results of execution to the main Thread and update the UI. But AsyncTask is not suitable for particularly time-consuming tasks.
2.1.1 Parameters:
AsyncTask is a generic class that provides three generic arguments, Params, Progress, and Result.
- Params indicates the type of the parameter
- Progress Indicates the Progress type of background tasks
- Result Indicates the type of Result returned by background tasks
AsyncTask declaration:
public abstract class Asynctask<Params,Progress,Result>
Copy the code
2.1.2 Methods:
AsyncTask provides some core methods:
- OnPreExecute () calls the preparation operation on the main thread for asynchronous tasks.
- The doInBackground (Params… Params) is automatically called when a child thread task is executed after onPreExecute(). Params represents the input parameter of an asynchronous task. The publishProgress method updates the completion of the task and returns the result to the onPostExecute() method when the call ends.
- OnProgressUpdate (Params… Params, which is used in the main thread to show the progress of the task, is called in the publishProgress() method.
- OnProgressExecute (Result Result) The return value of doInBackground in the main thread after the user obtains the return Result of the task.
- OnCancelled () is executed in the main thread. OnProgressExecute is executed instead of onProgressExecute when the asynchronous task is cancelled.
2.1.3 Call:
The procedure for using AsyncTask is as follows: 1. Create a subclass of AsyncTask to implement core method 2. Create instance object of subclass 3. Call execute to execute asynchronous thread tasks
2.1.4 Limitations of AsyncTask:
1. AsyncTask must be loaded in the main thread, that is, the first AsyncTask must be accessed in the main thread, and the object must be created in the main thread. 2. The onPreExecute(),onPostExecute(),doInBackground, and onProgressUpdate methods cannot be called directly from the program. 4. An AsyncTask method can only be used once
2.1.5 Working principle of AsyncTask:
- The execute method
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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
In the execute method, the system checks the status of the AsyncTask. If the status is RUNNING or FINISHED, the system throws an exception. If the status is idle, the system sets the status to RUNNING. The RUNNING state calls the onPreExecute method, and the parameter Params is passed to mWorker’s mParams. Exec. Execute is then called and mFuture is passed in, where exec is the sDefaultExecutor passed in.
- SDefaultExecutor sDefaultExecutor is a serial thread pool in which all asynctasks in a process are queued. In executeOnExecute, onPreExecute is executed first.
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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
MTasks represents the SerialExecutor task cache queue, and then adds a task to the task cache queue with an offer, calling the run method of r, which is the incoming mFuture. MFuture’s run method internally calls MWorker’s Call method, followed by a call to the doInBackground method to start performing background tasks. When the execution is complete, the scheduleNext method is called to execute the next task. In addition, mActive represents an AsyncTask object. If mActive is null, the scheduleNext method is also executed. In the scheduleNext method, if the cache queue is not empty, the task is fetched from the queue for execution. To sum up, SerialExecutor’s job is to add tasks to the cache queue, and THREAD_POOL_EXECUTOR executes the tasks.
- THREAD_POOL_EXECUTOR
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #"+ mCount.getAndIncrement()); }}; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);Copy the code
CorePoolSize + 1, maxumumPoolSize + 2, lasts 1s, and the task cache queue is LinkedBlockingQueue.
- PostResult method
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
The getHandler method is used to fetch the AsyncTask to fetch the internally contained sHandler, and the MESSAGE_POST_RESULT message is sent.
- sHandler
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked"."RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult<? > result = (AsyncTaskResult<? >) msg.obj; switch (msg.what) {case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break; }}}Copy the code
SHandler is a static Handler object. In order for the sHandler to perform a background switch to the main thread, the main thread’s Looper should be used to create the sHandler in the main thread. SHandler calls the Finish method after receiving MESSAGE_POST_RESULT. The source code is as follows:
- finish
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
}
else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
Copy the code
Call isCancelled() to check whether the task isCancelled. If cancelled, onCancelled(result) is called. Otherwise, onPostExecute(result) is called. If mStatus is set to FINISHED, the object execution is complete.
2.2, HandlerThread
2.2.1 Introduction and implementation
HandlerThread inherits Thread and can use Handler, which is simple to implement and saves system resource overhead. The implementation is as follows:
HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
mHandler = new Handler(thread.getLooper());
mHandler.post(new Runnable() {... });Copy the code
2.2.2 Source code and analysis
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public boolean quit() {
Looper looper = getLooper();
if(looper ! = null) { looper.quit();return true;
}
return false; }}Copy the code
It creates a queue of messages that need to be told by the Handler to execute a task. Since the HandlerThread’s run method is an infinite loop, the quit method can be used to terminate the thread’s execution.
2.3, IntentService
IntentService is a special Service that extends from Service. Since IntentService is an abstract class, you must subclass IntentService to use it. Also, IntentService is a service, so it has a higher priority in execution. IntentService encapsulates HandlerThread and Handler.
2.3.1, use,
1. Subclass IntentService, passing in the thread name and overriding the onHandlerIntent() method. Register in manifest.xml. 3. Start the service in the Activity
2.3.2 Source code analysis
- OnCreate calls the corresponding onCreate method when IntentThread is first started. Details are as follows:
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
Copy the code
The new thread is instantiated, the Looper of the worker thread is obtained, and a Handler object, mServiceHandler, is constructed. Messages sent through the mServiceHandler are executed in the HandlerThread.
- onStartCommand
The onStartCommand method is called each time IntentService is started, which handles the Intent for each background task.
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
Copy the code
As you can see, the onStart method is called in onStartCommand. In the onStart method, the ServiceHandler message is referenced, wrapped in MSG, and sent. This message is processed in the HandlerThread.
- ServiceHandler method
private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); }}Copy the code
IntentService onHandleIntent method is an abstract, you need to implement, the role is to distinguish the Intent and perform specific tasks, after calls to perform the task, stopSelf will stop service directly, when there are multiple tasks, StopSelf stops the service after the last task is completed.
Thread pools
In practice, thread pools tend to be more marked. Compared with threads, thread pools have lower performance overhead, avoid clogging due to a large number of threads grabbing resources, and are easier to manage.
The interface to thread pools in Java is ExecutorService, and the default implementation is ThreadPoolExecutor.
3.1, ThreadPoolExecutor
ThreadPoolExecutor is an implementation of a thread pool constructed as follows
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
Copy the code
- corePoolSize
The number of core threads, which will remain alive in the thread pool. If allowCoreThreadTimeOut is set to true, the core thread will also have timePout and will be killed after timeout.
- maximumPoolSize
Maximum number of threads.
- keepAliveTime
AllowCoreThreadTimeOut Specifies the timeout limit for non-core threads, after which non-core threads are recalled, or if allowCoreThreadTimeOut is set to true, core threads are recalled.
-
Unit Parameter unit of time
-
workQueue
A task queue in which Runnable objects submitted via the execute method of the thread pool are stored
- threadFactory
Thread factory, which creates threads for the thread pool
ThreadPoolExecutor must meet certain rules to perform threaded tasks
- Core threads are used first, and new threads are started when there are not enough core threads
- When the task book is larger than the number of core threads, it is inserted into the wait queue
- If the wait queue cannot be inserted and the number of threads has not reached the maximum, a new thread will be started
- If the wait queue cannot be inserted and a new thread cannot be created, the request is rejected
3.2 Classification of thread pools
Thread pools are divided into four different functions: FixedThreadPool, CachedThreadPool, ScheduledThreadPool, and SingkeThreadExecutor.
- FixedThreadPool
Created by the execute newFixedThreadPool method, a fixed size thread pool is created for each task submitted until the number of threads in the pool reaches the maximum. If a thread terminates abnormally, a new thread is created. Be able to respond to external requests more quickly. The concrete implementation is as follows:
public static ExecutorSrevice newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThread,nThreads,
0L,TimeUnit.MILLISECINDS,
new LinkedBlockingQueue<Runnable>());
}
Copy the code
- CachedThreadPool A thread pool that can be used to create threads as needed. For many short-term asynchronous tasks, this can improve performance. Execute () will reuse a constructed thread, and if no thread is available, a new thread will be created and added to the thread pool, removing threads that have not been used for more than 60 seconds. This is suitable for low-time tasks. The concrete implementation is as follows:
public static ExecutorService newCachedThreadPool(){
reutrn new ThreadPoolExecutor(0,Interger.MAX_VALUE,
60L,TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Copy the code
- ScheduledThreadPool Creates a thread pool of unlimited size that supports timed and periodic task execution. The concrete implementation is as follows:
public static ScheduledThreadPool newScheduledThreadPool(int corePoolSize){
reutrn new ScheduledThreadPool(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize,Integer.MAX_VALUE,0,MANOSECONDS,new DelayWorkQueue());
}
Copy the code
- SingkeThreadExecutor creates a single thread pool, i.e. there is only one thread in the pool, all tasks are executed sequentially, and if a thread in the pool terminates due to an exception, a new thread will replace it. This ensures that tasks are executed in the order they are submitted. The concrete implementation is as follows:
public static ExecutorService newScheduledThreadExecutor() {reutrn new FinalizeableDelegatedExecutorService (new ThreadPoolExecutor (1, 0 L, TimeUnit. MILLISECINDS, new LinkedBlockingQueue<Runnable>())); }Copy the code
Reference:
Exploring the Art of Android Development
www.cnblogs.com/absfree/p/5…
Blog.csdn.net/carson_ho/a…