简析Android线程

        Android沿用了JAVA的线程模型,其中的线程也分为主线程和子线程,其中的主线程也叫UI线程,主线程的作用是运行四大组件以及处理他们和用户的交互,而子线程的作用是执行耗时任务,比如网络请求,I/O操作等。从Android3.0开始系统要求网络访问必须在子线程中进行,否则网络访问将会失败并抛出NetWorkThreadException这个异常,这样做的目的是为了避免主线程由于被耗时操作所阻塞从未出现ANR现象。

Android中的线程形态除了传统的Thread的Thread以外,还包含了AsyncTask、HandlerThread、以及IntentService,这三者的底层都是线程,但他们具有特殊的表现形式,同时在使用上也是各有优缺点。本篇博客我们就来简析一下AsyncTask、HandlerThread、以及IntentService这三种独特的线程表现形式。

(1)AsyncTask

先来看看AsyncTask的定义:

public abstract class AsyncTask<Params, Progress, Result> {

三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。


一个异步任务的执行一般包括以下几个步骤:
1.execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。
2.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。
3.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。
4.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。
5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。


下面是一个简单的AsyncTask的用法示例,创建MyTask类继承AsyncTask:


 

    private class MyTask extends AsyncTask<String, Integer, String> {

        //onPreExecute方法用于在执行后台任务前做一些UI操作

        @Override

        protected void onPreExecute() {

            Log.i(TAG, "onPreExecute() called");

            textView.setText("loading...");

        }

 

        //doInBackground方法内部执行后台任务,不可在此方法内修改UI

        @Override

        protected String doInBackground(String... params) {

            Log.i(TAG, "doInBackground(Params... params) called");

            try {

                HttpClient client = new DefaultHttpClient();

                HttpGet get = new HttpGet(params[0]);

                HttpResponse response = client.execute(get);

                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

                    HttpEntity entity = response.getEntity();

                    InputStream is = entity.getContent();

                    long total = entity.getContentLength();

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();

                    byte[] buf = new byte[1024];

                    int count = 0;

                    int length = -1;

                    while ((length = is.read(buf)) != -1) {

                        baos.write(buf, 0, length);

                        count += length;

                        //调用publishProgress公布进度,最后onProgressUpdate方法将被执行

                        publishProgress((int) ((count / (float) total) * 100));

                        //为了演示进度,休眠500毫秒

                        Thread.sleep(500);

                    }

                    return new String(baos.toByteArray(), "gb2312");

                }

            } catch (Exception e) {

                Log.e(TAG, e.getMessage());

            }

            return null;

        }

 

        //onProgressUpdate方法用于更新进度信息

        @Override

        protected void onProgressUpdate(Integer... progresses) {

            Log.i(TAG, "onProgressUpdate(Progress... progresses) called");

            progressBar.setProgress(progresses[0]);

            textView.setText("loading..." + progresses[0] + "%");

        }

 

        //onPostExecute方法用于在执行完后台任务后更新UI,显示结果

        @Override

        protected void onPostExecute(String result) {

            Log.i(TAG, "onPostExecute(Result result) called");

            textView.setText(result);

 

            execute.setEnabled(true);

            cancel.setEnabled(false);

        }

 

        //onCancelled方法用于在取消执行中的任务时更改UI

        @Override

        protected void onCancelled() {

            Log.i(TAG, "onCancelled() called");

            textView.setText("cancelled");

            progressBar.setProgress(0);

 

            execute.setEnabled(true);

            cancel.setEnabled(false);

        }

    }

}

启用MyTask代码:

mTask = new MyTask();

mTask.execute("http://www.baidu.com");

doInBackground(Params... params)是一个抽象方法,我们继承AsyncTask时必须覆写此方法;onPreExecute()、onProgressUpdate(Progress... values)、onPostExecute(Result result)、onCancelled()这几个方法体都是空的,我们需要的时候可以选择性的覆写它们;publishProgress(Progress... values)是final修饰的,不能覆写,只能去调用,我们一般会在doInBackground(Params...params)中调用此方法;

AsyncTask的工作原理:

们从execute(Params... params)作为入口,分析一下AsyncTask的执行流程,execute方法又会调用executeOnExecutor方法,他们的实现如下:

public final AsyncTask<Params, Progress, Result> execute(Params... params){
    return executeOnExecuto(sDefaultExecutor,params);
}

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {

    if (mStatus != Status.PENDING) {

        switch (mStatus) {

            case RUNNING:

                //如果该任务正在被执行则抛出异常

                //值得一提的是,在调用cancel取消任务后,状态仍未RUNNING

                throw new IllegalStateException("Cannot execute task:"

                        + " the task is already running.");

            case FINISHED:

                //如果该任务已经执行完成则抛出异常

                throw new IllegalStateException("Cannot execute task:"

                        + " the task has already been executed "

                        + "(a task can be executed only once)");

        }

    }

 

    //改变状态为RUNNING

    mStatus = Status.RUNNING;

 

    //调用onPreExecute方法

    onPreExecute();

 

    mWorker.mParams = params;

    sExecutor.execute(mFuture);

 

    return this;

}

在上面的代码中,SDefalultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个线程池中排队执行,在executeOnExecutor方法中,AsyncTask的onPreEcecute方法最先执行,然后线程开始执行。

sDefaultExecutor是如何实现的,如下所示:

//串行任务执行器,其作用就是将多个任务一个一个的执行(execute())
   public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
     //默认的任务执行器,默认为串行任务执行
   private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
   
   //串行执行器的实现,AsyncTask.execute(param...) 方法最终是走到这里
   //其实现就是将任务加入到队列中 一个个排队 去实现任务
   private static class SerialExecutor implements Executor {
       //线性双向队列 用来存储所有的AsyncTask任务
       final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
       //正在执行的任务
       Runnable mActive;

       public synchronized void execute(final Runnable r) {
           //串行执行多个任务核心代码
           //将新的AsyncTask任务加入到双向队列中
           mTasks.offer(new Runnable() {
               public void run() {
                   try {
                       //执行任务
                       r.run();
                   } finally {
                       //当AsyncTask任务执行完毕后,执行下一个任务
                       scheduleNext();
                   }
               }
           });
           //如果没有正在执行的任务 从任务队列中直接获取任务 执行
           if (mActive == null) {
               scheduleNext();
           }
       }

       protected synchronized void scheduleNext() {
           //取出队列头部任务 交给线程池执行
           if ((mActive = mTasks.poll()) != null) {
               //线程池执行该任务
               THREAD_POOL_EXECUTOR.execute(mActive);
           }
       }
   }

从上述代码中,分析AsyncTask执行的任务是串行执行的过程,首先AsyncTask将FutureTask交给 SerialExecutor 的execute方法去处理, SerialExecutor 的execute方法会将FutureTask插入到mTasks(双向队列)中,如果没有正在执行的任务 mActive==null 从任务队列中直接获取任务 执行 scheduleNext 知道所有的任务执行完为止。

如何实现并行任务呢?

通过 executeOnExecutor 可自定义AsyncTask的执行方式,串行or并行,execute是串行执行,或者自定义线程池 asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Params... params); 可实现并行执行任务

(2)HanderThread

我们的应用程序当中为了实现同时完成多个任务,所以我们会在应用程序当中创建多个线程。为了让多个线程之间能够方便的通信,我们会使用Handler实现线程间的通信。

下面我们看看如何在线程当中实例化Handler。在线程中实例化Handler我们需要保证线程当中包含Looper(注意UI-Thread默认包含Looper)。

为线程创建Looper的方法如下:在线程run()方法当中先调用Looper.prepare()初始化Looper,然后再run()方法最后调用Looper.loop(),这样我们就在该线程当中创建好Looper。(注意Looper.loop()方法默认是死循环)

我们实现Looper有没有更加简单的方法呢?当然有,这就是我们的HandlerThread

使用步骤:

1.创建一个HandlerThread,及创建了一个包含Handler的线程:

HandlerThread handlerThread = new HandlerThread("leochin.com");
handlerThread.start(); //创建HandlerThread后一定要记得start()

2.获取Handler的Looper

Looper looper = handlerThread.getLooper();

3.创建Handler通过Looper初始化

Handler handler = new Handler(looper);

通过以上三步我们就成功创建HandlerThread。通过handler发送消息,就会在子线程中执行。

如果想让HandlerThread退出,则需要调用handlerThread.quit();

HandlerThread的内部实现也十分简单,源码如下:


package android.os;


public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

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

从HandlerThread的实现来看,它和普通的Thread有显著的不同。普通Thread主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。HandlerThread是一个很有用的类,它的一个具体使用场景是IntentService。

(3)IntentService

我们先来看看IntentService的特点:

  • 它本质是一种特殊的Service,继承自Service并且本身就是一个抽象类
  • 它可以用于在后台执行耗时的异步任务,当任务完成后会自动停止
  • 它拥有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务
  • 它内部通过HandlerThread和Handler实现异步操作
  • 创建IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作

IntentService的常规使用:

public  class MyIntentService extends IntentService {
    public static final String DOWNLOAD_URL="download_url";
    public static final String INDEX_FLAG="index_flag";
    public static UpdateUI updateUI;


    public static void setUpdateUI(UpdateUI updateUIInterface){
        updateUI=updateUIInterface;
    }

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

    /**
     * 实现异步任务的方法
     * @param intent Activity传递过来的Intent,数据封装在intent中
     */
    @Override
    protected void onHandleIntent(Intent intent) {

        //在子线程中进行网络请求
        Bitmap bitmap=downloadUrlBitmap(intent.getStringExtra(DOWNLOAD_URL));
        Message msg1 = new Message();
        msg1.what = intent.getIntExtra(INDEX_FLAG,0);
        msg1.obj =bitmap;
        //通知主线程去更新UI
        if(updateUI!=null){
            updateUI.updateUI(msg1);
        }
        //mUIHandler.sendMessageDelayed(msg1,1000);

        LogUtils.e("onHandleIntent");
    }
   

通过代码可以看出,我们继承了IntentService,这里有两个方法是必须实现的,一个是构造方法,必须传递一个线程名称的字符串,另外一个就是进行异步处理的方法onHandleIntent(Intent intent) 方法,其参数intent可以附带从activity传递过来的数据。这里我们的案例主要利用onHandleIntent实现异步下载图片,然后通过回调监听的方法把下载完的bitmap放在message中回调给Activity(当然也可以使用广播完成),最后通过Handler去更新UI。

IntentService原理分析:

首先看IntentService的onCreate方法:

@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时,它的onCreate方法将会被调用,其内部会去创建一个HandlerThread并启动它,接着创建一个ServiceHandler(继承Handler),传入HandlerThread的Looper对象,这样ServiceHandler就变成可以处理异步线程的执行类了(因为Looper对象与HandlerThread绑定,而HandlerThread又是一个异步线程,我们把HandlerThread持有的Looper对象传递给Handler后,ServiceHandler内部就持有异步线程的Looper,自然就可以执行异步任务了),那么IntentService是怎么启动异步任务的呢?其实IntentService启动后还会去调用onStartCommand方法,而onStartCommand方法又会去调用onStart方法,我们看看它们的源码:

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

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

从源码我们可以看出,在onStart方法中,IntentService通过mServiceHandler的sendMessage方法发送了一个消息,这个消息将会发送到HandlerThread中进行处理(因为HandlerThread持有Looper对象,所以其实是Looper从消息队列中取出消息进行处理,然后调用mServiceHandler的handleMessage方法),我们看看ServiceHandler的源码:

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

里其实也说明onHandleIntent确实是一个异步处理方法(ServiceHandler本身就是一个异步处理的handler类),在onHandleIntent方法执行结束后,IntentService会通过 stopSelf(int startId)方法来尝试停止服务。这里采用stopSelf(int startId)而不是stopSelf()来停止服务,是因为stopSelf()会立即停止服务,而stopSelf(int startId)会等待所有消息都处理完后才终止服务。最后看看onHandleIntent方法的声明:

protected abstract void onHandleIntent(Intent intent);

到此我们就知道了IntentService的onHandleIntent方法是一个抽象方法,所以我们在创建IntentService时必须实现该方法,通过上面一系列的分析可知,onHandleIntent方法也是一个异步方法。这里要注意的是如果后台任务只有一个的话,onHandleIntent执行完,服务就会销毁,但如果后台任务有多个的话,onHandleIntent执行完最后一个任务时,服务才销毁。最后我们要知道每次执行一个后台任务就必须启动一次IntentService,而IntentService内部则是通过消息的方式发送给HandlerThread的,然后由Handler中的Looper来处理消息,而Looper是按顺序从消息队列中取任务的,也就是说IntentService的后台任务时顺序执行的,当有多个后台任务同时存在时,这些后台任务会按外部调用的顺序排队执行,我们前面的使用案例也很好说明了这点。

好了,到这里我们就把AsyncTask、HandlerThread、IntentService三个线程的特殊表现形式分析完了!

 

参考文章:

https://www.cnblogs.com/ldq2016/p/8192230.html

https://blog.csdn.net/lmj623565791/article/details/47079737/

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值