1. ホーム
  2. アンドロイド

android.os の NetworkOnMainThreadException。

2022-02-08 02:49:18

android testを使用してWebサーバーのWebサービスにアクセスする場合、以下のコードでMainActivityのメインスレッドでWebサービスにアクセスされます。

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;
Toast; import android.widget;

public class MainActivity extends Activity {

    private static String NAMESPACE = "http://service.cxf.test/";
    // webService address
    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();//Get the phone root directory
        File storageDirectory = Environment.getExternalStorageDirectory();//Get the root directory of SD card

        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) {
                // send webservice request
                    sayHi("zxn");    
            }
        });
        
    }

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

    }

    private String sayHi(String name) {
        String result = null;
        // (1) Specify the namespace of the webservice and the method name to call
        method_name = "sayHi";
        SoapObject soapObj = new SoapObject(NAMESPACE, method_name);
        /**
         * (2) Set the values of the parameters of the calling method, which can be omitted if there are no parameters. Note that the parameters must correspond to the variable names in the @WebParam of the service declaration
         */
        soapObj.addProperty("name", name);
        // (3) Generate SOAP request information for calling the Webservice method. This information is described by the SoapSerializationEnvelope object
        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
                SoapEnvelope.VER11);
        // envelope.bodyOut = rpc;
        envelope.dotNet = false;
        envelope.setOutputSoapObject(soapObj);
        // (4) Create HttpTransportsSE object. You can specify the URL of the WSDL document of the WebService through the constructor of the AndroidHttpTransport class
        HttpTransportSE ht = new HttpTransportSE(URL);
        try {
            // (5) Call the WebService method using the call method
            ht.call(null, envelope);
            // (6) Use the getResponse method to get the return result of the WebService method
            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で、メインスレッドでネットワークにアクセスすると、以下のような例外が発生します。

<span style="background-color: rgb(255, 204, 255);">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)</span>
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





理由

Android.os.NetworkOnMainThreadException は、メインスレッドでネットワークにアクセスするなと言っています。これは、アンドロイド 3.0 から、プログラムがメインスレッドでネットワークにアクセスせず、ネットワークへのアクセスを別スレッドにするために必須となっています。

解決策

開発では、ネットワークへのアクセスがメインスレッドをブロックしないように、ネットワークへのアクセスを別スレッドや非同期スレッドAsyncTaskに置くのが一般的です。

 まず、AndroidManifest.xml ファイルの manifest ノードに、以下の設定を追加します。

<uses-permission android:name="android.permission.INTERNET"/>





1. これらの強制的なポリシーに関する問題を無視したい場合は、以下のように

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;
import android.os.StrictMode;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget;
Toast; import android.widget;

public class MainActivity extends Activity {

	private static String NAMESPACE = "http://service.cxf.test/";
	// webService address
	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("The result of the request is: "+result);
		}
		
	};
	
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        File rootDirectory = Environment.getRootDirectory();//Get the phone root directory
        File storageDirectory = Environment.getExternalStorageDirectory();//Get the root directory of SD card

        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;//set the message label
			message.obj = "zxn";
			message.setData(bundle);//message content
			handler.sendMessage(message);//send the 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) Specify the namespace of the webservice and the method name to call
		method_name = "sayHi";
		SoapObject soapObj = new SoapObject(NAMESPACE, method_name);
		/**
		 * (2) Set the value of the parameter to call the method, if there is no parameter, you can omit it. Note that the parameters must correspond to the variable names inside the @WebParam of the service declaration
		 */
		soapObj.addProperty("name", name);
		// (3) Generate SOAP request information for calling the Webservice method. This information is described by the SoapSerializationEnvelope object
		SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
				SoapEnvelope.VER11);
		// envelope.bodyOut = rpc;
		envelope.dotNet = false;
		envelope.setOutputSoapObject(soapObj);
		// (4) Create HttpTransportsSE object. You can specify the URL of the WSDL document of the WebService through the constructor of the AndroidHttpTransport class
		HttpTransportSE ht = new HttpTransportSE(URL);
		try {
			// (5) Call the WebService method using the call method
			ht.call(null, envelope);
			// (6) Use the getResponse method to get the return result of the WebService method
			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. 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;

public class MainActivity extends Activity {

	private static String NAMESPACE = "http://service.cxf.test/";
	// webService address
	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();//Get the phone root directory
        File storageDirectory = Environment.getExternalStorageDirectory();//Get the root directory of SD card

        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>() {
					
					// The onPostExecute method will be called by the UI thread after the doInBackground execution is complete.
					// The result of the background calculation will be passed to the UI thread via this method and displayed to the user in the interface.
					protected void onPostExecute(Object result) {
						super.onPostExecute(result);
						activity_main_btn1.setText("The result of the request is: "+result);//you can update the UI
					}

					//this method runs in the background thread, so you can't update the UI in this thread, the UI thread is the main thread
					protected Object doInBackground(String... params) {
						String result = sayHi("zxn");
// activity_main_btn1.setText("Request result is: "+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) Specify the namespace of the webservice and the method name to call
		method_name = "sayHi";
		SoapObject soapObj = new SoapObject(NAMESPACE, method_name);
		/**
		 * (2) Set the value of the parameter to call the method, if there is no parameter, you can omit it. Note that the parameters must correspond to the variable names inside the @WebParam of the service declaration
		 */
		soapObj.addProperty("name", name);
		// (3) Generate SOAP request information for calling the Webservice method. This information is described by the SoapSerializationEnvelope object
		SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
				SoapEnvelope.VER11);
		// envelope.bodyOut = rpc;
		envelope.dotNet = false;
		envelope.setOutputSoapObject(soapObj);
		// (4) Create HttpTransportsSE object. You can specify the URL of the WSDL document of the WebService through the constructor of the AndroidHttpTransport class
		HttpTransportSE ht = new HttpTransportSE(URL);
		try {
			// (5) Call the WebService method using the call method
			ht.call(null, envelope);
			// (6) Use the getResponse method to get the return result of the WebService method
			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;
	}

}