Android--Handler机制(二)(Android Studio)
下面接着上篇继续谈谈我对Handler机制的认识。
五、Handler, Looper, MessageQueue的关系详解
关系图见上一篇http://blog.csdn.net/u014294166/article/details/50677910
ActivityThread会创建所有的Activity并回调所有的Activity的方法,同时默认创建main线程,在创建main线程中会创建Looper,在创建Looper过程中又会默认创建Message。
源码跟踪分析如下:(先用word做的处理)
- Handler和Looper的关联源码
- Looper轮询
小结:Handler(sendMessage())负责发送消息,Looper(Looper.looper())负责接收Handler发送的消息,并直接把消息传给Handler自己(handMessage())。MessageQueue就是一个容器。
六、Handler与子线程
- 自定义与线程相关的Handler
/** * 自定义与线程相关的Handler */public class MainActivity extends AppCompatActivity { private Handler mainHandler = new Handler(){ @Override public void handleMessage(Message msg) { System.out.println("用来更新主线程中UI的handler---->" + Thread.currentThread()); } }; class MyThread implements Runnable{ private Handler handler;// handler的自定义线程 // 以下注释处的过程已经通过以上源码分析说明 @Override public void run() { Looper.prepare();// 创建一个loop对象 handler = new Handler(){// 获取当前线程的Looper对象 @Override public void handleMessage(Message msg) { //Toast.makeText(MainActivity.this, "current thread:" + Thread.currentThread(), Toast.LENGTH_LONG).show(); System.out.println("自定义的用来更新UI的handler------>" + Thread.currentThread()); } }; Looper.loop();// 死循环处理消息 } } @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyThread myThreadt = new MyThread(); new Thread(myThreadt).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } myThreadt.handler.sendEmptyMessage(1);// handler自定义的线程 mainHandler.sendEmptyMessage(1);// 主线程的handler Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); }}
耗时操作一般不直接在UI主线程上进行,否则容易出现卡死现象。
2 . HandlerThread
HandlerThread 继承于Thread,其本质就是Thread。与普通的Thread的差别在于,HandlerThread有Looper 成员变量,即主要的区别就在Looper的创建上,具体区别如下:
(1). 在普通Thread中实例化Handler时的一般步骤是:在线程run()方法当中先调用Looper.prepare()初始化Looper,然后再run()方法最后调用Looper.loop(),这样我们就在该线程当中创建好Looper。(注意:Looper.loop()方法默认是死循环)
(2). 在HandlerThread中创建一个HandlerThread即创建了一个Looper的线程,步骤如下:
*创建一个HandlerThread,即创建了一个包含Looper的线程。
HandlerThread handlerThread = new HandlerThread(“HandlerThread”);
handlerThread.start(); //创建HandlerThread后一定要记得start()
获取HandlerThread的Looper
Looper looper = handlerThread.getLooper();
创建Handler,通过Looper初始化
Handler handler = new Handler(looper);*
可以避免多线程并发时出现的空指针异常问题。比如说线程切换时Handler所需要Looper还没有创建。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handlerThread = new HandlerThread("HandlerThread"); handlerThread.start();// !! // Handler获取指定的Looper,无参时为默认个给定的Looper handler = new Handler(handlerThread.getLooper()){ @Override public void handleMessage(Message msg) { System.out.println("current thread----->" + Thread.currentThread()); } }; handler.sendEmptyMessage(1);// 以上三步创建HandlerThread,然后通过handler发送消息 //handlerThread.quit();// 如果想让HandlerThread退出,则需要调用handlerThread.quit(); }
3 . 主线程向子线程发送消息
以上所谈的都是子线程向主线程发送消息,通知主线程更新UI,但是也有主线程向子线程发送消息的情况。下面谈谈这种情况。
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button b_send;// 发送消息 private Button b_stop;// 停止发送消息 // 主线程的Handler private Handler mainHandler = new Handler(){ @Override public void handleMessage(Message msg) { Message message = new Message(); System.out.println("=====》Main Handler"); // 在主线程中给子线程每秒发送一个message subHandler.sendMessageDelayed(message, 1000); } }; private Handler subHandler;// 子线程的Handler @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); b_send = (Button) findViewById(R.id.btn_send); b_stop = (Button) findViewById(R.id.btn_stop); b_send.setOnClickListener(this); b_stop.setOnClickListener(this); // 子线程的looper的轮询是通过创建HandlerThread指定的 HandlerThread hthread = new HandlerThread("Handler Thread"); hthread.start(); subHandler = new Handler(hthread.getLooper()){ @Override public void handleMessage(Message msg) { Message message = new Message(); System.out.println("=====》Sub Handler"); // 在子线程中给主线程每秒发送一个message mainHandler.sendMessageDelayed(message, 1000); } }; } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_send: mainHandler.sendEmptyMessage(1); break; case R.id.btn_stop: mainHandler.removeMessages(1); break; default: break; } }}
子线程中利用主线程的Handler向主线程发消息,主线程中利用子线程的Handler向子线程发消息。
七、Android中更新UI的几种方式
- handler的post
- runOnUiThread
- handler的sendMessage
- View控件自身调用post方法
public class MainActivity extends AppCompatActivity { private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); //updateUI1(); //updateUI2(); //updateUI3(); updateUI4(); } public void updateUI1(){ new MyThread().start(); } class MyThread extends Thread{ @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); tv.setText("使用runOnUiThread的方法更新UI"); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } public void updateUI2(){ Handler handler = new Handler(); handler.post(new Runnable() { @Override public void run() { try { Thread.sleep(2000); tv.setText("Handler的post方法更新UI"); } catch (InterruptedException e) { e.printStackTrace(); } } }); } private Handler handler3 = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.what == 3){ tv.setText("利用Handler的sendMessage方法更新UI"); } } }; public void updateUI3(){ new Thread(new MyThread3()).start(); } class MyThread3 implements Runnable{ @Override public void run() { try { Thread.sleep(2000); handler3.sendEmptyMessage(3); } catch (InterruptedException e) { e.printStackTrace(); } } } public void updateUI4(){ tv.post(new Runnable() { @Override public void run() { try { Thread.sleep(1000); tv.setText("View控件自身调用post方法更新UI"); } catch (InterruptedException e) { e.printStackTrace(); } } }); }}
八、再谈“非UI线程更新UI”的问题
(1). 如下代码能正常更新UI
(2). 如下代码报如下异常:
FATAL EXCEPTION: Thread-186 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
不难看出,Android系统框架在对非UI线程直接操作UI信息进行判断的时候,准确度不够。在时间差内(代码片段中进行耗时操作,在本例中使用Thread.sleep模拟耗时操作)是可以在非UI线程直接更新UI信息的。
原因分析:
一步步查看setText()源码做分析:View中是通过如下代码检测当前线程是否是UI线程的,其中mThread即主线程。
View视图是通过ViewRootImpl类来绘制的,ViewRootImpl是在调用onResume()方法时创建的,而onResume()方法是在onCreate()之后调用的,在不休眠的情况下,ViewRootImpl还没有被创建出来,进而没来得及调用TextView里setText()方法后续的checkThread()方法。
更多相关文章
- android实现app通过jni调用C/C++方法
- android为按钮添加事件的三种方法
- Android在初始化时弹出popwindow的方法 .
- android开发环境搭建最新方法
- Android自定义属性 及 TypedArray的使用方法
- android程序退出当前activity的方法
- 获取Android的key的MD5和SHA1的方法
- Android AsyncTask两种线程池分析和总结
- Android中杀进程的几种方法 (1) - killBackgroundProcesses