轉載文章,原地址:http://blog.csdn.net/creativemobile/article/details/9319225
出處:http://blog.csdn.net/creativemobile
最近單位來了一個Android4.1平臺的360街景項目。在編寫該項目demo的過程中,爲了省事,打算直接在UI線程中訪問網絡數據源並生成Bitmap以填充相應的視圖。訪問網絡模塊的封裝採用了HttpClient的方式進行構建。編寫完工後執行程序,發現視圖顯示的還是本地的默認圖樣。在確認了網絡權限已被開啓的情況下,我開始懷疑是不是HttpClient封裝的粒度過大,導致其適用範圍受限的問題。於是乾脆採用Java平臺最底層的Socket套接字方式來實現網絡訪問,可是結果還是一樣的,仍舊無法得到網絡數據。經過調試發現,在客戶端發出請求之後,根本無法連接到服務端,也就無法解析後續的服務端的響應內容了。
以前在Android2.3.3平臺上研發怎麼沒有這個現象?難道是Android4.0的單線程模式的“禁令”較之以往更爲嚴格了。爲了使應用程序具有更好的交互性和更少的延遲時間,強勢要求開發人員在UI主線程中只能執行與UI相關的工作(如:更新視圖、與用戶交互等),其他方面的工作一律禁止執行。爲了驗證這個相反,在stackoverflow.com檢索了相關議題,從一位Google工程師的解答中基本得到了印證。也就是說,如果你非要在UI主線程中執行其他工作(如:訪問網絡、文件操作等),其實有這種編程強迫症的人在項目初期還是很多的,筆者就是其中的一位。你需要爲UI主線程所在的Activity設置線程策略,告知平臺請賦予我在UI主線程中進行其他工作的權限。具體做法有如下:
在你的Application、Activity或其它應用容器中添加如下代碼:
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads() // 捕捉讀取磁盤
.detectDiskWrites() // 捕捉寫入磁盤
.detectNetwork() // 捕捉網絡訪問 或使用detectAll() 火力全開
.penaltyLog() // 捕捉LogCat日誌
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
StrictMode類
StrictMode是一個開發者工具類,從Android 2.3平臺開始被引入。可以用於捕捉髮生在應用程序UI主線程中耗時的IO操作、網絡訪問或方法調用;也可以用來改進應用程序,使得UI主線程在處理IO操作和訪問網絡時顯得更平滑,避免其被阻塞,導致ANR警告。更多有關StrictMode的信息,請參見http://developer.android.com/reference/android/os/StrictMode.html。
這種非常規的做法,是在項目初期和開發模式下爲了達到更高的效率,而採取一種提高生產效率做法。在產品交付和運維時,我們還是要嚴格遵守Android平臺進程與線程安全管理機制。接下來是在實際開發中應該遵循的兩個原則:
UI主線程
在UI主線程中,只處理與UI相關及用戶交互的工作,耗時的工作一律交由後臺工作線程去搭理。常見的耗時工作處理方式有:
AsyncTask;
Handler、MessageQueue、Looper;
ExecutorService(execute/submit)
工作線程
在工作線程中,只做自己分內的事。決不干涉UI主線程的工作。在執行過程中如果存在涉及到UI的操作(如:更新視圖、重繪等),一律將其轉交給UI主線程進行處理。常見的轉交方式有:
Activity.runOnUiThread(new Runnable(){...});
View.post(new Runnable(){...});
View.postDelay(Runnable(){...},long)
示例
最後,提供AsyncMultiThreadActivity演示Android多線程與UI交互的方式,僅供讀者參考使用。
public class AsyncMultiThreadActivity extends Activity {
private TextView txView;
private Button button;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i("RootyInfo", "oncreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txView=(TextView)findViewById(R.id.textView1);
button=(Button)findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//創建一個用於展示前三種後臺線程和UI線程交互的線程
new TestThread(MainActivity.this).start();
//創建一個用於展示AsyncTask實現交互的TestAsyncTask
new TestAsyncTask().execute("Test"," AsyncTask");
}
});
}
class TestAsyncTask extends AsyncTask<String, Integer, String> {
//TestAsyncTask被後臺線程執行後,被UI線程被調用,一般用於初始化界面控件,如進度條
@Override
protected void onPreExecute() {
super.onPreExecute();
}
//doInBackground執行完後由UI線程調用,用於更新界面操作
@Override
protected void onPostExecute(String result) {
txView.setText(result);
super.onPostExecute(result);
}
//在PreExcute執行後被啓動AysncTask的後臺線程調用,將結果返回給UI線程
@Override
protected String doInBackground(String... params) {
StringBuffer sb=new StringBuffer();
for (String string : params) {
sb.append(string);
}
return sb.toString();
}
}
//用於線程間通信的Handler
class TestHandler extends Handler {
public TestHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
System.out.println("123");
txView.setText((String)msg.getData().get("tag"));
super.handleMessage(msg);
}
}
//後臺線程類
class TestThread extends Thread {
Activity activity;
public TestThread(Activity activity) {
this.activity = activity;
}
@Override
public void run() {
// 演示Activity.runOnUIThread(Runnable)方法的實現
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
txView.setText("Test runOnUIThread");
}
});
// 演示Activity.runOnUIThread(Runnable)方法的實現
txView.post(new Runnable() {
@Override
public void run() {
txView.setText("Test View.post(Runnable)");
}
});
// 演示Activity.runOnUIThread(Runnable)方法的實現
txView.postDelayed(new Runnable() {
@Override
public void run() {
txView.setText("Test View.postDelay(Runnable,long)");
}
}, 1000);
// 演示Handler方法的實現
Message msg=new Message();
Bundle bundle=new Bundle();
bundle.putString("tag", "Test Handler");
msg.setData(bundle);
new TestHandler(Looper.getMainLooper()).sendMessage(msg);
super.run();
}
}
}