zoukankan      html  css  js  c++  java
  • 有关Android线程的学习

          当应用程序启动时,系统会为其创建一个进程,也会创建一个线程名字叫做main,所有其所属组件的创建,系统事件的处理,系统的回调等一切应用相关的事情都运行在此名叫main的线程中。此线程即为常说的主线程(main thread)。俗称的UI线程(UI thread)也是它,因为只有主线程可以操作UI相关的事情,所以有人把主线程也称作UI线程。为什么非主线程不能操作UI呢?因为对UI操作常常会引发系统的回调,所以如果允许第三线程来操作可能会引发系统回调的紊乱,进而会打乱整个框架的时序!
          这里要特别注意的就是同一个进程中的所有组件运行在同一个线程中,Activiy,Service,BoradcastReceiver和ContentProvider都运行在主线程中。最容易引起误解的就是Service,文档和常识都会认为Service是放在后台用于操作费时运算的,但是实则不然,如果你在Service中做费时操作,同样会引发臭名昭著的ANR(Application Not Responding)。所以如果想把Service当做一个Server,必须在Service用HandlerThread或Thread创建一个Worker线程!
           Activity也是一样的,你startActivity()后,开启了一个新的Activity,但它们都运行在同一个线程中,所以你还是不能在原Activity中做费时操作!也即在调用startActivity()开启了一个新的Activity后,或者在onPause(), onStop(), onDestroy()中做费时操作会引发ANR。
          对于ContentProvider也是一样的,如果跟其他组件在同一进程内,那么调用ContentResolver的方法会相当于直接调用ContentProvider的方法。如果是在另外一个进程中,虽是通过IPC,但也是同步的,因为IBinder的同步的,也即调用ContentResolver时会把调用者的进程挂起,等待ContentProvider的进程操作结束,再把结果传给调用者进程!所以,如果ContentProvider中有费时操作,或者会同步锁数据库等,也一定要注意ANR的发生!
    所以一定要记住:一个进程只有一个主线程,所有组件都运行在主线程中。
    因此,如果有费时操作,必须要创建Worker线程!

     

       

     1. Message
        Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
     
        2. Handler
        Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
     
        3. Message Queue
        Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
        每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
     
        4. Looper
        Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper()得到当前线程的Looper就有可能为NULL。
     
     

        对于子线程使用Looper,API Doc提供了正确的使用方法:

    1. class LooperThread extends Thread { 
    2.     public Handler mHandler; 
    3.  
    4.     public void run() { 
    5.         Looper.prepare(); //创建本线程的Looper并创建一个MessageQueue
    6.  
    7.         mHandler = new Handler() { 
    8.             public void handleMessage(Message msg) { 
    9.                 // process incoming messages here 
    10.             } 
    11.         }; 
    12.    
    13.         Looper.loop(); //开始运行Looper,监听Message Queue 
    14.     } 
     

        这个Message机制的大概流程:

        1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。

        2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用该Message的target指向的Hander的dispatchMessage函数对Message进行处理。

        在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:

        1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;

        2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;

        3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。

        由此可见,我们实现的handleMessage方法是优先级最低的!

        3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!

     
        在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
        1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;
        2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。
     

    Android另外提供了一个工具类:AsyncTask。它使得UI thread的使用变得异常简单。它使创建需要与用户界面交互的长时间运行的任务变得更简单,不需要借助线程和Handler即可实现。

        1)  子类化AsyncTask
        2)  实现AsyncTask中定义的下面一个或几个方法
             onPreExecute() 开始执行前的准备工作;
             doInBackground(Params...) 开始执行后台处理,可以调用publishProgress方法来更新实时的任务进度;
             onProgressUpdate(Progress...)  在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
             onPostExecute(Result) 执行完成后的操作,传送结果给UI 线程。
     
             这4个方法都不能手动调用。而且除了doInBackground(Params...)方法,其余3个方法都是被UI线程所调用的,所以要求:
            1) AsyncTask的实例必须在UI thread中创建;
            2) AsyncTask.execute方法必须在UI thread中调用;
            
        同时要注意:该task只能被执行一次,否则多次调用时将会出现异常。而且是不能手动停止的,这一点要注意,看是否符合你的需求!
     
        在使用过程中,发现AsyncTask的构造函数的参数设置需要看明白:AsyncTask<Params, Progress, Result>
        Params对应doInBackground(Params...)的参数类型。而new AsyncTask().execute(Params... params),就是传进来的Params数据,你可以execute(data)来传送一个数据,或者execute(data1, data2, data3)这样多个数据。
        Progress对应onProgressUpdate(Progress...)的参数类型;
        Result对应onPostExecute(Result)的参数类型。
        当以上的参数类型都不需要指明某个时,则使用Void,注意不是void。不明白的可以参考上面的例子,或者API Doc里面的例子。
  • 相关阅读:
    sql.srcipt
    sowmodaldialog
    4) 删除虚拟应用程序
    JavaScript读写Cookies
    第5章 脚本运行期库对象
    npm serve md 工具 [MD]
    cleanmark 清除格式 博客内容提取 [MD]
    Hex编码 十六进制编码
    Windows Server AppFabric(Codename:"Dublin&Velocity")介绍
    WF4设计器模型:编辑范围ModelEditingScope
  • 原文地址:https://www.cnblogs.com/renyuan/p/2581632.html
Copyright © 2011-2022 走看看