一 进程
1.1 进程是针对造作系统而言的,打开运行一个应用程序就开始一个进程。对android系统而言,进程就是一个app应用。
1.2 但往往真实中开启一个程序可能并不止开启一个进程,可能还有处于后台的进程,或者其它服务进程。所以我们一般说一个程序至少有一个进程
1.3 进程间的数据是不共享的,如果需要跨进程访问数据,就要通过管道,文件, Socket套接字等技术实现跨进程通信
二 线程
2.1 线程是进程的一部分,是执行程序的最小单位,可以看作进程的一个分流
2.2 线程的作用往往是为了不影响其它线程的任务,一般用来处理独立的任务。
2.3 我们知道计算机处理代码是按照顺序执行的,如果全部在一个线程运行代码,就必须等待前面任务执行完,才能执行后面任务
2.4 这也是android不能在子线程更新UI的原因,UI更直观的展示在我们眼前,如果因为等待一个下载任务长时间更新不了UI,就会造成视觉上的卡顿,体验是非常不好的
2.5 所以android只有一个主线程(UI线程),是程序运行时候系统创建的,我们只能创建多个子线程去完成耗时任务
三 主线程
3.1 主线程是程序运行时系统创建的,我们来看下启动到创建主线程的过程
我们看到main入口函数会创建一个ActivityThread线程
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 ActivityThread线程里面有两个集合
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初始化
3.4 调用bindApplication()方法绑定application
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 总结
四 线程的创建
4.1 继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
}
}
//启动线程
new MyThread().start();
4.2 实现 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
//具体耗时逻辑
}
}
//启动线程
new Thread(new MyRunnable()).start();
4.3 使用Thread匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
五 AsyncTask异步任务
5.1 创建异步任务
/**
* 第一个参数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 执行异步任务
new MyAsyncTask().execute("1","2","3");
5.3 也可以线程池执行
//可以用内置线程池AsyncTask.SERIAL_EXECUTOR(单线程顺序执行),AsyncTask.THREAD_POOL_EXECUTOR(多线程并发执行)
//也可以用自定义线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"1","2","3");
六 HandlerThread
6.1 作用:
6.2 特点
6.3 场景
6.4 使用
创建Handle并绑定HandlerThread的循环器
//开启一个下载任务
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;
}
创建新线程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;
}
}
}
开启子线程
//开启下载线程
Handler dowmhandler = DownloadHandler();
dowmhandler.post(new DownloadRunnable(100));
七 线程池
7.1 线程池作用就是用来放线程的,可以有效的管理线程,重用线程,更合理的利用系统资源,从而减小内存的开销的系统资源的不稳定。
7.2 线程池的种类
可配置线程池
指定场景线程池
7.3 ThreadPoolExecutor可配置线程池使用
//构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
{
throw new RuntimeException("Stub!");
}
简单创建线程示例:
//创建线程池
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());
}
}
线程工厂的使用,我们可以在工厂里面自定义线程
//创建线程池工厂
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());
}
}
饱和策略的使用
创建线程池时,为防止资源被耗尽,任务队列都会选择创建有界任务队列,但种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时,这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池"超载"的情况。ThreadPoolExecutor自带的拒绝策略如下:
拒绝策略示例
//饱和策略的使用
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());
}
}
关闭线程池
//关闭线程池
executorService.shutdown();
7.4 FixedThreadPool固定线程池使用
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads , nThreads, 0L , TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()
);
}
创建固定线程池
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 CachedThreadPool缓存线程池的使用
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()
);
}
创建缓存线程池
//缓存线程池
ExecutorService cachedThreadPool=Executors.newCachedThreadPool();
cachedThreadPool.submit(new TaskRunnable());
7.6 SingleThreadExecutor单工作线程的线程池的使用
public static ExecutorService newSingleThreadExecutor{
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()
)
);
}
创建顺序线程池
//顺序线程池
ExecutorService singleThreadExecutor=Executors.newSingleThreadExecutor();
singleThreadExecutor.submit(new TaskRunnable());
7.7 ScheduledThreadPool定时周期任务线程池
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());
}
创建周期性线程池
//周期定时线程池
ExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(5);
scheduledThreadPool.submit(new TaskRunnable());
7.8 执行线程的方式execute和submit区别
fixedThreadPool.submit(new TaskRunnable());
fixedThreadPool.execute(new TaskRunnable());
八 Rxjava的对线程使用
8.1 RxJava 是一种响应式编程,来创建基于事件的异步操作库。常用于流时间的处理和配合Retrofit进行异步请求和线程切换
8.2 依赖库的集成
//RxAndroid中包含RxJava的内容,只引入RxAndroid还是会报错
dependencies {
compile 'io.reactivex.rxjava2:rxjava:2.1.3'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
}
8.3 流时间处理的简单示例
观察者和被观察者通过subscribe订阅,订阅完成后被观察者就可以像观察者发送数据
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 异步处理,线程切换示例
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());
}
});
可以看到线程切换主要是下面这两行
.subscribeOn(Schedulers.io())//切换到子线程
.observeOn(AndroidSchedulers.mainThread())//切换到UI线程
再配合map,flatMap等这些操作符可以实现复杂的数据变化,并且能保持流程逻辑的清晰。
九 Kotlin携程对线程的处理
9.1 kotlin的携程本质上是依赖线程的,但跟线程是有区别的,是一个线程调度的框架。
线程会调用系统资源来处理任务,但携程是在编辑语言层面实现的,即不需要多核CPU的支持就能实现并发。
携程的主要任务就是协助线程,是线程的助手,分担线程的任务。
9.2 携程远程库的依赖
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
9.3 创建携程runBlocking方法
private fun main() {
// 不指定调度器,在方法调用的线程执行
runBlocking {
// 这里是协程的作用域
Log.d("liduo", "123")
}
}
private fun main() {
// 指定调度器,在IO线程中执行
runBlocking(Dispatchers.IO) {
// 这里是协程的作用域
Log.d("liduo", "123")
}
}
9.4 launch方法
private fun main() {
// 作用域为GlobalScope
// 懒启动,主线程执行
val job = GlobalScope.launch(
context = Dispatchers.Main,
start = CoroutineStart.LAZY) {
Log.d("liduo", "123")
}
// 启动协程
job.start()
}
9.5 async方法
//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 线程切换withContext方法
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 携程间通信Channel
Channel用于协程间的通信。Channel本质上是一个并发安全的队列,类似BlockingQueue。在使用时,通过调用同一个Channel对象的send和receive方法实现通信
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 多个接收者BroadcastChannel
当遇到一个发送者对应多个接收者的场景时,可以使用BroadcastChannel。创建BroadcastChannel对象时,必须指定容量大小。接收者通过调用BroadcastChannel对象的openSubscription方法,获取ReceiveChannel对象来接收消息
// 创建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 多路复用select,类似Java中Nio的select方法
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 携程异步流,类似于RxJava的响应式编程
// 在主线程上调用
GlobalScope.launch(Dispatchers.Main) {
// 创建流
flow<Int> {
// 挂起,输出返回值
emit(1)
// 设置流执行的线程,并消费流
}.flowOn(Dispatchers.IO).collect {
Log.d("liduo", "$it")
}
}.join()
9.11 携程的调度器
注意:
9.12 协程的启动模式
十 总结
10.1 java环境可以根据场景选择不同的线程方式
10.2 kotlin 可以直接用携程了