Android thread detailed explanation

a process

1.1 The process is for the operating system. Opening and running an application starts a process. For the Android system, a process is an app application.

1.2 But often starting a program in reality may not only start one process, but may also have background processes or other service processes. So we generally say that a program has at least one process

1.3 The data between processes is not shared. If you need to access data across processes, you need to implement cross-process communication through technologies such as pipes, files, and sockets.

two threads

2.1 A thread is a part of a process and the smallest unit of executing a program, which can be regarded as a shunt of a process

2.2 The role of threads is often to deal with independent tasks in order not to affect the tasks of other threads.

2.3 We know that the computer processing code is executed in order. If all the codes are run in one thread, you must wait for the previous tasks to be completed before you can execute the subsequent tasks.

2.4 This is also the reason why android cannot update the UI in the sub-thread. The UI is more intuitively displayed in front of our eyes. If the UI cannot be updated for a long time because of waiting for a download task, it will cause visual lag and the experience is very bad.

2.5 So android has only one main thread (UI thread), which is created by the system when the program is running. We can only create multiple sub-threads to complete time-consuming tasks

three main threads

3.1 The main thread is created by the system when the program is running. Let's take a look at the process from starting to creating the main thread

We see that the main entry function will create an ActivityThread thread

public static void main(String[] args) {
    //....
 
    //创建Looper和MessageQueue对象,用于处理主线程的消息
    Looper.prepareMainLooper();
 
    //创建ActivityThread对象
    ActivityThread thread = new ActivityThread(); 
 
    //建立Binder通道(创建新线程)
    thread.attach(false);
 
    Looper.loop(); //消息循环运行
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

3.2 There are two collections in the ActivityThread thread

Activity information is all stored in the member variable mActivities of ActivityThread

mServices saves all service information 

public final class ActivityThread {
    //... 
    final H mH = new H();
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
 
    final ApplicationThread mAppThread = new ApplicationThread();  
    private class ApplicationThread extends ApplicationThreadNative {    
       //...  
    }
    private class H extends Handler {
            //...
    }
    //...
 
}

3.3 ActivityThread initialization

  • Start the message loop. Call Looper.prepareLoop() Looper.loop() to open the message loop of the main thread so that the ApplicationThread can call the life cycle method in the ActivityThread.
  • Notify the ActivityManagerService. Call the ActivityThread.attach() method, the attach() method calls attachApplication() and hands the ApplicationThread Binder to the ActivityManagerService, which means that the ActivityManagerService can control our application through the ApplicationThread and establish a communication channel from the server to the client.
  • Add GC Watcher. In the attach() method, GcWatcher, which monitors the memory usage of dialvik, is added. When the memory usage exceeds 3/4 of the total capacity, the Log is printed for recording, and the releaseSomeActivities() of ActivityManagerService is called to release the memory to prevent Out of memory causes the app to crash.

3.4 Call the bindApplication() method to bind the application 

After the attach() method calls attachApplication(), after a series of operations, it finally calls the bindApplication() method of ApplicationThread. In bindApplication, through the message mechanism, sendMessage is sent to ActivityThread, and handleMessage calls handleBindApplication() of ActivityThread. The Application object is created through reflection, and then onCreate creates the activity

 private void handleBindApplication(AppBindData data) {
       //创建appContext 
      final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
      try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            //通过反射创建Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
 
           try {
                //调用Application的onCreate方法
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
            }
        }
}
 
 public void callApplicationOnCreate(Application app) {
     app.onCreate();
 }

3.5 Summary

ActivityThread communicates between processes through ApplicationThread and AMS. After receiving the request from ActivityThread, AMS will call back the Binder method in ApplicationThread, and then ApplicationThread will send a message to H. After H receives the message, it will switch the logic in ApplicationThread to ActivityThread for execution. That is, switch to the main thread to execute

Creation of four threads

4.1 Inheriting the Thread class

class MyThread extends Thread {
    @Override
    public void run() {
      
     }
}
//启动线程
new MyThread().start();

4.2 Implement the Runnable interface

class MyRunnable  implements Runnable {
	@Override
	public void run() {
		//具体耗时逻辑
	}
}

//启动线程
new Thread(new MyRunnable()).start();

4.3 Using Thread anonymous inner class

new Thread(new Runnable() {
	@Override
	public void run() {

	}
}).start();

Five AsyncTask asynchronous tasks

5.1 Create an asynchronous task

/**
 * 第一个参数String类型,可以传单个类型也可以传类型数组,doInBackground里面获取的参数
 * 第二个参数Float类型,任务进度 onProgressUpdate获取
 * 第三个参数String类型,处理结果 onPostExecute 获取
 */
public class MyAsyncTask extends AsyncTask<String,Float,String>{
	@Override
	protected void onPreExecute() {
		super.onPreExecute();
		//准备执行,可以做一些准备工作,比如弹缓冲框,初始化数据等
	}

	@Override
	protected String doInBackground(String... strings) {
		//String参数数组,传几个就接受几个
		//处理耗时任务,子线程
		//返回处理后的结果
		String param1=strings[1];
		String param2=strings[1];
		String param3=strings[1];
		return null;
	}

	@Override
	protected void onProgressUpdate(Float... values) {
		super.onProgressUpdate(values);
		//任务处理进度
	}

	@Override
	protected void onPostExecute(String s) {
		super.onPostExecute(s);
		//执行结果返回,UI线程
	}


	@Override
	protected void onCancelled() {
		super.onCancelled();
		//处理异步任务
	}
}

5.2 Executing asynchronous tasks

 new MyAsyncTask().execute("1","2","3");

5.3 It can also be executed in thread pool

//可以用内置线程池AsyncTask.SERIAL_EXECUTOR(单线程顺序执行),AsyncTask.THREAD_POOL_EXECUTOR(多线程并发执行)
//也可以用自定义线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"1","2","3");

六 HandlerThread

6.1 Function:

Frequent creation of threads has a large memory overhead, so HandlerThread can use looper polling in sub-threads to avoid creating threads multiple times

6.2 Features

  • HandlerThread is a thread class that inherits Thread
  • It has its own Looper object inside, which can perform loop polling
  • The Handler created using the looper of HandlerThread can perform time-consuming operations in handleMessage because it is executed in the sub-thread
  • The advantage is that it will not block the main thread, but the disadvantage is that it cannot execute multiple tasks at the same time, it needs to be executed in an orderly manner, and the execution efficiency is low

6.3 Scenarios

HandlerThread is actually a thread; HandlerThread is more suitable for single-threaded + asynchronous queue scenarios, such as IO read and write operations on databases, files, etc. It does not take much time and does not cause large blockages. For network IO operations, HandlerThread is not suitable, because it has only one thread, and it has to wait in line one by one 

6.4 Use

Create Handle and bind HandlerThread's circulator

//开启一个下载任务
private Handler DownloadHandler(){
	if(handler == null){
		// 步骤1:创建HandlerThread实例对象
		// 传入参数 = 线程名字,作用 = 标记该线程
		handlerThread = new HandlerThread("download");
		// 步骤2:启动线程
		handlerThread.start();
		// 步骤3:创建工作线程Handler & 复写handleMessage()
		// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
		// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
		handler = new Handler(handlerThread.getLooper());
	}
	return handler;
}

 Create new thread Runnable

//开启下载进度线程
private class DownloadRunnable implements Runnable{
	private int start = 0;
	private int progres;

	public DownloadRunnable(int progres) {
		this.progres = progres;
	}

	@Override
	public void run() {
		while(start <= progres){
			Log.i("下载进度", ": "+start);
			start += 10;
		}
	}
}

Open child thread

//开启下载线程
Handler dowmhandler = DownloadHandler();
dowmhandler.post(new DownloadRunnable(100));

Seven thread pool

7.1 The role of the thread pool is to store threads. It can effectively manage threads, reuse threads, and make more reasonable use of system resources, thereby reducing the instability of system resources due to memory overhead.

7.2 Types of thread pools

Configurable thread pool

The parameters in ThreadPoolExecutor are configured according to the scene

Specify scene thread pool

FixedThreadPool fixed thread pool

CachedThreadPool cache thread pool

SingleThreadExecutor executes the thread pool sequentially

ScheduledThreadPool timed cycle task thread pool

7.3 ThreadPoolExecutor can configure thread pool usage

//构造函数
public ThreadPoolExecutor(int corePoolSize, 
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          TimeUnit unit, 
                          BlockingQueue<Runnable> workQueue, 
                          ThreadFactory threadFactory, 
                          RejectedExecutionHandler handler) 
{

    throw new RuntimeException("Stub!");

}

corePoolSize: The number of core threads. By default, the thread pool is empty, and threads are only created when tasks are submitted. If the number of currently running threads is less than corePoolSize, new threads will be created to process tasks; if it is equal to or equal to corePoolSize, no more threads will be created. If the prestartAllcoreThread method of the thread pool is called, the thread pool will create and start all core threads in advance to wait for tasks.
maximumPoolSize: The maximum number of threads allowed to be created by the thread pool. If the task queue is full and the number of threads is less than maximumPoolSize, the thread pool will still create new threads to process tasks.
keepAliveTime: Timeout event for non-core thread idle. If this event is exceeded, it will be recycled. If there are many tasks and the execution time of each task is very short, you can increase the keepAliveTime to increase the thread utilization. In addition, if the allowCoreThreadTimeOut property is set to true, keepAliveTime will also be applied to the core thread.
TimeUnit: The time unit of the keepAliveTime parameter. The optional units are days Days, hours HOURS, minutes MINUTES, seconds SECONDS, milliseconds MILLISECONDS, etc.
workQueue: task queue. Add tasks to this task queue if the current thread count is greater than corePoolSzie. The task queue is of the BlockingQueue type, that is, the blocking queue (the blocking queue will be mentioned in another blog post).
ThreadFactory: thread factory. You can use a thread factory to set a name for each created thread. Generally, there is no need to set this parameter.
RejectedExecutionHandler: Saturation policy, which is the coping strategy adopted when the current task queue and thread pool are full. The default is AbordPolicy, which means that new tasks cannot be processed and a RejectedExecutionException is thrown.

Simple thread creation example:

//创建线程池
public void createExecutorService(){
	//maximumPoolSize设置为2 ,拒绝策略为AbortPolic策略,直接抛出异常
	//线程池能执行的最大任务数为3(最大线程数)+0(队列长度)   SynchronousQueue没有容量
	ExecutorService executorService = new ThreadPoolExecutor(2, 3, 1000, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
	//添加并执行线程
	executorService.execute(new TaskRunnable());
}

//创建任务线程
class TaskRunnable implements Runnable{
	@Override
	public void run() {
		Log.e("taskRunnable", "run: "+Thread.currentThread().getName()+"---"+ new Date());
	}
}

The use of the thread factory, we can customize the thread in the factory

//创建线程池工厂
public void createExecutorService(){
	//maximumPoolSize设置为2 ,拒绝策略为AbortPolic策略,直接抛出异常
	//线程池能执行的最大任务数为3(最大线程数)+0(队列长度)   SynchronousQueue没有容量
	ExecutorService executorService = new ThreadPoolExecutor(2, 3, 1000, TimeUnit.MILLISECONDS, new SynchronousQueue<>(),
			//线程池工厂
			new ThreadFactory() {
				public Thread newThread(Runnable runnable) {
					//可以新建线程,进行命名、优先级等设置
					Log.e("taskRunnable", "newThread: "+"线程"+runnable.hashCode()+"创建" );
					//线程命名
					Thread thread = new Thread(runnable,"threadPool"+runnable.hashCode());
					return thread;
				}
			},
			new ThreadPoolExecutor.AbortPolicy());
	//添加并执行线程
	executorService.execute(new TaskRunnable());
}

//创建任务线程
class TaskRunnable implements Runnable{
	@Override
	public void run() {
		Log.e("taskRunnable", "run: "+Thread.currentThread().getName()+"---"+ new Date());
	}
}

Use of Saturation Strategies

When creating a thread pool, in order to prevent resources from being exhausted, the task queue will choose to create a bounded task queue, but in this mode, if the task queue is full and the number of threads created by the thread pool reaches the maximum number of threads you set, then You need to specify the RejectedExecutionHandler parameter of ThreadPoolExecutor, which is a reasonable rejection strategy, to handle the "overload" of the thread pool. The rejection strategy that comes with ThreadPoolExecutor is as follows:

  • AbortPolicy strategy: This strategy will directly throw an exception and prevent the system from working normally. It is not recommended to use it because it will cause blocking and cause the thread to be stuck here;
  • CallerRunsPolicy policy: If the number of threads in the thread pool reaches the upper limit, this policy will put the tasks in the task queue to run in the caller thread;
  • DiscardOledestPolicy policy: This policy discards the oldest task in the task queue, that is, the task that is added first in the current task queue and is about to be executed, and tries to submit it again;
  • DiscardPolicy policy: This policy will silently discard tasks that cannot be processed without any processing. Of course, using this strategy, the loss of tasks needs to be allowed in business scenarios;

Deny policy example

//饱和策略的使用
public void createExecutorService() {
	//maximumPoolSize设置为2 ,拒绝策略为AbortPolic策略,直接抛出异常
	//线程池能执行的最大任务数为3(最大线程数)+0(队列长度)   SynchronousQueue没有容量
	ExecutorService executorService = new ThreadPoolExecutor(2, 3, 1000, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), Executors.defaultThreadFactory(),
			//拒绝策略
			new RejectedExecutionHandler() {
				@Override
				public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {
					Log.e("taskRunnable", runnable.toString()+"执行了拒绝策略");
				}
			}
	);
	//添加并执行线程
	executorService.execute(new TaskRunnable());
}


//创建任务线程
class TaskRunnable implements Runnable{
	@Override
	public void run() {
		Log.e("taskRunnable", "run: "+Thread.currentThread().getName()+"---"+ new Date());
	}
}

 Close the thread pool

//关闭线程池
executorService.shutdown();
  • You can close the thread pool by calling the shutdown method or shutdownNow method of the thread pool. Their principle is to traverse the worker threads in the thread pool, and then call the interrupt method of the thread one by one to interrupt the thread, so tasks that cannot respond to interruption may not be terminated.
  • When all tasks are closed, it means that the thread pool is closed successfully, and calling the isTerminated method will return true. Usually call the shutdown method to close the thread pool If the task does not have to be executed, you can call the shutdownNow method

 7.4 FixedThreadPool fixed thread pool usage

public static ExecutorService newFixedThreadPool(int nThreads){
	return new ThreadPoolExecutor(nThreads , nThreads, 0L , TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()
    );
}
  • FixedThreadPool has only core threads, and the number is fixed, and there are no non-core threads.
  • keepAliveTime is set to 0L, which means that redundant threads will be terminated immediately. KeepAliveTime is an invalid parameter because no extra threads will be spawned
  • When the execute method is executed, if the currently running thread does not reach the corePoolSize (core thread number), a core thread is created to process the task, and if the core thread number is reached, the task is added to the LinkedBlockingQueue
  • FixedThreadPool is a thread pool with a fixed number of core threads, and these core threads will not be recycled
  • When the thread exceeds corePoolSize, the task is stored in the task queue. When there are idle threads in the thread pool, fetch tasks from the task queue for execution

Create a fixed thread pool

ExecutorService fixedThreadPool=Executors.newFixedThreadPool(5);
//execute和submit区别
//1. execute只能提交Runnable类型的任务,没有返回值,而submit既能提交Runnable类型任务也能提交Callable类型任务,返回Future类型。
//2. execute方法提交的任务异常是直接抛出的,而submit方法是是捕获了异常的,当调用FutureTask的get方法时,才会抛出异常
fixedThreadPool.submit(new TaskRunnable());
fixedThreadPool.execute(new TaskRunnable());

7.5 Use of CachedThreadPool cached thread pool

public static ExecutorService newCachedThreadPool(){
	return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()
    );
}
  • The corePoolSize of the CachedThreadPool is 0, and the maximumPoolSize is set to Integer.MAX_VALUE, which means that the CachedThreadPool has no core threads, and non-core threads are unbounded
  • If KeepAliveTime is set to 60L, the maximum time for an idle thread to wait for a new task is 60s
  • The blocking queue SynchronousQueue is used here, which is a blocking queue that does not store elements. Each insertion operation must wait for the removal operation of another thread, and any removal operation waits for the insertion operation of another thread.
  • Because the maximumPoolSize is unbounded, if the submitted task is larger than the thread processing task speed in the thread pool, new threads will be created continuously, and each time a task is submitted, a thread will be processed immediately
  • Therefore, CachedThreadPool is more suitable for a large number of tasks that need to be processed immediately and take less time.

Create a cache thread pool

//缓存线程池
ExecutorService cachedThreadPool=Executors.newCachedThreadPool();
cachedThreadPool.submit(new TaskRunnable());

7.6 Use of SingleThreadExecutor thread pool with single worker thread

public static ExecutorService newSingleThreadExecutor{
	return new FinalizableDelegatedExecutorService(
               new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()
               )
     );
}
  • Both corePoolSize and maximumPoolSize are 1, which means that SingleThreadExecutor has only one core thread, and other parameters are the same as FixedThreadPool
  • When the execute method is executed, if the number of currently running threads does not reach the number of core threads, that is, there are no currently running threads, a new thread is created to process the task.
  • If there is currently a running thread, the task is added to the blocking queue LinkedBlockingQueue, therefore, SingleThreadExecutor can ensure that all tasks are executed in sequence in one thread

Create a sequential thread pool

//顺序线程池
ExecutorService singleThreadExecutor=Executors.newSingleThreadExecutor();
singleThreadExecutor.submit(new TaskRunnable());

 7.7 ScheduledThreadPool timed cycle task thread pool

public staic ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
	return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize){
	super(corePoolSize,Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS,MILLISECONDS,new DelayedWorkQueue());
}
  • When the scheduleAtFixedRate or scheduleWithFixDelay method of ScheduledThreadPoolExecutor is executed, a ScheduledFutureTask (task wrapper class) that implements the RunnableScheduledFuture interface will be added to DelayedWorkQueue
  • Checks if running threads have reached corePoolSize. If not, create a new thread and start it, but instead of executing the task immediately, go to the DelayedWorkQueue to take out the ScheduledFutureTask, and then execute the task
  • If the running thread reaches the corePoolSize, the task is added to the DelayedWorkQueue. DelayedWorkQueue will sort the tasks, and the tasks to be executed first will be placed in front of the queue
  • This is different from the several thread pools introduced above. After the task is executed, the time variable of ScheduledFutureTask will be changed to the time to be executed next time and put back into DelayedWorkQueue

Create a periodic thread pool

//周期定时线程池
ExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(5);
scheduledThreadPool.submit(new TaskRunnable());

 7.8 The difference between execute and submit in the way of thread execution

fixedThreadPool.submit(new TaskRunnable());
fixedThreadPool.execute(new TaskRunnable());
  1. execute can only submit tasks of Runnable type and has no return value, while submit can submit both Runnable type tasks and Callable type tasks, and returns Future type.
  2. The task exception submitted by the execute method is directly thrown, and the submit method catches the exception. When the get method of FutureTask is called, the exception will be thrown

 Eight Rxjava's use of threads

8.1 RxJava is a reactive programming library for creating event-based asynchronous operations. Commonly used for stream time processing and with Retrofit for asynchronous requests and thread switching

8.2 Integration of dependent libraries

//RxAndroid中包含RxJava的内容,只引入RxAndroid还是会报错
dependencies {
    compile 'io.reactivex.rxjava2:rxjava:2.1.3'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
}

8.3 A Simple Example of Stream Time Processing

The observer and the observed subscribe through subscribe, and the observed can send data to the observer after the subscription is completed

 Observable.create(new ObservableOnSubscribe<Integer>() {

	@Override
	public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
		emitter.onNext(1);
		emitter.onNext(2);
		emitter.onNext(3);
		emitter.onComplete();
	}
}).subscribe(new Observer<Integer>() {

	@Override
	public void onSubscribe(Disposable d) {
		Log.d(TAG, "开始采用subscribe连接");
	}

	@Override
	public void onNext(Integer value) {
		Log.d(TAG, "对Next事件" + value + "作出响应");
	}

	@Override
	public void onError(Throwable e) {
		Log.d(TAG, "对Error事件作出响应");
	}

	@Override
	public void onComplete() {
		Log.d(TAG, "对Complete事件作出响应");
	}

});        

8.4 Asynchronous processing, thread switching example

Observable.create(new Observable.OnSubscribe<String>() {
				@Override
				public void call(Subscriber<? super String> subscriber) {
					Log.e(TAG, "===create: " + Thread.currentThread().getName());
					subscriber.onNext("1");
				}
			})
			.map(new Func1<String, Integer>() {
				@Override
				public Integer call(String s) {
					Log.e(TAG, "===String -> Integer: " + Thread.currentThread().getName());
					return Integer.valueOf(s);
				}
			})
			.flatMap(new Func1<Integer, Observable<String>>() {
				@Override
				public Observable<String> call(final Integer integer) {
					Log.e(TAG, "===Integer->Observable: " + Thread.currentThread().getName());
					return forEach(integer);
				}
			})
			.map(new Func1<String, Long>() {
				@Override
				public Long call(String s) {
					Log.e(TAG, "===String->Long: " + Thread.currentThread().getName());
					return Long.parseLong(s);
				}
			})
			.subscribeOn(Schedulers.io())
			.observeOn(AndroidSchedulers.mainThread())
			.subscribe(new Subscriber<Long>() {
				@Override
				public void onCompleted() {
				}

				@Override
				public void onError(Throwable e) {
				}

				@Override
				public void onNext(Long aLong) {
					Log.e(TAG, "===onNext: " + Thread.currentThread().getName());
				}
			});

You can see that thread switching is mainly the following two lines

.subscribeOn(Schedulers.io())//切换到子线程
.observeOn(AndroidSchedulers.mainThread())//切换到UI线程

Combined with operators such as map and flatMap, complex data changes can be realized and the process logic can be kept clear.

Nine Kotlin Ctrip's processing of threads

 9.1 Kotlin's Ctrip essentially relies on threads, but it is different from threads. It is a thread scheduling framework.

Threads will call system resources to process tasks, but Ctrip is implemented at the editing language level, that is, it can achieve concurrency without the support of multi-core CPUs.

The main task of Ctrip is to assist the thread, the assistant of the thread, and share the tasks of the thread.

9.2 Dependency of Ctrip's remote library

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"

9.3 Create Ctrip runBlocking method

private fun main() {
    // 不指定调度器,在方法调用的线程执行
    runBlocking {
        // 这里是协程的作用域
        Log.d("liduo", "123")
    }
}

private fun main() {
    // 指定调度器,在IO线程中执行
    runBlocking(Dispatchers.IO) {
        // 这里是协程的作用域
        Log.d("liduo", "123")
    }
}

9.4 launch method

private fun main() {
    // 作用域为GlobalScope
    // 懒启动,主线程执行
    val job = GlobalScope.launch(
            context = Dispatchers.Main, 
            start = CoroutineStart.LAZY) {
        Log.d("liduo", "123")
    }
    // 启动协程
    job.start()
}

9.5 async methods

//suspend标记
private suspend fun test(): Int {
    //作用域为GlobalScope,返回值为Int类型,,泛型可省略,自动推断
    val deffer = GlobalScope.async<Int> {
        Log.d("aa", "123")
        // 延时1s
        delay(1000)
    }
    // 获取返回值
    return deffer.await()
}

9.6 thread switching withContext method

private suspend fun test() {
    // IO线程启动并执行,启动模式DEFAULT
    GlobalScope.launch(Dispatchers.IO) {
        Log.d("aa", "start")
        // 线程主切换并挂起,泛型可省略,自动推断
        val result = withContext<String>(Dispatchers.Main) {
            // 网络请求
            "json data"
        }
        // 切换回IO线程
        Log.d("aa", result)
    }
}

9.7 Communication Channel between Ctrip

Channel is used for communication between coroutines. Channel is essentially a concurrent safe queue, similar to BlockingQueue. When in use, communication is achieved by calling the send and receive methods of the same Channel object

suspend fun main() {
    // 创建
    val channel = Channel<Int>()

    val producer = GlobalScope.launch {
        var i = 0
        while (true){
            // 发送
            channel.send(i++)
            delay(1000)
            // channel不需要时要及时关闭
            if(i == 10)
                channel.close()
        }
    }

    // 写法1:常规
    val consumer = GlobalScope.launch {
        while(true){
            // 接收
            val element = channel.receive()
            Log.d("liduo", "$element")
        }
    }
    
    // 写法2:迭代器
    val consumer = GlobalScope.launch {
        val iterator = channel.iterator()
        while(iterator.hasNext()){
            // 接收
            val element = iterator.next()
            Log.d("liduo", "$element")
        }
    }
    
    // 写法3:增强for循环
    val consumer = GlobalScope.launch {
        for(element in channel){
            Log.d("liduo", "$element")
        }
    }
    
    // 上面的协程由于不是懒启动,因此创建完成直接就会start去执行
    // 也就是说,代码走到这里,上面的两个协程已经开始工作
    // join方法会挂起当前协程,而不是上面已经启动的两个协程
    // 在Android环境中,下面两行代码可以不用添加
    // producer.join()
    // consumer.join()
}

9.8 Multiple Receiver BroadcastChannel

BroadcastChannel can be used when a sender corresponds to multiple receivers. When creating a BroadcastChannel object, the capacity must be specified. The receiver obtains the ReceiveChannel object to receive the message by calling the openSubscription method of the BroadcastChannel object

// 创建BroadcastChannel,容量为5
val broadcastChannel = BroadcastChannel<Int>(5)

// 创建发送者协程
GlobalScope.launch {
    // 发送 1
    broadcastChannel.send(1)
    delay(100)
    // 发送 2
    broadcastChannel.send(2)
    // 关闭
    broadcastChannel.close()
}.join()

// 创建接收者1协程
GlobalScope.launch {
    // 获取ReceiveChannel
    val receiveChannel = broadcastChannel.openSubscription()
    // 接收
    for (element in receiveChannel) {
        Log.d("receiver_1: ", "$element")
    }
}.join()

// 创建接收者2协程
GlobalScope.launch {
    // 获取ReceiveChannel
    val receiveChannel = broadcastChannel.openSubscription()
    // 接收
    for (element in receiveChannel) {
        Log.d("receiver_2: ", "$element")
    }
}.join()

9.9 Multiplexing select, similar to Nio's select method in Java

private suspend fun test() {
    // 创建一个Channel列表
    val channelList = mutableListOf<Channel<Int>>()
    // 假设其中有5个Channel
    channelList.add(Channel())
    channelList.add(Channel())
    channelList.add(Channel())
    channelList.add(Channel())
    channelList.add(Channel())
    
    // 调用select方法,协程挂起
    val result = select<Int> {
        // 对5个Channel进行注册监听,等待接收
        channelList.forEach {
            it.onReceive
        }
    }
    // 当5个Channel中任意一个接收到消息时,select挂起恢复
    // 并将返回值赋给result
    Log.d("liduo", "$result")
}

9.10 Ctrip asynchronous flow, similar to RxJava's responsive programming

// 在主线程上调用
GlobalScope.launch(Dispatchers.Main) {
    // 创建流
    flow<Int> {
        // 挂起,输出返回值
        emit(1)
      // 设置流执行的线程,并消费流
    }.flowOn(Dispatchers.IO).collect {
            Log.d("liduo", "$it")
        }
}.join()

9.11 Ctrip's scheduler

  1. Dispatchers.Default: The default dispatcher. It uses the shared thread pool of the JVM, and the maximum concurrency of the scheduler is the number of CPU cores, which is 2 by default.
  2. Dispatchers.Unconfined: Unconfined dispatcher. The scheduler does not restrict code execution to specific threads. That is, the code behind the suspending function will not actively resume execution in the thread before the suspending function, but will be executed on the thread that executes the suspending function.
  3. Dispatchers.IO: IO scheduler. It offloads blocking IO tasks to a shared thread pool. This scheduler shares threads with Dispatchers.Default.
  4. Dispatchers.Main: The main thread scheduler. Generally used to operate and update the UI.

Notice:

The threads allocated by Dispatchers.Default scheduler and Dispatchers.IO scheduler are daemon threads 

9.12 The startup mode of the coroutine

  1. CoroutineStart.DEFAULT: The coroutine is executed immediately and can be canceled at any time.
  2. CoroutineStart.LAZY: Create a coroutine, but do not execute it, and manually trigger the execution when the user needs it.
  3. CoroutineStart.ATOMIC: The coroutine is executed immediately, but cannot be canceled before the coroutine is executed. Currently in experimental stage.
  4. CoroutineStart.UNDISPATCHED: Execute coroutines in the current thread immediately until the first suspension is encountered. Currently in experimental stage.

Ten summary

10.1 The java environment can choose different threading methods according to the scene

  • Simple asynchronous processing can directly new Thread
  • Those who need progress or download can use MyAsyncTask asynchronous task
  • If you need to switch threads frequently or cooperate with network requests, you can use rxjava

10.2 kotlin can directly use Ctrip

Because Ctrip includes the function of threads and the ability of rxjava event flow, it can handle any asynchronous operation scenario

 

Guess you like

Origin blog.csdn.net/qq_29848853/article/details/130408640
Recommended