編輯:關於Android編程
任何程序都是靜態代碼,我們把這些靜態代碼打包好,然後放到運行環境當中,通過事件流的驅動使這些代碼運行起來。Android的環境也不例外。
靜態的代碼,在動態事件的驅動下,才會有效的運轉起來。
驅動Android程序運行起來的事件大致可以分為以下幾種:
用戶事件:如點擊屏幕,滑動等各種手勢;
系統事件:如屏幕方向的轉變;
線程通訊事件:線程之間互發消息,程序根據消息內容進行相應的響應;
進程通訊事件:這裡的進程包括本程序開啟的進程,也包括其他應用程序的進程。
下面來介紹動態事件驅動的第三種:線程通訊事件流
線程通訊事件流
線程通訊也是造成Android動態化的一個重要方面,當一個UI主線程收到其他線程發過來的消息,可以動態更改自己的頁面。
下面總結下線程之間通訊的幾種方式:
(從重要性和實用角度排序)
1.使用Handler實現
2.使用AsyncTask
3.Activity.runOnUiThread(Runnbale)
4.View.post(Runnbale)
5.View.postDelayed(Runnalbe,long)
2-5其實內部實現都是Handler。
下面一一介紹用法:
1.使用Handler實現
先來講Handler的實現,因為後邊的四個都是基於Handler的實現的,談到Handler的運行機制,不得不提Looper、Message、MessagerQueue了,它們之間的關系,下邊這個圖片描述的很清楚了。
用一句話總結就是在主線程中定義Handler對象,然後在子線程中調用這個Handler對象將封裝好的Message對象發送到主線程中Looper管理的MessageQueue中。上邊兩句話是說在子線程中向主線程發送消息。那我如果想從主線程向子線程發送消息呢?或者子線程之間發送消息呢?其實還是一個道理。線程A要想給線程B發送消息,就要獲取線程B中的Handler對象,然後通過此對象向線程B中的Looer中的MessageQueue中發送消息。注意,Looper和Handler是一一對應的。一個handler只能向其所在的線程發送Message消息。只不過UI線程的Looper是自動啟動好的,其他線程要想享受到Looper的服務,必須通過Looper.prepare()和Looper.loop();自行啟動。下邊寫了一個demo,以後可以學習參考:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { final Handler mHandler=new Handler(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyButton er=(MyButton) findViewById(R.id.ddd); Log.v("onCreate", "onCreate"); er.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.v("onClick", v.toString()); Intent sub= new Intent(MainActivity.this, SubActivity.class); startActivity(sub); } }); final Myrunnable murMyrunnable=new Myrunnable(); new Thread(murMyrunnable).start(); new Thread(new Runnable() { //延遲兩秒 public Handler mHandler; public void run() { Looper.prepare(); Looper.loop(); mHandler = new Handler() { public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, "wewe"+msg.what, Toast.LENGTH_SHORT).show(); } }; } }).start(); //創建一個線程 new Thread(new Runnable() { @Override public void run() { //延遲兩秒 try { Thread.sleep( 5000 ); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.post(new Runnable() { // @Override public void run() { er.setText("變了"); Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show(); //創建一個線程 Message sdfMessage=new Message(); sdfMessage.what=12 ; murMyrunnable.mHandler.sendMessage(sdfMessage); } }); } }).start(); //er.setClickable(false); } class Myrunnable implements Runnable{ //延遲兩秒 public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, "wewe"+msg.what, Toast.LENGTH_SHORT).show(); } }; Looper.loop(); } } }
2.使用AsyncTask
關於AsyncTask的使用網上已經有很多資料這裡不再詳述,只是簡單介紹下用法,以及其內部與handler的關系。
AsyncTask屏蔽了很多多線程的實現細節,很適合初學者使用,現在通過代碼來介紹:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { final Handler mHandler=new Handler(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyButton er=(MyButton) findViewById(R.id.ddd); Log.v("onCreate", "onCreate"); er.setOnClickListener(new OnClickListener() { @Override public void onClick(View v){MyAsyncTask myAsyncTask = new MyAsyncTask(MainActivity.this); myAsyncTask.execute(20); } }); } /* * integer:啟動任務執行的輸入參數,integer:後台任務完成的進度值的類型;String:後台執行任務完成後返回結果的類型 * 這三個參數可以根據需要進行設定 */ class MyAsyncTask extends AsyncTask{ ProgressDialog pDialog; Context mContext; public MyAsyncTask( Context mContext){ this.mContext=mContext; } //核心函數,可以理解為在子線程中執行 @Override protected String doInBackground(final Integer... params) { Integer percent=params[0]; while (percent<101) { percent++; publishProgress(percent); Log.v("percent", percent+""); } return null; } /* * 上個函數的結果作為參數,傳給result形參,函數內部執行更新UId的更新操作,可以理解為在UI線程中執行 * @see android.os.AsyncTask#onPostExecute(java.lang.Object) */ @Override protected void onPostExecute(String result) { // TODO Auto-generated method stub super.onPostExecute(result); } /* * 在執行第一個函數之前執行,做一些准備工作,可以理解為在UI線程中執行 * @see android.os.AsyncTask#onPreExecute() */ @Override protected void onPreExecute() { //這裡的准備工作是顯示進度條 pDialog=new ProgressDialog(mContext); pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pDialog.show(); } /* * 可以在第一個函數中調用,更新新進度注意是可以,無需求可以不用使用。 * @see android.os.AsyncTask#onProgressUpdate(java.lang.Object[]) */ @Override protected void onProgressUpdate(Integer... values) { pDialog.setProgress(values[0]); } }
AsyncTask內部實際上new了一個Handler。如下圖:
再來看下InternalHandler的定義:
private static class InternalHandler extends Handler { @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } }
顯而易見,InternalHandler繼承自Handler。
重點不是Handler在哪個類中new的,而是只要是new了Handler,就可以通過Handler向創建Handler的那個線程中發送Message消息。
這就是為什麼使用AsyncTask時必須在UI線程中創建的原因。
大概的原來不再贅述,原理與上一部分Handler的運行機制大同小異。
3.Activity.runOnUiThread(Runnbale)
在子線程中調用Activity.runOnUiThread(Runnbale)方法,在傳過來的Runnbale參數的run方法裡更新主線程UI。具體可以參考以下代碼:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyButton er=(MyButton) findViewById(R.id.ddd); //創建一個線程 new Thread(new Runnable() { @Override public void run() { //延遲兩秒 try { Thread.sleep( 5000 ); } catch (InterruptedException e) { e.printStackTrace(); } runOnUiThread(new Runnable() { @Override public void run() { er.setText("變了"); Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show(); } }); } }).start(); //er.setClickable(false); } }
注意為了保證線程安全,要更改的UI控件必須是final的。為什麼說Activity.runOnUiThread(Runnbale)內部是用Handler實現的呢?我們來看下runOnUiThread(Runnbale)的源碼就清楚了。
/** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. * * @param action the action to run on the UI thread */ public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
看到mHandler對象了嗎?我們來看下它的定義:
final Handler mHandler = new Handler();
所以是不是很清楚了。不管Handler在哪個類中new出來,用Handler傳值都會傳到創建Handler的那個線程中,重點的不是在哪個類中new,而是Handler在哪個線程中。
4.View.post(Runnbale)
其實這個的用法和上邊講的很類似。在子線程中調用某個view的post(Runnbale)方法,在傳過來的Runnbale參數的run方法裡更新主線程UI。具體可以參考以下代碼:
new Thread(new Runnable() { @Override public void run() { //延遲兩秒 try { Thread.sleep( 5000 ); } catch (InterruptedException e) { e.printStackTrace(); } er.post(new Runnable() { @Override public void run() { er.setText("變了"); Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show(); } }); } }).start();
為什麼可以這樣調用,因為View中也定義了Handler的對象,看下邊的源碼:
找到mHandler的定義:
5.View.postDelayed(Runnalbe,long)
和上邊的類似,無非是延遲一些毫秒數來執行Runnable裡面的方法,如View.postDelayed(Runnalbe,2000)是延遲2秒執行。
歡迎大家留言討論。
後邊將介紹造成安卓動態化的第三種因素:進程之間的通訊。
怎樣才能寫出優秀的Android App,是每一個程序員追求的目標。那麼怎麼才能寫出一個優秀的App呢?相信很多初學者也會有這種迷茫。一句話來回答這個問題:細節很重要。今
Android listView 繪制表格效果圖:二,創建步驟:1,創建布局:activity_main中的布局:<LinearLayout xmlns
今天我們來看一下一個內存洩漏檢測神器 leakcanary(https://github.com/square/leakcanary)首先我們來看一下leakcanary
本文均屬自己閱讀源碼的點滴總結,轉賬請注明出處謝謝。歡迎和大家交流。qq:1037701636 email:[email protected]:系統