美文网首页
(十一)Android的线程和线程池

(十一)Android的线程和线程池

作者: YongtaoHuang | 来源:发表于2019-08-28 15:10 被阅读0次

    11.1 主线程和子线程

    Android主线程(UI线程)主要处理和界面相关得事情。主要运行四大组件以及处理它们和用户得交互。
    Android子线程往往用于执行耗时操作,比如网络请求,I/O操作等。
    子线程如何与UI线程通讯:通过handler传Message:

        public static final int UPDATE_TEXT = 1;
    
        private TextView text;
    
        private Handler handler = new Handler() {
    
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case UPDATE_TEXT:
                        // 在这里可以进行UI操作
                        text.setText("Nice to meet you");
                        break;
                    default:
                        break;
                }
            }
    
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            text = (TextView) findViewById(R.id.text);
            Button changeText = (Button) findViewById(R.id.change_text);
            changeText.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.change_text:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            Message message = new Message();
                            message.what = UPDATE_TEXT;
                            handler.sendMessage(message); // 将Message对象发送出去
                        }
                    }).start();
                    break;
                default:
                    break;
            }
        }
    
    }
    

    11.2 Android中的线程形态

    除了传统得Thread以外:
    AsyncTask:底层采用线程池
    HandlerThread:底层采用线程
    IntentService:底层采用线程

    11.2.1 AsyncTask

    AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler。

    /*
    抽象类 AsyncTask
    Params: 表示参数的类型
    Progress: 表示后台任务的执行进度的类型
    Result: 表示后台任务的返回结果的类型
    */
    
    public abstract class AsyncTask<Params, Progress, Result> {
        // 主线程中执行,在异步任务执行之前,做一些准备工作
        protected void onPreExecute() {}
        // 在线程池中执行,执行异步任务
        protected abstract Result doInBackground(Params... params);
        // 主线程中执行,在异步任务执行时,随着进度发生变化
        protected void onProgressUpdate(Progress... values) {}
        // 主线程中执行,在异步任务执行之前,做一些准备工作
        protected void onPostExecute(Result result) {}
    
    }
    

    在Android 3.0之后,AsyncTask可以通过execute()方法串行执行,也可以通过executeOnExecutor()方法并行执行。
    我们举一个下载文件的例子:

    public class DownloadTask extends AsyncTask<String, Integer, Integer> {
    
        public static final int TYPE_SUCCESS = 0;
        public static final int TYPE_FAILED = 1;
        public static final int TYPE_PAUSED = 2;
        public static final int TYPE_CANCELED = 3;
    
        private DownloadListener listener;
    
        private boolean isCanceled = false;
    
        private boolean isPaused = false;
    
        private int lastProgress;
    
        public DownloadTask(DownloadListener listener) {
            this.listener = listener;
        }
    
        @Override
        protected Integer doInBackground(String... params) {
            InputStream is = null;
            RandomAccessFile savedFile = null;
            File file = null;
            try {
                long downloadedLength = 0; // 记录已下载的文件长度
                String downloadUrl = params[0];
                String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
                String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
                file = new File(directory + fileName);
                if (file.exists()) {
                    downloadedLength = file.length();
                }
                long contentLength = getContentLength(downloadUrl);
                if (contentLength == 0) {
                    return TYPE_FAILED;
                } else if (contentLength == downloadedLength) {
                    // 已下载字节和文件总字节相等,说明已经下载完成了
                    return TYPE_SUCCESS;
                }
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        // 断点下载,指定从哪个字节开始下载
                        .addHeader("RANGE", "bytes=" + downloadedLength + "-")
                        .url(downloadUrl)
                        .build();
                Response response = client.newCall(request).execute();
                if (response != null) {
                    is = response.body().byteStream();
                    savedFile = new RandomAccessFile(file, "rw");
                    savedFile.seek(downloadedLength); // 跳过已下载的字节
                    byte[] b = new byte[1024];
                    int total = 0;
                    int len;
                    while ((len = is.read(b)) != -1) {
                        if (isCanceled) {
                            return TYPE_CANCELED;
                        } else if(isPaused) {
                            return TYPE_PAUSED;
                        } else {
                            total += len;
                            savedFile.write(b, 0, len);
                            // 计算已下载的百分比
                            int progress = (int) ((total + downloadedLength) * 100 / contentLength);
                            publishProgress(progress);
                        }
                    }
                    response.body().close();
                    return TYPE_SUCCESS;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (is != null) {
                        is.close();
                    }
                    if (savedFile != null) {
                        savedFile.close();
                    }
                    if (isCanceled && file != null) {
                        file.delete();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return TYPE_FAILED;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values) {
            int progress = values[0];
            if (progress > lastProgress) {
                listener.onProgress(progress);
                lastProgress = progress;
            }
        }
    
        @Override
        protected void onPostExecute(Integer status) {
            switch (status) {
                case TYPE_SUCCESS:
                    listener.onSuccess();
                    break;
                case TYPE_FAILED:
                    listener.onFailed();
                    break;
                case TYPE_PAUSED:
                    listener.onPaused();
                    break;
                case TYPE_CANCELED:
                    listener.onCanceled();
                default:
                    break;
            }
        }
    
        public void pauseDownload() {
            isPaused = true;
        }
    
    
        public void cancelDownload() {
            isCanceled = true;
        }
    
        private long getContentLength(String downloadUrl) throws IOException {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url(downloadUrl)
                    .build();
            Response response = client.newCall(request).execute();
            if (response != null && response.isSuccessful()) {
                long contentLength = response.body().contentLength();
                response.close();
                return contentLength;
            }
            return 0;
        }
    
    }
    

    11.2.2 AsyncTask的工作原理

    AsyncTask中有两个线程池:THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR;一个Handler:InternalHandler。
    直接贴源代码:

    public abstract class AsyncTask<Params, Progress, Result> {
        //THREAD_POOL_EXECUTOR用于真正执行任务
        public static final Executor THREAD_POOL_EXECUTOR;
        //SERIAL_EXECUTOR 用于任务排队
        public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
        //InternalHandler 用于将执行环境从线程池换到主线程
        private static InternalHandler sHandler;
    
    
    }
    

    11.2.3 HandlerThread

    HandlerThread继承了Thread,它是一种可以使用Handler的Thread,且具有消息循环的效果。
    原理:内部HandlerThread.run()方法中有Looper,通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。

    11.2.4 IntentService

    为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个集开启线程和自动停止于一身的IntentService类。
    IntentService是一种特殊的Service,它继承了Service,是一个抽象类。可用于执行后台耗时任务。它是一个服务,所以优先级比单纯的线程要高。使用方式和普通Service没有区别。

    11.3 Android中的线程池

    线程池技术:预先创建了若干数量的线程,并且不能由用户直接对线程进行控制,在此基础上复用这些线程完成任务的执行。
    好处
    1、消除了频繁创建和消亡线程的系统资源开销
    2、面对过量任务的提交能够平缓的劣化
    本质
    使用了一个线程安全的工作队列连接生产者和消费者。

    线程池.png

    11.3.1 ThreadPoolExecutor

    Android中的线程池概念来源于java中的Executor,Executor是一个接口,真正的线程池实现是ThreadPoolExecutor。来看ThreadPoolExecutor的构造函数源码:

        public ThreadPoolExecutor(int corePoolSize, // 核心线程数
                                  int maximumPoolSize, // 最大线程数
                                  long keepAliveTime, // 非核心线程闲置时的超时时长
                                  TimeUnit unit, //参考时间单位
                                  BlockingQueue<Runnable> workQueue, // 任务队列
                                  ThreadFactory threadFactory) // 线程共存
    

    我们来看一下AsyncTask的线程池配置:

    public abstract class AsyncTask<Params, Progress, Result> {
        private static final String LOG_TAG = "AsyncTask";
    
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        
        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        private static final int KEEP_ALIVE_SECONDS = 30;
    
        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);
    
        public static final Executor THREAD_POOL_EXECUTOR;
    
        static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory);
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
        }
    }
    

    ※ 核心线程数量为2到4;
    ※ 最大线程数为CPU核心数量的2倍+1
    ※ 闲置超时时间为30秒
    ※ 任务队列容量128

    11.3.2 线程池的分类

    Android线程池分4类:
    1、FixedThreadPool:只有核心线程,数量固定,任务队列没有大小限制。
    2、CachedThreadPool:只有非核心线程,任务队列相当于空,适合执行大量的耗时较少的任务。
    3、ScheduledThreadPool:核心线程数量固定,非核心线程数量没有限制
    4、SingleThreadPool:只有一个核心线程

    相关文章

      网友评论

          本文标题:(十一)Android的线程和线程池

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