多线程,多线程,多线程!重要的事情多说几遍。在Android编程中不可能不遇到多线程的问题,我们都知道UI界面即主线程不能做耗时的操作,必须要利用多线程的特性进行耗时的操作,然后UI界面负责实时更新就哦了。
BUT!Thread,Handler都是什么鬼,我迷糊很久很久啦!这不,课题需要,今天好好整理了一下思路,结合课本和示例,终于弄清楚这都是什么了,于是迫不及待写了这一Blog!
First,Thread这里我们就不多说了,学过JAVA的筒子应该都知道,这就是java中用来另起线程的类,继承Thread,覆盖run()方法即另起一个新线程,在run()方法中进行耗时的操作。那么问题来了,Handler什么?是Android里特有的另起线程的类吗?和Thread一样吗?非也!下面我来详细讲一讲Handler这哥们!
Android的消息传递机制是另一种形式的“事件处理”,这个机制是为了解决Android应用的多线程的问题——Android平台只允许UI线程修改Activity里的UI自检,这样就导致一种矛盾,耗时的操作不能再UI线程中执行,另起的线程又不能修改Activity里的UI组件,这让老夫如何是好呢!憋捉急,Handler的消息传递机制帮你实现!
Handler类的主要作用:
1、在新启动的线程中发送消息
2、在主线程中获取、处理消息
即:在主线程中出现如下代码:
final Handler myHandler = new Handler(){ public void handleMessage(Message msg){ if(msg.what == 0x123){ /** * 修改UI的相关操作 */ } } };然后另起一个新线程发送消息:
new Timer().schedule(new TimerTask(){ @Override public void run() { // TODO Auto-generated method stub myHandler.sendEmptyMessage(0x123); } }, 0,1200);上述new TimerTask()本质就是启动一条新线程。
上述就是handler在android的多线程干了什么事情的演示。
貌似说到这里,我可以关闭页面了,不行,我们必须深入了解Handler的工作原理,学习与Handler一起工作的几个组件:
Message:Handler接受和处理的消息对象(上面代码中的0x123);
Looper:每个线程只能拥有一个Looper,它的loop()方法负责读取MessageQueue中的消息,读到消息之后就把消息发送给该消息的Handler进行处理,BUT,我们在上面的代码中好像没有发现这个Looper,还有他的loop()方法,憋捉急,一会儿说;
MessageQueue:消息队列,先进先出的方式来管理Message,程序创建Looper对象时会在它的构造器中创建MessageQueue对象;
我们之前说,Handler有两个作用,发送消息和处理消息,程序用Handler发送的消息送到指定的MessageQueue,也就是说,如果希望Handler正常工作,则在当前线程中必须有一个MessageQueue,否则消息没有地方保存了,不过MessageQueue是Looper管理的,也就是说,如果希望Handler正常工作,在当前线程必须有一个Looper对象!如果希望Handler正常工作,在当前线程必须有一个Looper对象!重要的事情多说几遍,反正复制粘贴。
那么怎么样让当前线程必须有一个Looper对象,而我们前面的代码怎么没有呢!答案来了:
主UI线程中,系统已经初始化了一个Looper对象,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可以通过Handler来发送消息、处理消息,这就是为什么我们上面的代码没有看到Looper这个鬼;
程序员自己启动的子线程,程序员必须自己创建一个Looper对象,程序员自己启动的子线程,程序员必须自己创建一个Looper对象,并启动它,创建Looper对象调用它的prepare()方法即可。然后调用Looper的静态loop()方法来启动它。loop()方法使用一个死循环不断取出MessageQueue中的消息,将取出的消息分给该消息对应Handler进行处理。
在线程中使用Handler的步骤如下:
1、调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象的时候,它的构造器也创建了与之配套的MessageQueue
2、有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息
3、调用Looper的loop()方法启动Looper
下面看一个Demo,帮助大家理解:
用户输入一个数值上限,点击“计算”按钮时,该应用会把该上限数值发送到新启动的线程,在新线程中计算该范围内的所有质数(特别耗时的操作):
package com.example.handlertest; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { EditText text; Button cal; TextView show; mThread calThread; class mThread extends Thread{ public Handler mHandler; @Override public void run() { // TODO Auto-generated method stub mHandler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub if(msg.what == 0x123){ int upper = msg.getData().getInt("KEY"); List<Integer> list = new ArrayList<Integer>(); outer: for(int i=2; i<upper ; i++){ for(int j=2; j<i;j++){ if(i!=2 && i%j==0){ continue outer;//想跳出两层循环,则需要这样处理 } } list.add(i); } Toast.makeText(MainActivity.this, list.toString(), Toast.LENGTH_LONG).show(); show.setText(list.toString()); } } }; } } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); text = (EditText) findViewById(R.id.editText1); cal = (Button) findViewById(R.id.button1); calThread = new mThread(); calThread.start(); cal.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub int x = Integer.parseInt(text.getText().toString()); Message msg = new Message(); msg.what = 0x123; Bundle bundler = new Bundle(); bundler.putInt("KEY", x); msg.setData(bundler); calThread.mHandler.sendMessage(msg); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
报错信息显示,没有Looper呀亲!
所以代码改为如下:
class mThread extends Thread{ public Handler mHandler; @Override public void run() { // TODO Auto-generated method stub Looper.prepare(); mHandler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub if(msg.what == 0x123){ int upper = msg.getData().getInt("KEY"); List<Integer> list = new ArrayList<Integer>(); outer: for(int i=2; i<upper ; i++){ for(int j=2; j<i;j++){ if(i!=2 && i%j==0){ continue outer;//想跳出两层循环,则需要这样处理 } } list.add(i); } Toast.makeText(MainActivity.this, list.toString(), Toast.LENGTH_LONG).show(); show.setText(list.toString()); } } }; Looper.loop(); } }