-1

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?

Paul
  • 3,644
  • 9
  • 47
  • 113

2 Answers2

0

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

jayesh gurudayalani
  • 1,368
  • 1
  • 9
  • 14
  • 1
    Is this kotlin code? – Paul May 05 '23 at 10:35
  • yes but instead of coroutines , you can use another mechanism that execute your api calling code on io thread not main thread as coroutines you can use in kotlin – jayesh gurudayalani May 05 '23 at 10:36
  • 1
    Being that I'm not using kotlin on the project, what can I use in java for android that does the same thing since AsyncTask is deprecated. – Paul May 05 '23 at 10:44
  • You can go with RXJava that allows you easy thread switching along with rich operators . It will also allow you to manage/cancel your task https://www.digitalocean.com/community/tutorials/android-rxjava-retrofit – jayesh gurudayalani May 05 '23 at 10:47
  • If I use something like this it's not good: `Thread thread = new Thread(new Runnable(){`? – Paul May 05 '23 at 10:48
  • yes @Paul not suggested with ```Thread thread = new Thread(new Runnable(){``` – jayesh gurudayalani May 05 '23 at 10:50
  • Can I ask you in java how would you, if you can give an example? I was thinking about creating a separate class that makes the http request process the data and then return it, but you always have to have a wait. – Paul May 05 '23 at 11:07
0

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

ahvroyal
  • 1
  • 1
  • This class was deprecated in API level 30. – Paul May 05 '23 at 10:40
  • That was the easiest way to go, however there are other options that you can use; what i can suggest to you is to use OkHttp library for this purpose. OkHttp library guide : https://guides.codepath.com/android/Using-OkHttp Note that the modern preferred way is to use kotlin coroutines for this, but that is kotlin only. – ahvroyal May 05 '23 at 11:03