android.os.NetworkOnMainThreadException异常是因为4.0后,联网线程不能写在主线程里。两个解决方案
方案一:去强制跳过线程检测
@SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads().detectDiskWrites().detectNetwork() .penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects().penaltyLog().penaltyDeath() .build()); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //mycode }
方案二:开启子线程联
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(runnable).start(); } Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bundle data = msg.getData(); String val = data.getString("value"); } } Runnable runnable = new Runnable(){ @Override public void run() { // 联网请求 //得到result Message msg = new Message(); Bundle data = new Bundle(); data.putString("value",result); msg.setData(data); handler.sendMessage(msg); } }
一般推荐使用第二种。
今天在网上找到了了第三种解决方案:
AsyncTask
方法二这种匿名线程的方式是存在缺陷的:
线程的开销较大,如果每个任务都要创建一个线程,那么应用程 序的效率要低很多;
线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。
另外,前面已经看到,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。
为了解决这一问题,Android在1.5版本引入了AsyncTask。AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程 中执行,这就有效地避免了使用Handler带来的麻烦。阅读AsyncTask的源码可知,AsyncTask是使用 java.util.concurrent 框架来管理线程以及任务的执行的,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了 匿名线程存在的问题。
AsyncTask是抽象类,其结构图如下图所示:
AsyncTask定义了三种泛型类型 Params,Progress和Result。
Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比。
Result 后台执行任务最终返回的结果,比如String。
子类必须实现抽象方法doInParams… p) ,在此方法中实现任务的执行工作,比如连接网络获取数据等。通常还应该实现onPostExecute(Result r)方法,因为应用程序关心的结果在此方法中返回。需要注意的是AsyncTask一定要在主线程中创建实例。
AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,需要注意的是这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。在任务 的执行过程中,这些方法被自动调用,运行过程,如下图所示:
onPreExecute() 当任务执行之前开始调用此方法,可以在这里显示进度对话框。
doInParams… ) 此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的 进度。
onProgressUpdate(Progress…) 此方法在主线程执行,用于显示任务执行的进度。
onPostExecute(Result) 此方法在主线程执行,任务执行的结果作为此方法的参数返回。
本人亲测示例:
public class SecondActivity extends Activity {
private EditText ed1;
private TextView tv;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ed1 = (EditText) findViewById(R.id.editText1);
btn = (Button) findViewById(R.id.p_w_picpathView1);
tv = (TextView) findViewById(R.id.textView1);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
PageTask task = new PageTask();
task.execute(ed1.getText().toString());
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.second, menu);
return true;
}
class PageTask extends AsyncTask{
@Override
protected Object doInBackground(Object... params) {
try {
HttpClient client = new DefaultHttpClient();
// params[0]代表连接的url
HttpGet get = new HttpGet((String) params[0]);
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
long length = entity.getContentLength();
InputStream is = entity.getContent();
String s = null;
if (is != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[128];
int ch = -1;
int count = 0;
while ((ch = is.read(buf)) != -1) {
baos.write(buf, 0, ch);
count += ch;
if (length > 0) {
// 如果知道响应的长度,调用publishProgress()更新进度
publishProgress((int) ((count / (float) length) * 100));
}
// 为了在模拟器中清楚地看到进度,让线程休眠100ms
Thread.sleep(100);
}
s = new String(baos.toByteArray()); }
// 返回结果
return s;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onCancelled() {
super.onCancelled();
}
@Override
protected void onPostExecute(Object result) {
// 返回HTML页面的内容
if(result!=null)
tv.append((String)result);
}
@Override
protected void onPreExecute() {
// 任务启动,可以在这里显示一个对话框,这里简单处理
Toast.makeText(SecondActivity.this, "正在加载", Toast.LENGTH_LONG).show();
}
@Override
protected void onProgressUpdate(Object... values) {
// 更新进度
}
}