This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 3 years ago.
Unable to get particular record from database getting this output.
My output is :
java.lang.RuntimeException: Unable to start activity ComponentInfo{appwork.com.example/appwork.com.example.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.arch.lifecycle.LiveData.observe(android.arch.lifecycle.LifecycleOwner, android.arch.lifecycle.Observer)' on a null object reference
I am using room database, Modelview, Repository and Dao file to get Live data but unable to get particular record from database via following things
<pre><code>
public class MainActivity extends AppCompatActivity {
private NoteViewModel noteViewModel;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
noteViewModel =
ViewModelProviders.of(this).get(NoteViewModel.class);
noteViewModel.getNote("test7").observe(this, new Observer<Note>()
{
#Override
public void onChanged(#Nullable Note note)
{
Toast.makeText(MainActivity.this,
"found title is : " +note.getTitle(),
Toast.LENGTH_LONG).show();
}
});
}
}
public class NoteViewModel extends AndroidViewModel
{
private LiveData<Note> note;
public NoteViewModel(#NonNull Application application)
{
super(application);
// note = repository.getNote("");
}
public LiveData<Note> getNote(String search)
{
repository.getNote(search);
if(note != null)
{
return note;
}
return null;
}
}
public class NoteRepository {
private NoteDao noteDao;
private LiveData<Note> note;
public NoteRepository(Application application)
{
NoteDatabase noteDatabase =
NoteDatabase.getInstance(application);
noteDao = noteDatabase.noteDao();
}
public LiveData<Note> getNote(String search)
{
new SearchNoteAsyncTask(noteDao).execute(search);
if(note != null)
{
return note;
}
return null;
}
public static void asyncFinished(LiveData<Note> results)
{
note = results;
}
public static class SearchNoteAsyncTask extends AsyncTask<String, Void, LiveData<Note>>
{
private NoteDao noteDao;
private LiveData<Note> note;
private SearchNoteAsyncTask(NoteDao noteDao)
{
this.noteDao = noteDao;
}
public LiveData<Note> doInBackground(String... search)
{
return noteDao.getNote(search[0]);
}
public void onPostExecute(LiveData<Note> result)
{
asyncFinished(result);
}
}
}
#Dao
public interface NoteDao{
#Query("Select * from note_table where title =:search Order By priority DESC")
LiveData<Note> getNote(String search);
}
I am getting response in Repository call but unable to get value from public
static class SearchNoteAsyncTask extends AsyncTask<String, Void, LiveData<Note>>
Any working example will be great!
thanks
Problem is that you are returning a different reference of Livedata to your View in -
public LiveData<Note> getNote(String search){
repository.getNote(search);
if(note != null)
{
return note; // this is the livedata reference that UI is observing
}
return null;
}
And when you get the response from repo you change the reference of livedata -
public static void asyncFinished(LiveData<Note> results) {
note = results;
}
But your UI is listening to previous LiveData.
Instead of changing the reference, you should change the value in original note livedata.
That would look something like -
public static void asyncFinished(LiveData<Note> results) {
note.setValue(results.getValue); // not the preferred way though, but it will help you understand why your current implementation doesn't work.
}
And best would be to use Transformation on the original livedata -
public LiveData<Note> getNote(String search){
return Transformations.map(repository.getNote(search),
value -> note.setValue(value));
}
Related
Why i should pass the LiveData to the activity to get the value of allQuestions. I am a little bit confused.
I tried to get the List of allQuestions in QuestionViewModel from the repo directly but it didn't work.
This is what's in my brain.
public class QuestionViewModel extends AndroidViewModel {
private Repository repo;
private List<QuestionModel> allQuestions;
public QuestionViewModel(#NonNull Application application) {
super(application);
repo = new Repository(application);
allQuestions = repo.getAllQuestions().getValue(); //This line did not work for me (︶︹︺)
}}
What i did do to get it work.
ViewModel
public class QuestionViewModel extends AndroidViewModel {
private Repository repo;
private LiveData<List<QuestionModel>> allQuestions;
public List<QuestionModel> CurrentListOfQues;
public QuestionViewModel(#NonNull Application application) {
super(application);
repo = new Repository(application);
allQuestions = repo.getAllQuestions();
}
public LiveData<List<QuestionModel>> GetAllQues() {
if (allQuestions == null) {
allQuestions = new LiveData<List<QuestionModel>>() {
#Override
public void observe(#NonNull LifecycleOwner owner, #NonNull Observer<? super List<QuestionModel>> observer) {
super.observe(owner, observer);
}
};
}
return allQuestions;
}
public void setCurrent(List<QuestionModel> CurrentListOfQues) {
if (this.CurrentListOfQues == null) {
this.CurrentListOfQues = CurrentListOfQues;
}
}
Question Activity
QVM = new ViewModelProvider(this).get(QuestionViewModel.class);
QVM.GetAllQues().observe(this, new Observer<List<QuestionModel>>() {
#Override
public void onChanged(List<QuestionModel> allQuestions) {
QVM.setCurrent(allQuestions);
}
});
Is this the right way to get the list in my viewmodel? Why allQuestions = repo.getAllQuestions().getValue(); is always null
my database class is
public abstract class MyDatabase extends RoomDatabase {
private static MyDatabase instance;
public abstract DataAccess dataAccess();
public static MyDatabase getInstance(final Context context) {
if(instance == null) {
synchronized (MyDatabase.class) {
if(instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "app.db")
.createFromAsset("test.db")
.fallbackToDestructiveMigration()
.build();
}
}
}
return instance;
}
repository class is
public class MyRepository {
private DataAccess dataAccess;
public MyRepository(Application application) {
MyDatabase myDatabase = MyDatabase.getInstance(application);
dataAccess = mDatabase.dataAccess();
}
public LiveData<List<String>> getVillages(int id) {
return dataAccess.getVillage(id);
}
private LiveData<List<VillagesModel>> fetchAllVillages(String search) {
return dataAccess.AllVillages(search);
}
}
DAO class
#Dao
public interface DataAccess {
#Query("SELECT distinct Village FROM Villages WHERE village like :search")
LiveData<List<String>> AllVillages(String search);
#Query("SELECT Village FROM Villages WHERE id = :id")
LiveData<List<String>> getVillages(int id);
}
ViewModel class is
public class VillageViewModel extends AndroidViewModel {
private MyRepository repository;
private LiveData<List<lVillagesModel>> allVillages;
public VillageViewModel(#NonNull Application application) {
super(application);
repository = new MyRepository(application);
}
public LiveData<List<VillagesModel>> getAllVillages(String poSearch) {
return repository.AllVillages(pinSearch, poSearch);
}
}
in Fragment on button click i called public method getVillages is returning always null.
in Fragment Button Onclick method i used observer like this
VillageViewModel.getAllVillages(search).observe(getViewLifecycleOwner(), new Observer<List<VillagesModel>>() {
#Override
public void onChanged(List<VillagesModel> fvillagesModels) {
// updated the recyclerview contents here.
adapter.notifyDataSetChanged(); // this is recyclerView adapter
}
}
This call on ViewModel method returns nothing. While debugging the app, I found that Query from DAO is returning null even though Database has prepopulated values.
Please guide me to solve the problem as I already wasted 6 hours searching in internet for solution.
here is my problem:
i have used MVVM/Repository design pattern like this:
Activity -(Observes)-> ViewModel's LiveData -> Repository -> WebService API (GET Resource)
i have another calls for UPDATING Resource to WebService.
Problem:
after changing resource on the server. how i can make the Resource livedata to update itself with new servers data
i want to force it fetch data from server again because some other data may have been changed.
and i dont want to use local database (Room) and change it because my server data might be changed. and they need to fetch each time.
The Only solution passed my Mind was to create a Livedata Source (as dataVersion) to it.
and increment it after every update like this (pseudo code):
dataVersion = new MutableLiveData();
dataVersion.setValue(0);
// my repository get method hasnt anything to do with the dataVersion.
myData = Transformation.switchmap(dataVersion, versionNum -> { WebServiceRepo.getList() });
and how dataVersion should get updated in ViewModel.
You could extend MutableLiveData to give it manual fetch functionality.
public class RefreshLiveData<T> extends MutableLiveData<T> {
public interface RefreshAction<T> {
private interface Callback<T> {
void onDataLoaded(T t);
}
void loadData(Callback<T> callback);
}
private final RefreshAction<T> refreshAction;
private final Callback<T> callback = new RefreshAction.Callback<T>() {
#Override
public void onDataLoaded(T t) {
postValue(t);
}
};
public RefreshLiveData(RefreshAction<T> refreshAction) {
this.refreshAction = refreshAction;
}
public final void refresh() {
refreshAction.loadData(callback);
}
}
Then you can do
public class YourViewModel extends ViewModel {
private RefreshLiveData<List<Project>> refreshLiveData;
private final GithubRepository githubRepository;
private final SavedStateHandle savedStateHandle;
public YourViewModel(GithubRepository githubRepository, SavedStateHandle savedStateHandle) {
this.githubRepository = githubRepository;
this.savedStateHandle = savedStateHandle;
refreshLiveData = Transformations.switchMap(savedStateHandle.getLiveData("userId", ""), (userId) -> {
githubRepository.getProjectList(userId);
});
}
public void refreshData() {
refreshLiveData.refresh();
}
public LiveData<List<Project>> getProjects() {
return refreshLiveData;
}
}
And then repository can do:
public RefreshLiveData<List<Project>> getProjectList(String userId) {
final RefreshLiveData<List<Project>> liveData = new RefreshLiveData<>((callback) -> {
githubService.getProjectList(userId).enqueue(new Callback<List<Project>>() {
#Override
public void onResponse(Call<List<Project>> call, Response<List<Project>> response) {
callback.onDataLoaded(response.body());
}
#Override
public void onFailure(Call<List<Project>> call, Throwable t) {
}
});
});
return liveData;
}
I try to make sample login page with two fields (username, password) and save button with android architecture component, using android data binding, validating the data in viewmodel and from view model I make call to repository for remote server call as mentioned in official doc, remote server return me userid with success so how can I start new fragment from view model using this success? I learn something about singleLiveEvent and EventObserver, but I'm not able to find there clear usage example:
LoginViewModel
private MutableLiveData<String> snackbarStringSingleLiveEvent= new MutableLiveData<>();
#Inject
public LoginViewModel(#NonNull AppDatabase appDatabase,
#NonNull JobPortalApplication application,
#NonNull MyApiEndpointInterface myApiEndpointInterface) {
super(application);
loginRepository = new LoginRepository(application, appDatabase, myApiEndpointInterface);
snackbarStringSingleLiveEvent = loginRepository.getLogin(username.get(), password.get(), type.get());
}
public MutableLiveData<String> getSnackbarStringSingleLiveEvent() {
return snackbarStringSingleLiveEvent;
}
Repository
public SingleLiveEvent<String> getLogin(String name, String password, String type) {
SingleLiveEvent<String> mutableLiveData = new SingleLiveEvent<>();
apiEndpointInterface.getlogin(name, password, type).enqueue(new Callback<GenericResponse>() {
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(response.body().getMessage());
}
#Override
public void onFailure(Call<GenericResponse> responseCall, Throwable t) {
mutableLiveData.setValue(Constant.FAILED);
}
});
return mutableLiveData;
}
Login Fragment
private void observeViewModel(final LoginViewModel viewModel) {
// Observe project data
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new Observer<String>() {
#Override
public void onChanged(String s) {
}
});
}
How can I use EventObserver in above case? Any practical example?
Check out below example about how you can create single LiveEvent to observe only one time as LiveData :
Create a class called Event as below that will provide our data once and acts as child of LiveData wrapper :
public class Event<T> {
private boolean hasBeenHandled = false;
private T content;
public Event(T content) {
this.content = content;
}
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
public boolean isHandled() {
return hasBeenHandled;
}
}
Then declare this EventObserver class like below so that we don't end up placing condition for checking about Event handled every time, everywhere :
public class EventObserver<T> implements Observer<Event<T>> {
private OnEventChanged onEventChanged;
public EventObserver(OnEventChanged onEventChanged) {
this.onEventChanged = onEventChanged;
}
#Override
public void onChanged(#Nullable Event<T> tEvent) {
if (tEvent != null && tEvent.getContentIfNotHandled() != null && onEventChanged != null)
onEventChanged.onUnhandledContent(tEvent.getContentIfNotHandled());
}
interface OnEventChanged<T> {
void onUnhandledContent(T data);
}
}
And How you can implement it :
MutableLiveData<Event<String>> data = new MutableLiveData<>();
// And observe like below
data.observe(lifecycleOwner, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
// And this is how you add data as event to LiveData
data.setValue(new Event(""));
Refer here for details.
Edit for O.P.:
Yes, data.setValue(new Event("")); is meant for repository when you've got response from API (Remember to return same LiveData type you've taken in VM instead of SingleLiveEvent class though).
So, let's say you've created LiveData in ViewModel like below :
private MutableLiveData<Event<String>> snackbarStringSingleLiveEvent= new MutableLiveData<>();
You provide value to this livedata as Single Event from repository like below :
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(new Event(response.body().getMessage())); // we set it as Event wrapper class.
}
And observe it on UI (Fragment) like below :
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
Event.java
public class Event<T> {
private T content;
private boolean hasBeenHandled = false;
public Event(T content) {
this.content = content;
}
/**
* Returns the content and prevents its use again.
*/
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
/**
* Returns the content, even if it's already been handled.
*/
public T peekContent() {
return content;
}
}
EventObserver.java
public class EventObserver<T> implements Observer<Event<? extends T>> {
public interface EventUnhandledContent<T> {
void onEventUnhandledContent(T t);
}
private EventUnhandledContent<T> content;
public EventObserver(EventUnhandledContent<T> content) {
this.content = content;
}
#Override
public void onChanged(Event<? extends T> event) {
if (event != null) {
T result = event.getContentIfNotHandled();
if (result != null && content != null) {
content.onEventUnhandledContent(result);
}
}
}
}
Example, In ViewModel Class
public class LoginViewModel extends BaseViewModel {
private MutableLiveData<Event<Boolean>> _isProgressEnabled = new MutableLiveData<>();
LiveData<Event<Boolean>> isProgressEnabled = _isProgressEnabled;
private AppService appService;
private SchedulerProvider schedulerProvider;
private SharedPreferences preferences;
#Inject
LoginViewModel(
AppService appService,
SchedulerProvider schedulerProvider,
SharedPreferences preferences
) {
this.appService = appService;
this.schedulerProvider = schedulerProvider;
this.preferences = preferences;
}
public void login(){
appService.login("username", "password")
.subscribeOn(schedulerProvider.executorIo())
.observeOn(schedulerProvider.ui())
.subscribe(_userLoginDetails::setValue,
_userLoginDetailsError::setValue,
() -> _isProgressEnabled.setValue(new Event<>(false)),
d -> _isProgressEnabled.setValue(new Event<>(true))
)
}
}
In Login Fragment,
viewModel.isProgressEnabled.observe(this, new EventObserver<>(hasEnabled -> {
if (hasEnabled) {
// showProgress
} else {
// hideProgress
}
}));
Using Event and EventObserver class we can achieve the same like SingleLiveEvent class but if you are thinking a lot of boilerplate code just avoid this method. I hope it would help you and give some idea about why we are using SingleEvent in LiveData.
I understand that Google gives the guidelines to use LiveData between the ViewModel and UI but there are edge cases where using LiveData as a SingleLiveEvent is like reinventing the wheel. For single time messaging between the view model and user interface we can use the delegate design pattern. When initializing the view model in the activity we just have to set the activity as the implementer of the interface. Then throughout our view model we can call the delegate method.
Interface
public interface Snackable:
void showSnackbarMessage(String message);
UI
public class MyActivity extends AppCompatActivity implements Snackable {
private MyViewModel myViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
this.myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
this.myViewModel.setListener(this);
}
#Override
public void showSnackbarMessage(String message) {
Toast.makeText(this, "message", Toast.LENGTH_LONG).show();
}
}
View Model
public class MyViewModel extends AndroidViewModel {
private Snackable listener;
public MyViewModel(#NonNull Application application) {
super(application);
}
public void setListener(MyActivity activity){
this.listener = activity;
}
private void sendSnackbarMessage(String message){
if(listener != null){
listener.showSnackbarMessage(message);
}
}
private void anyFunctionInTheViewModel(){
sendSnackbarMessage("Hey I've got a message for the UI!");
}
}
So I'm using Android Room library to perform requests on a SQLite DB.
I can't make requests on the UI thread, so I'm using AsyncTask.
My app is simple, I juste want to be able to insert new Users into my database.
So there is my UserDao.java :
#Dao
public interface UserDao {
#Insert
long insert(User user);
#Update
void update(User user);
#Delete
void delete(User user);
}
And there is my User.java :
#Entity
public class User {
#PrimaryKey(autoGenerate = true)
private int id;
private String name;
private int age;
// Getter / Setter
}
And then, to perform my Insert request, I have to make an entire AsyncTask class :
public static class insertAsyncTask extends AsyncTask<User, Void, User> {
private WeakReference<Activity> weakActivity;
private UserDao dao;
// Constructeur
insertAsyncTask(Activity activity, UserDao dao) {
this.weakActivity = new WeakReference<>(activity);
this.dao = dao;
}
#Override
protected User doInBackground(final User... params) {
return this.dao.getById(this.dao.insert(params[0]));
}
#Override
protected void onPostExecute(User u) {
// Some code
}
And finally, I'm calling the Insert like that :
new AppDatabase.insertAsyncTask(this, db.userDao()).execute(myUser);
So is it possible to make my AsyncTask class more generic ?
I would like to be able to use this class for multiple requests (like Insert, Update, Delete ...).
Or should I do a generic DAO instead ?
Thanks in advance and sorry for my English ;)
Yes you can use it anywhere first you have to make generic Async task class for all methods
You can create an interface, pass it to AsyncTask (in constructor), and then call method in onPostExecute()
For example:
1.Your interface:
public interface OnTaskRoomCompleted{
void onTaskCompleted(User);
}
2.Your Activity:
public class YourActivity implements OnTaskRoomCompleted{
// your Activity
}
3.And your AsyncTask:
public class YourTask extends AsyncTask<User,Object,User>{ //change Object to required type
private OnTaskRoomCompleted listener;
public YourRoomTask(OnTaskCompleted listener){
this.listener=listener;
}
// required methods
protected void onPostExecute(Object user){
// your stuff
listener.onTaskRoomCompleted(user);
}
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
UserId = view.findViewById(R.id.user_id);
UserName= view.findViewById(R.id.user_name_id);
UserEmail=view.findViewById(R.id.user_email_id);
save = view.findViewById(R.id.bn_save);
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String id = UserId.getText().toString();
String name = UserName.getText().toString();
String email =UserEmail.getText().toString();
String userData[] ={id,name,email};
GetUsersAsyncTask task = new GetUsersAsyncTask();
try {
String status = task.execute(userData).get();
Toast.makeText(getContext(),status , Toast.LENGTH_SHORT).show();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
//emty the fields of the add user in the UI
UserId.setText("");
UserName.setText("");
UserEmail.setText("");
}
});
}
private static class GetUsersAsyncTask extends AsyncTask<String[], Void,String> {
#Override
protected String doInBackground(String[]... strings) {
String[] userDatas = strings[0];
int id = Integer.parseInt(userDatas[0]);
String name = userDatas[1];
String email =userDatas[2];
User user = new User();
user.setId(id);
user.setName(name);
user.setEmail(email);
MainActivity.myAppDatabase.myDao().addUser(user);
return "Data Inserted SuccessFully....";
}
}