android之android.os.NetworkOnMainThreadException异常

android之android.os.NetworkOnMainThreadException异常

使用android测试访问web服务器的webservice时,在MainActivity的主线程中访问webservice,代码如下:

package com.example.myandroidpro;

import java.io.File;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

  private static String NAMESPACE = "http://service.cxf.test/";
  // webService地址
  private static String URL = "http://192.168.1.119:8080/CxfWebService/services/HelloService/";

  private String method_name = null;
  private Button activity_main_btn1;

  @SuppressLint("NewApi") @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    StrictMode.ThreadPolicy policy=new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
    
    File rootDirectory = Environment.getRootDirectory();//获取手机根目录
    File storageDirectory = Environment.getExternalStorageDirectory();//获取SD卡根目录

    for(File file : rootDirectory.listFiles()){
      System.err.println(file.isDirectory()+","+file.getName());
    } 
    
    findVIew();
    
    activity_main_btn1.setOnClickListener(new OnClickListener() {
      
      @Override
      public void onClick(View arg0) {
        //发送webservice请求
          sayHi("zxn");	
      }
    });
    
  }

  private void findVIew() {
    activity_main_btn1 = (Button) findViewById(R.id.activity_main_btn1);

  }

  private String sayHi(String name) {
    String result = null;
    // (1) 指定webservice的命名空间和调用的方法名
    method_name = "sayHi";
    SoapObject soapObj = new SoapObject(NAMESPACE, method_name);
    /**
     * (2) 设置调用方法的参数值,如果没有参数,可以省略。 要注意的是,参数必须和服务声明的@WebParam里面的变量名对应
     */
    soapObj.addProperty("name", name);
    // (3) 生成调用Webservice方法的SOAP请求信息。该信息由SoapSerializationEnvelope对象描述
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
        SoapEnvelope.VER11);
    // envelope.bodyOut = rpc;
    envelope.dotNet = false;
    envelope.setOutputSoapObject(soapObj);
    // (4)创建HttpTransportsSE对象。通过AndroidHttpTransport类的构造方法可以指定WebService的WSDL文档的URL
    HttpTransportSE ht = new HttpTransportSE(URL);
    try {
      // (5)使用call方法调用WebService方法
      ht.call(null, envelope);
      // (6)使用getResponse方法获得WebService方法的返回结果
      if (envelope.getResponse() != null) {
        System.out.println(envelope.getResponse());
        result = String.valueOf(envelope.getResponse());
        Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT)
            .show();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return result;
  }

}
由于在主线程中访问网络,android版本为4.3,导致如下异常:
05-15 02:00:05.769: W/System.err(2269): android.os.NetworkOnMainThreadException
05-15 02:00:05.799: W/System.err(2269): 	at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1133)
05-15 02:00:05.799: W/System.err(2269): 	at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
05-15 02:00:05.810: W/System.err(2269): 	at libcore.io.IoBridge.connectErrno(IoBridge.java:144)
05-15 02:00:05.810: W/System.err(2269): 	at libcore.io.IoBridge.connect(IoBridge.java:112)
05-15 02:00:05.869: D/dalvikvm(2269): GC_FOR_ALLOC freed 345K, 14% free 2674K/3076K, paused 48ms, total 52ms
05-15 02:00:05.869: W/System.err(2269): 	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
05-15 02:00:05.880: W/System.err(2269): 	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
05-15 02:00:05.880: W/System.err(2269): 	at java.net.Socket.connect(Socket.java:842)
05-15 02:00:05.889: W/System.err(2269): 	at libcore.net.http.HttpConnection.<init>(HttpConnection.java:76)
05-15 02:00:05.889: W/System.err(2269): 	at libcore.net.http.HttpConnection.<init>(HttpConnection.java:50)
05-15 02:00:05.899: W/System.err(2269): 	at libcore.net.http.HttpConnection$Address.connect(HttpConnection.java:340)
05-15 02:00:05.899: W/System.err(2269): 	at libcore.net.http.HttpConnectionPool.get(HttpConnectionPool.java:87)
05-15 02:00:05.899: W/System.err(2269): 	at libcore.net.http.HttpConnection.connect(HttpConnection.java:128)
05-15 02:00:05.899: W/System.err(2269): 	at libcore.net.http.HttpEngine.openSocketConnection(HttpEngine.java:316)
05-15 02:00:05.899: W/System.err(2269): 	at libcore.net.http.HttpEngine.connect(HttpEngine.java:311)
05-15 02:00:05.909: W/System.err(2269): 	at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
05-15 02:00:05.909: W/System.err(2269): 	at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
05-15 02:00:05.909: W/System.err(2269): 	at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:81)
05-15 02:00:05.909: W/System.err(2269): 	at libcore.net.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:197)
05-15 02:00:05.919: W/System.err(2269): 	at org.ksoap2.transport.ServiceConnectionSE.openOutputStream(ServiceConnectionSE.java:126)
05-15 02:00:05.919: W/System.err(2269): 	at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:185)
05-15 02:00:05.929: W/System.err(2269): 	at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:118)
05-15 02:00:05.929: W/System.err(2269): 	at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:113)
05-15 02:00:05.969: W/System.err(2269): 	at com.example.myandroidpro.MainActivity.sayHi(MainActivity.java:77)
05-15 02:00:05.969: W/System.err(2269): 	at com.example.myandroidpro.MainActivity.access$0(MainActivity.java:58)
05-15 02:00:05.969: W/System.err(2269): 	at com.example.myandroidpro.MainActivity$1.onClick(MainActivity.java:47)
05-15 02:00:05.969: W/System.err(2269): 	at android.view.View.performClick(View.java:4240)
05-15 02:00:05.979: W/System.err(2269): 	at android.view.View$PerformClick.run(View.java:17721)
05-15 02:00:05.979: W/System.err(2269): 	at android.os.Handler.handleCallback(Handler.java:730)
05-15 02:00:06.012: W/System.err(2269): 	at android.os.Handler.dispatchMessage(Handler.java:92)
05-15 02:00:06.012: W/System.err(2269): 	at android.os.Looper.loop(Looper.java:137)
05-15 02:00:06.019: W/System.err(2269): 	at android.app.ActivityThread.main(ActivityThread.java:5103)
05-15 02:00:06.019: W/System.err(2269): 	at java.lang.reflect.Method.invokeNative(Native Method)
05-15 02:00:06.019: W/System.err(2269): 	at java.lang.reflect.Method.invoke(Method.java:525)
05-15 02:00:06.034: W/System.err(2269): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
05-15 02:00:06.034: W/System.err(2269): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
05-15 02:00:06.039: W/System.err(2269): 	at dalvik.system.NativeStart.main(Native Method)
原因: 

android.os.NetworkOnMainThreadException是说不要在主线程中访问网络,这个是android3.0版本开始就强制程序不能在主线程中访问网络,要把访问网络放在独立的线程中。

在开发中,为了防止访问网络阻塞主线程,一般都要把访问网络放在独立线程中或者异步线程AsyncTask中。

1、想要忽略这些强制策略问题的话,可以在onCreate()方法里面加上 

StrictMode.ThreadPolicy policy=new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
并在方法上加上@SuppressLint("NewApi"),重试,OK。


2、将网络访问放到单独线程中:

package com.example.myandroidpro;

import java.io.File;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.StrictMode;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

  private static String NAMESPACE = "http://service.cxf.test/";
  // webService地址
  private static String URL = "http://192.168.1.119:8080/CxfWebService/services/HelloService/";

  private String method_name = null;
  private Button activity_main_btn1;

  private int ANDROID_ACCESS_CXF_WEBSERVICES = 001; 
  
  
  private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
      String result = (String) msg.getData().get("result");
      String obj = (String) msg.obj;//
      activity_main_btn1.setText("请求结果为:"+result);
    }
    
  };
  
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    File rootDirectory = Environment.getRootDirectory();//获取手机根目录
    File storageDirectory = Environment.getExternalStorageDirectory();//获取SD卡根目录

    for(File file : rootDirectory.listFiles()){
      System.err.println(file.isDirectory()+","+file.getName());
    } 
    
    findVIew();
    
    activity_main_btn1.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        Thread accessWebServiceThread = new Thread(new WebServiceHandler());
        accessWebServiceThread.start();
        
      }
    });
    
  }

  class WebServiceHandler implements Runnable{
    @Override
    public void run() {
      Looper.prepare();
      String result = sayHi("zxn");
      Message message = new Message();
      Bundle bundle = new Bundle();
      bundle.putString("result", result);
      message.what = ANDROID_ACCESS_CXF_WEBSERVICES;//设置消息标示
      message.obj = "zxn";
      message. setData(bundle);//消息内容
      handler.sendMessage(message);//发送消息
      Looper.loop();
    }
    
  }
  
  private void findVIew() {
    activity_main_btn1 = (Button) findViewById(R.id.activity_main_btn1);

  }

  private String sayHi(String name) {
    String result = null;
    // (1) 指定webservice的命名空间和调用的方法名
    method_name = "sayHi";
    SoapObject soapObj = new SoapObject(NAMESPACE, method_name);
    /**
     * (2) 设置调用方法的参数值,如果没有参数,可以省略。 要注意的是,参数必须和服务声明的@WebParam里面的变量名对应
     */
    soapObj.addProperty("name", name);
    // (3) 生成调用Webservice方法的SOAP请求信息。该信息由SoapSerializationEnvelope对象描述
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
        SoapEnvelope.VER11);
    // envelope.bodyOut = rpc;
    envelope.dotNet = false;
    envelope.setOutputSoapObject(soapObj);
    // (4)创建HttpTransportsSE对象。通过AndroidHttpTransport类的构造方法可以指定WebService的WSDL文档的URL
    HttpTransportSE ht = new HttpTransportSE(URL);
    try {
      // (5)使用call方法调用WebService方法
      ht.call(null, envelope);
      // (6)使用getResponse方法获得WebService方法的返回结果
      if (envelope.getResponse() != null) {
        System.out.println(envelope.getResponse());
        result = String.valueOf(envelope.getResponse());
        Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT)
            .show();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return result;
  }

}

MainActivity的相应布局如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context=".MainActivity" >

  <LinearLayout android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <TextView
      android:id="@+id/activity_main_tv1"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="@string/hello_world" />

    <Button
      android:id="@+id/activity_main_btn1"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="@string/test_for_cxf_webservice" />
  </LinearLayout>
</RelativeLayout>

3、将网络访问放到异步任务AsyncTask中,代码如下:

package com.example.myandroidpro;

import java.io.File;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

  private static String NAMESPACE = "http://service.cxf.test/";
  // webService地址
  private static String URL = "http://192.168.1.119:8080/CxfWebService/services/HelloService/";

  private String method_name = null;
  private Button activity_main_btn1;

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    File rootDirectory = Environment.getRootDirectory();//获取手机根目录
    File storageDirectory = Environment.getExternalStorageDirectory();//获取SD卡根目录

    for(File file : rootDirectory.listFiles()){
      System.err.println(file.isDirectory()+","+file.getName());
    } 
    
    findVIew();
    
    activity_main_btn1.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        accessWSAction();
      }

      private void accessWSAction() {
        new AsyncTask<String, Void, Object>() {
          
          //在doInBackground 执行完成后,onPostExecute 方法将被UI 线程调用,
          // 后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户.
          protected void onPostExecute(Object result) {
            super.onPostExecute(result);
            activity_main_btn1.setText("请求结果为:"+result);//可以更新UI
          }

          //该方法运行在后台线程中,因此不能在该线程中更新UI,UI线程为主线程
          protected Object doInBackground(String... params) {
            String result = sayHi("zxn");
//						activity_main_btn1.setText("请求结果为:"+result);
            return result;
          }

        }.execute();
        
      }
    });
  }

  private void findVIew() {
    activity_main_btn1 = (Button) findViewById(R.id.activity_main_btn1);

  }

  private String sayHi(String name) {
    String result = null;
    // (1) 指定webservice的命名空间和调用的方法名
    method_name = "sayHi";
    SoapObject soapObj = new SoapObject(NAMESPACE, method_name);
    /**
     * (2) 设置调用方法的参数值,如果没有参数,可以省略。 要注意的是,参数必须和服务声明的@WebParam里面的变量名对应
     */
    soapObj.addProperty("name", name);
    // (3) 生成调用Webservice方法的SOAP请求信息。该信息由SoapSerializationEnvelope对象描述
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
        SoapEnvelope.VER11);
    // envelope.bodyOut = rpc;
    envelope.dotNet = false;
    envelope.setOutputSoapObject(soapObj);
    // (4)创建HttpTransportsSE对象。通过AndroidHttpTransport类的构造方法可以指定WebService的WSDL文档的URL
    HttpTransportSE ht = new HttpTransportSE(URL);
    try {
      // (5)使用call方法调用WebService方法
      ht.call(null, envelope);
      // (6)使用getResponse方法获得WebService方法的返回结果
      if (envelope.getResponse() != null) {
        System.out.println(envelope.getResponse());
        result = String.valueOf(envelope.getResponse());
//				Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT)
//						.show();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return result;
  }

}
posted @ 2015-03-15 11:40  代码缔造的帝国  阅读(239)  评论(0编辑  收藏  举报