在初学handler的时候不得不说自己当时真是毫无悟性,硬是把handler和开通子线程混为一谈,当时脑子混乱的不行,也是看了一些博客后理清了头脑。
android是不允许在主线程中进行耗时操作的,否则会报错,出现ANR(Application Not Responding)现象,这时候必须开一个子线程,此时是不需要必须有handler的,而如果我们在子线程中去操作UI,那么程序就回给我们抛出异常。这时候我们就需要在子线程中通过handler来更新UI。
现在小小总结一下,自己都觉得可笑。
现在通过一个点击按钮下载一张图片的demo来详解一下handler。
Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,所以Handler把压入消息队列有两大体系,Post和Message。
Post
对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。
在Handler中,关于Post方式的方法有:
- boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。
- boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
- boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
- void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。
Message
Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重 写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。
对于Message对象,一般使用Message.obtain()这个静态的方法或者 Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的, 才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。 Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是 调用的Message.obtain()。 Message.obtain()方法具有多个重载方法,大致可以分为为两类,一类是无需传递Handler对象,对于这类的方法,当填充好消息后,需要调用Handler.sendMessage()方法来发送消息到消息队列中。第二类需要传递一个Handler对象,这类方法可以直接使用 Message.sendToTarget()方法发送消息到消息队列中,这是因为在Message对象中有一个私有的Handler类型的属性 Target,当时obtain方法传递进一个Handler对象的时候,会给Target属性赋值,当调用sendToTarget()方法的时候,实 际在它内部还是调用的Target.sendMessage()方法。
Handler中,与Message发送消息相关的方法有:
- Message obtainMessage():获取一个Message对象。
- boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
- boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行。
- boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行。
- boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message对象到消息队列中,在UI线程取到消息后,延迟执行。
- void removeMessage():从消息队列中移除一个未响应的消息。
DownloadContent.java
<span style="font-size:14px;">public class DownloadContent {
private ProgressDialog dialog;
private static int IS_FINISH = 1;
public DownloadContent(Context context) {
dialog = new ProgressDialog(context);
dialog.setTitle("提示");
dialog.setMessage("正在玩命加载数据......");
}
@SuppressLint("HandlerLeak")
public void downLoad(final String path, final DownloadCallback callback) {
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
byte[] result = (byte[]) msg.obj;
callback.loadContent(result);
if (msg.what == IS_FINISH) {
dialog.dismiss();
}
}
};
</span>
<span style="font-size:14px;">/*开通子线程有两种方法:<span style="font-family: verdana, 'ms song', Arial, Helvetica, sans-serif;">用 Runnable 或者 Thread </span><p>创建线程哪种方式更好呢?什么情况下使用它?这个问题很容易回答,如果你知道Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口更好了。*/</p>
//调用Runnable接口,写法一
// class MyThread implements Runnable {
// @Override
//public void run() {
// HttpClient httpClient = new DefaultHttpClient();
// HttpGet httpGet = new HttpGet(path);
// HttpResponse response;
// try {
// response = httpClient.execute(httpGet);
// if (response.getStatusLine().getStatusCode() == 200) {
// byte[] result = EntityUtils.toByteArray(response
// .getEntity());
// Message message = Message.obtain();
// message.obj = result;
// message.what = IS_FINISH;
// handler.sendMessage(message);
// }
// } catch (Exception e) {
//
// e.printStackTrace();
// } finally {
// httpClient.getConnectionManager().shutdown();
// }
//}
//}
//new Thread(new MyThread()).start();</span>
</pre><pre name="code" class="java"><span style="font-size:14px;"><pre name="code" class="java" style="color: rgb(57, 57, 57); font-size: 14px; line-height: 21px;"> /*<span style="font-family: verdana, 'ms song', Arial, Helvetica, sans-serif;">调用Runnable接口,写法二</span></span>
<span style="font-size:14px;"> new Thread() {
public void run() {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(path);
HttpResponse response;
try {
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
byte[] result = EntityUtils.toByteArray(response
.getEntity());
Message message = Message.obtain();
message.obj = result;
message.what = IS_FINISH;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.getConnectionManager().shutdown();
}
};
}.start();*/</span>
<span style="font-size:14px;">//继承Thread</span>
<span style="font-size:14px;"> class MyThread extends Thread{
public void run() {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(path);
HttpResponse response;
try {
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
byte[] result = EntityUtils.toByteArray(response
.getEntity());
Message message = Message.obtain();
message.obj = result;
message.what = IS_FINISH;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.getConnectionManager().shutdown();
}
};
}
MyThread myThread = new MyThread();
myThread.start();
</span>
<span style="font-size:14px;"> dialog.show();
}
public interface DownloadCallback {
public void loadContent(byte[] result);
}
}</span>
MainActivity:
<span style="font-size:14px;">public class MainActivity extends ActionBarActivity {
private final String pathString = "http://attachments.gfan.com/forum/attachments2/day_120709/120709183388fdedffcd0878e9.jpg";
private Button button;
private ImageView image;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.btn);
image = (ImageView) findViewById(R.id.img);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DownloadContent donwContent = new DownloadContent(MainActivity.this);
donwContent.downLoad(pathString, new DownloadCallback() {
@Override
public void loadContent(byte[] result) {
Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
image.setImageBitmap(bitmap);
}
});
}
});
}
}</span>
<span style="font-size:14px;">public class PostActivity extends Activity {
private Button btn;
private ImageView img;
private ProgressDialog dialog;
private static Handler handler = new Handler();
private String path="http://img5.imgtn.bdimg.com/it/u=3829988479,3729798728&fm=21&gp=0.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btn);
img = (ImageView) findViewById(R.id.img);
dialog = new ProgressDialog(getApplicationContext());
dialog.setTitle("提示");
dialog.setMessage("正在努力下载");
dialog.setCancelable(false);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new MyThread()).start();
}
});
}
public class MyThread implements Runnable{
private Bitmap bitmap;
private InputStream is;
public void run(){
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
int code = conn.getResponseCode();
if(code==200){
is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
handler.post(new Runnable() {
public void run() {
img.setImageBitmap(bitmap);
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}</span>