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-class
AsyncTask定义了三种泛型类型 Params,Progress和Result。

  • Params 启动任务执行的输入参数,比如HTTP请求的URL。

  • Progress 后台任务执行的百分比。

  • Result 后台执行任务最终返回的结果,比如String。

子类必须实现抽象方法doInParams… p) ,在此方法中实现任务的执行工作,比如连接网络获取数据等。通常还应该实现onPostExecute(Result r)方法,因为应用程序关心的结果在此方法中返回。需要注意的是AsyncTask一定要在主线程中创建实例。
AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,需要注意的是这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。在任务 的执行过程中,这些方法被自动调用,运行过程,如下图所示:

AsyncTask-Usage

  • 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) {

// 更新进度

}

}