Android多线程

Android多线程

Android中的多线程本质上也是Java的多线程,同时添加了一些不同的特性和使用的场景。其中,最主要的一个区别就是Android中主线程和子线程中的区分,Android中的主线程是UI线程,负责运行四大组件并与用户实现交互,需要保持较高的反应速度,所以主线程不允许进行耗时的操作(比如说网络请求和访问),否则容易出现ANR现象,子线程则负责处理 一些耗时的任务,而如果子线程中想要实现对UI的操作,则需要通过Android的handler消息机制。

为什么子线程中不允许对UI进行操作呢
因为Android的UI控件并不是线程安全,多线程的并发访问会带来UI控件的不可预期的状态,且考虑到加锁机制会带来性能上的问题,因此Android在设计初期就禁止子线程处理UI。UI操作时ViewRootImpl会对操作者所在的线程进行checkThread,如果非主线程,会抛出CalledFromWrongThreadException。

那么Android除了java原生的Thread/Runnable等线程形态,还有哪些包装过了的有特点的线程形式?

  • AsyncTask: AsyncTask 封装了线程池和Handler,主要是为了方便开发者不去写自己的后台线程和定义Handler,而方便更新UI界面。
  • IntentService: IntentService从名字来看,可以知道它是一个服务,其内部采用HandlerThread执行任务,执行完毕后自动退出。其特点是它是Service,比起其他线程来说具有更高的优先级,不容易被系统杀死,而能够保证任务的执行。
  • HandlerThread: HandlerThread是一个具有消息循环loop的线程,也就是一开始就准备好了loop的线程,在其内部可以直接使用Handler。

AsyncTask

很棒的参考:你真的了解AsyncTask

  • AsyncTask 是一个轻量级的一部任务类,在线程池中执行后台任务。然后将任务的进度和最终结果传递给主线程,并在主线程中更新UI。

  • AsyncTask是一个抽象的泛型类,使用时必须继承并实现它的子类,并且至少重写doInBackground(Params...)方法。

    // 三个泛型参数 不需要传递参数时,可以用void代替
    public abstract class AsyncTask<Params,Progress,Result>
  • AsyncTask的使用

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    //在线程池中执行 该方法必须返回计算结果给onPostExecute()
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            // 可以使用该方法返回任务的进度,该方法会调用onProgressUpdate()
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }
    // 被主线程调用执行 因此这里可以有UI操作
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
    // 在主线程中调用执行 任务执行结束后,会调用该方法,因此这里可以有UI操作
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
    }
    // 主线程调用execute方法,执行任务前,会调用[1] onPreExecute()完成一些准备工作
    // onPreExecute()是在主线程中执行
    new DownloadFilesTask().execute(url1, url2, url3);
    // 也可以调用cancel来取消任务的执行
  • AsyncTask的使用注意事项:
  1. AsyncTask的类必须在主线程中加载,这一点液晶在Android4.1以上版本上自动完成
  2. AsyncTask 对象必须在主线程中创建
  3. execute方法必须在主线程中调用
  4. 不要直接调用 onPreExecute(),onPostExecute(Result)doInBackground(Params...)onProgressUpdate(Progress...)
  5. 一个AsyncTask对象只能执行一次,否则会运行报错。
  • AsyncTask实现原理:
  1. AsyncTask中有两个线程池:SerialExecutor用于任务的排队,THREAD_POOL_EXECUTOR用于真正执行任务,定义了一个InternalHandler用于对UI进行操作,该handler是一个静态的Handler对象,所以想要对UI进行操作,这个handler对象必须在UI线程中创建,也就是为什么AsyncTask类为什么要在UI线程加载。
  2. ThreadPoolExecutor是真正建立线程池的类,它在AsyncTask类中创建线程池的参数如下:

    public abstract class AsyncTask<Params, Progress, Result> {
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU数
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;
    
    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
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    }
    // 核心线程数 = CPU数+1
    // 最大线程数 = CPU数*2 + 1
    // 非核心线程的超时时间为1秒
    // 任务队列的容量为128

Android 开发手册写明:AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by thejava.util.concurrentpackage such as Executor,ThreadPoolExecutorand FutureTask

HandlerThread

  • HandlerThread 是Thread的子类,它的特点是可以使用Handler的Thread。它的run方法体如下所示,外界可以通过handler来通知HandlerThread来处理某些事情,从这可以看出Handler的用处不仅仅局限于处理UI相关的问题。

    public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized(this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid =-1;   
    }

    IntentService

  • IntentService封装了HandlerThread和Handler,但是它继承了Service,所以导致它的优先级比单纯线程要高,所以IntentService适合执行一些高优先级的后台任务。

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue,
                      ThreadFactory threadFactory,
                      RejectedExecutionHandler handler) {
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • corePoolSize: 线程池的核心线程数,默认情况下, 核心线程会在线程池中一直存活, 即使处于闲置状态. 但如果将allowCoreThreadTimeOut设置为true的话, 那么核心线程也会有超时机制, 在keepAliveTime设置的时间过后, 核心线程也会被终止.
  • maximumPoolSize: 最大的线程数, 包括核心线程, 也包括非核心线程, 在线程数达到这个值后,新来的任务将会被阻塞.
  • keepAliveTime: 超时的时间, 闲置的非核心线程超过这个时长,讲会被销毁回收, 当allowCoreThreadTimeOut为true时,这个值也作用于核心线程.
  • unit:超时时间的时间单位.
  • workQueue:线程池的任务队列, 通过execute方法提交的runnable对象会存储在这个队列中.
  • threadFactory: 线程工厂, 为线程池提供创建新线程的功能.
  • handler: 任务无法执行时,回调handler的rejectedExecution方法来通知调用者.
  1. 如果线程池中线程的数目少于corePoolSize,就算线程池中有其他的没事做的核心线程,线程池还是会重新创建一个核心线程;直到核心线程数目到达corePoolSize(常驻线程就位)

  2. 如果线程池中线程的数目大于或者等于corePoolSize,但是工作队列workQueue没有满,那么新的任务会放在队列workQueue中,按照FIFO的原则依次等待执行;(当有核心线程处理完任务空闲出来后,会检查这个工作队列然后取出任务默默执行去)
  3. 如果线程池中线程数目大于等于corePoolSize,并且工作队列workQueue满了,但是总线程数目小于maximumPoolSize,那么直接创建一个线程处理被添加的任务。
  4. 如果工作队列满了,并且线程池中线程的数目到达了最大数目maximumPoolSize,那么就会用最后一个构造参数handler处理;**默认的处理方式是直接丢掉任务,然后抛出一个异常。

转自:https://www.cnblogs.com/zoe-mine/p/7954605.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值