Android 开发艺术探索笔记(十五) 之 Android 的

2018-04-17  本文已影响31人  innovatorCL

一、概述

Android 的线程分主线程和子线程,主线程主要处理界面相关的事情,而子线程往往用于执行耗时操作。Android 中扮演子线程角色的有:AsyncTaskIntentServiceHandlerThreadAsyncTask 封装了线程池和 Handler,主要用于在子线程中更新UI。 HandlerThread 是一种具有消息循环的线程,在它的内部可以使用 Handler。IntentService 是一个 Service,系统对其进行了封装使其可以方便地执行后台任务,执行完任务后自动调用 onDestroy() 退出。它是一个 Service,因而在执行后台任务的时候不容易被系统杀死。

二、AsyncTask

AsyncTask 是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后在主线程中回调执行的进度以及最终结果,从而更新 UI。其实,AsyncTask 内部封装了 Thread 和 Handler。

ASyncTask 是一个抽象类。

public abstract class AsyncTask<Params,Progress,Result>

主要有 4 个核心的方法:

举个栗子:

class DownloadTask extends AsyncTask<URL,Integer,Long>{

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Long doInBackground(URL... urls) {
            return null;
        }

        @Override
        protected void onPostExecute(Long aLong) {
            super.onPostExecute(aLong);
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }
    
    
    new DownloadTask().execute(url1,url2,url3);

划重点来啦 !!!

三、HandlerThread

HandlerThread 继承 Thread,它封装有 Looper 和 MessageQueue,可以绑定 Handler,从而实现线程间通信/异步消息处理机制。

举个栗子

/**
 * 使用 HandlerThread
 */
public class HandlerThreadActivity extends AppCompatActivity {

    private TextView mTvServiceInfo;

    private HandlerThread mCheckMsgThread;
    //HandlerThread 的 handler
    private Handler mCheckMsgHandler;
    private boolean isUpdateInfo;

    private static final int MSG_UPDATE_INFO = 0x110;

    //与UI线程管理的handler
    private Handler mHandler = new Handler();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);

        //创建后台线程
        initBackThread();

        mTvServiceInfo = findViewById(R.id.id_textview);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //开始查询
        isUpdateInfo = true;
        mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //停止查询
        isUpdateInfo = false;
        mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);

    }

    private void initBackThread() {
        mCheckMsgThread = new HandlerThread("check-message-coming");
        mCheckMsgThread.start();

        //绑定 HandlerThread 的 Looper
        mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {

                checkForUpdate();
                if (isUpdateInfo) {
                    //循环执行 HandlerThread Handler 的 handleMessage()
                    mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
                }
            }
        };


    }

    /**
     * 模拟从服务器解析数据
     */
    private void checkForUpdate() {
        try {
            //模拟耗时
            Thread.sleep(1000);
            //切换回主线程
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";
                    result = String.format(result, (int) (Math.random() * 3000 + 1000));
                    mTvServiceInfo.setText(Html.fromHtml(result));
                }
            });

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //释放资源
        mCheckMsgThread.quit();
    }

}

这个栗子里面通过 HandlerThread 线程的 Handler 来通知 UI 线程的 Handler 来更新 UI,同时在 handleMessage() 方法中循环执行 HandlerThread 的方法,这个也是一种定时循环执行任务的方法。

源码分析:

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    
    //内部有一个 Handler 成员变量
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }


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

    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

我们仔细看看 run(),这里面调用了 Looper.prepare()Loop.loop()Looper.prepare() 中创建了一个 Looper 对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在 Looper 对象的构造过程中,初始化了一个 MessageQueue ,作为该 Looper 对象成员变量。Looper.loop() 就不断地循环从 MessageQueue 中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。

如果我们不想自己初始化 HandlerThread 的 Handler 对象,可以直接使用 HandlerThread 里面的 Handler,使用的时候就只能用 post(Runnable r),因为 HandlerThread 的 Handler 没有派生子类,只能使用 Message.callback 来执行任务。

源码

四、IntentService

IntentService 是一种特殊的 Service,它继承了 Service,并且是一个抽象类,子类必须实现 onHandleIntent() 才可以使用。IntentService 可用于执行后台任务,我们可以通过 startService(Intent) 来提交请求,该Service会在需要的时候创建,当完成所有的任务以后自己关闭,且请求是在工作线程处理的。而且由于它是 Service,比单纯的后台线程优先级高,不容易被系统杀死。

优点:

举个栗子

使用 IntentService 上传照片

public class UploadImgService extends IntentService {

    private static final String ACTION_UPLOAD_IMG = "com.zhy.blogcodes.intentservice.action.UPLOAD_IMAGE";
    public static final String EXTRA_IMG_PATH = "com.zhy.blogcodes.intentservice.extra.IMG_PATH";

    /**
     * 外部调用开启任务
     * @param context
     * @param path
     */
    public static void startUploadImg(Context context, String path) {

        Intent intent = new Intent(context, UploadImgService.class);
        intent.setAction(ACTION_UPLOAD_IMG);
        intent.putExtra(EXTRA_IMG_PATH, path);
        context.startService(intent);
    }


    public UploadImgService() {
        super("UploadImgService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_UPLOAD_IMG.equals(action)) {
                final String path = intent.getStringExtra(EXTRA_IMG_PATH);
                handleUploadImg(path);
            }
        }
    }

    private void handleUploadImg(String path) {
        try {
            //模拟上传耗时
            Thread.sleep(3000);

            Intent intent = new Intent(MainActivity.UPLOAD_RESULT);
            intent.putExtra(EXTRA_IMG_PATH, path);
            //通过广播通知 UI 线程
            sendBroadcast(intent);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("TAG","onCreate");
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("TAG","onBind");
        return null;
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.e("TAG","onSatrtCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Log.e("TAG","onSatrt");
        super.onStart(intent, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("TAG","onDestroy");
    }

}

上面的代码主要就是继承IntentService,然后复写onHandleIntent方法,根据传入的intent来选择具体的操作。

运行 Log:
04-16 07:56:59.894 19936-19936/com.innovator.intentservicetest E/TAG: onCreate
onSatrtCommand
onSatrt
04-16 07:57:00.500 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt
04-16 07:57:00.774 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt
04-16 07:57:01.788 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt
04-16 07:57:01.965 19936-19936/com.innovator.intentservicetest E/TAG: onSatrtCommand
onSatrt

通过 startUploadImg(Context context, String path) 来创建多个任务,可以看到 onCreate()只调用了一次,onStartCommand()onStart() 会回调多次。

划重点啦 !!!

源码解析:

package android.app;

import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;


public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    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);
        }
    }


    public IntentService(String name) {
        super();
        mName = name;
    }


    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
                super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

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

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


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    protected abstract void onHandleIntent(Intent intent);
}

可以看到它在 onCreate() 里面初始化了一个 HandlerThread,同时实例化了一个绑定该 HandlerThread 的 Handler。当任务开始的时候,会回调 onStartCommand(),即回调 onStart(),在 onStart() 中可以看到这个 Handler 发送了一条消息给自己,于是就会调用自己的处理方法,最后调用 onHandleIntent((Intent)msg.obj) 方法,所以我们在使用 IntentService 的时候需要重写 onHandleIntent((Intent)msg.obj)

而且,由于 HandlerThread 是串行执行任务的,所以 IntentService 也是串行执行任务的,执行完成后会调用 stopSelf(msg.arg1); 判断是否销毁该 Service(stopSelf(msg.arg1);会等待所有消息都处理完毕才会终止服务)。所以当任务完成,销毁 Service 回调 onDestory()时,源码里面会释放了我们的 Looper: mServiceLooper.quit()

源码

五、线程池

使用线程池有几个好处:

5.1 ThreadPoolExecutor

ThreadPoolExecutor 是线程池的真正实现,它的构造方法为:

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

ThreadPoolExecutor 执行任务时的规则:

5.2 线程池分类

使用方法:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
fixedThreadPool.execute(Runnable r);

使用方法:

ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
cacheThreadPool.execute(Runnable r);

使用方法:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
      //延迟2秒后执行该任务
      scheduledThreadPool.schedule(new Runnable() {
          @Override
          public void run() {

          }
      }, 2, TimeUnit.SECONDS);
      
      //延迟1秒后,每隔2秒执行一次该任务
      scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
          @Override
          public void run() {

          }
      }, 1, 2, TimeUnit.SECONDS);

使用方法:

ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();

      //延迟1秒后,每隔2秒执行一次该任务
      singleThreadScheduledPool.scheduleAtFixedRate(new Runnable() {
          @Override
          public void run() {
              String threadName = Thread.currentThread().getName();
              Log.v("zxy", "线程:" + threadName + ",正在执行");
          }
      },1,2,TimeUnit.SECONDS);
      
   singleThreadScheduledPool.execute(Runnable r);   

六、参考资料

上一篇下一篇

猜你喜欢

热点阅读