編輯:關於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性能優化之布局優化篇
怎樣才能寫出優秀的Android App,是每一個程序員追求的目標。那麼怎麼才能寫出一個優秀的App呢?相信很多初學者也會有這種迷茫。一句話來回答這個問題:細節很重要。今
Android listView 繪制表格實例詳解
Android listView 繪制表格效果圖:二,創建步驟:1,創建布局:activity_main中的布局:<LinearLayout xmlns
工作中遇到的Android內存優化問題(3)-leakcanary源碼解析
今天我們來看一下一個內存洩漏檢測神器 leakcanary(https://github.com/square/leakcanary)首先我們來看一下leakcanary
Android Camera HAL3中拍照Capture模式下多模塊間的交互與幀Result與幀數據回調
本文均屬自己閱讀源碼的點滴總結,轉賬請注明出處謝謝。歡迎和大家交流。qq:1037701636 email:gzzaigcn2009@163.comSoftware:系統