Android中级篇之Handler详解(上)
2015-05-30 00:51
411 查看
在详解之前,贴出我们的基本代码。
在子线程中更新UI会抛出什么样的异常?
首先,当我们在子线程中直接更新UI会抛出什么异常呢。运行我们的程序,贴出我们的报错代码。
产生这个异常的根本原因是Android中不允许在子线程中直接更新UI,那么我们要更新UI可以怎么做呢?接下来,我们通过handler这种机制来更新UI。
我们这里呢使用了handler一个比较简单点的更新UI的方法。运行我们的程序,没有报错。那么为什么它能执行成功呢,这是因为我们这个线程是执行在UI线程里的,所以它是可以直接更新UI的。
修改我们的布局文件
修改我们的
上面程序运行正常。
注意我们不能直接
运行我们的程序,发现会出错。这是因为我们不能直接这样频繁
上面这段代码中,我们可以看到
其中
获取
这里,我们不但可以使用
还可以使用
发送
我们通常使用
但是还有一种方法也可以发送一个消息。那就是使用
解释原因
通过异常我们可以看到,这个是没有
为什么
那么我们刚刚也说了,我们必须通过
解决办法
我们来看看
源码
实质上还是通过
y1笑而过的博客园
y1笑而过的新浪博客
y1笑而过的安卓巴士博客
y1笑而过的51CTO技术博客
layout_main.xml布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" /> </LinearLayout>
MainActivity
public class MainActivity extends Activity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.layout_main); initView(); initEvent(); } private void initView() { textView = (TextView) findViewById(R.id.textview); new Thread() { public void run() { try { Thread.sleep(1000); textView.setText("更新"); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } private void initEvent() { } }
在子线程中更新UI会抛出什么样的异常?
首先,当我们在子线程中直接更新UI会抛出什么异常呢。运行我们的程序,贴出我们的报错代码。
05-29 22:43:10.524: E/AndroidRuntime(20222): FATAL EXCEPTION: Thread-13219 05-29 22:43:10.524: E/AndroidRuntime(20222): Process: com.scp.handler, PID: 20222 05-29 22:43:10.524: E/AndroidRuntime(20222): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6386) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:874) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.view.View.requestLayout(View.java:16500) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.checkForRelayout(TextView.java:6714) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.setText(TextView.java:3893) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.setText(TextView.java:3744) 05-29 22:43:10.524: E/AndroidRuntime(20222): at android.widget.TextView.setText(TextView.java:3719) 05-29 22:43:10.524: E/AndroidRuntime(20222): at com.scp.handler.MainActivity$1.run(MainActivity.java:25)
产生这个异常的根本原因是Android中不允许在子线程中直接更新UI,那么我们要更新UI可以怎么做呢?接下来,我们通过handler这种机制来更新UI。
handler.post用法
public class MainActivity extends Activity { private TextView textView; private Handler handler = new Handler();//Handler @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.layout_main); initView(); initEvent(); } private void initView() { textView = (TextView) findViewById(R.id.textview); new Thread() { public void run() { try { Thread.sleep(1000); handler.post(new Runnable() { @Override public void run() { textView.setText("更新"); } }); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } private void initEvent() { } }
我们这里呢使用了handler一个比较简单点的更新UI的方法。运行我们的程序,没有报错。那么为什么它能执行成功呢,这是因为我们这个线程是执行在UI线程里的,所以它是可以直接更新UI的。
handler.postDelayed用法
修改我们的布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" /> <ImageView android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> </LinearLayout>
修改我们的
MainActivity,各位自己准备三张图片,我们做一个图片轮播的程序。
public class MainActivity extends Activity { private TextView textView; private ImageView imageView; private int[] imgIds = new int[] { R.drawable.img1, R.drawable.img2, R.drawable.img3 }; private int index;// 图片索引 private MyRunnable myRunnable = new MyRunnable(); private Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.layout_main); initView(); initEvent(); } private void initView() { textView = (TextView) findViewById(R.id.textview); imageView = (ImageView) findViewById(R.id.imageview); handler.postDelayed(myRunnable, 1000); } class MyRunnable implements Runnable { @Override public void run() { index++; index = index % 3; imageView.setImageResource(imgIds[index]); /** * r:Runnable对象 * * delayMillis:每隔一段时间执行一次,单位是毫秒 */ handler.postDelayed(myRunnable, 1000); } } private void initEvent() { } }
上面程序运行正常。
注意我们不能直接
setText
//省略上面代码 public void run() { index++; index = index % 3; imageView.setImageResource(imgIds[index]); textView.setText(index);//这是我们新加的 /** * r:Runnable对象 * * delayMillis:每隔一段时间执行一次,单位是毫秒 */ handler.postDelayed(myRunnable, 1000); }
运行我们的程序,发现会出错。这是因为我们不能直接这样频繁
setText
05-29 23:26:14.774: E/AndroidRuntime(24525): FATAL EXCEPTION: main 05-29 23:26:14.774: E/AndroidRuntime(24525): Process: com.scp.handler, PID: 24525 05-29 23:26:14.774: E/AndroidRuntime(24525): android.content.res.Resources$NotFoundException: String resource ID #0x1 05-29 23:26:14.774: E/AndroidRuntime(24525): at android.content.res.Resources.getText(Resources.java:289) 05-29 23:26:14.774: E/AndroidRuntime(24525): at android.widget.TextView.setText(TextView.java:3968) 05-29 23:26:14.774: E/AndroidRuntime(24525): at com.scp.handler.MainActivity$MyRunnable.run(MainActivity.java:41) 05-29 23:26:14.774: E/AndroidRuntime(24525): at android.os.Handler.handleCallback(Handler.java:733) 05-29 23:26:14.774: E/AndroidRuntime(24525): at android.os.Handler.dispatchMessage(Handler.java:95) 05-29 23:26:14.774: E/AndroidRuntime(24525): at android.os.Looper.loop(Looper.java:136) 05-29 23:26:14.774: E/AndroidRuntime(24525): at android.app.ActivityThread.main(ActivityThread.java:5336) 05-29 23:26:14.774: E/AndroidRuntime(24525): at java.lang.reflect.Method.invokeNative(Native Method) 05-29 23:26:14.774: E/AndroidRuntime(24525): at java.lang.reflect.Method.invoke(Method.java:515) 05-29 23:26:14.774: E/AndroidRuntime(24525): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:871) 05-29 23:26:14.774: E/AndroidRuntime(24525): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687) 05-29 23:26:14.774: E/AndroidRuntime(24525): at dalvik.system.NativeStart.main(Native Method)
handler.sendMessage用法
//修改部分代码 private void initView() { textView = (TextView) findViewById(R.id.textview); imageView = (ImageView) findViewById(R.id.imageview); new Thread() { public void run() { try { Thread.sleep(2000); Message message = new Message(); message.what = 01; message.obj = "更新UI"; handler.sendMessage(message);// 发送消息 } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // 在子线程中更新UI文本信息 switch (msg.what) { case 01: String update = (String) msg.obj; textView.setText(update); break; default: break; } } };
上面这段代码中,我们可以看到
Message message = new Message(); message.what = 01; message.obj = "更新UI"; handler.sendMessage(message);// 发送消息
其中
message.what = 01;是
handleMessage区分我们发送的是哪一个消息。作为一个执行ID来用。
message.obj = "更新UI";则是存储数据用的,它不仅仅可以发送一段文本,还可以发送一个对象,大家可以自行试试发送一个对象,这里我们就不演示了,使用方法都相同。
handler.sendMessage(message);发送一个消息。
public void handleMessage(Message msg) { // 在子线程中更新UI文本信息 switch (msg.what) { case 01: String update = (String) msg.obj; textView.setText(update); break; default: break; } }
获取
message对象
这里,我们不但可以使用
Message message = new Message();来得到一个
message对象;
还可以使用
Message message = handler.obtainMessage();来得到一个
message对象。他们大体上是一样的。
发送
message消息
我们通常使用
handler.sendMessage(message);来发送一个消息;
但是还有一种方法也可以发送一个消息。那就是使用
message.sendToTarget();我们需要注意的是,当我们使用这种方式来发送一个消息时,我们
message对象必须是通过
Message message = handler.obtainMessage();来得到的,否则会出现异常。
05-30 00:14:26.974: E/AndroidRuntime(28528): FATAL EXCEPTION: Thread-13840 05-30 00:14:26.974: E/AndroidRuntime(28528): Process: com.scp.handler, PID: 28528 05-30 00:14:26.974: E/AndroidRuntime(28528): java.lang.NullPointerException 05-30 00:14:26.974: E/AndroidRuntime(28528): at android.os.Message.sendToTarget(Message.java:360) 05-30 00:14:26.974: E/AndroidRuntime(28528): at com.scp.handler.MainActivity$2.run(MainActivity.java:34)
message.setTarget(handler)用法
解释原因
通过异常我们可以看到,这个是没有
handler来处理我们发送的消息。我们需要绑定一个
handler来处理这个消息。
为什么
Message message = handler.obtainMessage();就可以?
那么我们刚刚也说了,我们必须通过
Message message = handler.obtainMessage();来得到
message对象,之后才能使用
message.sendToTarget();来发送消息。大家想一想,我们这个时候是通过
handler来得到的一个消息,换言之这个
message绑定了
handler,所以它可以。
解决办法
我们来看看
message还有一个方法
message.setTarget(handler);这个方法是设置一个handler来处理我们发送的消息。当我们在使用
Message message = new Message();来得到一个
message对象;时,我们就可以使用这样一个方法来解决消息无人处理的问题。
源码
public void sendToTarget(){ target.sendMessage(this);//target也就是handler }
实质上还是通过
handler在发送消息,只不过方式不同而已。
更多Handler用法将在下篇进行讲解。
y1笑而过的CSDN博客y1笑而过的博客园
y1笑而过的新浪博客
y1笑而过的安卓巴士博客
y1笑而过的51CTO技术博客
相关文章推荐
- android soap协议与webservice
- [Android1.5]TextView跑马灯效果
- Android 中文 API (29) —— CompoundButton
- Android中AppWidget的分析与应用:AppWidgetProvider .
- 关联android源码
- Android中的事件传递和消费机制
- webview中的javascript调用android命令实现android latex显示
- Android 开发工具类 25_getJSON
- 五步学会Android的ListView控件
- android之传感器
- Android中使用TabHost实现类似标签栏的效果
- Android:CalledFromWrongThreadException
- Android 文本伸缩效果
- android 的listview 3大优化策略
- Android-进程生命周期
- Android-Manifest文件
- Android-应用程序资源
- Android优化技术详解-第八章 性能优化(笔记)
- Android Studio导出JavaDoc出现编码问题
- android项目打包成apk