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: