Android Handler使用
1. 介绍
Handler允许向关联线程的消息队列(MessageQueue)发送消息(Message)和可执行对象(Runnable).
每个Handler实例都与某个线程(即创建该Handler的线程)及该线程的消息队列所关联
Handler主要有两种用途
- 调度消息和可执行对象在未来某个时间点的处理
- 在其他线程中执行动作
2. 实例
2.1 基本实例
注意: 实例中myRunnable方法是被UI线程执行
File: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.lineto.testhandler.MainActivity"> <Button android:id="@+id/start" android:text="start" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/stop" android:text="stop" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
File: MainActivity.java
public class MainActivity extends AppCompatActivity { private Button start; private Button stop; private Handler handler; private Runnable myRunnable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); start = (Button)findViewById(R.id.start); stop = (Button)findViewById(R.id.stop); View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { Button button = (Button)v; if (button == start) { handler.post(myRunnable); } else if (v == stop) { handler.removeCallbacks(myRunnable); } } }; start.setOnClickListener(listener); stop.setOnClickListener(listener); handler = new Handler(); myRunnable = new Runnable() { @Override public void run() { System.out.println("Run method"); handler.postDelayed(myRunnable, 3000); } }; } }
2.2 进度条实例
在该实例中, 分别采用两种方式来实现
一种是runnable方法由UI线程来执行, 另一种是runnable方法由新线程来执行
可以发现第一种会导致界面卡顿, 后面一种才是Handler的正确使用方法
File: activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.lineto.handlerprogressbar.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="ProgressBar With Handler" /> <ProgressBar android:id="@+id/progressbar" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="300dp" android:layout_height="wrap_content" android:visibility="gone" /> <Button android:id="@+id/start" android:text="Start" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/stop" android:text="Stop" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
File: MainActivity.java
public class MainActivity extends AppCompatActivity { private ProgressBar progressBar; private Button start; private Button stop; private Handler handler; private boolean use_new_thread = true; private boolean thread_running = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressBar = (ProgressBar)findViewById(R.id.progressbar); start = (Button)findViewById(R.id.start); stop = (Button)findViewById(R.id.stop); final Runnable myRunnable = new Runnable() { int i = 0; @Override public void run() { do { try { Thread.sleep(1000); } catch (Exception e) { System.out.println(e); } if (i < 100) { i += 10; } else { i = 0; } Message msg = handler.obtainMessage(); msg.arg1 = i; handler.sendMessage(msg); } while(thread_running); } }; View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { if (v == start) { Toast toast = Toast.makeText(MainActivity.this, "Start", Toast.LENGTH_SHORT); toast.show(); progressBar.setVisibility(View.VISIBLE); if (use_new_thread) { thread_running = true; Thread thread = new Thread(myRunnable); thread.start(); } else { thread_running = false; handler.post(myRunnable); } } else if(v == stop) { Toast toast = Toast.makeText(MainActivity.this, "Stop", Toast.LENGTH_SHORT); toast.show(); progressBar.setVisibility(View.GONE); if (use_new_thread) { thread_running = false; } else { handler.removeCallbacks(myRunnable); } } } }; start.setOnClickListener(listener); stop.setOnClickListener(listener); handler = new Handler() { @Override public void handleMessage(Message msg) { progressBar.setProgress(msg.arg1); if (!use_new_thread) { handler.post(myRunnable); } } }; } }
3. Looper
Looper是用于给线程添加一个消息队列(MessageQueue), 并且循环等待; 当有消息时会唤起线程来处理消息的类.
UI线程默认包含一个Looper, 所以通常不需要为新线程创建Looper;当为新线程创建Looper后, 通常使用Handler来进行消息交互
下面是一个典型的Looper使用方法
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
详细的实例参考<Android实战技巧:消息循环与Looper>
4. HandlerThread
HandlerThread是一个拥有Looper的Thread, 然后可以使用该Looper得到一个Handler(一定要在start()方法之后)
需要注意的是程序结束的时候需要退出Looper(quit()/quitSafely())
相关实例参考<Android HandlerThread 完全解析>
5. Message
Message用于在消息循环中通信, 用来发消息给某个Handler, 可以通过Handler的obtainMessage()从消息池中获取一个Message引用;Message常用的方法是赋值, 相关操作查询API即可