Android 多线程

作者: 雪纳瑞的哈士奇 | 来源:发表于2018-09-21 16:16 被阅读18次

    App 启动的时候 Android 系统自动启动一个 Linux process 这个 process 包含一个 Thread 成为 UI Thread / Main Thread. 通常应用的所有组件都运行在这一个 process 中. 同时也可以通过修改四大组件在 AndroidManifest.xml 中的代码块<activity><service><provider><receiver>中的 android:process 属性指定其运行在不同的 process 中 当一个组件在启动的时候 如果该 process 已经存在了 那么该组件就直接通过这个 process 被启动起来 并且运行在这个 process 的 UI Thread中

    UI Thread 中运行着很多重要的逻辑: 系统事件处理(system Events) 用户输入事件处理(input Events) UI 绘制(Application) Service Alarm 我们编写的代码泽施穿插在这些逻辑的中间 比如对用户触摸时间的检测和响应 对用户输入的处理 自定义 View 的绘制 如果我们插入的代码比较耗时 比如网络请求 或者数据库读取 那么就会阻塞 UI 线程其他的逻辑执行 从而导致界面卡顿 如果卡顿超过五秒 系统就回报 ANR 错误 所以耗时操作需要另起线程

    获取到数据之后 更新 UI 的时候 最好只在 UI 线程里面更新 子线程里面更新的方法(之所以子线程不能更新界面,是因为Android在线程的方法里面采用checkThread进行判断是否是主线程,而这个方法是在ViewRootImpl中的,这个类是在onResume里面才生成的,因此,如果这个时候子线程在onCreate方法里面生成更新UI,而且没有做阻塞,就是耗时多的操作,还是可以更新UI的。)

    Android 提供了四种常用的操作多线程的方式:

    Handler + Thread

    AsyncTask

    ThreadPoolExecutor

    IntentService

    Handler + Thread

    Android 主线程包括一个消息队列 (MessageQueue) 此消息队列可以存入一系列的 message 或者 Runnable 对象 通过一个 handler 你可以往这个消息队列中发送 Message 或者 Runnable对象 并且处理这些对象 每次创建一个新的 Handle 对象 他会绑定于创建她的线程(UI 线程)以及该线程的消息队列 从这时起 这个 Handler 就会开始吧 Message 或者 Runnable 对象传递到消息队列中 并在出队列的时候执行他们 Handler 可以吧一个 Message 对象或者 Runnable 对象压入消息队列中 进而在 UI 线程中获取 Message 或者执行 Runnable 对象 Handler 把消息压入队列有两种方式 Post 和 SendMessage

    post 方式:

    post 允许把一个 Runnable 对象压入到队列消息中 它的方法有 post(Runnable) postAtTime(Runnable,long) postDelayed(Runnable,long)

    对于 Handler 的 post 方式来说 他会传递一个 Runnable 对象到消息队列中 在这个 Runnable 对象中 重写run()方法 一般在这个 run() 方法中写入需要在 UI 线程上的操作

    new Handler().post(new Runnable() {
    
    @Override
    
        public void run() {
    
             new ImageView(getContext()).setImageBitmap(new Bitmap());
    
        }
    
    });
    
    sendMessage:

    sendMessage 允许吧一个包含消息数据的 Message对象压入到消息队列中 她的方法有

    sendEmptyMessage(int) sendMessage(Message) sendMessageAtTime(Message,long) sendMessageDelayed(Message,long)

    Handler 如果使用 sendMessage 的方式吧消息压入到消息队列中 需要传递一个 Message 对象 而在 Handler 中 需要重写 HandleMessage()方法 用于获取工作线程传递过来的消息 此方法运行在 UI 线程上 Message 是一个 final 类 所以不可以被继承

    Message message = new Message();
            message.what = 1;
            handler.sendMessage(message);
        }
    
        private Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == 1){
                    //更新 UI 代码
                }
            }
        };
    

    这个时候或许有人会有疑问:更新 UI 不是只能在主线程中更新吗 那么 Handler 是怎么回事儿呢?

    更新UI只能在主线程中更新,子线程中操作是危险的. 这个时候,Handler就出现了来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。

    ——————————这一段来自 windroid之父 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u013252711/article/details/37820719?utm_source=copy

    优缺点:Handler 用法简单明了 可以将多个异步任务更新 UI 的代码放在一起 但是处理单个异步任务代码显得比较多

    使用范围:多个异步任务的更新 UI

    AsyncTask

    AsyncTask是Android 提供的轻量级异步类 可以直接继承AsyncTask 在类中实现异步操作 并提供接口返回当前异步执行的成都(可以通过接口实现 UI 进度更新) 最后反馈执行的结果给 UI 主线程

    AsyncTask通过阻塞 BlockingQuery(Runnable) 储存待执行的任务 利用静态线程池 THREAD_POOL_EXECUTOR 提供一定数量的线程 默认是128个 在 Android 3.0以前默认采取数据的是并行任务执行器 3.0以后改成了默认采用串行任务执行器 通过静态串行任务执行器SERIAL_EXECUTOR 控制任务串行执行 循环取出任务给THREAD_POOL_EXECUTOR 中的线程执行 执行完一个 再执行下一个

    class DownloadTask extends AsyncTask<Integer, Integer, String>{  
        // AsyncTask<Params, Progress, Result>
        //后面尖括号内分别是参数(例子里是线程休息时间),进度(publishProgress用到),返回值类型
        @Override
        protected void onPreExecute() {
            //第一个执行方法
            super.onPreExecute();
        }
        @Override
        protected String doInBackground(Integer... params) {
            //第二个执行方法,onPreExecute()执行完后执行
            for(int i=0;i<=100;i++){
                publishProgress(i);
                try {
                    Thread.sleep(params[0]);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "执行完毕";
        }
        @Override
        protected void onProgressUpdate(Integer... progress) {
            //这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数
            //但是这里取到的是一个数组,所以要用progesss[0]来取值
            //第n个参数就用progress[n]来取值
            tv.setText(progress[0]+"%");
            super.onProgressUpdate(progress);
        }
        @Override
        protected void onPostExecute(String result) {
            //doInBackground返回时触发,换句话说,就是doInBackground执行完后触发
            //这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"
            setTitle(result);
            super.onPostExecute(result);
        }
    }
    

    优缺点:处理单个异步任务简单 可以获取到异步任务的进度 可以通过 cancel 方法取消还没有执行完的AsyncTask 处理多个异步任务闲的代码较多

    适用范围:单个异步任务的处理

    ThreadPoolExecutor

    ThreadPoolExecutor提供了一组线程池 可以管理多个线程并执行 这样一方面减少了每个并行任务独自建立线程的开销 另一方面可以管理多个并发线程的公共资源 从而提高了多线程的小鹿 所以ThreadPoolExecutor比较适合一组任务的执行 Excutors 利用工厂模式对ThreadPoolExecutor进行了封装 使用起来更加方便

    Executors 提供了四种创建ExecutorService 的方法 她们的场景使用如下

    Executors.newFixedThreadPool()
    创建一个定长的线程池 提交一个任务就创建一个县城 直到到达线程池的最大长度 这是线程会保持长度不再变化

    Executors.newCachedThreadPool()
    创建一个可缓存的线程池 如果当前线程池的长度超过了处理时所需要的 他可以灵活的收回空闲的线程 当需要增加时 他可以灵活的添加心的线程 而不会对线程池的长度做任何的限制

    Executors.newScheduledThreadPool()
    创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer

    Executors.newSingleThreadExecutor()
    创建一个单线程化的executor,它只创建唯一的worker线程来执行任务

    使用范围:批量处理任务

    IntentService

    ntentService继承自Service,是一个经过包装的轻量级的Service,用来接收并处理通过Intent传递的异步请求。客户端通过调用startService(Intent)启动一个IntentService,利用一个work线程依次处理顺序过来的请求,处理完成后自动结束Service。 一个可以处理异步任务的简单Service

    此文章摘抄与 https://www.jianshu.com/p/2b634a7c49ec

    相关文章

      网友评论

        本文标题:Android 多线程

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