1

I have a Room database which stores vocabulary data. Also I am trying to make a widget which shows one in the vocabulary database.

When I start my app and add a widget to home screen, no problem with starting the app. But whenever I start my app after adding the widget, I get an error that says my loaded data is null.

Here is my Widget code:

public class VocaWidget extends AppWidgetProvider {

    private AppWidgetManager manager;

    private RemoteViews remoteView;

    private AsyncTask<Void, Void, LiveData<Vocabulary>> showVocaTask;
    private Callable myTask;

    private UpdateWidgetReceiver receiver;
    private final int UPDATE_WIDGET_ID = 17;
    public final String UPDATE_WIDGET = "action.updatewidget.update";

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.d("HSK APP", "VocaWidget onReceive()");

        if (remoteView == null) {
            remoteView = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        }
        manager = AppWidgetManager.getInstance(context);

        ComponentName widgetName = new ComponentName(context.getPackageName(), VocaWidget.class.getName());
        int[] widgetIds = manager.getAppWidgetIds(widgetName);
        setPendingIntent(context, widgetIds);
    }

    @Override
    public void onEnabled(final Context context) {
        Log.d("HSK APP", "VocaWidget onEnabled()");
        if (remoteView == null) {
            remoteView = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        }
        if (receiver == null) {
            receiver = new UpdateWidgetReceiver();
        }
        IntentFilter filter = new IntentFilter(UPDATE_WIDGET);
        context.getApplicationContext().registerReceiver(receiver, filter);

        super.onEnabled(context);
        AppHelper.loadInstance(context);
    }

    private void setPendingIntent(Context context, int[] appWidgetIds) {
        Intent intent = new Intent(UPDATE_WIDGET);
        PendingIntent updateWidgetPI = PendingIntent.getBroadcast(context, UPDATE_WIDGET_ID, intent, 0);
        remoteView.setOnClickPendingIntent(R.id.widget_voca_button, updateWidgetPI);
        manager.updateAppWidget(appWidgetIds, remoteView);
    }

    @Override
    public void onUpdate(final Context context, AppWidgetManager appWidgetManager, final int[] appWidgetIds) {
        Log.d("HSK APP", "VocaWidget onUpdate()");
        super.onUpdate(context, appWidgetManager, appWidgetIds);

        if (showVocaTask == null) {
            showVocaTask = new AsyncTask<Void, Void, LiveData<Vocabulary>>() {
                @Override
                protected LiveData<Vocabulary> doInBackground(Void... voids) {
                    LiveData<Vocabulary> vocabulary = null;
                    VocaRepository repository = VocaRepository.getInstance();
                    vocabulary = repository.getRandomVocabulary();
                    return vocabulary;
                }

                @Override
                protected void onPostExecute(LiveData<Vocabulary> vocabularyLiveData) {
                    super.onPostExecute(vocabularyLiveData);
                    if (vocabularyLiveData == null || vocabularyLiveData.getValue() == null) {
                        Log.d("HSK APP", "getRamdonVocabulary task null");
                        return;
                    }
                    Vocabulary vocabulary = vocabularyLiveData.getValue();
                    Log.d("HSK APP", "onwidget vocabulary: " + vocabulary.eng);

                    remoteView.setTextViewText(R.id.widget_eng, vocabulary.eng);
                    remoteView.setTextViewText(R.id.widget_kor, vocabulary.kor);
                    manager.updateAppWidget(appWidgetIds, remoteView);
                }
            };
        }
        showVocaTask.execute();
    }

    @Override
    public void onDisabled(Context context) {
        Log.d("HSK APP", "VocaWidget onDisabled()");
        super.onDisabled(context);

        context.getApplicationContext().unregisterReceiver(receiver);
    }


    public static class UpdateWidgetReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("HSK APP", "WidgetReceiver onReceive()");/*
            if (intent.getAction().equalsIgnoreCase(UPDATE_WIDGET)) {
                showVocaTask.execute();
            }*/
        }
    }
}

And here is my Database Repository code:

// Singleton
public class VocaRepository {

    private VocaDao vocaDao;
    private final ExecutorService executor = Executors.newSingleThreadExecutor();

    private static VocaRepository instance;
    private VocaDatabase database;

    private LiveData<List<Vocabulary>> allVocabulary;

    public static VocaRepository getInstance() {
        if (instance == null) {
            loadInstance();
        }
        return instance;
    }

    public static void loadInstance() {
        synchronized (VocaRepository.class) {
            if (instance == null) {
                instance = new VocaRepository();
                instance.database = VocaDatabase.getInstance();
                instance.vocaDao = VocaDatabase.getInstance().vocaDao();
            }
        }
    }

    private LiveData<List<Vocabulary>> loadVocabulary() {
        if (allVocabulary == null || allVocabulary.getValue() == null) {
            try {
                Future future = executor.submit(new LoadTask());
                allVocabulary = (LiveData<List<Vocabulary>>) future.get(1, TimeUnit.SECONDS);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Log.d("HSK APP", "Load Vocabulary result: " + (allVocabulary != null));
        return allVocabulary;
    }

    public LiveData<List<Vocabulary>> getVocabulary(final String eng) {
        return vocaDao.loadVocabulary(eng);
    }

    public LiveData<List<Vocabulary>> getAllVocabulary() {
        loadVocabulary();
        return allVocabulary;
    }

    public LiveData<Vocabulary> getRandomVocabulary() {
        loadVocabulary();

        Random random = new Random();
        int index = random.nextInt(allVocabulary.getValue().size()); // Error!
        return new MutableLiveData<>(allVocabulary.getValue().get(index));
    }

    private class LoadTask implements Callable {
        @Override
        public Object call() throws Exception {
            return vocaDao.loadAllVocabulary();
        }
    }
}

As you can see, whenever I try to get any data, I check whether the loaded data(allVocabulary in VocaRepository) is null and load it if it is so. But it always give me a NullPointerException:

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:354)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
        at Database.source.VocaRepository.getRandomVocabulary(VocaRepository.java:74)
        at hsk.practice.myvoca.widget.VocaWidget$1.doInBackground(VocaWidget.java:97)
        at hsk.practice.myvoca.widget.VocaWidget$1.doInBackground(VocaWidget.java:91)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 

I struggled with this problem for several weeks but I have no idea. Any suggestions will be appreciated.

Isolet
  • 11
  • 3
  • In the method `getRandomVocabulary()`, you are calling `loadVocabulary()`and it looks like there is an exception raised due to which **catch** block is executed and `allVocabulary` is **null**. I would suggest debug and see if `allVocabulary` has data in it or not. – Waqar UlHaq Mar 03 '20 at 10:29
  • @WaqarUlHaq It is not from catch block, instead from ```getRamdomVocabulary()```.I found that ```allVocabulary.getValue()``` is null after ```loadVocabulary()``` is executed. But I still wonder why it is. – Isolet Mar 03 '20 at 10:34
  • `allVocabulary ` is populated from method `loadVocabulary()`, that's why I asked you to check if there is any exception or not while executing these lines `Future future = executor.submit(new LoadTask()); allVocabulary = (LiveData>) future.get(1, TimeUnit.SECONDS);` – Waqar UlHaq Mar 03 '20 at 10:39
  • @WaqarUlHaq There is no exception while executing ```loadVocabulary()```. To update, I found that the type of the returned object from ```future.get()``` is ```androidx.room.RoomTrackingLiveData@b286c34```, but its ```getValue()``` returns null. – Isolet Mar 03 '20 at 10:52
  • the thing is that `allVocabulary` is **null** and it looks like something happened in `loadVocabulary()` method – Waqar UlHaq Mar 03 '20 at 10:55
  • @WaqarUlHaq It is not null, only its ```getValue()``` is null. – Isolet Mar 03 '20 at 11:00

0 Answers0