1. 程式人生 > >Android學習筆記三十八:Android4.0 Socket異常,需要另外開闢執行緒進行Socket程式設計

Android學習筆記三十八:Android4.0 Socket異常,需要另外開闢執行緒進行Socket程式設計

Socket socket = new Socket();
socket.connect(new InetSocketAddress(ConstData.TCP_IP,
                    ConstData.TCP_PORT), 2000);
通不過去,直接異常處理,這是因為android 3.0+以上 已經不建議在activity中新增耗時操作,要介面和資料脫離。4.0以上的通訊都必須放到執行緒裡去做 不能在UI執行緒。

解決辦法,另起執行緒或Service處理socket。
如果一定要想在UI執行緒操作,新增如下程式碼
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads().detectDiskWrites().detectNetwork()
                .penaltyLog().build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects().penaltyLog().penaltyDeath()
                .build());

經測試,另起一個service,在service的onStartCommand()發郵件,會令service無法啟動,可能因為service也在主執行緒中。

如果在主執行緒中進行socket,則丟擲:NetworkOnMainThreadException異常。

最近單位來了一個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或其它應用容器中新增如下程式碼:

[java] view plaincopyprint?

  1. public void onCreate() {  

  2.     if (DEVELOPER_MODE) {  

  3.         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()  

  4.                 .detectDiskReads() // 捕捉讀取磁碟   

  5.                 .detectDiskWrites() // 捕捉寫入磁碟   

  6.                 .detectNetwork()   // 捕捉網路訪問 或使用detectAll() 火力全開   

  7.                 .penaltyLog() // 捕捉LogCat日誌   

  8.                 .build());  

  9.         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()  

  10.                 .detectLeakedSqlLiteObjects()  

  11.                 .detectLeakedClosableObjects()  

  12.                 .penaltyLog()  

  13.                 .penaltyDeath()  

  14.                 .build());  

  15.     }  

  16.     super.onCreate();  

  17. }  

 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互動的方式,僅供讀者參考使用。

[java] view plaincopyprint?

  1. public class AsyncMultiThreadActivity extends Activity {    

  2.     private TextView txView;    

  3.     private Button button;    

  4.     /** Called when the activity is first created. */   

  5.     @Override   

  6.     public void onCreate(Bundle savedInstanceState) {    

  7.         Log.i("RootyInfo", "oncreate");    

  8.         super.onCreate(savedInstanceState);    

  9.         setContentView(R.layout.main);        

  10.         txView=(TextView)findViewById(R.id.textView1);    

  11.         button=(Button)findViewById(R.id.button1);    

  12.         button.setOnClickListener(new OnClickListener() {    

  13.             @Override   

  14.             public void onClick(View v) {  

  15.                 //建立一個用於展示前三種後臺執行緒和UI執行緒互動的執行緒     

  16.                 new TestThread(MainActivity.this).start();    

  17.                 //建立一個用於展示AsyncTask實現互動的TestAsyncTask     

  18.                 new TestAsyncTask().execute("Test"," AsyncTask");    

  19.             }    

  20.         });    

  21.     }    

  22.     class TestAsyncTask extends AsyncTask<String, Integer, String> {    

  23.         //TestAsyncTask被後臺執行緒執行後,被UI執行緒被呼叫,一般用於初始化介面控制元件,如進度條     

  24.         @Override   

  25.         protected void onPreExecute() {    

  26.             super.onPreExecute();    

  27.         }    

  28.         //doInBackground執行完後由UI執行緒呼叫,用於更新介面操作     

  29.         @Override   

  30.         protected void onPostExecute(String result) {    

  31.             txView.setText(result);    

  32.             super.onPostExecute(result);    

  33.         }    

  34.         //在PreExcute執行後被啟動AysncTask的後臺執行緒呼叫,將結果返回給UI執行緒     

  35.         @Override   

  36.         protected String doInBackground(String... params) {    

  37.             StringBuffer sb=new StringBuffer();    

  38.             for (String string : params) {    

  39.                 sb.append(string);    

  40.             }    

  41.             return sb.toString();    

  42.         }    

  43.     }  

  44.     //用於執行緒間通訊的Handler     

  45.     class TestHandler extends Handler {    

  46.         public TestHandler(Looper looper) {    

  47.             super(looper);    

  48.         }    

  49.         @Override   

  50.         public void handleMessage(Message msg) {    

  51.             System.out.println("123");    

  52.             txView.setText((String)msg.getData().get("tag"));    

  53.             super.handleMessage(msg);    

  54.         }    

  55.     }   

  56.     //後臺執行緒類     

  57.     class TestThread extends Thread {    

  58.         Activity activity;    

  59.         public TestThread(Activity activity) {         

  60.             this.activity = activity;    

  61.         }    

  62.         @Override   

  63.         public void run() {    

  64.             // 演示Activity.runOnUIThread(Runnable)方法的實現     

  65.             activity.runOnUiThread(new Runnable() {                 

  66.                 @Override   

  67.                 public void run() {    

  68.                     txView.setText("Test runOnUIThread");    

  69.                 }    

  70.             });    

  71.             // 演示Activity.runOnUIThread(Runnable)方法的實現     

  72.             txView.post(new Runnable() {    

  73.                 @Override   

  74.                 public void run() {    

  75.                     txView.setText("Test View.post(Runnable)");    

  76.                 }    

  77.             });    

  78.             // 演示Activity.runOnUIThread(Runnable)方法的實現     

  79.             txView.postDelayed(new Runnable() {    

  80.                 @Override   

  81.                 public void run() {    

  82.                     txView.setText("Test View.postDelay(Runnable,long)");    

  83.                 }    

  84.             }, 1000);    

  85.             // 演示Handler方法的實現     

  86.             Message msg=new Message();    

  87.             Bundle bundle=new Bundle();    

  88.             bundle.putString("tag", "Test Handler");    

  89.             msg.setData(bundle);                

  90.             new TestHandler(Looper.getMainLooper()).sendMessage(msg);    

  91.             super.run();    

  92.         }    

  93.     }    

  94. }