1. 程式人生 > >關於安卓高版本訪問網路出現.NetworkOnMainThreadException異常的問題

關於安卓高版本訪問網路出現.NetworkOnMainThreadException異常的問題

  一、問題

  在安卓2.3版本之前,我們在MainThread裡面進行網路操作時沒有問題的,但是在2.3版本之後(也就是3.0等),就會出現.NetworkOnMainThreadException異常。舉一個例子,比如我們要顯示一張網路圖片,以csdn的logo為例,可以這樣寫:

package com.example.netimageviewer;

import java.io.IOException;
import com.example.netimageviewer.service.ImageUtil;
import android.os.Bundle;
import android.os.StrictMode;
import android.app.Activity;
import android.graphics.Bitmap;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private Button button;
	private ImageView imageView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		     
		button = (Button) findViewById(R.id.button);
		imageView = (ImageView) findViewById(R.id.img);

		button.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
                              
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}
  獲取圖片的工具類:
package com.example.netimageviewer.service;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import com.example.netimageviewer.util.StreamUtil;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class ImageUtil {

	public static Bitmap getImage(String addressUrl) throws IOException {

		URL url = new URL(addressUrl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("GET");
		conn.setConnectTimeout(5000);
		InputStream is = conn.getInputStream();
		byte[] b = StreamUtil.getByte(is);
		Bitmap bf = BitmapFactory.decodeByteArray(b, 0, b.length);

		return bf;
	}
}
  把inputstream轉化成一個byte[] 的工具類:
package com.example.netimageviewer.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class StreamUtil {

	public static byte[] getByte(InputStream is) throws IOException{
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		byte[] b = new byte[1024];
		int len = 0;
		while ((len = is.read(b)) != -1) {
			stream.write(b, 0, len);
		}
		is.close();
		byte[] byteStream=stream.toByteArray();
		System.out.println(byteStream.toString());
		return byteStream;
	}
}
  然後新增上訪問網路許可權:<uses-permission android:name="android.permission.INTERNET"/>,最後執行程式,如果在2.3版本之前就可以顯示出圖片,但是在2.3版本之後就會出現.NetworkOnMainThreadException異常。

二、解決方法:

  1、一是在主執行緒的onCreate()方法中加入如下程式碼:

		StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
				.detectDiskReads().detectDiskWrites().detectNetwork()
				.penaltyLog().build());
		StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
				.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
				.penaltyLog().penaltyDeath().build());
注:
       通過查閱相關資料,發現,自從Android 2.3之後,系統增加了一個類:StrictMode。這個類對網路的訪問方式進行了一定的改變。Android的官方文件給出了這個類設定的目的:StrictMode是一個系統提供的開發工具,用以檢測在開發過程中因為偶然的事故從而造成的系統潛在的問題,進而提示開發者對其進行修復。StrictMode通常用於捕獲磁碟訪問或者網路訪問中與主程序之間互動產生的問題,因為在主程序中,UI操作和一些動作的執行是最經常用到的,它們之間會產生一定的衝突問題。將磁碟訪問和網路訪問從主執行緒中剝離可以使磁碟或者網路的訪問更加流暢,提升響應度和使用者體驗。

       顯然,大多數初學者在進行網路開發時,會選擇將訪問網路的程式碼直接放到主程序中,由於和主程序的首要工作——UI互動——相矛盾,因此,必須設定一定的檢測機制,以保證系統執行的流暢,所有的異常都可以被檢測。 使用的時候只需要在你專案執行的入口Activity的OnCreate中放入這段程式碼,那麼整個專案程式都有用。不需要每個Activity裡面加入。

       但是呢,從StrictMode類的用途我們就可以知道這不是一個好方法。
  2、一個比較穩妥的方法就是使用AsyncTask,這個類相信都不陌生。那程式碼就可以這樣來寫了:

package com.example.netimageviewer.util;

import java.io.InputStream;
import java.net.URL;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

public class DownImage extends AsyncTask<String, Void, Bitmap> {

	private ImageView imageView;

	public DownImage(ImageView imageView) {
		this.imageView = imageView;
	}

	@Override
	protected Bitmap doInBackground(String... params) {
		String url = params[0];
		Bitmap bitmap = null;
		try {
			InputStream is = new URL(url).openStream();
			bitmap = BitmapFactory.decodeStream(is);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bitmap;
	}

	@Override
	protected void onPostExecute(Bitmap result) {
		super.onPostExecute(result);
		imageView.setImageBitmap(result);
	}

}
那麼MainActivity的程式碼就應該是這樣的了:
package com.example.netimageviewer;

import java.io.IOException;
import com.example.netimageviewer.util.DownImage;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import com.android.internal.util.*;
import android.widget.ImageView;


public class MainActivity extends Activity {

	private Button button;
	private ImageView imageView;
	private String uriCSDN = "http://csdnimg.cn/www/images/csdnindex_logo.gif?20121229";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		button = (Button) this.findViewById(R.id.button);
		imageView = (ImageView) this.findViewById(R.id.img);
		button.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				new DownImage(imageView).execute(uriCSDN);
			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}
  另外還有朋友建議使用Thread、Handler、Runnable等來實現,當然這也可以,思維靈活,方法多多。