How to resolve android.os.NetworkOnMainThreadException

Hello friends, I have seen so many new developers facing a specific error android.os.NetworkOnMainThreadException

Why this android crash happened

Main this application crash happened on Android’s Honeycomb(3.0) and above, but it’s works fine on Android’s older versions(2.x). Its because Honeycomb(3.0) and above that version are much stricter about abuse against the UI Thread. For example, when an Android device running 3.0 or above detects a network access on the UI thread, a NetworkOnMainThreadException will be thrown:

Solution of this crash

E/AndroidRuntime(673): java.lang.RuntimeException: Unable to start activity
    ComponentInfo{com.example/com.example.ExampleActivity}: android.os.NetworkOnMainThreadException

 

Creating New Android Project

So let’s start by creating a new android project

1. Create a new project in Eclipse from File ⇒ New ⇒ Android Application Project. I had left my main activity name as MainActivity.java and gave the package name as info.androidstation.networkonmainthread

2. As we are fetching the Data by making HTTP calls, we need to add INTERNET permission in our AndroidManifest.xml file. Open AndroidManifest.xml and add the following permission.

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

3. We are also checking weather the internet is available or not? We need to add ACCESS_NETWORK_STATE permission in our AndroidManifest.xml file. Open AndroidManifest.xml and add the following permission.

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

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidstation.networkonmainthread"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.INTERNET" /><!--  This permission is for Internet connection established. -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><!--  This permission is to check weather Internet connection is available or not -->

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

4. We are now creating a Utility class called Utility.java. In this class we will check weather internet is available or not? We will also download data from the url using GET method and after that we will read all the downloaded data and convert it into simple string.

Utility.java

package info.androidstation.networkonmainthread.utility;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;

public class Utility {
	private static String TAG = "Utility Log";

	public static boolean isOnline(Context ctx)//Checking Internet is available or not { 
		ConnectivityManager connMgr = (ConnectivityManager) ctx
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
		if (networkInfo != null && networkInfo.isConnected())
			return true;
		else
			return false;
	}

	// Given a URL, establishes an HttpUrlConnection and retrieves
	// the web page content as a InputStream, which it returns as
	// a string.
	public static String downloadDataFromUrl(String myurl) throws IOException {
		InputStream is = null;
		try {
			URL url = new URL(myurl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setReadTimeout(10000); // time in milliseconds
			conn.setConnectTimeout(15000); // time in milliseconds
			conn.setRequestMethod("GET"); // request method GET OR POST
			conn.setDoInput(true);
			// Starts the query
			conn.connect(); // calling the web address
			int response = conn.getResponseCode();
			Log.d(TAG, "The response is: " + response);
			is = conn.getInputStream();

			// Convert the InputStream into a string
			String contentAsString = readInputStream(is);
			return contentAsString;

			// Makes sure that the InputStream is closed after the app is
			// finished using it.
		} finally {
			if (is != null) {
				is.close();
			}
		}
	}

	// Reads an InputStream and converts it to a String.
	public static String readInputStream(InputStream stream) throws IOException {
		int n = 0;
		char[] buffer = new char[1024 * 4];
		InputStreamReader reader = new InputStreamReader(stream, "UTF8");
		StringWriter writer = new StringWriter();
		while (-1 != (n = reader.read(buffer)))
			writer.write(buffer, 0, n);
		return writer.toString();
	}
}

5. I am adding a TextView to show the downloaded data. Open the layout file of your main activity and add a TextView element.

activity_main.xml

<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="info.androidstation.networkonmainthread.MainActivity" >

    <TextView
        android:id="@+id/tvData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:isScrollContainer="true"
        android:scrollbars="vertical"
         />

</RelativeLayout>

6. In main activity class (MainActivity.java) I have declared things; one inner class extends AsyncTask and another one is function. Asynctask will call the downloadData function of Utility class in doInBackground override method it’s the separate thread which will not affect to the UI thread. while function directly call the downloadData function from the UI thread so this will throw exception.

MainActivity.java

package info.androidstation.networkonmainthread;

import info.androidstation.networkonmainthread.utility.Utility;

import java.io.IOException;

import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

	private TextView tvData;
	private String url = "http://www.androidstation.info/people.json";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tvData = (TextView) findViewById(R.id.tvData);
		tvData.setMovementMethod(new ScrollingMovementMethod());

	//	getDataFromUrl(); // Connect url from the main thread for get data this will throw NetworkOnMainThreadExcection

		new GetJSONTask().execute(url); //execute asynctask object this will resolve NetworkOnMainThreadExcection 
	}

	private void getDataFromUrl() {
		try {
			tvData.setText(Utility.downloadDataFromUrl(url));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// Uses AsyncTask to create a task away from the main UI thread(For Avoid
	// NetworkOnMainThreadException). This task takes a
	// URL string and uses it to create an HttpUrlConnection. Once the
	// connection
	// has been established, the AsyncTask downloads the contents of the data as
	// an InputStream. Than, the InputStream is converted into a string, which
	// is
	// displayed in the TextView by the AsyncTask's onPostExecute method. Which
	// called after doInBackgroud Complete
	private class GetJSONTask extends AsyncTask<String, Void, String> {
		private ProgressDialog pd;

		// onPreExecute called before the doInBackgroud start for display
		// progress dialog.
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			pd = ProgressDialog.show(MainActivity.this, "", "Loading", true,
					false); // Create and show Progress dialog
		}

		@Override
		protected String doInBackground(String... urls) {

			try {
				return Utility.downloadDataFromUrl(urls[0]);
			} catch (IOException e) {
				return "Unable to retrieve data. URL may be invalid.";
			}
		}

		// onPostExecute displays the results of the doInBackgroud and also we
		// can hide progress dialog.
		@Override
		protected void onPostExecute(String result) {
			pd.dismiss();
			tvData.setText(result);
		}
	}
}

This Example Output Will look like this:

android.os.NetworkOnMainThreadException
Hope you all find this tutorial useful. Happy coding. 🙂