Android java widget call http error android.os.NetworkOnMainThreadException

317 Views Asked by At

I am having the following error:

FATAL EXCEPTION: main
Process: com.name.app, PID: 21535
java.lang.RuntimeException: Unable to start receiver com.name.app.MyWidget: 
android.os.NetworkOnMainThreadException
at android.app.ActivityThread.handleReceiver(ActivityThread.java:4012)
at android.app.ActivityThread.access$1500(ActivityThread.java:232)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2025)
....

I need to make a widget every certain time make an http call to a website for scraping.

I have the following code:

package com.name.app;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.widget.RelativeLayout;
import android.widget.RemoteViews;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;

public class MyWidget extends AppWidgetProvider {

    void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_design);

        //get the widget value
        SharedPreferences preferences = context.getSharedPreferences("PREFS", 0);
        int value = preferences.getInt("value", 1);

        //set the value in the textview
        views.setTextViewText(R.id.text, "" + value);

        //update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);

        //reschedule the widget refresh
        AlarmHandler alarmHandler = new AlarmHandler(context);
        alarmHandler.cancelAlarmManager();
        alarmHandler.setAlarmManager();

        Document doc;

        try {
            // fetching the target website
            doc = Jsoup.connect("https://pageWeb...").get();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        Log.d("WIDGET", "Widget updated!");
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        for (int appWidgetId: appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    @Override
    public void onDisabled(Context context) {
        //stop updating the widget
        AlarmHandler alarmHandler = new AlarmHandler(context);
        alarmHandler.cancelAlarmManager();

        Log.d("WIDGET", "Widget removed!");
    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AmazonWidget"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <receiver
            android:name=".MyWidget"
            android:exported="true">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>

        <receiver
            android:name=".WidgetService"
            android:enabled="true" />
    </application>

</manifest>

Going into the app's information about permissions, it tells me there are no permissions required.

Can you tell me where I'm going wrong?

2

There are 2 best solutions below

7
jayesh gurudayalani On

As per the error , it looks like you are calling api on main thread which is not allowed so we need to move you api calling to IO thread . Can you try below mentioned solution

From

try {
            // fetching the target website
            doc = Jsoup.connect("https://pageWeb...").get();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

To

val job = CoroutineScope(Dispatchers.IO).launch {
            try {
                // fetching the target website
                doc = Jsoup.connect("https://pageWeb...").get();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        job.cancel()

job object you need to declare it as member and job.cancel() need to place in onDisabled method

2
ahvroyal On

You shouldn't do networking on main thread in android application, instead consider using AsyncTask for doing network operations in which it offloads the work out of the main thread.

AsyncTask reference : https://developer.android.com/reference/android/os/AsyncTask