美文网首页
Android线程之异步处理技术/消息机制的应用/Thread的

Android线程之异步处理技术/消息机制的应用/Thread的

作者: Amy_LuLu__ | 来源:发表于2018-02-12 10:26 被阅读0次

    注意:本篇文章是本人阅读相关文章所写下的总结,方便以后查阅,所有内容非原创,侵权删。

    本篇文章内容来自于:
    Android开发艺术探索
    Android第一行代码
    Android高级进阶
    Android中Handler的使用
    Android异步处理技术

    目录

    1. 异步处理技术有哪些?
    2. Thread(基础类)
      --2.1 创建线程(2种方法)
      --2.2 线程分类(主线程+Binder线程+后台线程)
    3. HandlerThread
    4. AsyncQueryHandler(待补)
    5. IntentService(待补)
    6. Executor Framework 线程池
    7. AsyncTask

    1.异步处理技术有哪些?

    异步处理技术继承图

    2. Thread(基础类)

    线程是Java语言的一种概念,是实际执行任务的基本单元。
    Thread是Android中异步处理技术的基础。

    2.1 创建线程(2种方法)

    方法一:继承Thread类并重写run方法

    public class Mythread extends Thread {
        @Override
        public void run() {
            //实现具体的逻辑,如文件读写,网络请求等
        }
        public void startThread(){
            Mythread mythread = new Mythread();
            mythread.start();//使用start启动线程
        }
    }
    

    方法二:实现Runnable接口并实现run方法

    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            //实现具体的逻辑,如文件读写,网络请求等
        }
        public void startThread(){
            MyRunnable runnable = new MyRunnable();
            Thread thread = new Thread(runnable);
            thread.start();//同样利用start启动线程
        }
    }
    

    2.2 线程分类

    Android应用中各种类型的线程本质上都基于Linux系统的pthreads。
    在应用层可以分为三种类型的线程:

    1.主线程/UI线程
    主线程随着应用启动而启动。
    主线程用来运行Android组件,同时刷新屏幕上的UI元素。
    非主线程更新UI组件,会抛出CallFromWrongThreadException异常。

    为什么只有主线程才能操作UI?
    因为Android的UI工具不是线程安全的,只能让他在同一个线程中操作UI来保证线程安全。

    为什么主线程会出现阻塞?
    主线程中创建的Handler会顺序执行接收到的消息,包括从其他线程发送的消息。
    因此如果消息队列中前面的消息没有很快执行完,那么它可能会阻塞队列中的其他消息的及时处理。

    2.Binder线程
    Binder线程用于不同进程之间线程的通信。
    每个进程都维护了一个线程池,用来处理其他进程中线程发送的消息。

    其他进程有哪些?
    其他进程包括系统服务、Intents、ContentProviders和Service等

    大部分情况下,应用不需要关心Binder进程,因为系统会优先将请求转换为使用主线程。

    一个典型的需要使用Binder进程场景是:
    应用提供一个给其他进程通过AIDL接口绑定的Service。

    3.后台线程
    在应用中显式创建的线程都是后台线程。

    2.3 Thread和Handler、Looper配合使用

    (1) 主线程Thread中可直接使用Handler

    public class MainActivity extends BaseActivity {
        //在执行new Handler()的时候,默认情况下Handler会绑定当前代码执行的线程
        //handler在主线程中创建,所以自动绑定主线程
        private Handler handler = new Handler();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            MyThread myThread = new MyThread();
            myThread.start();
        }
    
        class MyThread extends Thread {
            @Override
            public void run() {
    
                //...
    
                //向另外一个线程发送消息
                //运行Runnable代码的线程与Handler所绑定的线程是一致的
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        //进行操作
                    }
                };
                handler.post(runnable);
            }
        }
    }
    

    (2) 子线程使用Handler必须先创建Looper

            new Thread("thread1") {
                @Override
                public void run() {
                    Looper.prepare();
                    Handler handler = new Handler();
                    Looper.loop();
                }
            }.start();
    

    3. HandlerThread

    HandlerThread是一个集成了Looper和MessageQueue的线程。
    当启动HandlerThread时,会同时生成Looper和MessageQueue,然后等待消息进行处理。
    使用HandlerThread的好处是开发者不需要自己去创建和维护Looper。

    HandlerThread中只有一个消息队列,队列中的消息是顺序执行的,因此是线程安全的。队列中的人物可能会被前面没有执行完的任务阻塞。

    3.1 使用HandlerThread

    用法和普通线程一样。

            HandlerThread handlerThread = new HandlerThread("handlerThread");
            handlerThread.start();
    
            Handler handler = new Handler(handlerThread.getLooper()){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //处理 在handlerThread中
                }
            };
    

    3.2 HandlerThread的进一步用法(开始接受消息前进行初始化)

    可以重写HandlerThread的onLooperPrepared函数。
    比如可以在这个函数中创建于HandlerThread关联的Handler实例,这同时也可以对外隐藏我们的Handler实例,提供公共方法来让外界来调用。

    public class MyHandlerThread extends HandlerThread {
    
        private Handler handler;
    
        public MyHandlerThread(String name) {
            super(name);
        }
    
        @Override
        protected void onLooperPrepared() {
            super.onLooperPrepared();
            handler = new Handler(getLooper()){
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case 1:
                            break;
                    }
                }
            };
        }
        
        public void publishedMethod1(){
            handler.sendEmptyMessage(1);
        }
        public void publishedMethod2(){
            handler.sendEmptyMessage(2);
        }
    }
    

    4. AsyncQueryHandler

    AsyncQueryHandler是用于在ContentProvider上面执行异步的CRUD操作的工具类。
    CRUD操作会被放在一个单独的子线程中执行,当操作结束获取到结果后,将通过消息的方式传递给调用AsyncQueryHandler的线程,通常是主线程。

    5. IntentService

    Service的每个生命周期函数都是运行在主线程的,因此它本身不是一个异步处理技术。
    为了能够在Service中实现在子线程中处理耗时任务。Android引入了一个Service的子类:IntentService。

    6. Executor Framework 线程池

    创建和销毁对象(例如线程),是存在开销的
    如果应用中频繁出现线程的创建和销毁,那么会影响到应用的性能。
    使用Java Executor框架可以通过线程池等机制解决这个问题

    Executor框架为开发者提供了如下能力:

    • 创建工作线程池,同时通过队列来控制能够在这些线程执行的任务的个数。
    • 检测导致线程意外终止的错误
    • 等待线程执行完成并获取执行结果
    • 批量执行线程,并通过固定的顺序获取执行结果。
    • 在合适的时机启动后台线程,从而保证线程执行结果可以很快反馈给用户

    Executor框架的基础是Executor接口
    Executor的主要目的是分离任务的创建和它的执行。

    public interface Executor {
        void execute(Runnable command);
    }
    

    开发者可以通过实现Executor接口并重写execute方法从而实现自己的Executor类。但实际应用中需要增加类似队列,任务优先级的功能,最终实现一个线程池。

    线程池是任务队列和工作线程的集合,这两者组合起来实现生产者消费者模式。

    Executor框架为开发者提供了预定义的线程池实现

    • 固定大小的线程池:通过Executors.newFixedThreadPool(n)创建,其中n是线程池中线程的个数
    • 可变大小的线程池:通过Executors.newCachedThreadPool()创建。当有新的任务需要执行时,线程池会创建新的线程来处理它,空闲的线程会等待60s来执行新任务,当没有任务可执行时自动销毁,因此可变大小线程池会根据任务队列的大小而变化。
    • 单个线程的线程池:通过Executors.newSingleThreadExecutor()创建,这个线程池中永远只有一个线程来串行执行任务队列中的任务。

    预定义的线程池都是基于ThreadPoolExecutor类之上构建的,
    而通过ThreadPoolExecutor可以自定义线程的一些行为。

    ThreadPoolExecutor自定义线程池

     ThreadPoolExecutor executor = new ThreadPoolExecutor(
                                  int corePoolSize, //核心线程数,核心线程会一直存在于线程池中,即使当前没有任务需要处理;当线程数小于核心线程数时,即使当前有空闲的线程,线程池也会优先创建新的线程来处理任务。
                                  int maximumPoolSize,//最大线程数,当线程数大于核心线程数,且任务队列已经满了,这时线程池就会创建新的线程,知道线程数量达到最大线程数为止。
                                  long keepAliveTime,//线程的空闲存活时间,当线程的空闲时间超过这个时间,线程会被销毁,直到线程数等于核心线程数。
                                  TimeUnit unit,//keepAliveTime的单位,可选的有TimeUnit类中的单位
                                  BlockingQueue<Runnable> workQueue);//线程池所使用的任务缓冲队列。
    

    7. AsyncTask

    AsyncTask是在Executor框架基础上进行的封装,它将耗时任务移动到工作线程中执行,同时提供方便的接口实现工作线程和主线程的通信。

    import android.os.AsyncTask;
    
    public class FullTask extends AsyncTask<Params,Progress,Result>{
    //Params 执行AsyncTask时需要传入的参数,可用于在后台任务中使用
    //Progress 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里的泛型当进度单位。
    //Result 当任务执行完成后,需要对结果进行返回,则这里泛型作为返回值单位。
    
        @Override
        //会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。比如显示一个进度条对话框
        protected void onPreExecute() {
            super.onPreExecute();
        }
        
        @Override
        //在子线程运行,用于处理耗时操作
        //任务一旦执行完,就通过return语句返回任务的执行结果
        //不可UI操作,如果反馈当前任务的执行速度,调用publishProgress(Progress ...)方法
        protected Result doInBackground(Params... params) {
            return null;
        }
    
        @Override
         //当后台任务中调用publishProgress,onProgressUpdate会很快被调用
         //可进行UI操作
        protected void onProgressUpdate(Progress... values) {
            super.onProgressUpdate(values);
        }
    
        @Override
        //当后台任务执行完毕并通过return语句返回时,调用该方法。
        //可UI操作,可用于提醒任务执行的结果,以及关闭掉进度条对话框
        protected void onPostExecute(Result result) {
            super.onPostExecute(result);
        }
    
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }
    

    使用

    new AsyncTask().execute();
    //不同系统版本 AsyncTask的execute和executeOnExecutor方法的运行有差别(串行和并行的区别)。不同的版本不同。
    

    一个应用中使用的所有AsyncTask实例会共享全局的属性,即所有AsyncTask实例会共享一个线程池。
    如果AsyncTask中的任务是串行执行的,那么应用中所有的AsyncTask会进行排队,只有等前面的任务执行完成后才会执行下一个。
    如果AsyncTask是异步执行的话,那么在四核CPU系统上,最多也只有五个任务可以同时进行,其他任务需要在队列中排队,等待空闲的线程。

    AsyncTask内部实现
    AsyncTask内部封装了Thread和Handler,通过AsyncTask可以更加方便地执行后台任务以及主线程中访问UI。
    但是AsyncTask并不适合特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池

    相关文章

      网友评论

          本文标题:Android线程之异步处理技术/消息机制的应用/Thread的

          本文链接:https://www.haomeiwen.com/subject/ihoctftx.html