• Android中Handler 、Thread和Runnable之间的关系ZZ


    在多线程编程的时候,我们经常会用到Handler,Thread和Runnable这三个类,我们来看看这三个类之间是怎么样的关系?

      首先说明Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应。

        而Runnable是一个接口,Thread是Runnable的子类。可以说,他俩都算一个进程。 HandlerThread顾名思义就是可以处理消息循环的线程,他是一个拥有Looper的线程,可以处理消息循环。

        与其说Handler和一个线程绑定,不如说Handler是和Looper一一对应的。

        最后需要说明的是,在UI线程(主线程)中:

    复制代码
    1  mHandler=new Handler();
    2 
    3     mHandler.post(new Runnable(){
    4 
    5     void run(){
    6 
    7        //在run方法里执行代码...
    8      }
    9     });
    复制代码

      这个线程其实是在UI线程之内运行的,并没有新建线程。

     

        常见的新建线程的方法是:

    复制代码
    1   Thread thread = new Thread();
    2 
    3     thread.start();
    4 
    5  
    6 
    7     HandlerThread thread = new HandlerThread("string");
    8 
    9     thread.start();
    复制代码

    Android中Handler的使用方法——构建定时器 收藏 

      

       ◆ 在代码中定义一个整形常量,代表消息的ID。此处不妨对其取名为TIMERID。   

     

       ◆创建自己的Handler,在该Handler中HandlerMessage处理函数中。在消息处理函数中,我们首先发送消息TIMERID,并指定其延迟的时间,单位为毫秒。然后可以调用相应的事务处理函数。需要注意的是,如果事件处理函数花费的时间过长,则下次消息到来时,会导致不能及时处理。

     

       ◆创建startTimer函数,在该函数中触发定时器,实际上就是发送一个TIMERID消息,来第一次触发消息。

     

       ◆创建stopTimer函数,在该函数中停止定时器,实际上就是把TIMERID的消息从消息队列中删除即可。

     

        下面让我们来以代码作为说明。

    复制代码
     1 package com.android.mytimer; 
     2 
     3 import android.os.Handler; 
     4 
     5 import android.os.Message; 
     6 
     7 
     8 public class MyTimer extends Handler { 
     9 
    10         private static int TIMERID = 0; //静态变量,保证ID唯一。当ID超过整形最大值时,应该把它恢复为0 
    11 
    12         private final int mInterval; 
    13 
    14         public interface CallBack{ 
    15 
    16             void timerCallBack(); 
    17 
    18         }
    19 
    20 
    21         private CallBack mCallBack;
    22 
    23 
    24         public MyTimer(int interval, CallBack callback) 
    25 
    26         { 
    27 
    28             mInterval = interval; 
    29 
    30             mCallBack = callback; 
    31 
    32             TIMERID++; 
    33 
    34         }
    35         
    36         @Override 
    37 
    38         public void handleMessage(Message msg) { 
    39 
    40             // TODO Auto-generated method stub 
    41 
    42             super.handleMessage(msg); 
    43 
    44             if( msg.what == TIMERID) 
    45 
    46             { 
    47                 Message message = obtainMessage(TIMERID); 
    48 
    49                 this.sendMessageDelayed(message, mInterval); 
    50 
    51                 //for speed up, NO NULL Pointer exception check 
    52 
    53                 mCallBack.timerCallBack(); 
    54             } 
    55         } 
    56 
    57         public void startTimer() 
    58 
    59         { 
    60             Message msg = this.obtainMessage(TIMERID); 
    61 
    62             this.sendMessage(msg); 
    63 
    64         }
    65 
    66         public void stopTimer() 
    67 
    68         { 
    69 
    70             this.removeMessages(TIMERID); 
    71 
    72         } 
    73 }
    复制代码

    其中接口CallBack是一个回调函数,使用这个定时器的应用必须实现该接口,并且在构造函数当中传入该类。如下

    复制代码
     1 public class TestTimer implement MyTImer.CallBack{
     2  
     3         MyTimer mTimer;
     4  
     5         public TestTimer()
     6  
     7         {
     8  
     9             mTimer = new MyTImer(10, this);
    10  
    11             mTimer.start();
    12  
    13         }
    14  
    15         void timerCallBack();
    16  
    17         {
    18  
    19             Log.i("TestTimer", "this is the timerCallBack");
    20  
    21         }
    22  
    23 }
    复制代码

    一> Handler的定义:

              主要接受子线程发送的数据, 并用此数据配合主线程更新UI.

              解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。  如果此时需要一个耗时的操作,例如: 联网读取数据,    或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,,会收到Android系统的一个错误提示  "强制关闭".  这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的. 这个时候,Handler就出现了.,来解决这个复杂的问题 ,    由于Handler运行在主线程中(UI线程中),  它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据)  , 把这些消息放入主线程队列中,配合主线程进行更新UI。(文/springhi-2009)

    <二> Handler一些特点

            handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),
            它有两个作用: (1):  安排消息或Runnable 在某个主线程中某个地方执行, (2)安排一个动作在不同的线程中执行
          
            Handler中分发消息的一些方法
            post(Runnable)
            postAtTime(Runnable,long)
            postDelayed(Runnable long)
            sendEmptyMessage(int)
            sendMessage(Message)
            sendMessageAtTime(Message,long)
            sendMessageDelayed(Message,long)

            以上post类方法允许你排列一个Runnable对象到主线程队列中,
            sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

    <三> Handler实例

          (1) 子类需要继承Hendler类,并重写handleMessage(Message msg) 方法, 用于接受线程数据

          以下为一个实例,它实现的功能为 : 通过线程修改界面Button的内容

    复制代码
      1 public class MyHandlerActivity extends Activity {
      2   
      3 
      4     Button button;
      5   
      6      MyHandler myHandler;
      7 
      8     protected void onCreate(Bundle savedInstanceState) {
      9   
     10          super.onCreate(savedInstanceState);
     11  
     12          setContentView(R.layout.handlertest);
     13 
     14          button = (Button) findViewById(R.id.button);
     15   
     16          myHandler = new MyHandler();
     17   
     18          // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据 
     19 
     20          // Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象
     21   
     22          // (2): 让一个动作,在不同的线程中执行.
     23   
     24          // 它安排消息,用以下方法
     25   
     26          // post(Runnable)
     27   
     28          // postAtTime(Runnable,long)
     29   
     30          // postDelayed(Runnable,long)
     31   
     32          // sendEmptyMessage(int)
     33 
     34          // sendMessage(Message); 
     35 
     36          // sendMessageAtTime(Message,long)
     37 
     38          // sendMessageDelayed(Message,long)
     39 
     40          // 以上方法以 post开头的允许你处理Runnable对象
     41   
     42          //sendMessage()允许你处理Message对象(Message里可以包含数据,)
     43 
     44          MyThread m = new MyThread();
     45 
     46          new Thread(m).start();
     47 
     48      }
     49   
     50 
     51      /**
     52   
     53       * 接受消息,处理消息 ,此Handler会与当前主线程一块运行
     54  
     55       * */
     56 
     57      class MyHandler extends Handler {
     58   
     59          public MyHandler() {
     60  
     61          }
     62   
     63          public MyHandler(Looper L) {
     64   
     65              super(L);
     66 
     67          }
     68 
     69          // 子类必须重写此方法,接受数据
     70   
     71         @Override
     72         public void handleMessage(Message msg) {
     73   
     74              // TODO Auto-generated method stub
     75   
     76             Log.d("MyHandler", "handleMessage......");
     77   
     78              super.handleMessage(msg);
     79   
     80              // 此处可以更新UI
     81   
     82             Bundle b = msg.getData();
     83   
     84              String color = b.getString("color");
     85   
     86              MyHandlerActivity.this.button.append(color);
     87   
     88     } 
     89 
     90    }
     91   
     92      class MyThread implements Runnable {
     93 
     94          public void run() {
     95   
     96              try {
     97 
     98                  Thread.sleep(10000);
     99 
    100              } catch (InterruptedException e) {
    101   
    102                  e.printStackTrace();
    103 
    104              }
    105  
    106             Log.d("thread.......", "mThread........");
    107   
    108              Message msg = new Message();
    109   
    110             Bundle b = new Bundle();// 存放数据
    111   
    112              b.putString("color", "我的");
    113   
    114              msg.setData(b);
    115   
    116              MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
    117   
    118          }
    119  
    120      }
    121 
    122  }
    复制代码
  • 相关阅读:
    Python 实现红绿灯
    ELK使用1-Elasticsearch使用
    CF Educational Codeforces Round 21
    Codeforces Round #408 (Div. 2)
    2017年 湘潭邀请赛(湖南)or 江苏省赛
    Tinkoff Challenge
    欧几里德算法与扩展欧几里德算法
    operator的各种问题
    树状数组 Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2) C. Fountains
    Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2) D. Field expansion
  • 原文地址:https://www.cnblogs.com/zhoug2020/p/12841410.html
Copyright © 2020-2023  润新知