Android Learning:多线程与异步消息处理机制

  

  在最近学习Android项目源码的过程当中,遇到了不少多线程以及异步消息处理的机制。因为以前对这块的知识只是浅尝辄止,并无系统的理解。可是工程中反复出现让我意识到这个知识的重要性。因此我整理出这篇博客,主要介绍了线程和异步处理机制的意义和用法,目的在于帮助初学者可以加深对异步消息处理机制的理解,在实际Android工程中可以更多地使用AsyncTask工具类在子线程中进行UI更新。 html

 

1、Android当中的多线程[1]

  在Android当中,当一个应用程序的组件启动的时候,而且没有其余的应用程序组件在运行时,Android系统就会为该应用程序组件开辟一个新的线程来执行。默认的状况下,在一个相同Android应用程序当中,其里面的组件都是运行在同一个线程里面的,这个线程咱们称之为Main线程。当咱们经过某个组件来启动另外一个组件的时候,这个时候默认都是在同一个线程当中完成的。固然,咱们能够本身来管理咱们的Android应用的线程,咱们能够根据咱们本身的须要来给应用程序建立额外的线程。数据库

 

2、Main Thread 和 Worker Thread

  在Android当中,一般将线程分为两种,一种叫作Main Thread,除了Main Thread以外的线程均可称为Worker Thread。当一个应用程序运行的时候,Android操做系统就会给该应用程序启动一个线程,这个线程就是咱们的Main Thread,这个线程很是的重要,它主要用来加载咱们的UI界面,完成系统和咱们用户之间的交互,并将交互后的结果又展现给咱们用户,因此Main Thread又被称为UI Thread。编程

  Android系统默认不会给咱们的应用程序组件建立一个额外的线程,全部的这些组件默认都是在同一个线程中运行。然而,某些时候当咱们的应用程序须要完成一个耗时的操做的时候,例如访问网络或者是对数据库进行查询时,此时咱们的UI Thread就会被阻塞。例如,当咱们点击一个Button,而后但愿其从网络中获取一些数据,若是此操做在UI Thread当中完成的话,当咱们点击Button的时候,UI线程就会处于阻塞的状态,此时,咱们的系统不会调度任何其它的事件,更糟糕的是,当咱们的整个现场若是阻塞时间超过5秒钟(官方是这样说的),这个时候就会出现 ANR (Application Not Responding)的现象,此时,应用程序会弹出一个框,让用户选择是否退出该程序。对于Android开发来讲,出现ANR的现象是绝对不能被容许的。安全

  另外,因为咱们的Android UI控件是线程不安全的,因此咱们不能在UI Thread以外的线程当中对咱们的UI控件进行操做。所以在Android的多线程编程当中,咱们有两条很是重要的原则必需要遵照:网络

  • 绝对不能在UI Thread当中进行耗时的操做,不能阻塞咱们的UI Thread
  • 不能在UI Thread以外的线程当中操纵咱们的UI元素

 

3、多线程的常见操做[2]

  一、建立线程多线程

  在Android中,提供了两种建立线程的方法。(一种是经过Thread类的构造方法建立线程对象,并重写run()方法实现,另外一种是经过实现Runnable接口来实现。)框架

  /*第一种方法:*/
  Thread thread=new Thread(new Runnable(){
    @Override
    public void run(){
    //要执行的操做
    }
  });
  thread.start();

  /*第二种方法:*/
  public class MainActivity extends Activity implement Runnable{
  @Override
      public void run(){
        //要执行的操做
        }
  }

  二、开启线程异步

  三、线程休眠ide

  四、中断线程  工具

  

4、如何处理UI Thread 和 Worker Thread之间的通讯

  既然在Android当中有两条重要的原则要遵照,那么咱们可能就有疑问了?咱们既不能在主线程当中处理耗时的操做,又不能在工做线程中来访问咱们的UI控件,那么咱们好比从网络中要下载一张图片,又怎么能将其更新到UI控件上呢?这就关系到了咱们的主线程和工做线程之间的通讯问题了。在Android当中,提供了两种方式来解决线程直接的通讯问题,一种是经过Handler的机制,还有一种就是 AsyncTask 机制。

(一)、Handler机制 

  就应用程序而言,Android系统中JAVA的应用程序和其余系统上相同,都是靠消息驱动来工做的,他们大体的工做原理以下: 

(1)、有一个消息队列,能够往这个消息队列中投递消息。 

(2)、有一个消息循环,不断从消息队列中取出消息,而后处理。 

  在Android中,一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue(用于存放message)。接下来,咱们来了解几个类:循环者Looper类,消息处理类Handler,消息类Message。 

 1. Message

  Message是在线程之间传递的消息,它能够在内部携带少许的信息,用于在不一样的线程之间交换数据,如Message的what字段,和arg1,arg2来携带一些整型的数据,使用obj字段携带一个Object对象。消息类(Message)被存放在MessageQueue中,一个MessageQueue中能够包含多个Message对象。每一个Message对象能够经过Message.obtain()方法或者Handler.obtainMessage()方法得到。

 2. Handler

  Handler是处理者的意思,主要用于发送和处理消息。发送信息通常使用Handler的sendMessage()方法,发出的消息通过一系列展转处理后,最终会传递到Handler的handleMessage()方法中。消息处理类(Handler)容许发送和处理Message和Runnable对象到其所在线程的MessageQueue中。它主要有两个做用:

  (1)、将Message或Runnable应用post()方法或sendMessage()方法发送到MessageQueue中,在发送时能够指定延时时间、发送时间或者要携带的bundle数据。当MessageQueue循环到该Message时,调用相应的Handler对象的handlerMessage()方法对其进行处理。

  (2)、在子线程中与主线程进行通讯,也就是在工做线程中与UI线程进行通讯。)另外,在一个线程中只能有一个Looper和MessageQueue,可是能够有多个Handler,并且这些Handler能够共享一个Looper和MessageQueue。

  3. MessageQueue

  MessageQueue是消息队列的意思,它主要用于存放全部经过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每一个线程中只会有一个MessageQueue对象。 

  4. Looper

  Looper是每一个线程中的MessageQueue的管家,调用Looper的Loop()方法后,就会进入到一个无限的循环中,每当发现MessageQueue中存在一条消息,就会将它取出来,并传递到Handler的handleMessage()方法中。Looper对象用来为一个线程开启一个消息循环,用来操做MessgeQueue。默认状况下,Android中新建立的线程是没有开启消息循环的(主线程除外)。

异步消息处理机制流程图[3] 

  总结一下流程为:首先在主线程中建立一个Handler对象,并重写handleMessage()方法。而后再子线程中须要进行UI操做时,就建立一个Message对象,并经过Handler把这条消息发送出去。以后这条消息会被添加到MessageQueue的队列等待被处理,而Looper则会一直尝试从MessageQueue中取出这条待处理的信息,最后发回给Handler的HandleMessage()方法中。

  因为Handler是在主线程中建立的,因此handleMessage()方法也会在主线程中运行,这样就能够放心进行UI操做了。 

public class MainActivity extends Activity {
    private TextView tv;
    private Handler handler=new Handler(){
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 1://获取到what属性为1的message
                tv.setText((String)msg.obj);//将message的内容填充到TextView中
                break;
            default:
                break;
            }
        };
    };      

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn=(Button) findViewById(R.id.btn);
        tv = (TextView) findViewById(R.id.tv);
        btn.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    
                    @Override
                    public void run() {
                        Message msg=new Message();
                        msg.obj="您已点击按钮,数据发生改变";//message的内容
                        msg.what=1;//指定message
                        handler.sendMessage(msg);//handler发送message                        
                    }
                }).start();                
            }
        });
    }
}

 

(二)、AsyncTask

  AsyncTask:异步任务,从字面上来讲,就是在咱们的UI主线程运行的时候,异步的完成一些操做。AsyncTask容许咱们的执行一个异步的任务在后台。咱们能够将耗时的操做放在异步任务当中来执行,并随时将任务执行的结果返回给咱们的UI线程来更新咱们的UI控件。经过AsyncTask咱们能够轻松的解决多线程之间的通讯问题,AsyncTask工具类的出现极大地方便了咱们在子线程中进行UI操做,但其本质仍然是异步消息处理机制,只不过是Android替咱们作的很好的封装而已[4]

  怎么来理解AsyncTask呢?通俗一点来讲,AsyncTask就至关于Android给咱们提供了一个多线程编程的一个框架,其介于Thread和Handler之间,咱们若是要定义一个AsyncTask,就须要定义一个类来继承AsyncTask这个抽象类,并实现其惟一的一个 doInBackgroud 抽象方法。要掌握AsyncTask,咱们就必需要一个概念,总结起来就是: 3个泛型,4个步骤。

  3个泛型指的是什么呢?咱们来看看AsyncTask这个抽象类的定义,当咱们定义一个类来继承AsyncTask这个类的时候,咱们须要为其指定3个泛型参数:

AsyncTask <Params, Progress, Result>

Params: 这个泛型指定的是咱们传递给异步任务执行时的参数的类型

Progress: 这个泛型指定的是咱们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型

Result: 这个泛型指定的异步任务执行完后返回给UI线程的结果的类型

 咱们在定义一个类继承AsyncTask类的时候,必需要指定好这三个泛型的类型,若是都不指定的话,则都将其写成Void,例如:

AsyncTask <Void, Void, Void>

  4个步骤:当咱们执行一个异步任务的时候,其须要按照下面的4个步骤分别执行

onPreExecute():这个方法是在执行异步任务以前的时候执行,而且是在UI Thread当中执行的,一般咱们在这个方法里作一些UI控件的初始化的操做,例如弹出要给ProgressDialog

doInBackground(Params... params):在onPreExecute()方法执行完以后,会立刻执行这个方法,这个方法就是来处理异步任务的方法,Android操做系统会在后台的线程池当中开启一个worker thread来执行咱们的这个方法,因此这个方法是在worker thread当中执行的,这个方法执行完以后就能够将咱们的执行结果发送给咱们的最后一个 onPostExecute 方法,在这个方法里,咱们能够从网络当中获取数据等一些耗时的操做

onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,咱们在异步任务执行的时候,有时候须要将执行的进度返回给咱们的UI界面,例以下载一张网络图片,咱们须要时刻显示其下载的进度,就可使用这个方法来更新咱们的进度。这个方法在调用以前,咱们须要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将咱们的进度时时刻刻传递给 onProgressUpdate 方法来更新

onPostExecute(Result... result): 当咱们的异步任务执行完以后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,咱们能够将返回的结果显示在UI控件上

  为何咱们的AsyncTask抽象类只有一个 doInBackground 的抽象方法呢??缘由是,咱们若是要作一个异步任务,咱们必需要为其开辟一个新的Thread,让其完成一些操做,而在完成这个异步任务时,我可能并不须要弹出要给ProgressDialog,我并不须要随时更新个人ProgressDialog的进度条,我也并不须要将结果更新给咱们的UI界面,因此除了 doInBackground 方法以外的三个方法,都不是必须有的,所以咱们必需要实现的方法是 doInBackground 方法

  若是想要启动这个任务,须要执行结构如method().execute(params)的代码:

    private void task() {
        new AsyncTask<String, Void, Boolean>() {
            @Override
            protected Boolean doInBackground(String... params) {  /*传递多个String参数*/
                try {
                    //执行耗时操做
                } catch (Exception e) {
                    //打印异常
                }
            }

            @Override
            protected void onPostExecute(Boolean isSuccess) {
                if (isSuccess) {
                    //耗时操做成功后的操做
                } else {
                    //打印错误
                }
            }
        }.execute(params); /*传递参数*/

 

[1] xiaoluo501395377.Android 多线程-----AsyncTask详解.http://www.cnblogs.com/xiaoluo501395377/p/3430542.html.

[2] 银色的流星.Android线程与异步消息处理机制.http://www.cnblogs.com/scetopcsa/p/3661963.html.

[3] u012339794.Android异步消息处理机制.http://www.lai18.com/content/1849027.html.

[4] 郭霖. 第一行代码 Android[J]. 2014.  

相关文章
相关标签/搜索