Question

How to Fix NetworkOnMainThreadException in Android (Java)

javaandroidandroid-networkingnetworkonmainthread

Question

I am getting an error while running my Android RSS reader project.

The following code loads and parses an RSS feed:

URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();

When this code runs, Android throws the following exception:

android.os.NetworkOnMainThreadException

How can this issue be fixed correctly in Android?

Short Answer

By the end of this page, you will understand why Android throws NetworkOnMainThreadException, why network work should never run on the UI thread, and how to move RSS fetching code to a background thread safely. You will also learn how developers typically handle networking, parsing, and UI updates in real Android apps.

Concept

NetworkOnMainThreadException happens when your Android app tries to perform a network operation on the main thread.

In Android, the main thread is also called the UI thread. It is responsible for:

  • drawing the screen
  • responding to taps and scrolls
  • updating views
  • keeping the app responsive

Network calls such as this line:

url.openStream()

can take an unknown amount of time. The internet may be slow, the server may delay its response, or the connection may fail.

If Android allowed this work on the main thread, the app could freeze. To prevent that, Android throws NetworkOnMainThreadException.

Your code is doing two kinds of work:

  • network I/O: downloading the RSS XML from the URL
  • parsing: reading the XML using SAX

The important issue is the network request. That must happen off the main thread.

This matters in real programming because almost every app performs slow operations such as:

  • API requests
  • downloading files
  • database access
  • reading large files

A responsive app separates:

  • background work for slow tasks
  • main-thread work for updating the UI

That separation is a core Android programming rule.

Mental Model

Think of the main thread as a cashier serving customers in a busy store.

  • If the cashier keeps helping customers, the line moves.
  • If the cashier leaves to drive across town to pick up a package, everyone waits.

A network request is like sending someone across town. It may take a few seconds or much longer.

So instead of making the cashier do it, you send a worker in the background. When the package arrives, the cashier is told the result and updates the screen.

In Android terms:

  • main thread = the cashier
  • background thread = the worker
  • UI update = telling the cashier what to display
Take Quiz

Syntax and Examples

The core fix is: move network work off the main thread.

Problematic code

This code causes the exception if called on the UI thread:

URL url = new URL(urlToRssFeed);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);

The openStream() call performs network I/O.

Simple background thread example

new Thread(() -> {
    try {
        URL url = new URL(urlToRssFeed);
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        XMLReader xmlReader = parser.getXMLReader();
        RssHandler rssHandler = new RssHandler();
        xmlReader.setContentHandler(rssHandler);

        InputSource inputSource = new InputSource(url.openStream());
        xmlReader.parse(inputSource);

        Feed feed = rssHandler.getFeed();

        runOnUiThread(() -> {
            // Update your UI here with feed data
        });
    } catch (Exception e) {
        e.printStackTrace();
        runOnUiThread(() -> {
            // Show an error message here
        });
    }
}).start();

What this example does

  • Creates a new background thread
  • Downloads and parses the RSS feed there
  • Switches back to the UI thread with runOnUiThread() to update views

Important rule

  • Do network work in a background thread
  • Do UI updates on the main thread

Also add internet permission

Without this permission, networking will fail for a different reason:

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

Add it inside AndroidManifest.xml.

Step by Step Execution

Consider this example:

new Thread(() -> {
    try {
        URL url = new URL(urlToRssFeed);
        InputSource inputSource = new InputSource(url.openStream());

        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        XMLReader xmlReader = parser.getXMLReader();

        RssHandler handler = new RssHandler();
        xmlReader.setContentHandler(handler);
        xmlReader.parse(inputSource);

        Feed feed = handler.getFeed();

        runOnUiThread(() -> {
            // show feed on screen
        });
    } catch (Exception e) {
        runOnUiThread(() -> {
            // show error
        });
    }
}).start();

Step by step

  1. new Thread(...).start()

    • A background thread is created.
    • The code inside it does not block the UI.
  2. new URL(urlToRssFeed)

    • Builds a URL object from the RSS link.
  3. url.openStream()

    • Opens a network connection and starts reading data.
    • This is the operation that would crash if done on the main thread.
  4. Create SAX parser objects

    • SAXParserFactory, SAXParser, and XMLReader are prepared.
    • These objects parse the XML feed.
  5. Set the content handler

    • RssHandler receives XML parsing events.
  6. xmlReader.parse(inputSource)

    • The RSS XML is parsed.
    • The handler extracts feed data.
  7. handler.getFeed()

    • Returns the parsed result.
  8. runOnUiThread(...)

    • Switches back to the main thread.
    • Safe place to update TextView, RecyclerView, or other UI elements.
  9. catch (Exception e)

    • Handles failures such as invalid URLs, parsing errors, or network issues.

The key idea is that the slow work happens in the background, while display changes happen on the UI thread.

Real World Use Cases

This rule applies far beyond RSS parsing.

Common situations where background threading is required

  • Fetching JSON from a REST API
  • Downloading images or files
  • Uploading form data
  • Reading data from a remote server
  • Parsing XML or JSON after downloading it
  • Syncing app data with cloud services

Example scenarios

Weather app

A weather app requests forecast data from an API. The request must happen in the background, then the screen updates with temperature data.

News app

A news app downloads article feeds and parses them. This is very similar to your RSS reader case.

Login screen

A login form sends credentials to a backend server. The request runs in the background while a loading indicator is shown.

File downloader

A file download may take many seconds. Running it on the main thread would freeze the app immediately.

In all these cases, the pattern is the same:

  1. start background work
  2. wait for result or error
  3. update UI on main thread
Take Quiz

Real Codebase Usage

In real Android projects, developers usually do more than just start a raw thread.

Common patterns

1. Guard clauses before networking

Developers validate input early:

if (urlToRssFeed == null || urlToRssFeed.isEmpty()) {
    return;
}

This avoids starting bad requests.

2. Try-with-resources or proper stream closing

Streams should be closed to avoid leaks.

try (InputStream stream = url.openStream()) {
    InputSource inputSource = new InputSource(stream);
    xmlReader.parse(inputSource);
}

3. Error handling

Production apps distinguish different failures:

  • no internet connection
  • timeout
  • malformed XML
  • invalid URL
  • server error

4. Loading state

Apps often show and hide a loading indicator:

  • show spinner before request
  • hide spinner after success or failure

5. Repository or service classes

Instead of putting networking directly in an Activity, developers often create classes like:

  • RssService
  • FeedRepository
  • NewsApiClient

This keeps UI code cleaner.

6. Modern Android tools

In newer Android apps, developers often use:

  • ExecutorService
  • Handler
  • Kotlin coroutines
  • Retrofit
  • WorkManager for deferrable background work

But the core rule remains the same: do not block the main thread.

Cleaner example using a helper method

private void loadFeed(String urlToRssFeed) {
    new Thread(() -> {
        try {
            Feed feed = fetchFeed(urlToRssFeed);
            runOnUiThread(() -> showFeed(feed));
        } catch (Exception e) {
            runOnUiThread(() -> showError(e.getMessage()));
        }
    }).start();
}

private Feed fetchFeed(String urlToRssFeed) throws Exception {
    URL url = new URL(urlToRssFeed);
    SAXParserFactory factory = SAXParserFactory.newInstance();
    SAXParser parser = factory.newSAXParser();
    XMLReader xmlReader = parser.getXMLReader();
    RssHandler handler = new RssHandler();
    xmlReader.setContentHandler(handler);

    try (InputStream stream = url.openStream()) {
        InputSource inputSource = new InputSource(stream);
        xmlReader.parse(inputSource);
    }

    return handler.getFeed();
}

This separates fetching logic from UI logic.

Common Mistakes

1. Performing network requests directly in onCreate() or button handlers

Broken example:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    URL url = new URL(urlToRssFeed);
    InputSource is = new InputSource(url.openStream());
}

Why it is wrong:

  • onCreate() runs on the main thread
  • openStream() performs network I/O

How to avoid it:

  • move the network call to a background thread

2. Updating UI from a background thread

Broken example:

new Thread(() -> {
    textView.setText("Loaded");
}).start();

Why it is wrong:

  • Android views must be updated on the main thread

Fix:

new Thread(() -> {
    runOnUiThread(() -> textView.setText("Loaded"));
}).start();

3. Forgetting the internet permission

Broken setup:

  • No INTERNET permission in the manifest

Fix:

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

4. Not closing streams

Broken example:

InputSource inputSource = new InputSource(url.openStream());
xmlReader.parse(inputSource);

Why it is risky:

  • open streams may remain unclosed

Fix:

try (InputStream stream = url.openStream()) {
    InputSource inputSource = new InputSource(stream);
    xmlReader.parse(inputSource);
}

5. Catching exceptions but ignoring them

Broken example:

catch (Exception e) {
}

Why it is bad:

  • hides useful debugging information
  • makes failures hard to understand

Fix:

catch (Exception e) {
    e.printStackTrace();
}

6. Trying to disable the exception instead of fixing the design

Some beginners search for a way to bypass the rule. That may hide the symptom, but it does not solve the real problem.

The correct fix is architectural:

  • move slow work off the main thread

Comparisons

ApproachWhat it doesGood forLimitations
Main thread networkingRuns request directly on UI threadNever recommendedCauses NetworkOnMainThreadException and freezes UI
new Thread()Runs work in background manuallySmall learning examplesMore manual management
ExecutorServiceManages background threads more cleanlyReal apps with multiple tasksSlightly more setup
AsyncTaskOlder Android background helperLegacy codebases onlyDeprecated, should not be used in new code
Kotlin coroutinesSimplifies async codeModern Android appsRequires Kotlin
Retrofit/OkHttpNetworking librariesProduction API workExtra dependency, but very common

new Thread() vs AsyncTask

Featurenew Thread()AsyncTask
Still valid for basic Java useYesLegacy only
Recommended for new Android appsSometimes for simple casesNo
Built into old Android tutorialsYesYes
Modern best practiceLimitedNo

openStream() vs a networking library

OptionUse case
URL.openStream()Simple demos or very small examples
OkHttp / RetrofitReal-world apps needing timeouts, interceptors, JSON handling, and better error management
Take Quiz

Cheat Sheet

Quick rules

  • NetworkOnMainThreadException means you did network I/O on the UI thread.
  • url.openStream() is a network operation.
  • Network calls must run on a background thread.
  • UI updates must run on the main thread.
  • Add internet permission in AndroidManifest.xml.

Basic pattern

new Thread(() -> {
    try {
        // network work here
        runOnUiThread(() -> {
            // update UI here
        });
    } catch (Exception e) {
        runOnUiThread(() -> {
            // show error here
        });
    }
}).start();

Permission

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

Safe stream usage

try (InputStream stream = url.openStream()) {
    InputSource inputSource = new InputSource(stream);
    xmlReader.parse(inputSource);
}

Remember

  • Slow task -> background thread
  • Screen update -> main thread
  • Never block the UI thread

FAQ

Why does Android throw NetworkOnMainThreadException?

Because your app is trying to do a network request on the main UI thread, which can freeze the interface.

Which line in my code causes the problem?

This line is the main cause:

url.openStream()

It opens a network connection and reads data from the internet.

Can I parse XML on the main thread if the file is local?

If the file is truly local and small, parsing may be possible on the main thread, but heavy parsing can still make the UI lag. Network access must not run there.

Is AsyncTask the right fix?

For old codebases, you may see it used, but it is deprecated. Prefer a background thread, ExecutorService, or modern Android tools.

Do I also need internet permission?

Yes. Add this to the manifest:

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

Can I update a TextView from the background thread?

No. Use runOnUiThread() or another main-thread mechanism.

What is the best modern approach in Android?

In modern apps, developers often use Retrofit with coroutines or other structured background execution tools. But the core idea is still the same: keep network work off the main thread.

Take Quiz

Mini Project

Description

Build a small Android feature that downloads an RSS feed in the background and displays whether loading succeeded or failed. This project demonstrates the correct separation between network work and UI updates.

Goal

Create a simple RSS loader that fetches and parses a feed on a background thread, then updates the UI safely on the main thread.

Requirements

  • Start the RSS download from a method such as loadFeed()
  • Perform the network request and XML parsing on a background thread
  • Update the UI on the main thread after success or failure
  • Close the network stream properly
  • Handle exceptions and show an error message if loading fails
Take Quiz

Keep learning

Related questions

View more