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.
>) future.get(1, TimeUnit.SECONDS);`
– Waqar UlHaq Mar 03 '20 at 10:39