Handler线程间通信
1 package com.hixin.appexplorer; 2 3 import java.util.List; 4 5 import android.app.Activity; 6 import android.app.ProgressDialog; 7 import android.content.Context; 8 import android.content.pm.PackageInfo; 9 import android.content.pm.PackageManager; 10 import android.os.Bundle; 11 import android.os.Handler; 12 import android.os.Message; 13 import android.view.LayoutInflater; 14 import android.view.View; 15 import android.view.ViewGroup; 16 import android.view.Window; 17 import android.view.WindowManager; 18 import android.widget.BaseAdapter; 19 import android.widget.GridView; 20 import android.widget.ImageView; 21 import android.widget.TextView; 22 23 public class MainActivity extends Activity implements Runnable { 24 25 GridView gv; 26 private List<PackageInfo> packageInfos; 27 private ProgressDialog pd; 28 private Handler mHandler = new Handler() { 29 @Override 30 public void handleMessage(Message msg) { 31 // TODO Auto-generated method stub 32 super.handleMessage(msg); 33 gv.setAdapter(new GridViewAdapter(MainActivity.this)); 34 pd.dismiss(); 35 setProgressBarIndeterminateVisibility(false); 36 } 37 38 }; 39 @Override 40 protected void onCreate(Bundle savedInstanceState) { 41 super.onCreate(savedInstanceState); 42 // requestWindowFeature(Window.FEATURE_NO_TITLE); 43 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 44 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 45 setContentView(R.layout.show_app_grid); 46 47 gv = (GridView)this.findViewById(R.id.gv_apps); 48 pd = ProgressDialog.show(this,"请稍候...", "正在搜索你所安装的应用程序",true,false); 49 setProgressBarIndeterminateVisibility(true); 50 Thread t = new Thread(this); 51 t.start(); 52 } 53 54 @Override 55 public void run() { 56 // TODO Auto-generated method stub 57 packageInfos = getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); 58 try { 59 Thread.sleep(2000); 60 } catch (InterruptedException e) { 61 // TODO Auto-generated catch block 62 e.printStackTrace(); 63 } 64 mHandler.sendEmptyMessage(0); 65 } 66 67 68 class GridViewAdapter extends BaseAdapter{ 69 70 LayoutInflater inflater; 71 public GridViewAdapter(Context context) { 72 inflater = LayoutInflater.from(context); 73 } 74 @Override 75 public int getCount() { 76 // TODO Auto-generated method stub 77 return packageInfos.size(); 78 } 79 80 @Override 81 public Object getItem(int position) { 82 // TODO Auto-generated method stub 83 return packageInfos.get(position); 84 } 85 86 @Override 87 public long getItemId(int position) { 88 // TODO Auto-generated method stub 89 return position; 90 } 91 92 @Override 93 public View getView(int position, View convertView, ViewGroup parent) { 94 // TODO Auto-generated method stub 95 96 View view = inflater.inflate(R.layout.gv_item, null); 97 TextView tv = (TextView)view.findViewById(R.id.gv_item_appname); 98 ImageView iv = (ImageView)view.findViewById(R.id.gv_item_icon); 99 tv.setText(packageInfos.get(position).applicationInfo.loadLabel(getPackageManager())); 100 iv.setImageDrawable(packageInfos.get(position).applicationInfo.loadIcon(getPackageManager())); 101 102 return view; 103 } 104 105 } 106 107 } 108 109
当需要显示的项目很多,会造成很大的延迟。这时候屏幕会一直黑屏。给用户很不好的体验。应该在获取信息的时候,显示一些进度信息给用户看,增加用户体验。
程序在期间获取安装程序信息,主界面上显示进度条,当信息获取完毕之后(获取信息在新线程里执行),发送一条消息通知主线程,主线程关掉进度条,加载列表。
基本概念
Looper:每一个线程都可以产生一个Looper,用来管理线程的Message,Looper对象会建立一个MessgaeQueue数据结构来存放message。
Handler:与Looper沟通的对象,可以push消息或者runnable对象到MessgaeQueue,也可以从MessageQueue得到消息。
线程A的Handler对象引用可以传递给别的线程,让别的线程B或C等能送消息来给线程A。
线程A的Message Queue里的消息,只有线程A所属的对象可以处理。
注意:Android里没有global的MessageQueue,不同进程(或APK之间)不能通过MessageQueue交换消息。
本例中线程A就是UI线程,线程B就是新建的处理线程
在handler.obtainMessage()的参数是这样写的:
Message android.os.Handler.obtainMessage(int what, int arg1, int arg2, Object obj)
public final Message obtainMessage (int what, int arg1, int arg2, Object obj)
Since: API Level 1
Same as obtainMessage(), except that it also sets the what, obj, arg1,and arg2 values on the returned Message.
Parameters
what Value to assign to the returned Message.what field.
arg1 Value to assign to the returned Message.arg1 field.
arg2 Value to assign to the returned Message.arg2 field.
obj Value to assign to the returned Message.obj field.
而Handler中obtainMessage与new Message的区别:
obtainmessage()是从消息池中拿来一个msg 不需要另开辟空间new
new需要重新申请,效率低,obtianmessage可以循环利用;
1 //use Handler.obtainMessage(),instead of msg = new Message(); 2 //because if there is already an Message object,that not be used by 3 //any one ,the system will hand use that object,so you don't have to 4 //create and object and allocate memory. 5 //it is also another example of object recycling and reusing in android. 6 Message msg = mHandler.obtainMessage(); 7 msg.what = UPDATE_LISTVIEW; 8 msg.obj = current + "/" + total + "songs"; 9 //this method is called from worker Thread,so we cannot update UI from here. 10 msg.sendToTarget();
1 Message msg = handler.obtainMessage(); 2 msg.arg1 = i; 3 msg.sendToTarget(); 4 5 6 Message msg=new Message(); 7 msg.arg1=i; 8 handler.sendMessage(msg);
Handler相关说明:
解释:安卓的UI线程(即OnCreate函数创建的线程)是线程非安全的。也就是说,在UI线程中,使用sleep这样的函数会导致整个线程延迟,但是我们在安卓开发中,往往会经常遇到一些延迟比较厉害的操作,(例如通过HTTP获取数据信息)如果放在主线程中,则会影响UI界面的渲染。但是如果另外新开一个线程,则由于UI线程只能在主线程中修改,而导致无法修改主线程的UI界面。这个时候Handler就出来解决这个问题。
handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 也就是说Handler对象初始化后,就默认与对它初始化的进程的消息队列绑定,因此可以利用Handler所包含的消息队列,制定一些操作的顺序。
Handler主要两大作用:
1. 提供post操作。post操作主要将Runnable对象放进主线程(UI)线程中的队列中操作。post还支持延迟操作。使用post后,Runnable是按照队列的形式逐个执行的。
2. handlerMessage操作。主要用于新开一个线程与主线程中的通信。新开的线程执行完毕后,可以通过sendMessage给主线程发送消息,并且传递一些参数,然后主线程就可以修改UI界面了。
Handler提供的函数:
post类方法允许你排列一个Runnable对象到主线程队列中:
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中:
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
应用实例:
1,传递Message。用于接受子线程发送的数据, 并用此数据配合主线程更新UI。
在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,用Handler中的handlerMessge方法处理传过来的数据信息,并操作UI。类sendMessage(Message msg)方法实现发送消息的操作。 在初始化Handler对象时重写的handleMessage方法来接收Messgae并进行相关操作。
2,传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。
Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。
另外,Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应。而Runnable是一个接口,Thread是Runnable的子类。所以说,他俩都算一个进程。xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/startButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="start" android:layout_centerInParent="true" /> <Button android:id="@+id/endButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="end" android:layout_below="@id/startButton" /> </RelativeLayout>
package com.example.androidexpriment; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { private String TAG = "MainActivity"; //声明两个按钮控件 private Button startButton = null; private Button endButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器 startButton = (Button)findViewById(R.id.startButton); startButton.setOnClickListener(new StartButtonListener()); endButton = (Button)findViewById(R.id.endButton); endButton.setOnClickListener(new EndButtonListener()); Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); } class StartButtonListener implements OnClickListener{ @Override public void onClick(View v) { //调用Handler的post方法,将要执行的线程对象添加到队列当中 handler.post(updateThread); } } class EndButtonListener implements OnClickListener{ @Override public void onClick(View v) { handler.removeCallbacks(updateThread); } } //创建一个Handler对象 Handler handler = new Handler(); //将要执行的操作写在线程对象的run方法当中 Runnable updateThread = new Runnable(){ @Override public void run() { Log.i(TAG,"UpdateThread"); Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); //在run方法内部,执行postDelayed或者是post方法 handler.postDelayed(updateThread, 3000); } }; }
点击start后,程序的运行结果就是每隔3秒钟,就会在控制台打印一行UpdateTread。这是因为实现了Runnable接口的updateThread对象进入了空的消息队列即被立即执行run方法,而在run方法的内部,又在3000ms之后将其再次发送进入消息队列中。
注意这种方法创建Handler对象并不需要重写handlerMessage方法。
从输出结果能看出来:
post方法虽然发送的是一个实现了Runnable接口的类对象,但是它并非创建了一个新线程,而是执行了该对象中的run方法。也就是说,整个run中的操作和主线程处于同一个线程。这样对于那些简单的操作,似乎并不会影响。但是对于耗时较长的操作,就会出现“假死”。为了解决这个问题,就需要使得handler绑定到一个新开启线程的消息队列上,在这个处于另外线程的上的消息队列中处理传过来的Runnable对象和消息。Runnable对象只是作为一个封装了操作的对象被传递,并未产生新线程。
下面这种写法也是可以的:
package com.example.androidexpriment; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { TestThread t = null; private String TAG = "MainActivity"; //声明两个按钮控件 private Button startButton = null; private Button endButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器 startButton = (Button)findViewById(R.id.startButton); startButton.setOnClickListener(new StartButtonListener()); endButton = (Button)findViewById(R.id.endButton); endButton.setOnClickListener(new EndButtonListener()); Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); t= new TestThread(1); } class StartButtonListener implements OnClickListener{ @Override public void onClick(View v) { //调用Handler的post方法,将要执行的线程对象添加到队列当中 handler.post(t); } } class EndButtonListener implements OnClickListener{ @Override public void onClick(View v) { handler.removeCallbacks(t); } } //创建一个Handler对象 Handler handler = new Handler(); class TestThread extends Thread{ int prime; TestThread(int prime) { this.prime = prime; } @Override public void run() { //在run方法内部,执行postDelayed或者是post方法 handler.postDelayed(t, 3000); Log.i(TAG,"TestThread-->" + Thread.currentThread().getId()); } } }
虽然创建了一个Thread,但是并没有执行Thread的start()方法。考虑到Thread和Runnable之间的关系,上面的两种代码并无实质差别,所以logcat中甚至都没出现启动新线程的日志。
然而,如果稍加修改:加上启动方法
class StartButtonListener implements OnClickListener{ @Override public void onClick(View v) { //调用Handler的post方法,将要执行的线程对象添加到队列当中 handler.post(t); t.start(); } }
可以明显看到,虽然启动了新线程,但post仍然可以把这个线程推到主线程里面去,线程由虚拟机自动结束。
所以,在UI线程(主线程)中:
mHandler=new Handler();
mHandler.post(new Runnable(){
void run(){
//执行代码..
}
});
这个线程其实是在UI线程之内运行的,并没有新建线程。
常见的新建线程的方法是:参考J2SE文档的
1、
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
-
The following code would then create a thread and start it running:
PrimeThread p = new PrimeThread(143); p.start();
2、
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
-
The following code would then create a thread and start it running:
PrimeRun p = new PrimeRun(143); new Thread(p).start();
尽量按照上面给出的两种方式做,不要受网上影响简单的从Threa创建,那样不能做到传递参数。
static void |
sleep(long millis)
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.
|
代码验证:
package com.example.androidexpriment; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { private String TAG = "MainActivity"; //声明两个按钮控件 private Button startButton = null; private Button endButton = null; TestThread t = null; int flag = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器 startButton = (Button)findViewById(R.id.startButton); startButton.setOnClickListener(new StartButtonListener()); endButton = (Button)findViewById(R.id.endButton); endButton.setOnClickListener(new EndButtonListener()); Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); t= new TestThread(1); } class StartButtonListener implements OnClickListener{ @Override public void onClick(View v) { //调用Handler的post方法,将要执行的线程对象添加到队列当中 t.start(); } } class EndButtonListener implements OnClickListener{ @Override public void onClick(View v) { handler.sendEmptyMessage(33); flag = 5; } } class TestThread extends Thread{ int prime; TestThread(int prime) { this.prime = prime; } @Override public void run() { //在run方法内部,执行postDelayed或者是post方法 try { while(true) { Log.i(TAG,"TestThread-->" + Thread.currentThread().getId()); //handler.sendEmptyMessageDelayed(22,3000); Thread.sleep(3000); handler.sendEmptyMessage(22); if(flag == 5) //线程最佳的退出方法,就是自杀,也就是在线程的函数里面自然的return 出来 return; } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //创建一个Handler对象 Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); switch(msg.what) { case 22: Log.i(TAG,"StartButton"); Log.i(TAG,"Handler-->" + Thread.currentThread().getId()); break; case 33: Log.i(TAG,"EndButton"); Log.i(TAG,"Handler-->" + Thread.currentThread().getId()); break; } } }; }