【android 多线程】Android多线程详细教程

更新时间:2020-03-09    来源:php应用    手机版     字体:

【www.bbyears.com--php应用】

在Android应用的开发过程中,我们不可避免的要使用多线程,获取服务器数据、下载网络数据、遍历文件目录查找特定文件等等耗时的工作都离不开线程的知识。Android继承了Java的多线程体系,同时又实现了许多更加简易的API来操作线程。通过这些API,我们可以方便快捷的实现线程的创建、线程间的交互。我打算记下最近自己学习Android多线程机制时的学习笔记,一来可以供以后翻阅查看,二来为那些正疑惑与此的朋友提供一条解决问题的途径。

先大致说一下我想写的内容:

一、AsyncTask

二、Thread 与 Handler

三、HandlerThread

全部内容分为3篇Bolg。

下面正式开始第一部分吧!

一、AsyncTask

(0)参考文档:http://developer.android.com/reference/android/os/AsyncTask.html

(1)简单介绍:

  AsyncTask这是一个很便捷的关于线程的API,它将比较耗时(几秒)的工作放在非UI线程中运行,然后将结果的展示放在UI线程中。整个过程不涉及到任何关于Thread、Handler及Message等的处理。

  先来说说AsyncTask的构造吧,它接收3个泛型参数,分别代表了后台工作的传入参数类型、进度的参数类型、结果的参数类型。每一个的具体含义会在接下来的AsyncTask的执行过程中讲到。要想使用AsyncTask就必须拓展为它的子类同时指定3个泛型参数,然后在子类的中重写几个重要的方法。下面就AsyncTask的执行过程来写。

(2)AsyncTask的执行过程:

  AsyncTask中的回调方法有:

onPreExecute(),UI线程中执行,一般用来处理执行异步任务前的准备工作,比如弹出一个进度条,展现一个对话框告知用户正在执行一项任务。

doInBackground(Params...),在非UI线程中执行,这里是异步任务的核心,所有需要耗时的工作全在这里执行,因为不是在UI线程中,因此不会存在阻塞UI线程的危险,不会引发ANR错误。

onProgressUpdate(Progress...),在UI 线程中执行,一般用来更新任务执行的进度,它的调用时在publishProgress(Progress...)调用之后接着被调用的,我们可以在doInBackground(Params...)中多次调用publishProgress(Progress...)来告知用户任务的执行状态。

onPostExecute(Result),在UI线程中执行,任务完成后(doInBackground(Params...)执行结束)且任务没被取消时,会执行该方法,用来处理异步任务得到的结果。

onCancelled(Result),在UI线程中执行,当任务以柔和的方式结束(AsyncTask.cancel(false))这个方法会取代onPostExecute(Result)得以执行(当然这里也要看程序的执行逻辑,有没有在doInBackground(Params...)中检测任务是否被取消)

  关于泛型参数:

  三个泛型参数分别是:Params,Progress,Result。在上面的回调方法接受中我们可以看到它们分别所处那个方法,Params是启动异步任务时传入的参数,比如启动异步任务从网上下载一张图片传入图片的URL地址。Progress是进度参数类型,调用publishProgress(Progress...)时的参数需和Progress类型相容。比如用一个整数去描述进度,Progress及时Integer。Result是结果参数类型,异步任务完成后,如需返回一个结果,那么这个结果的类型就是Result类型。特别地,可以用Void去代替任何一个或多个泛型参数,这样做就以为着不需要参数。

(3)一个简单的例子:

话说百闻不如一见,下面我就用一个极简单的例子说明AsyncTask的使用过程。先说我要干啥,我用一个异步任务来更新一个进度条(简单吧!粗暴吧!颤抖吧,地球人!O(∩_∩)O)

注:示例没什么格式,强迫症患者见谅!!

先给出布局:



    
    
    


下面是AsyncTask的逻辑代码:

package comfallblank.github.asynctasktest;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
public class MainActivity extends AppCompatActivity {
    private ProgressBar mProgressBar;
    private Button mStartButton;
    private Button mCancelButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProgressBar = (ProgressBar) findViewById(R.id.progress);
        //设置ProgressBar暂时不可见,一会在异步任务的中显示它。
        mProgressBar.setVisibility(View.GONE);
        final MyTask myTask =  new MyTask();
        mStartButton = (Button) findViewById(R.id.start);
        mStartButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myTask.execute();
            }
        });
        mCancelButton = (Button) findViewById(R.id.cancel);
        mCancelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myTask.cancel(false);
            }
        });
    }
   private class MyTask extends AsyncTask{
       private static final  String TAG = "MyTask";
       @Override
       protected void onPreExecute() {
           super.onPreExecute();
           //准备工作,显示进度条
           mProgressBar.setVisibility(View.VISIBLE);
           //打印日志,查看执行路径,下同
           Log.d(TAG,"onPreExecute");
       }
       @Override
       protected String doInBackground(Void... voids) {
           Log.d(TAG,"doInBackground");
           //每1s对进度条更新10%
           synchronized (this){
               for (int i = 1;i<=10;i++){
                   try {
                       Thread.sleep(1000*1);
                       publishProgress(i*10);
                       if (isCancelled()){
                           return "Task cancelled";
                       }
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
           return "Task executed";
       }
       @Override
       protected void onProgressUpdate(Integer... values) {
           super.onProgressUpdate(values);
           mProgressBar.setProgress(values[0]);
           Log.d(TAG,"onProgressUpdate");
       }
       @Override
       protected void onPostExecute(String s) {
           super.onPostExecute(s);
           Log.d(TAG, s);
           Log.d(TAG,"onPostExecute");
       }
       @Override
       protected void onCancelled(String s) {
           super.onCancelled(s);
           Log.d(TAG, s);
           Log.d(TAG, "onCancelled");
       }
   }
}


LogCat日志:

执行完毕,没有中途取消任务:

10-06 13:55:05.871 6737-6737/comfallblank.github.asynctasktest D/MyTask: onPreExecute
10-06 13:55:05.891 6737-13040/comfallblank.github.asynctasktest D/MyTask: doInBackground
10-06 13:55:06.922 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:07.923 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:08.914 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:09.925 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:09.975 6737-6753/comfallblank.github.asynctasktest W/art: Suspending all threads took: 18.035ms
10-06 13:55:10.926 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:11.937 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:12.938 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:13.939 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:14.950 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:15.951 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:15.971 6737-6737/comfallblank.github.asynctasktest D/MyTask: Task executed
10-06 13:55:15.971 6737-6737/comfallblank.github.asynctasktest D/MyTask: onPostExecute

中途取消任务:

10-06 13:56:10.644 14129-14129/comfallblank.github.asynctasktest D/MyTask: onPreExecute
10-06 13:56:10.654 14129-14302/comfallblank.github.asynctasktest D/MyTask: doInBackground
10-06 13:56:11.665 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:12.666 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:13.667 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:14.668 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:15.689 14129-14129/comfallblank.github.asynctasktest D/MyTask: Task cancelled
10-06 13:56:15.689 14129-14129/comfallblank.github.asynctasktest D/MyTask: onCancelled

关于AsyncTask的一些注意事项:

(1)AsyncTask对象只能使用一次,也就是说一个对象执行完毕或者中途被cancel后,不能再次调用execute().

(2)AsyncTask.execute()只能在主线程中调用。

(3)AsyncTask不能处理并发,多个AsyncTask也是有执行顺序的,且同一个应用的所有AsyncTask都是在同一进程中执行,按照队列排序依次执行。

(4)AsyncTask只能处理那些耗时不是特别长的任务,对于需要长时间执行的任务最好自己实现Thread。

AsyncTask很方便,它屏蔽了所有Thread、Handler、Looper、Message及Messagequene的细节,这或许就是封装的魅力吧!但当你真正明白了Android的线程通讯之后你就会明白那些良好设计带来的美感。


二、Thread 与 Handler

  本节涉及的概念较多,有Thread,Handler,Looper,Message,MessageQuene,对于Looper和MessageQueue只是简单的谈及它们在线程通信中的作用,Thread,Handler及Message我会尽力讲清楚些。

  1.Thread基础:

  1)参考文档:http://developer.android.com/reference/java/lang/Thread.html

  我包括我的好些朋友学东西时都会忽略官方文档的重要性,其中很大的原因是因为英文的缘故吧,但其实看懂文档要求英语能力并不高。看别人写的文章、博客、书籍,听别人讲技术,那始终是经过他人过滤后的知识,而这一切未必是你需要的。前车之鉴固然重要,但并不是每一个老人的话都得听。歪果仁写东西有个特点就是他不仅把原理给你讲清楚了,还爱举列子,该怎么做不建议怎么做都会涉及。说了这么多,就是建议自己去看看Android的开发文档,从Guide到Reference内化出自己的知识体系。

  2)简单介绍:

  看Android的Thread继承树,我们就知道它完全继承了Java的线程体系,在这就不赘言过多的Thread细节了,我就如何开启一个线程讲一讲。开启一个新线程有两种方法,第一种是拓展Thread类,在子类中重写run()方法;第二种是声明Thread对象时传入一个Runnable对象,因为只涉及到一个抽象方法,Runnable对象可以用jdk8的Lambda表达式来代替,这样使得线程的创建变得更加简单。创建好了线程后,如何让线程开始执行呢?调用Thread.run()即可让线程进入待执行队列,当获得CPU时间时它就运行起来了。具体的用法例子会在后面的示例中展现。

  2.Handler基础:

  1)参考文档:http://developer.android.com/reference/android/os/Handler.html

  2)概述:

  之前看过一部美剧《天蝎计划》,在介绍团队负责人时,这样说道”He is our goverment handler“。Android中的Handler就是这样一个概念,它是线程通信的发送者和处理者,线程要进行通信时会让handler发出相应的消息,通过Looper传递,Handler发出的消息会在目的线程中得以执行。再举个栗子吧,这是我在《Android编程权威指南》中看到的,它是这么描述线程通信的:两个线程的通信就好像现实中的两个人通过信件来通信,消息队列(MessageQueue)相对于通信时候的信箱;Message(消息)相当于信件;Looper相当于邮递员,它是MessageQueue的操作者;Handler时线程通信的发出者和处理者;每当Thread想要进行通信,它会让Handler投递一个Message给相应的MessageQueue,Looper会一直循环将MessageQueue里的Message发向它的目的地,到达目的地后Looper通知相应的Handler来处理消息。

  每一个Handler都是和唯一的Looper对象绑定的,也就是说一个Handler既仅可以个一个Looper绑定,但一个Looper可以有好几个Handler与之关联。Looper操作的MessageQueue是Handler取得消息进行处理和发出消息的地方。

  3)使用介绍:

  前面说过每一个Handler都是和特别的Looper绑定好的,同时Handler又是处理消息的地方,所以Handler中既要说明和哪个Looper绑定,又要告知怎么处理消息。所以Handler有4个构造方法,下面我来一一介绍:

    Handler()这是无参数构造方法,它默认和当前线程的Looper绑定,未指定消息的处理方式。
    Handler(Looper looper)它需要一个Looper对象作为参数传入,构成出来的Handler对象将和给定的Looper对象绑定。
    Handler(Handler.Callback callback)它需要一个Handler.Callback对象作为参数传入,构造处理的对象和当前线程的Looper绑定并用传入的Handler.Callback对象来处理消息。
    Handler(Looper looper,(Handler.Callback callback)这是第二种和第三种的结合,构成出一个和指定Looper绑定并用指定Callback的回调方法处理消息的Handler对像
    还有一种使用Handler的方法就是自己拓展Handler类,在子类中实现handlerMessage(Message msg)(这就是接口Callback中的抽象方法),这也是我用的比较多的方式。

  Handler的使用就是发送和处理消息,处理消息是在Callback接口中定义好的,当Looper操作的MessageQueue中有消息时,Looper对通知所有与它绑定的Handler调用handlerMessage(Message msg)去处理消息。那怎么发送消息呢?Hander中有一个方法叫sendMessage(Message msg),当调用这个方法后Handler就往Looper操作的MessageQueue中投递一个Message对象,发送消息就算是完成了。简单吧?so easy!

  关于Handler的其他方法还请查看文档,将主干的部分弄清楚了,指端末节随着使用慢慢就熟络了。一口气吃不成胖子。对了,下面的Message部分还会提及Handler的一些内容。

  3.Message基础:

  1)参考文档:http://developer.android.com/reference/android/os/Message.html

  2)使用介绍:

  线程通信既然叫通信肯定有一个消息的载体,Message就是这个消息的载体,它包含了线程想要通信的全部内容,比如线程运行后得到的结果就和可以包含在Message中。关于Message的构造本来可以按正常的方式构造(就是new一个Message对象,词穷不知道怎么说,就叫它”正常的方式“O(∩_∩)O~),但官方推荐的做法是通过Handler.obtainMessage()获得Message对象,因为这样的Message是从即将被回收的Message中取得的,会避免GC的反复运行和减少运行时的内存消耗。

  Message是消息的携带者,它有许多的携带消息的方法,比如setData(Bundle),Message.obj,Message.arg1,Message.arg2等等,需要特别说明的是Message里有一个公开的整形的全局变量what,即Message.what,它一般用来阐述消息的类型,Handler的handlerMessage(Message msg)通常会先检验Message的what变量,然后在决定如何处理。毕竟一个应用中和主线程通信的不可能只用一个线程,一种消息。

  4.Looper与MessageQueue简单介绍:

  1)参考文档:

  Looper:http://developer.android.com/reference/android/os/Looper.html

  MessageQueue:http://developer.android.com/reference/android/os/MessageQueue.html

  2)简单介绍:

  需要注意的地方就是,一个普通Thread创建时是没有Looper对象和它关联的,我们必须在线程的创建中进行关联,具体做法就是在Thread的创建时调用Looper.prepare()进行绑定,调用Looper.loop()使得与线程绑定的Looper对象开始工作。Looper中有一个巨好用的方法,Looper.getMainLooper(),这个方法直接返回当前应用的UI线程的Looper对象,有了Looper对象就可以往主线程发消息了,一会我在示例中会用到这样方法。

  关于MessageQueue,其实在线程通信中我们并不直接使用它,只需知道我们通过Handler发送出去的消息都是放在它里的就行了,它是一个”第层次“的对象,我们也不能直接往它里添加消息。但对于理解整个线程通信过程还是很重要的。

  5.实践――示例

  说了这么多,是时候用实践检验了!先说说我打算怎么做吧!我打算从UI线程向非UI线程(是不是叫主线程和子线程更好?)发一个消息,然后在非UI线程中处理这个消息(这里我只是打印一下日志),然后从非UI线程向主线程发送一个消息,然后在UI线程中处理这个消息(也是简单的打印一下)。好像有些偷懒,但真正使用也大致是这样的,就是把打印换成具体的任务。好了,开始吧!

  先给出布局吧!



    
    


  布局很简单,就是一个textview和两个button,一个button开启线程,并接受来自子线程的信息,另一个从主线程发出消息给子线程。

  下面给出代码逻辑:

  第一部分是子线程的代码:

package comfallblank.github.threadandhandler;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
/**
 * Created by fallb on 2015/10/7.
 */
public class MyThread extends Thread {
    public static final int MSG_WORKER_THREAD = 100;
    private static final  String TAG = "MyThread";
    private Handler mWorkerHandler;
    private Handler mMainHandler;
    public MyThread(Handler handler) {
        mMainHandler = handler;
        mWorkerHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if(msg.what == MainActivity.MSG_MAIN){
                    Log.d(TAG,"Message:"+msg.obj);
                }
            }
        };
    }
    @Override
    public void run() {
        Looper.prepare();
        Message msg = mMainHandler.obtainMessage();
        msg.what = MyThread.MSG_WORKER_THREAD;
        msg.obj="子线程发出的消息";
        mMainHandler.sendMessage(msg);
        Looper.loop();
    }
    public Handler getWorkerHandler() {
        return mWorkerHandler;
    }
}

  第二份是主线程的:

package comfallblank.github.threadandhandler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
    public static final int MSG_MAIN = 100;
    private static final String TAG = "MainActivity";
    private Button mStartThread;
    private Button mSendMessage;
    private Handler mHandler = new MyHandler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final MyThread thread = new MyThread(mHandler);
        mStartThread = (Button) findViewById(R.id.startThread);
        mStartThread.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d(TAG, "开启线程");
                thread.start();
            }
        });
        mSendMessage = (Button) findViewById(R.id.send_message);
        mSendMessage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Handler workerHandler = thread.getWorkerHandler();
                Message msg = workerHandler.obtainMessage();
                msg.what = MSG_MAIN;
                msg.obj = "来自主线程的消息";
                workerHandler.sendMessage(msg);
            }
        });
    }
    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MyThread.MSG_WORKER_THREAD) {
                Log.d(TAG,"Message:"+msg.obj);
            }
        }
    }
}


  Logcat打印日志如下:

  10-07 19:34:34.424 19671-19671/comfallblank.github.threadandhandler D/MainActivity: 开启线程
  10-07 19:34:34.454 19671-19671/comfallblank.github.threadandhandler D/MainActivity: Message:子线程发出的消息
  10-07 19:34:37.267 19671-19671/comfallblank.github.threadandhandler D/MyThread: Message:来自主线程的消息

  6.关于Handler再说两句:

  Handler对象除了发出消息外,还有一族方法,来发出一个Runnable对象,Handler.post(Runnable runnable)及时其中的一个,它向MessageQueue中添加这个Runnable对象,然后目的线程的MessageQueue会执行该方法。

本文来源:http://www.bbyears.com/jiaocheng/85962.html

猜你感兴趣

热门标签

更多>>

本类排行