編輯:關於Android編程
先看演示:
一個Handler允許你發送和處理消息(Message)以及與一個線程的消息隊列相關的Runnable對象。每個Handler實例都和單個線程以及該線程的消息隊列有關。當你創建了一個新Handler,它就會和創建它的線程/消息隊列綁定,在那以後,它就會傳遞消息以及runnable對象給消息隊列,然後執行它們。
需要使用Handler有兩大主要的原因:
(1)在將來的某個時間點調度處理消息和runnable對象;
(2)將需要執行的操作放到其他線程之中,而不是自己的;
調度處理消息是通過調用post(Runnable), postAtTime(Runnable, long),postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message),sendMessageAtTime(Message, long)和sendMessageDelayed(Message,long)等方法完成的。其中的post版本的方法可以讓你將Runnable對象放進消息隊列;sendMessage版本的方法可以讓你將一個包含有bundle對象的消息對象放進消息隊列,然後交由handleMessage(Message)方法處理。(這個需要你復寫Handler的handleMessage方法)
Handler在實際開發中是很常用的,主要是用來接收子線程發送的數據,然後主線程結合此數據來更新界面UI。
Android應用程序啟動時,他會開啟一個主線程(也就是UI線程),管理界面中的UI控件,進行事件派發,比如說:點擊一個按鈕,Android會分發事件到Button上從而來響應你的操作。但是當你需要執行一個比較耗時的操作的話,例如:進行IO操作,網絡通信等等,若是執行時間超過5s,那麼Android會彈出一個“經典”的ANR無響應對話框,然後提示按“Force quit”或是“Wait”。解決此類問題的方法就是:我們把一些耗時的操作放到子線程中去執行。但因為子線程涉及到UI更新,而Android主線程是線程不安全的,所以更新UI的操作只能放在主線程中執行,若是放在子線程中執行的話很會出問題。所以這時就需要一種機制:主線程可以發送“命令/任務”給子線程執行,然後子線程反饋執行結果;
若在主線程中實例化一個Handler對象,例如:
Handler mHandler =newHandler();
此時它並沒有新派生一個線程來執行此Handler,而是將此Handler附加在主線程上,故此時若你在Handler中執行耗時操作的話,還是會彈出ANR對話框!
下面就Handler的使用舉一些例子,加深理解。
public class MainActivity extends Activity implements OnClickListener { private final static String TAG = "HandlerTest"; private final static int DELAY_TIME = 1000; private Button btnStart; private Button btnStop; Context mContext = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mContext = this; Log.i(TAG, "Main thread id = " + Thread.currentThread().getId()); btnStart = (Button) findViewById(R.id.btn_start); btnStart.setOnClickListener(this); btnStop = (Button) findViewById(R.id.btn_stop); btnStop.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btn_start: mHandler.postDelayed(workRunnable, DELAY_TIME); break; case R.id.btn_stop: mHandler.removeCallbacks(workRunnable); break; } } Runnable workRunnable = new Runnable() { int counter = 0; public void run() { if (counter++ < 1) { Log.i(TAG, "workRunnable thread id = " + Thread.currentThread().getId()); mHandler.postDelayed(workRunnable, DELAY_TIME); } } }; Handler mHandler = new Handler(); }
說明:發現thread id是相同的,這就說明:默認情況下創建的Handler會綁定到主線程上,你不能做太耗時的操作。
public class MyThread2 extends Activity { private Handler handler = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); HandlerThread handlerThread = new HandlerThread("myHandlerThread"); handlerThread.start(); handler = new Handler(handlerThread.getLooper()); handler.post(new MyRunnable()); System.out.println("Oncreate---The Thread id is :" + Thread.currentThread().getId()); setContentView(R.layout.main); } private class MyRunnable implements Runnable { public void run() { System.out.println("Runnable---The Thread is running"); System.out.println("Runnable---The Thread id is :" + Thread.currentThread().getId()); try { Thread.sleep(6000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
在這個demo中,用到了HandlerThread,在HandlerThread對象中可以通過getLooper方法獲取一個Looper對象控制句柄,我們可以將其這個Looper對象映射到一個Handler中去來實現一個線程同步機制。於是就有以下結果;
1:控制台的輸出: Oncreate---The Thread id is :1
Runnable---The Threadis running
Runnable---The Threadid is :10
2:程序啟動後,我們立刻看到main.xml中的內容。
這樣就達到了多線程的結果
這裡介紹幾種模型:
(消息處理隊列掛在主線程上)
public class MainActivity extends Activity implements OnClickListener { private final static String TAG = "HandlerTest"; private final static int TASK_BEGIN = 1; private final static int TASK_1 = 2; private final static int TASK_2 = 3; private final static int TASK_END = 4; private Button btnStart = null; private Button btnStop = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnStart = (Button) findViewById(R.id.btn_start); btnStart.setOnClickListener(this); btnStop = (Button) findViewById(R.id.btn_stop); btnStop.setOnClickListener(this); Log.i(TAG, "[M_TID:" + Thread.currentThread().getId() + "]"); } Handler mHandler = new Handler() { // 注意:在各個case後面不能做太耗時的操作,否則出現ANR對話框 @Override public void handleMessage(Message msg) { switch (msg.what) { case TASK_BEGIN: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_BEGIN"); break; case TASK_1: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_1"); break; case TASK_2: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_2"); break; case TASK_END: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_END"); finish(); break; } super.handleMessage(msg); } }; public void onClick(View view) { switch (view.getId()) { case R.id.btn_start: // 啟動任務(消息只有標識,立即投遞) mHandler.sendEmptyMessage(TASK_BEGIN); Log.i(TAG, "Send TASK_BEGIN to handler."); // 開始任務1(在mHandler的消息隊列中獲取一個Message對象,避免重復構造) Message msg1 = mHandler.obtainMessage(TASK_1); msg1.obj = "This is task1"; mHandler.sendMessage(msg1); Log.i(TAG, "Send TASK_1 to handler."); // 開啟任務2(和上面類似) Message msg2 = Message.obtain(); msg2.arg1 = 10; msg2.arg2 = 20; msg2.what = TASK_2; mHandler.sendMessage(msg2); Log.i(TAG, "Send TASK_2 to handler."); break; case R.id.btn_stop: // 結束任務(空消息體,延時2s投遞) mHandler.sendEmptyMessageDelayed(TASK_END, 2000); Log.i(TAG, "Send TASK_END to handler."); break; } } }
在子線程中發送消息。
public class MainActivity extends Activity { private final static String TAG = "HandlerTest"; private final static int TASK_BEGIN = 1; private final static int TASK_1 = 2; private final static int TASK_2 = 3; private final static int TASK_END = 4; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.i(TAG, "[M_TID:" + Thread.currentThread().getId() + "]" + "This is in main thread."); workThread.start(); } Handler mHandler = new Handler() { // 注意:在各個case後面不能做太耗時的操作,否則出現ANR對話框 @Override public void handleMessage(Message msg) { switch (msg.what) { case TASK_BEGIN: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_BEGIN"); break; case TASK_1: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_1"); break; case TASK_2: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_2"); break; case TASK_END: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_END"); finish(); break; } super.handleMessage(msg); } }; Thread workThread = new Thread() { // 你可以在run方法內做任何耗時的操作,然後將結果以消息形式投遞到主線程的消息隊列中 @Override public void run() { // 啟動任務(消息只有標識,立即投遞) mHandler.sendEmptyMessage(TASK_BEGIN); Log.i(TAG, "[S_TID:" + Thread.currentThread().getId() + "]" + "Send TASK_START to handler."); // 開始任務1(在mHandler的消息隊列中獲取一個Message對象,避免重復構造) Message msg1 = mHandler.obtainMessage(TASK_1); msg1.obj = "This is task1"; mHandler.sendMessage(msg1); Log.i(TAG, "[S_TID:" + Thread.currentThread().getId() + "]" + "Send TASK_1 to handler."); // 開啟任務2(和上面類似) Message msg2 = Message.obtain(); msg2.arg1 = 10; msg2.arg2 = 20; msg2.what = TASK_2; mHandler.sendMessage(msg2); Log.i(TAG, "[S_TID:" + Thread.currentThread().getId() + "]" + "Send TASK_2 to handler."); // 結束任務(空消息體,延時2s投遞) mHandler.sendEmptyMessageDelayed(TASK_END, 2000); Log.i(TAG, "[S_TID:" + Thread.currentThread().getId() + "]" + "Send TASK_END to handler."); } }; }
主線程只管通過Handler往子線程的消息隊列中投遞消息即可。
public class MainActivity extends Activity { private final static String TAG = "HandlerTest"; private final static int TASK_BEGIN = 1; private final static int TASK_1 = 2; private final static int TASK_2 = 3; private final static int TASK_END = 4; private MyHandler mHandler = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.i(TAG, "[M_TID:" + Thread.currentThread().getId() + "]" + "This is in main thread."); HandlerThread myLooperThread = new HandlerThread("my looper thread"); myLooperThread.start(); Looper looper = myLooperThread.getLooper(); mHandler = new MyHandler(looper); // 啟動任務(消息只有標識,立即投遞) mHandler.sendEmptyMessage(TASK_BEGIN); Log.i(TAG, "[S_ID:" + Thread.currentThread().getId() + "]" + "Send TASK_START to handler."); // 開始任務1(在mHandler的消息隊列中獲取一個Message對象,避免重復構造) Message msg1 = mHandler.obtainMessage(TASK_1); msg1.obj = "This is task1"; mHandler.sendMessage(msg1); Log.i(TAG, "[S_ID:" + Thread.currentThread().getId() + "]" + "Send TASK_1 to handler."); // 開啟任務2(和上面類似) Message msg2 = Message.obtain(); msg2.arg1 = 10; msg2.arg2 = 20; msg2.what = TASK_2; mHandler.sendMessage(msg2); Log.i(TAG, "[S_ID:" + Thread.currentThread().getId() + "]" + "Send TASK_2 to handler."); // 結束任務(空消息體,延時2s投遞) mHandler.sendEmptyMessageDelayed(TASK_END, 2000); Log.i(TAG, "[S_ID:" + Thread.currentThread().getId() + "]" + "Send TASK_END to handler."); } class MyHandler extends Handler { public MyHandler(Looper looper) { super(looper); } // 現在在每個case之後,你可以做任何耗時的操作了 @Override public void handleMessage(Message msg) { switch (msg.what) { case TASK_BEGIN: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_BEGIN"); break; case TASK_1: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_1"); break; case TASK_2: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_2"); break; case TASK_END: Log.i(TAG, "[H_TID:" + Thread.currentThread().getId() + "] Get TASK_END"); finish(); break; } super.handleMessage(msg); } } }
3 Demo源碼
package mm.shandong.com.testhandler; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; import java.io.Serializable; import java.util.HashMap; import java.util.Map; public class TestHandlerActivity extends AppCompatActivity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_handler); textView= (TextView) findViewById(R.id.textView); } public void createSimpleHandler(View view){ final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1:///一般用16進制表示如0x0001 textView.setText(String.valueOf(msg.arg1)); break; } } }; new Thread(){ @Override public void run() { int i=0; while (i<50){ try { Thread.sleep(100); Message msg=new Message(); msg.what=1; msg.arg1=i; handler.sendMessage(msg); i++; } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } public void createSimpleHandler2(View view){ final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 0x0002:///一般用16進制表示如0x0001 Bundle bundle=msg.getData(); Map map= (Map) bundle.get("map"); String name= (String) map.get("name"); textView.setText(name); break; } } }; new Thread(){ @Override public void run() { int i=0; while (i<50){ try { Thread.sleep(100); Message msg=new Message(); msg.what=0x0002; Bundle bundle=new Bundle(); Map map=new HashMap<>(); map.put("name","張三"+i); bundle.putSerializable("map", (Serializable) map); msg.setData(bundle); handler.sendMessage(msg); i++; } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } public void createSimpleHandler3(View view){ final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1:///一般用16進制表示如0x0001 Toast.makeText(TestHandlerActivity.this,"這是一條空消息",Toast.LENGTH_SHORT).show(); break; } } }; handler.sendEmptyMessage(1); } public void createSimpleHandler4(View view){ final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1:///一般用16進制表示如0x0001 Toast.makeText(TestHandlerActivity.this,"這是延遲發送的一條空消息",Toast.LENGTH_SHORT).show(); break; } } }; handler.sendEmptyMessageDelayed(1,2000); } public void createSimpleHandler5(View view){ final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1:///一般用16進制表示如0x0001 Toast.makeText(TestHandlerActivity.this,"這是通過obtainMessage從消息池中得到一條消息並發送," + ""+"參數是:"+msg.arg1,Toast.LENGTH_LONG).show(); break; } } }; Message msg=handler.obtainMessage(); msg.what=1; msg.arg1=3; handler.sendMessage(msg); } public void createSimpleHandler6(View view){ MThreadNoLooper mThreadNoLooper=new MThreadNoLooper(); mThreadNoLooper.start(); if(mThreadNoLooper.handler!=null){ mThreadNoLooper.handler.sendEmptyMessage(1); } } public void createSimpleHandler7(View view){ MThread mThread=new MThread(); mThread.start(); try { Thread.sleep(100); if(mThread.handler!=null){ mThread.handler.sendEmptyMessage(1); } } catch (InterruptedException e) { e.printStackTrace(); } } public class MThreadNoLooper extends Thread{ public Handler handler; @Override public void run() { handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1:///一般用16進制表示如0x0001 Toast.makeText(TestHandlerActivity.this,"這是子線程中的handler監聽到的消息",Toast.LENGTH_LONG).show(); break; } } }; } } public class MThread extends Thread{ public Handler handler; @Override public void run() { Looper.prepare(); handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1:///一般用16進制表示如0x0001 Toast.makeText(TestHandlerActivity.this,"這是子線程中的handler監聽到的消息",Toast.LENGTH_LONG).show(); break; } } }; Looper.loop(); } } }
觸摸事件相關方法:ViewGroupdispatchTouchEvent(MotionEvent) 用於分發touch事件onInterceptTouchEvent(Mo
每次應用程序運行時,應用程序的application類保持實例化的狀態。通過擴展applicaiton類,可以完成以下3項工作: 1.對android運行時廣播的應用程序
簡介:為什麼要用Fragment?使用Fragment可以在一個Activity中實現不同的界面。Fragment與Fragment之間的動畫切換,遠比Activity與
簡析大家知道,我們在開發一款產品的時候為了達到良好的用戶體驗,我們可以在應用中適當的加上一些動畫效果,譬如平移、縮放、旋轉等等,但是這些常用的動畫在Android很早期的