Android多线程


本博客将讲述Android多线程中的消息处理机制,以及AsyncTask异步任务,将基于源码进行分析。

Android消息处理机制

每个Android应用在被启动时都会创建一个线程,这个线程称为主线程或UI线程,Android应用的所有操作默认运行在这个线程中。然而,为了保证UI的流畅性,通常将耗时的操作(如IO操作、网络请求)放在子线程中。为了防止主线程被一些耗时长的操作阻塞,Android提供的消息处理机制来解决这个问题的。
Android启动时,主线程中会关联一个消息队列,所有的操作都会被封装成消息然后交给主线程来处理。为了保证主线程不会主动退出,会将获取消息的操作放在一个死循环中,这样程序就相当于一直在执行死循环而不会退出。
在这里插入图片描述

如上图所示,我们看到一个叫做Handler的东西不断的从子线程中一个叫做Looper的容器中取出消息队列的东西,又不断的往队列里面post,到底这些是怎么实现的呢?

一个简单的例子

为了大家能够更好的理解消息处理的过程,这里先放一个简单的例子作为开头,我们在这个例子中让主线程一开始出现一个进度条,随后创建一个子线程,子线程中有延时操作,延时操作结束后,进度条消失。

/**
*点击按钮,出现进度条,在子线程线程中设置延时,
*然后发送消息给主线程Handler,主线程Handler将进度条取消
**/
public class MainActivity extends Activity
{
	ProgressDialog progressDialog;
	Handler handler = new Handler()//在主线程中创建Handler
	{
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == 0) {
				progressDialog.cancel();
			}
		}
	};
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Button startBtn = (Button) findViewById(R.id.button);
		progressDialog = new ProgressDialog(MainActivity.this);
		startBtn.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				progressDialog.show();
				new Thread(new Runnable() {
					@Override
					public void run() {
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        handler.sendEmptyMessage(0);//发送消息
					}
				}).start();
			}
		});
	}
}

上面的代码中,我们看到Handler用在了两个地方,一个是创建时覆写了handle Message,另一个是子线程中用sendEmptyMessage发送消息。

Handler

在子线程中执行完耗时操作后,很多情况下需要更新UI,但我们却不能在子线程中更新UI,此时最常用的手段就是用Handler将一个消息post到UI线程中,然后再在Handler的handleMessage方法中进行处理。注意更新UI的Handler必须要在主线程中创建。
其实Handler就是一个消息处理器,每个Handler关联一个消息队列,消息队列又被封装在Looper中,而每个Looper又会关联一个线程,最终则相当于每个消息队列关联一个线程。Handler将信息投递给消息队列,然后再由对应的线程从消息队列中逐个取出,并且执行。默认情况下,消息队列只有一个,即主线程的消息队列。而Handler投递消息的方法有两种,post和send。
post方法:

public final boolean post(Runnable r)
{
  return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
  // 1. 创建1个消息对象(Message)
  Message m = Message.obtain();//创建Message对象可用关键字new 或 Message.obtain()
  // 2. 将 Runable对象 赋值给消息对象(message)的callback属性
  m.callback = r;  
  // 3. 返回该消息对象
  return m;
} 
public final boolean sendMessageDelayed(Message msg, long delayMillis{
  if (delayMillis < 0) {
  delayMillis = 0;
  }
  return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  // 1. 获取对应的消息队列对象(MessageQueue)
  MessageQueue queue = mQueue;
  // 2. 调用了enqueueMessage方法 
  return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  // 1. 将msg.target赋值为this,即 :把 当前的Handler实例对象作为msg的target属性
  msg.target = this;    
 // 2. 调用消息队列的enqueueMessage(),即:Handler发送的消息,最终是保存到消息队列
  return queue.enqueueMessage(msg, uptimeMillis);
}

send方法:

public final boolean sendMessage(Message msg)
{
   return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
   if (delayMillis < 0) {
       delayMillis = 0;
   }
   return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

可以看到,post方法最终还是用send方法实现的。

Message

因为上文提到了message对象,所以这里稍微看一下Message类,在后面讲消息处理的时候会再谈到Message。

public final class Message implements Parcelable{
	Handler target;//target处理
	Runnable callback;//Runnable类型的callback
	Message next;//下一条消息,消息队列是链式存储的
	//代码省略
}

Looper

至于Handler是如何关联消息队列以及线程的呢,我们可以从下面的代码中获悉,Handler在内部通过Looper.myLooper()来获取Looper对象,并且与之关联,获取消息队列。

public Handler(Callback callback, boolean async) {
// 仅贴出关键代码
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called  Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback=null;
}

再看下面的代码,myLooper()方法返回的是sThreadLocal.get(),sThreadLocal是当前的线程,在prepareMainLooper()方法中prepare()将Looper对象放进了sThreadLocal中。

public static void prepare() {
        prepare(true);
}
 
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {//每个线程只能有一个looper
        throw new RuntimeException("Only one Looper may be created       per thread");
    }
    //设置本线程的looper。
    sThreadLocal.set(new Looper(quitAllowed));
}

所以最终,创建Handler的同时则自动在当前线程sThreadLocal中设置Looper对象,并获取该对象,进而获取消息队列。这样一来,Handler自然和消息队列以及线程关联起来了。

消息处理

Looper通过loop()方法建立消息循环:

public static void loop() {
  	 //里面调用了sThreadLocal.get()获得刚才创建的Looper对象
	final Looper me = myLooper();
	if (me == null) {//如果Looper为空则会抛出异常
          throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this 				 thread.");
       }
	final MessageQueue queue = me.mQueue;
       for (;;) {
        //这是一个死循环,从消息队列不断的取消息
	       Message msg = queue.next(); // might block
	       if (msg == null) {
	        //由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler 		sendMessage enqueueMessage   后队列里才有消息
	      	       return;    
	       }
	       msg.target.dispatchMessage(msg);
	}
}

在上面的loop方法中,我们看到通过dispatchMessage方法来处理消息,根据适才看过的Message代码,target是Handler类型,所以说,Handler将消息投递到消息队列后,消息队列有将消息分发给Handler来处理。我们来看看消息处理函数的具体实现:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //callback在message的构造方法中初始化或者使用handler.post(Runnable)时候才不为空
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,该属性为null,此段不执行
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//最终执行handleMessage方法
    }
}
private static void handleCallback(Message message) {
 	message.callback.run();
}

如果message的Runnable类型变量callback不为空,即对应上述Handler投递方式中的post方法,则调用handleCallBack()进而调用callback的run()。如果callback为空,则检查mCallBack是否为null,不为空就调用mCallBack的handleMessage()方法。

Android中的AysncTask

为了更方便地在子线程中进行更新 UI 操作,Android 基于异步处理消息机制封装了一个工具类 AsyncTask。AsyncTask可以很容易且正确地使用UI线程,AsyncTask允许进行后台操作,并在不显示使用工作线程或Handler机制的情况下,将结果反馈给UI线程。但是AsyncTask只能用于短时间的操作(最多几秒就应该结束的操作),如果需要长时间运行在后台,就不适合使用AsyncTask了,只能去使用Java提供的其他API来实现。

AysncTask的使用

由于AsyncTask是一个抽象类,所以我们必须通过继承使用AsyncTask,下面我们给出一个例子,下载的异步任务,点击“开始下载”,开始下载,下载后显示下载文件名称以及字节数。

public class MainActivity extends Activity implements Button.OnClickListener {

    TextView textView = null;
    Button btnDownload = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView)findViewById(R.id.textView);
        btnDownload = (Button)findViewById(R.id.btnDownload);
        Log.i("iSpring", "MainActivity -> onCreate, Thread name: " + Thread.currentThread().getName());
    }

    @Override
    public void onClick(View v) {
        //要下载的文件地址
        String[] urls = {
                "https://blog.csdn.net/fjnu_se/article/details/80965142",
                "https://blog.csdn.net/fjnu_se/article/details/80965139",
                "https://blog.csdn.net/fjnu_se/article/details/80965134",
                "https://blog.csdn.net/fjnu_se/article/details/80965133",
                "https://blog.csdn.net/fjnu_se/article/details/80933809"
        };

        DownloadTask downloadTask = new DownloadTask();
        downloadTask.execute(urls);//执行一个异步任务
    }

    /**
     * public abstract class AsyncTask<Params, Progress, Result>
     * 三种泛型类型分别表示:参数类型、后台任务执行的进度类型、返回的结果类型
     * 在此例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型
     */

    private class DownloadTask extends AsyncTask<String, Object, Long> {

        /**
         * 在execute(Params...params)被调用后立即执行,执行在UI线程,
         * 一般用来在执行后台任务对UI做一些标记。
         */
        @Override
        protected void onPreExecute() {
            Log.i("iSpring", "DownloadTask -> onPreExecute, Thread name: " + Thread.currentThread().getName());
            super.onPreExecute();
            btnDownload.setEnabled(false);
            textView.setText("开始下载...");
        }

        /**
         * 在onPreExcecute()完成后立即执行,用于执行较为耗时的操作,此方法将接受输入参数返回计算结果。
         * 在执行过程中可以调用publishProgress(Progress...values)来更新进度条信息。
         * @param params
         * @return
         */
        @Override
        protected Long doInBackground(String... params) {
            Log.i("iSpring", "DownloadTask -> doInBackground, Thread name: " + Thread.currentThread().getName());
            //totalByte表示所有下载的文件的总字节数
            long totalByte = 0;
            //params是一个String数组
            for(String url: params){
                //遍历Url数组,依次下载对应的文件
                Object[] result = downloadSingleFile(url);
                int byteCount = (int)result[0];
                totalByte += byteCount;
                //在下载完一个文件之后,我们就把阶段性的处理结果发布出去
                publishProgress(result);
                //如果AsyncTask被调用了cancel()方法,那么任务取消,跳出for循环
                if(isCancelled()){
                    break;
                }
            }
            //将总共下载的字节数作为结果返回
            return totalByte;
        }

        /**
         * 下载文件后返回一个Object数组:下载文件的字节数以及下载的博客的名字
         */
        private Object[] downloadSingleFile(String str){
            Object[] result = new Object[2];
            int byteCount = 0;
            String blogName = "";
            HttpURLConnection conn = null;
            try{
                URL url = new URL(str);
                conn = (HttpURLConnection)url.openConnection();
                InputStream is = conn.getInputStream();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buf = new byte[1024];
                int length = -1;
                while ((length = is.read(buf)) != -1) {
                    baos.write(buf, 0, length);
                    byteCount += length;
                }
                String respone = new String(baos.toByteArray(), "utf-8");
                int startIndex = respone.indexOf("<title>");
                if(startIndex > 0){
                    startIndex += 7;
                    int endIndex = respone.indexOf("</title>");
                    if(endIndex > startIndex){
                        //解析出博客中的标题
                        blogName = respone.substring(startIndex, endIndex);
                    }
                }
            }catch(MalformedURLException e){
                e.printStackTrace();
            }catch(IOException e){
                e.printStackTrace();
            }finally {
                if(conn != null){
                    conn.disconnect();
                }
            }
            result[0] = byteCount;
            result[1] = blogName;
            return result;
        }

        /**
         * 执行在UI线程。在调用publishProgress(Progress...progress)时,此方法被执行,
         * 直接将进度信息更新到UI组件上。
         * @param values
         */
        @Override
        protected void onProgressUpdate(Object... values) {
            Log.i("iSpring", "DownloadTask -> onProgressUpdate, Thread name: " + Thread.currentThread().getName());
            super.onProgressUpdate(values);
            int byteCount = (int)values[0];
            String blogName = (String)values[1];
            String text = textView.getText().toString();
            text += "\n博客《" + blogName + "》下载完成,共" + byteCount + "字节";
            textView.setText(text);
        }

        /**
         * 执行在UI线程。当后台操作结束时,此方法将会被调用,
         * doInBackground()函数返回的计算结果将作为参数传递到此方法中,直接将结果显示到UI组件中。
         * @param aLong
         */
        @Override
        protected void onPostExecute(Long aLong) {
            Log.i("iSpring", "DownloadTask -> onPostExecute, Thread name: " + Thread.currentThread().getName());
            super.onPostExecute(aLong);
            String text = textView.getText().toString();
            text += "\n全部下载完成,总共下载了" + aLong + "个字节";
            textView.setText(text);
            btnDownload.setEnabled(true);
        }

        @Override
        protected void onCancelled() {
            Log.i("iSpring", "DownloadTask -> onCancelled, Thread name: " + Thread.currentThread().getName());
            super.onCancelled();
            textView.setText("取消下载");
            btnDownload.setEnabled(true);
        }
    }
}

在上面的每个方法中的注解可得知,当我们使用AsyncTask时,需要在代码中调用execute()方法,创建异步任务类继承AsyncTask后,doInBackground是一个抽象方法,必须覆写,而onPreExecute、onProgressUpdate、onPostExecute、onCancelled这几个方法是空方法,需要的时候也可以覆写它们,至于publishProgress是final修饰的方法,只能调用,一般在doInBackground中调用来更新进度条,这些方法顺序为:onPreExecute–>doInBackground–>publishProgress–>onProgressUpdate–>onPostExecute
使用同时,我们需要注意几点:

  • 异步任务的实例必须在UI线程中创建。
  • execute方法必须在UI线程中调用。
  • 不能在doInBackground中更改UI组件。
  • 一个任务实例只能执行一次。

AysncTask的底层原理

先看AsyncTask的源码解析:
当我们执行异步任务时,将在主线程调用execute方法,该方法源码如下:

//该方法运行在主线程
 @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        //将任务放到线程池中执行,线程池中线程都是子线程
        return executeOnExecutor(sDefaultExecutor, params);
    }

可见,调用execute后,进而调用了executeOnExecutor方法,传进去的参数有两个:sDefaultExecutor以及params,params是参数值,sDefaultExecutor是ThreadPoolExecutor的实例,用于管理提交AsyncTask的任务,源码如下:

// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
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>(10);

public static final Executor THREAD_POOL_EXECUTOR
		= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
				TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

//线性执行的执行器
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//内部交互的handler
private static final InternalHandler sHandler = new InternalHandler();
//默认的Executor(顺序执行)
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
 *顺序执行的Executor
 */
private static class SerialExecutor implements Executor {
	final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
	Runnable mActive;

	public synchronized void execute(final Runnable r) {
		mTasks.offer(new Runnable() {
			public void run() {
				try {
					r.run();
				} finally {
					scheduleNext();
				}
			}
		});
		if (mActive == null) {
			scheduleNext();
		}
	}
	protected synchronized void scheduleNext() {
		if ((mActive = mTasks.poll()) != null) {
            //将任务交给THREAD_POOL_EXECUTOR执行
			THREAD_POOL_EXECUTOR.execute(mActive);
		}
	}
}

可见,sDefaultExecutor中有一个execute方法,该方法负责将异步任务分发给THREAD_POOL_EXECUTOR线程池执行。

我们继续接着往下看,进入ExecuteOnExecutor之后的源码:

//该函数体运行在主线程
 @MainThread
/**
 *@param exec执行任务的线程池
 *@param params参数
 *@return 该AsyncTask实例
 */
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                                                   Params... params) {
    if (mStatus != Status.PENDING) {//状态检测,只有在PENDING状态下才能正常运行,PENDING:待定状态
        switch (mStatus) {
            case 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)");
        }
    }
    mStatus = Status.RUNNING;
    onPreExecute(); // 执行任务前的准备处理
    mWorker.mParams = params;//UI线程、传递过来的参数
    exec.execute(mFuture);//交给线程池管理器进行调度,参数为FutureTask类型,构造mFuture时mWorker被传了进去
    return this;
}

程序先判断了AsyncTask的状态,初始情况下,AsyncTask的状态为PENDING。调用execute方法时会判断该任务的状态,如果是非PENDING状态就会抛出异常,所以AsyncTask实例只能运行一次。随后,改变AsyncTask的状态以及执行onPreExecute方法。后面,我们可以看到,参数sDefaultExecutor执行了它的execute方法,不过,它传进去的参数是mFuture又是什么呢,还有mWorker又是什么呢,我们可以从AsyncTask的构造函数中得到答案:

public AsyncTask() {
	mWorker = new WorkerRunnable<Params, Result>() {
		public Result call() throws Exception {
			mTaskInvoked.set(true);
			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
			return postResult(doInBackground(mParams));//调用doInBackground函数,并将最后结果通过postResult投递出去。
		}
	};
	mFuture = new FutureTask<Result>(mWorker) {
		@Override
		protected void done() {
			try {
				final Result result = get();

				postResultIfNotInvoked(result);
			} catch (InterruptedException e) {
				android.util.Log.w(LOG_TAG, e);
			} catch (ExecutionException e) {
				throw new RuntimeException("An error occured while executing doInBackground()",
						e.getCause());
			} catch (CancellationException e) {
				postResultIfNotInvoked(null);
			} catch (Throwable t) {
				throw new RuntimeException("An error occured while executing "
						+ "doInBackground()", t);
			}
		}
	};
}
//用getHandler()把设置message,并发送。
private Result postResult(Result result) {
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
 }

mFutrue: mFutrue是AsyncTask的抽象内部类FutureTask的实例对象,创建mFuture实例时,将mWorker传进来作为参数,并调用mWorker完成后台任务,完成后调用done方法。

mWorker: mWorker是AsyncTask的抽象内部类WorkerRunnable的实例对象,它实现了Callable接口中的call方法,在call方法中调用doInBackground函数,并将最后结果通过postResult投递出去。在postResult中,出现了内部封装的Handler,我们看看它相关的代码:

private static class InternalHandler extends Handler {  
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  
    @Override  
    public void handleMessage(Message msg) {  
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
        switch (msg.what) {  
            case MESSAGE_POST_RESULT:  
                // There is only one result  
                result.mTask.finish(result.mData[0]);  
                break;  
            case MESSAGE_POST_PROGRESS:  
                result.mTask.onProgressUpdate(result.mData);  
                break;  
        }  
    }  
} 
private void finish(Result result) {
        // 先判断是否调用了Cancelled()
        // 1. 若调用了则执行我们复写的onCancelled()
        // 即 取消任务时的操作
        if (isCancelled()) {
            onCancelled(result);
        } else {

        // 2. 若无调用Cancelled(),则执行我们复写的onPostExecute(result)
        // 即更新UI操作
            onPostExecute(result);
        }
        // 注:不管AsyncTask是否被取消,都会将AsyncTask的状态变更为:FINISHED
        mStatus = Status.FINISHED;
}

若收到的消息为MESSAGE_POST_RESULT,则通过finish() 将结果通过Handler传递到主线程,若收到的消息为MESSAGE_POST_PROGRESS,则回调onProgressUpdate()通知主线程更新进度的操作。

本文参考
[1]: https://www.jianshu.com/p/b4d745c7ff7a
[2]: https://blog.csdn.net/many_a_little/article/details/78240686
[3]: https://blog.csdn.net/csdn1125550225/article/details/80384670
《Android开发进阶——从小工到专家》一书 作者:何红辉

本文作者:陈栩 本文地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值