处女男学Android(二)---Handler预习篇之Android的线程与UI
前言
Handler是安卓中的一个重要的概念,本篇没有记录Handler的用法,而是为了说明为什么要用Handler,算是学习Handler之前的“预习”吧。
一、Android的线程安全与UI线程
同Java类似,当一个应用程序第一次启动时,Android会同时启动一条主线程(MainThread),主线程主要负责处理与UI相关的事件,主线程通常又称作UI线程。出于性能优化考虑,Android的UI线程并不是线程安全的,这意味着如果有多线程并发操作UI组件,可能会导致线程安全问题。那么为了解决这个问题,Android制定了一条简单的规则:只允许UI线程修改Activity里的UI组件。下面可以通过一个程序的例子来验证一下,功能很简单,大致是这样的:
界面上有一个TextView和一个Button,通过点击Button来修改TextView的字体颜色,当然,这个操作要放到一个新的线程中去,也就是在其他线程中修改UI,看看会不会出错。
Layout代码(activity_main.xml):
<LinearLayout 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" android:orientation="vertical" > <TextView android:id="@+id/tv_view1" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1.0" android:text="@string/hello_world" android:textSize="20sp" android:gravity="center_horizontal|center_vertical"/> <Button android:id="@+id/btn_changeColor" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Change Text Color" /></LinearLayout>
Activity代码:
package com.example.handlertest;import android.graphics.Color;import android.os.Bundle;import android.support.v7.app.ActionBarActivity;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MainActivity extends ActionBarActivity {private TextView textView1;private Button button1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView1 = (TextView) findViewById(R.id.tv_view1);button1 = (Button) findViewById(R.id.btn_changeColor);button1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// 创建一个WorkerThread并启动Thread thread = new MyThread();thread.start();}});}class MyThread extends Thread {@Overridepublic void run() {try {Thread.sleep(3 * 1000); // 休眠3秒来模拟一个耗时任务} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}textView1.setTextColor(Color.RED); // 修改界面上TextView的字体颜色}}}
运行程序效果如下:
很明显,程序出错了,那我们看看LogCat下的错误日志:
Only the original thread that created a view hierarchy can touch its views.
这句话的意思显而易见,也验证了Google的做法,也就是说哪个线程创建的UI,哪个线程才有权利去修改这个UI。但是这也并不是针对所有的UI组件,比如ProgressBar就是一个特例,它可以在WorkerThread中修改progress属性的值,在此就不再演示。既然Google不允许我们在WorkerThread里面操作UI,那我们只好放在主线程操作UI了,界面不变,Activity的代码修改如下:
package com.example.handlertest;import android.graphics.Color;import android.os.Bundle;import android.support.v7.app.ActionBarActivity;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MainActivity extends ActionBarActivity {private TextView textView1;private Button button1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView1 = (TextView) findViewById(R.id.tv_view1);button1 = (Button) findViewById(R.id.btn_changeColor);button1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {try {Thread.sleep(10 * 1000); // 休眠10秒来模拟一个耗时任务} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}textView1.setTextColor(Color.RED); // 修改界面上TextView的字体颜色}});}}
运行程序效果如下:
运行结果是显而易见的,程序被阻塞了,Button按下之后无法弹起,用户继续点击之后又弹出了错误窗口:
XXX is not responding.
Would you like to close it?
这个也就是所谓的“ANR”问题,应用程序无法响应,对于焦躁的用户来说,这是及其槽糕和致命的体验。
二、矛盾诞生
通过上面的例子我们发现,既不能在主线程中执行UI操作以及耗时任务,容易被阻塞;同时也不能在其他非UI线程中操作UI,因为操作的结果无法反馈给主线程,那我们应该如何处理?很明显,需要一种机制来解决Android线程之间的通信问题,而正是Handler为我们提供了完整的处理机制和解决方案。
更多相关文章
- 由安装Busybox到Android过程中想到的
- Android(安卓)修改spinner 字体颜色 样式的方法
- Android的线程和线程池
- android消息机制
- 图解 Android(安卓)Handler 线程消息机制
- Android调用webservice
- Android的线程和线程池
- Android中的Handler在多线程中的使用
- Android的单线程模型