玩转AndroidHandler组件

在android中,有很多功能是不能放在onCreate或者onStart方法里面,因为这些功能相对

来说费时比较长,比如说下载一个文件,下载的过程比较长,但是如果写在Activity中,

那么这段时间Activity是完全没有响应的,那么就可以将这种处理大量数据或者耗时比较

长的东西放在一个单独的线程中来完成,即Activity是一个线程,而下载的是在另外一个

线程,那么这样就可以使得下载跟Activity之间互不影响,从而得到了良好的用户体验

 

这里有两种队列,一种是线程队列,就是用postXX方法或者removeCallbacks方法对线程对象的操作。另一种是消息队列,用sendMessage和handleMessage方法来对消息对象进行处理

 


玩转AndroidHandler组件_第1张图片
 

handler采用的是一个消息队列的方式,每一个handler都有一个与之关联的消息队列,而且是先进先出的方式执行,即:每次加入一个handler,然后拿出来,对其进行处理,然后再拿出另一个,再进行处理

 

例子一:这个例子仅仅是对线程对象进行操作的测试

Java代码 复制代码  收藏代码
  1. package org.hualang.handler;   
  2.   
  3. import android.app.Activity;   
  4. import android.os.Bundle;   
  5. import android.os.Handler;   
  6. import android.view.View;   
  7. import android.widget.Button;   
  8.   
  9. public class HandlerTest extends Activity {   
  10.        
  11.     private Button mybutton1;   
  12.     private Button mybutton2;   
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {   
  15.         super.onCreate(savedInstanceState);   
  16.         setContentView(R.layout.main);   
  17.            
  18.         mybutton1 = (Button)findViewById(R.id.mybutton1);   
  19.         mybutton2 = (Button)findViewById(R.id.mybutton2);   
  20.            
  21.         mybutton1.setOnClickListener(new Button.OnClickListener()   
  22.         {   
  23.   
  24.             @Override  
  25.             public void onClick(View arg0) {   
  26.                 /**  
  27.                  * 调用Handler的post方法,将要执行的线程对象添加到  
  28.                  * 线程队列中  
  29.                  */  
  30.                 handler.post(updateThread);   
  31.             }   
  32.                
  33.         });   
  34.         mybutton2.setOnClickListener(new Button.OnClickListener()   
  35.         {   
  36.   
  37.             @Override  
  38.             public void onClick(View v) {   
  39.                 // TODO Auto-generated method stub   
  40.                 handler.removeCallbacks(updateThread);   
  41.             }   
  42.                
  43.         });     
  44.            
  45.     }   
  46.     //创建Handler对象   
  47.     Handler handler = new Handler();   
  48.     /**  
  49.      * 将要执行的操作卸载写入线程对象的run()方法当中  
  50.      */  
  51.     Runnable updateThread = new Runnable()   
  52.     {   
  53.         public void run()   
  54.         {   
  55.             System.out.println("更新线程");   
  56.             //在run方法内部,执行postXX的方法,每隔3秒会执行一次   
  57.             handler.postDelayed(updateThread, 3000);   
  58.         }   
  59.     };   
  60. }  

 

运行结果如下:


玩转AndroidHandler组件_第2张图片
 程序解释:首先创建一个Handler对象,然后创建一个继承自Runnable接口的线程

程序首先点击按钮“开始”,于是会马上执行post方法,将执行的线程对象添加到线程队列中,这时会马上执行

Java代码 复制代码  收藏代码
  1. public void run()   
  2.         {   
  3.             System.out.println("更新线程");   
  4.             //在run方法内部,执行postXX的方法,每隔3秒会执行一次   
  5.             handler.postDelayed(updateThread, 3000);   
  6.         }  

 

然后,执行postDelayed方法,由于里面设置的间隔时间,所以每3秒会调价一个handler对象到线程队列中,并且一直执行,直到点击“结束”按钮,调用removeCallbacks方法将其从线程队列中移除

 

 

例子2:下面的例子将简单的对线程对象和消息对象进行处理

Java代码 复制代码  收藏代码
  1. package org.hualang.handlertest2;   
  2.   
  3. import android.app.Activity;   
  4. import android.os.Bundle;   
  5. import android.os.Handler;   
  6. import android.os.Message;   
  7. import android.view.View;   
  8. import android.widget.Button;   
  9. import android.widget.ProgressBar;   
  10.   
  11. public class HandlerTest2 extends Activity {   
  12.     private ProgressBar bar = null;   
  13.     private Button start = null;   
  14.     @Override  
  15.     public void onCreate(Bundle savedInstanceState) {   
  16.         super.onCreate(savedInstanceState);   
  17.         setContentView(R.layout.main);   
  18.         bar = (ProgressBar)findViewById(R.id.progress1);   
  19.         start = (Button)findViewById(R.id.start);   
  20.         start.setOnClickListener(new Button.OnClickListener()   
  21.         {   
  22.   
  23.             @Override  
  24.             public void onClick(View v) {   
  25.                 bar.setVisibility(View.VISIBLE);   
  26.                 handler.post(handlerThread);   
  27.             }   
  28.                
  29.         });   
  30.     }   
  31.     /**  
  32.      * 使用匿名内部类来复写hanlder当中的hanldrMessage方法  
  33.      * 这里的msg对象就是从线程部分发送过来的对象  
  34.      */  
  35.     Handler handler = new Handler()   
  36.     {   
  37.         public void handleMessage(Message msg)   
  38.         {   
  39.             bar.setProgress(msg.arg1);   
  40.             handler.post(handlerThread);   
  41.         }   
  42.     };   
  43.     //线程类,该类使用的是匿名内部类的方式进行声明   
  44.     Runnable handlerThread = new Runnable()   
  45.     {   
  46.         int i = 0;   
  47.         public void run()   
  48.         {   
  49.             System.out.println("开始线程");   
  50.             i = i + 10;   
  51.             /**  
  52.              * 得到一个消息对象,Message类是由android操作系统提供  
  53.              * obtainMessage方法用来得到Message对象  
  54.              */  
  55.             Message msg = handler.obtainMessage();   
  56.             /**  
  57.              * Message中有个成员变量,即msg独享的arg1参数  
  58.              * 将其值设置为i。用arg1或arg2这两个成员变量传递  
  59.              * 消息,优点是系统性能消耗较少  
  60.              */  
  61.             msg.arg1 = i;   
  62.             try {   
  63.                 //当前线程休眠1秒   
  64.                 Thread.sleep(5000);   
  65.             } catch (InterruptedException e) {   
  66.                 // TODO Auto-generated catch block   
  67.                 e.printStackTrace();   
  68.             }   
  69.             /**  
  70.              * 发送一个消息,用sendMessage是将msg加入到消息  
  71.              * 队列中。而post是将线程加入到线程队列中  
  72.              */  
  73.             handler.sendMessage(msg);   
  74.             if( i == 100)   
  75.             {   
  76.                 /**  
  77.                  * 如果i=100的时候,就将线程对象  
  78.                  * 从handler当中移除  
  79.                  */  
  80.                 handler.removeCallbacks(handlerThread);   
  81.                 bar.setVisibility(View.GONE);   
  82.             }   
  83.         }   
  84.     };   
  85. }  

  

main.xml

Xml代码 复制代码  收藏代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7. <ProgressBar  
  8.     android:id="@+id/progress1"  
  9.     android:layout_width="fill_parent"  
  10.     android:layout_height="wrap_content"  
  11.     android:visibility="gone"  
  12.     style="?android:attr/progressBarStyleHorizontal"  
  13. />  
  14. <Button  
  15.     android:id="@+id/start"  
  16.     android:layout_width="wrap_content"  
  17.     android:layout_height="wrap_content"  
  18.     android:gravity="center"  
  19.     android:text="点击我"  
  20. />  
  21. </LinearLayout>  

 

运行结果:


玩转AndroidHandler组件_第3张图片
 
玩转AndroidHandler组件_第4张图片
 

程序说明:

1、当点击按钮后,会执行按钮的onClick方法中的

Java代码 复制代码  收藏代码
  1. bar.setVisibility(View.VISIBLE);   
  2. handler.post(handlerThread);  

 将进度条显示出来,并且将线程对象加入到线程队列中

2、线程对象对先打印出一个“开始线程”,然后i的值增加10,然后从系统中获取一个Message对象

3、将i赋给Message对象的参数arg1

4、当前线程休眠5秒,然后通过sendMessage方法发送一个Message对象发送到消息队列中

5、然后再执行,通过handleMessage方法设置进度条的值,并且将其加入到进程队列中

Java代码 复制代码  收藏代码
  1. Handler handler = new Handler()   
  2.     {   
  3.         public void handleMessage(Message msg)   
  4.         {   
  5.             bar.setProgress(msg.arg1);   
  6.             handler.post(handlerThread);   
  7.         }   
  8.     };  

 6、循环执行,直到i=100,进度条隐藏,并将线程对象从线程队列中取出

对于Handler来说,它和与它调用它的Activity是出于同一线程的,上一篇并没有调用线程
的start方法,而是直接执行的run方法。而启动一个线程是调用的start方法

上一篇博客里的对Handler的调用时通过Runnable接口来实现的,并且是通过run()方法来启动那个线程的,而且是Activity和Handler是两个线程独立运行的,互补干扰,但是实际情况确实,Activity所在的线程和Handler的线程是同一个线程,下面进行一下实验

 

Java代码 复制代码  收藏代码
  1. package org.hualang.handlertest3;   
  2.   
  3. import android.app.Activity;   
  4. import android.os.Bundle;   
  5. import android.os.Handler;   
  6. import android.util.Log;   
  7.   
  8. public class HandlerTest3 extends Activity {   
  9.     private Handler handler = new Handler();   
  10.     private String TAG = "System.out";   
  11.     @Override  
  12.     public void onCreate(Bundle savedInstanceState) {   
  13.         super.onCreate(savedInstanceState);   
  14.         handler.post(r);   
  15.         setContentView(R.layout.main);   
  16.         //Thread t = new Thread(r);   
  17.         //t.start();   
  18.            
  19.         Log.d(TAG,"Activity id:"+Thread.currentThread().getId());   
  20.         Log.d(TAG,"Activity name:"+Thread.currentThread().getName());   
  21.            
  22.     }   
  23.     Runnable r = new Runnable()   
  24.     {   
  25.         public void run()   
  26.         {   
  27.             Log.d(TAG,"Handler id:"+Thread.currentThread().getId());   
  28.             Log.d(TAG,"Handler name:"+Thread.currentThread().getName());   
  29.             try {   
  30.                 Thread.sleep(5000);   
  31.             } catch (InterruptedException e) {   
  32.                 // TODO Auto-generated catch block   
  33.                 e.printStackTrace();   
  34.             }   
  35.         }   
  36.     };   
  37. }  

 

运行结果:

证明是同一个线程的两个依据:

①Activity的id或name和Handler的id或name是同样的

②我设置了

 handler.post(r);
 setContentView(R.layout.main);

也就是,如果执行后马上显示文本信息,那么可以证明它们不在同一个线程,但是实际情况是要先执行了handler后5秒,才显示文本信息,说明它们在同一线程


玩转AndroidHandler组件_第5张图片
 
玩转AndroidHandler组件_第6张图片
 

如果将代码改为

Java代码 复制代码  收藏代码
  1. //handler.post(r);   
  2. setContentView(R.layout.main);   
  3. Thread t = new Thread(r);   
  4. t.start();  

 再次执行,运行结果如下,通过start启动线程,它们不在同一个线程中
玩转AndroidHandler组件_第7张图片
 

 

----------------------------------------------------------------------------------------------------------------

Looper即循环的从队列当中取得消息的功能,如果在线程中使用Looper
那么,就会循环的从线程队列当中取得消息并处理,如果队列当中没有消息的话
,线程就进入了休眠状态

Looper很少自己创建,在Android中给出了HandlerThread类,并且具有循环取得并处理消息的功能

 

下面来实现这种Activity和Handler分别在两个线程中执行,实现真正的异步处理

Java代码 复制代码  收藏代码
  1. package org.hualang.handlertest;   
  2.   
  3. import android.app.Activity;   
  4. import android.os.Bundle;   
  5. import android.os.Handler;   
  6. import android.os.HandlerThread;   
  7. import android.os.Looper;   
  8. import android.os.Message;   
  9. import android.util.Log;   
  10.   
  11. public class HandlerTest4 extends Activity {   
  12.     /** Called when the activity is first created. */  
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {   
  15.         super.onCreate(savedInstanceState);   
  16.         setContentView(R.layout.main);   
  17.         Log.d("System.out","Activity所在线程的id:"+Thread.currentThread().getId());   
  18.         /**  
  19.          * 生成一个HandlerThread对象,实现了使用Looper来处理消息队列的功能  
  20.          * 这个类由Android应用程序框架提供  
  21.          */  
  22.         HandlerThread handlerThread = new HandlerThread("handlerThread");   
  23.         handlerThread.start();   
  24.         MyHandler handler = new MyHandler(handlerThread.getLooper());   
  25.         Message msg = handler.obtainMessage();   
  26.         /**  
  27.          * 将Message对象发送到目标对象  
  28.          * 所谓的目标对象,就是生成该msg对象的handler对象  
  29.          */  
  30.         msg.sendToTarget();   
  31.     }   
  32.     class MyHandler extends Handler   
  33.     {   
  34.         public MyHandler()   
  35.         {      
  36.         }   
  37.         public MyHandler(Looper looper)   
  38.         {   
  39.             super(looper);   
  40.         }   
  41.         public void handleMessage(Message msg)   
  42.         {   
  43.             Log.d("System.out""handler所在线程的id:"+Thread.currentThread().getId());   
  44.         }   
  45.     }   
  46. }  

 

运行结果:


玩转AndroidHandler组件_第8张图片
 

可以看到,Activity和Handler是在两个不同的线程中执行的,这样就是实现了真正的异步处理

1、首先创建一个HandlerThread对象,这个HandlerThread类实现了循环的取得消息并处理

2、用start方法启动一个新线程

3、创建MyHandler类,里面传递的参数即Looper方法所获得的可以循环在队列中取得的消息

4、MyHandler类调用的是带参数Looper的构造方法,并且实现了handlerMessage方法

5、获取一个Message对象

6、将这个对象发送到生成该msg对象的handler对象,从而执行了handleMessage方法

 

-----------------------------------------------------------------------------------------------------

最后,将说一下Message里传送的数据的使用,这里的msg对象可以使用arg1,arg2或者obj

arg1 and arg2 are lower-cost alternatives to using setData() if you only need to store a few integer values. 也就是相对于setData()方法,如果你仅仅保存一些简单的整形数的话,arg1,arg2对资源的要求较低,而setData()方法一般用于传递大量数据的时候会用到

 

如果是msg.obj,那么可以这样用

msg.obj = "Welcome to china";

然后在handleMessage()方法中用

String str = (String)msg.obj;来获得传递的值

 

如果使用getData()方法的话,需要用到Bundle对象来传递,下面用个例子来说明

Java代码 复制代码  收藏代码
  1. Bundle b = new Bundle();   
  2. b.putInt("age"22);   
  3. b.putString("name""loulijun");   
  4. msg.setData(b);   
  5. msg.sendToTarget();  

 

上面的代码用来设置要传递的数据

下面的代码用来获取Bundle传递过来的数据并且用Toast来显示

Java代码 复制代码  收藏代码
  1. Bundle b = msg.getData();   
  2.             int age = b.getInt("age");   
  3.             String name = b.getString("name");   
  4.             Toast toast = Toast.makeText(getApplicationContext(), "age="+age+"name="+name, Toast.LENGTH_LONG);   
  5.             toast.show();  

 

package org.hualang.handlertest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

public class HandlerTest4 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.d("System.out","Activity所在线程的id:"+Thread.currentThread().getId());
        /**
         * 生成一个HandlerThread对象,实现了使用Looper来处理消息队列的功能
         * 这个类由Android应用程序框架提供
         */
        HandlerThread handlerThread = new HandlerThread("handlerThread");
        /**
         * 使用HandlerThread的getLooper()方法之前,必须先调用该类的start()方法,否则是个null,会报错
         */
        handlerThread.start();
        MyHandler handler = new MyHandler(handlerThread.getLooper());
        Message msg = handler.obtainMessage();
        /**
         * 将Message对象发送到目标对象
         * 所谓的目标对象,就是生成该msg对象的handler对象
         */
        //msg.obj = "Hello world";
        Bundle b = new Bundle();
        b.putInt("age", 22);
        b.putString("name", "loulijun");
        msg.setData(b);
        msg.sendToTarget();
    }
    class MyHandler extends Handler
    {
    public MyHandler()
    {
    }
    public MyHandler(Looper looper)
    {
    super(looper);
    }
    public void handleMessage(Message msg)
    {
    //String str = (String)msg.obj
    Bundle b = msg.getData();
    int age = b.getInt("age");
    String name = b.getString("name");
    Toast toast = Toast.makeText(getApplicationContext(), "age="+age+"name="+name, Toast.LENGTH_LONG);
    toast.show();
    Log.d("System.out", "handler所在线程的id:"+Thread.currentThread().getId());
    }
    }
}

 运行结果:


玩转AndroidHandler组件_第9张图片
 

你可能感兴趣的:(玩转AndroidHandler组件)