关于Android线程和线程池的那些事

线程相关

目前常用的线程有:

  • Thread 最基础线程执行方式
  • AsyncTask 封装了线程池和Handler,为了方便开发者在子进程中更新UI
  • HandlerThread
  • IntentService 内部采用HandlerThread执行任务,本质是一个Service,但是表现的更像Thread。因为Thread在后台容易被杀死,所以常用来在后台运行。

AsyncTask

使用方法

    class MyAsyncTask extends AsyncTask<URL,Integer,Long>{
        /**
         * 执行异步任务
         * @param urls
         * @return
         */
        @Override
        protected Long doInBackground(URL... urls) {
            int count=100;
            long totalSize=0;
            for (int i=0;i<count;i++){
                totalSize+=10;
                publishProgress(i*100/count); //调用onProgressUpdate
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (isCancelled()){
                    break;
                }
            }
            return totalSize;
        }

        /**
         * 执行之前调用
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        /**
         * 完成时调用
         * @param aLong
         */
        @Override
        protected void onPostExecute(Long aLong) {
            super.onPostExecute(aLong);
            tv_display.setText("Download"+aLong+"bytes");
        }

        /**
         * 后台进度发生改变时调用
         * @param values
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            tv_display.setText("进度"+values[0]);
        }
    }
  new  MyAsyncTask().execute();

注意事项

  1. AsyncTask 必须在主线程创建
  2. 必须在主线程执行,也就是调用excute();
  3. 不要手动调用doInBackground,onPreExecute,onPostExecute,onProgressUpdate
  4. 每个AsyncTask对象仅能执行一次
  5. AsyncTask在android3.0 以上是串行执行,在android 3.0以下是并行执行。表现状态为用同一个AsyncTask类创建多个AsyncTask对象,并同时调用excute。android 3.0以上会在一个任务执行完成以后,执行另外一个任务。而早期版本会同时执行。
  6. 如果希望android3.0以上,AsyncTask也能并发执行,那么需要调用executeOnExecutor用来替代excute

HandlerThread

源码

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

可以看到其内部的run方法,直接新建了Looper。这样就能在非UI线程中,创建Handler了。
这个方法一般配合IntentService使用,

注意事项

  • Handler的run 方法是个死循环,在不使用的时候,需要手动调用quit,或者quitSafely来结束线程

IntentService

使用方法

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        this(null);
    }

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: "+intent.getStringExtra("action"));
        SystemClock.sleep(3000);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: MyIntentService");
    }
}
 Intent intent=new Intent(ThreadTestActivity.this,MyIntentService.class);
                for (int i=0;i<5;i++){

                    intent.putExtra("action","action"+i);
                    startService(intent);
                }

注意:虽然IntentService很像thread ,但是本质仍然是service,需要在androidManifest中进行注册。

源码

 @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

可以看出IntentService在创建的时候,就创建了HandlerThread,然后通过他的Looper创建了Handler。

而在IntentService每次被startServcie的时候,都会调用onStartCommand,然后startCommand又会去调用onstart方法,所以我们来看下onstart方法。

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

可以看出,在onstart方法中,intentService 将startSevice的intent和startId。放入了handler中。既让放入了handler中,那么我们就需要去看下handlerMessage方法

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

这个就很简单了,在handler中如果收到消息,则调用抽象方法onHandlerIntent 去处理,而onHandlerIntent ,就是我们需要实现的方法。也就实现了,在非UI线程执行某个操作。

而最后的stopSerlf,传入的是startId。这个方法会对比现在传入的startId和IntentrService收到的最后第一个startId是不是一样的。也就是当前处理的消息是不是最后一条消息,如果是最后一条消息,则停止该service。

注意: 由于IntentService内部是由handler实现的,所以也具有Handler的特点。也就是串行执行的,一个方法执行完成以后,才会执行下一个方法。

线程池相关

android中常用的线程池

  • ThreadPoolExecutor
  • FixedThreadPool
  • CachedThreadPool
  • ScheduledThreadPool
  • SingleThreadExecutor
  • 其中ThreadPoolExecutor可以算作是线程池接口Executor最基础的实现。后面几个都是对于ThreadPoolExecutor的封装

ThreadPoolExecutor

构造方法

  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

corePoolSize

核心线程的数量,默认一直存活。可以通过allowCoreThreadTimeOut和keepAliveTime配置是否闲置超时关闭和超时的时间

maximumPoolSize

线程池最大的线程数,超出将阻塞

keepAliveTime

配置非核心线程超时关闭的时间。通过allowCoreThreadTimeOut也可以配置核心线程超时关闭的时间

unit

超时的单位,枚举值。

workQueue

存储任务的队列

threadFactory

线程工厂,提供创建新进程的功能

FixedThreadPool

核心代码

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

从上面代码可以看出,是创建了一个具有以下特点的线程池

  • 核心线程一直存活,
  • 线程数量固定的线程池。
  • 只包含核心线程。
  • 队列没有大小限制。

该线程使用场景:适合数量较少耗时较长的任务

CachedThreadPool

核心代码

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

从上面代码可以看出,该方法创建了一个具有以下特点的线程池

  • 没有核心线程
  • 线程数量几乎没有限制的
  • 超时时间为60秒
  • 所有的任务都会立即执行

该线程使用场景:适合数量大耗时较少的任务

ScheduledThreadPool

核心代码

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

该线程池的特点

  • 核心线程固定
  • 非核心线程几乎无限大
  • 非核心线程超时的时候,会立马回收

该线程使用场景:执行定时和具有周期性的任务

SingleThreadExecutor

核心代码

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

该线程池的特点

  • 只有一个核心线程
  • 没有非核心线程
  • 可以确保所有任务在一个线程中都能按顺序执行

该线程使用场景:可以统一所有外界任务到一个线程中,任务之间不需要处理线程同步的问题。

猜你喜欢

转载自blog.csdn.net/cqn2bd2b/article/details/125398732