一、问题
在安卓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());注:
显然,大多数初学者在进行网络开发时,会选择将访问网络的代码直接放到主进程中,由于和主进程的首要工作——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等来实现,当然这也可以,思维灵活,方法多多。