傳送門 ☞ 輪子的專欄 ☞ 轉載請註明 ☞ http://blog.csdn.net/leverage_1229html
最近單位來了一個Android4.1平臺的360街景項目。在編寫該項目demo的過程當中,爲了省事,打算直接在UI線程中訪問網絡數據源並生成Bitmap以填充相應的視圖。訪問網絡模塊的封裝採用了HttpClient的方式進行構建。編寫完工後執行程序,發現視圖顯示的仍是本地的默認圖樣。在確認了網絡權限已被開啓的狀況下,我開始懷疑是否是HttpClient封裝的粒度過大,致使其適用範圍受限的問題。因而乾脆採用Java平臺最底層的Socket套接字方式來實現網絡訪問,但是結果仍是同樣的,仍舊沒法獲得網絡數據。通過調試發現,在客戶端發出請求以後,根本沒法鏈接到服務端,也就沒法解析後續的服務端的響應內容了。java
之前在Android2.3.3平臺上研發怎麼沒有這個現象?難道是Android4.0的單線程模式的「禁令」較之以往更爲嚴格了。爲了使應用程序具備更好的交互性和更少的延遲時間,強勢要求開發人員在UI主線程中只能執行與UI相關的工做(如:更新視圖、與用戶交互等),其餘方面的工做一概禁止執行。爲了驗證這個相反,在stackoverflow.com檢索了相關議題,從一位Google工程師的解答中基本獲得了印證。也就是說,若是你非要在UI主線程中執行其餘工做(如:訪問網絡、文件操做等),其實有這種編程強迫症的人在項目初期仍是不少的,筆者就是其中的一位。你須要爲UI主線程所在的Activity設置線程策略,告知平臺請賦予我在UI主線程中進行其餘工做的權限。具體作法有以下:android
在你的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是一個開發者工具類,從Android 2.3平臺開始被引入。能夠用於捕捉髮生在應用程序UI主線程中耗時的IO操做、網絡訪問或方法調用;也能夠用來改進應用程序,使得UI主線程在處理IO操做和訪問網絡時顯得更平滑,避免其被阻塞,致使ANR警告。更多有關StrictMode的信息,請參見http://developer.android.com/reference/android/os/StrictMode.html。安全
這種很是規的作法,是在項目初期和開發模式下爲了達到更高的效率,而採起一種提升生產效率作法。在產品交付和運維時,咱們仍是要嚴格遵照Android平臺進程與線程安全管理機制。接下來是在實際開發中應該遵循的兩個原則:網絡
在UI主線程中,只處理與UI相關及用戶交互的工做,耗時的工做一概交由後臺工做線程去搭理。常見的耗時工做處理方式有:多線程
AsyncTask;app
Handler、MessageQueue、Looper;運維
ExecutorService(execute/submit)ide
在工做線程中,只作本身份內的事。決不干涉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(); } } }