How to use constructor in Realm v0.87.1 on Android - java

I want use Realm version 0.87.1 for Database in android, and i create provider class to save values but when use this class constructor in main class (activity) show me this error, please see error from this image : Error image Link
Task_Provider class :
public class Task_Provider extends RealmObject {
public Task_Provider() {
}
public String getAddTask() {
return addTask;
}
public void setAddTask(String addTask) {
this.addTask = addTask;
}
public long getAdded_date() {
return added_date;
}
public void setAdded_date(long added_date) {
this.added_date = added_date;
}
public long getWhen_date() {
return when_date;
}
public void setWhen_date(long when_date) {
this.when_date = when_date;
}
public boolean isComplete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
public Task_Provider(String addTask, long added_date, long when_date, boolean complete) {
this.addTask = addTask;
this.added_date = added_date;
this.when_date = when_date;
this.complete = complete;
}
private String addTask;
#PrimaryKey
private long added_date;
private long when_date;
private boolean complete;
}
Main Activity (Button setOnClickListener) :
String addTask = dialog_edit_task.getText().toString();
long now = System.currentTimeMillis();
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(getActivity()).build();
Realm.setDefaultConfiguration(realmConfiguration);
Realm realm = Realm.getDefaultInstance();
Task_Provider task_provider = new Task_Provider(addTask, now, 0, false);
realm.beginTransaction();
realm.copyToRealm(task_provider);
realm.commitTransaction();
realm.close();
How can i fix this problem? tnx all <3

Did you try cleaning your project? I got with that issue, but disappeared as i cleaned it. You have an empty constructor already :)
Otherwise, you can try to create the object realm defaults.
Because copyToRealm... your Task_Provider object is already a realm object, since you are extending it on the class declaration.
Task_Provider task = realm.createObject(Task_Provider.class);
task.setAddTask("");
...
Hope it helps!

Related

Pass object between fragments using shared ViewModel

I'm trying to insert a object (repo) into the database and pass it to the next fragment.
I get the id of the inserted object and then I try to get the entity by the id, so the object includes the id.
onLoaded() should set the object in the ViewModel so I can use it in the next fragment.
The problem: Sometimes onLoaded seems to be called late or not at all. And then I can't add more information in the second fragment.
How can I make sure 'onLoaded' is called before I use the second fragment?
Or is there a better approach I should use? Maybe only passing the id in the ViewModel?
Fragment 1:
private void actionCloneRepo(#NonNull View view) {
TextInputEditText edittext_username = view.findViewById(R.id.edittext_username);
TextInputEditText edittext_token = view.findViewById(R.id.edittext_token);
TextInputEditText edittext_git_name = view.findViewById(R.id.edittext_git_name);
TextInputEditText edittext_git_email = view.findViewById(R.id.edittext_git_email);
Repo repo = new Repo(
Objects.requireNonNull(edittext_url.getText()).toString(),
Objects.requireNonNull(edittext_username.getText()).toString(),
Objects.requireNonNull(edittext_token.getText()).toString(),
Objects.requireNonNull(edittext_git_name.getText()).toString(),
Objects.requireNonNull(edittext_git_email.getText()).toString()
);
repoViewModel = new ViewModelProvider(requireActivity()).get(RepoViewModel.class);
int id = (int) repoViewModel.insert(repo);
repoViewModel.getRepoById(id).observe(getViewLifecycleOwner(), this::onLoaded);
NavHostFragment.findNavController(AddProject1Fragment.this)
.navigate(R.id.action_AddProject1Fragment_to_AddProject2Fragment);
}
private void onLoaded(Repo repo){
repoViewModel.setCurrentRepo(repo);
}
ViewModel:
public class RepoViewModel extends AndroidViewModel {
private final RepoRepository repoRepository;
private final LiveData<List<Repo>> allRepos;
private Repo currentRepo;
public RepoViewModel(Application application) {
super(application);
repoRepository = new RepoRepository(application);
allRepos = repoRepository.getAllRepos();
}
public Repo getCurrentRepo() {
return currentRepo;
}
public void setCurrentRepo(Repo currentRepo) {
this.currentRepo = currentRepo;
}
public LiveData<Repo> getRepoById(int id) {
return repoRepository.getRepoById(id);
}
public LiveData<List<Repo>> getAllRepos() {
return allRepos;
}
public long insert(Repo repo) {
return repoRepository.insert(repo);
}
public void update(Repo repo) {
repoRepository.update(repo);
}
public void delete(Repo repo) {
repoRepository.delete(repo);
}
}

Room Database RxJava Background Thread

I am trying to use my RoomDatabase on the background thread, using RxJava.
My DAO class:
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert;
import java.util.List;
#Dao
public interface MeasurementDAO
{
#Insert
public void insertMeasurements(Measurements m);
}
My Entity class (getter and setter methods left out for brevity):
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;
#Entity
public class Measurements
{
#NonNull
#PrimaryKey
public String mId;
public String finalResultIn;
public String finalResultFt;
public String lengthFt;
public String widthFt;
public String heightFt;
public String lengthIn;
public String widthIn;
public String heightIn;
public Measurements(String finalResultFt, String finalResultIn, String lengthFt, String widthFt, String heightFt,
String lengthIn, String widthIn, String heightIn)
{
this.finalResultFt = finalResultFt;
this.finalResultIn = finalResultIn;
this.lengthFt = lengthFt;
this.widthFt = widthFt;
this.heightFt = heightFt;
this.lengthIn = lengthIn;
this.widthIn = widthIn;
this.heightIn = heightIn;
}
}
Finally, here is my MeasurementDatabase class:
#Database(entities = {Measurements.class}, version = 1)
public abstract class MeasurementDatabase extends RoomDatabase
{
private static final String DB_NAME = "measurement_db";
private static MeasurementDatabase instance;
public static synchronized MeasurementDatabase getInstance(Context context)
{
if(instance == null)
{
instance = Room.databaseBuilder(context.getApplicationContext(), MeasurementDatabase.class,
DB_NAME)
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
public abstract MeasurementDAO measurementDAO();
}
In my fragment, I'm trying to insert on the background thread once a menu item is clicked:
final MeasurementDatabase appDb =
MeasurementDatabase.getInstance(getActivity());
//fill the values with the appropriate;
final Measurements m = new Measurements(
cubicInches.getText().toString(),
cubicFeet.getText().toString(),
len_ft.getText().toString(),
width_ft.getText().toString(),
height_ft.getText().toString(),
len_in.getText().toString(),
width_in.getText().toString(),
height_in.getText().toString());
Observable.just(appDb)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MeasurementDatabase>(){
#Override
public void onSubscribe(Disposable d) {
appDb.measurementDAO().insertMeasurements(m);
}
#Override
public void onNext(MeasurementDatabase measurementDatabase)
{
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
I am getting an error saying:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
What is missing from my RxJava code thats not putting the process on the background thread?
Create an Observable and write your logic inside it. You can subscribe the observable and get the boolean.
public Observable<Boolean> insertObject(Measurements m) {
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(ObservableEmitter<Boolean> e) {
appDb.measurementDAO().insertMeasurements(m);
e.onNext(true);
e.onComplete();
}
}).subscribeOn(Schedulers.io());
}
Thanks to some clues from #CommonsWare, I was able to find the piece that I was missing:
Completable.fromAction(() -> appDb.measurementDAO().insertMeasurements(m))
.subscribeOn(Schedulers.io())
.subscribe();
I know you have an answer, however you never know if this fails. I'm sure your dao insert method could return a long[] (id's of inserted rows).
You could easily do:
Completable.fromCallable(() ->
appDb.measurementDAO().insertMeasurements(m).length != 0 ?
Completable.complete() :
Completable.error(new IllegalStateException("Error inserting " + m.toString())))
.subscribeOn(Schedulers.io())
.subscribe(() -> { }, Throwable::printStackTrace);

Android Architecture SingleLiveEvent and EventObserver Practicle Example in Java

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!");
}
}

How to get RealmList inside Thread?

Hi in my app i need to get RealmList from RealmObject(PlayList.class). The problem is that i trying to do this on other thread.(some loop engine with tick() method) So my solution it was to get the RealmList from the RealmObject and convert it to ArrayList and than do with it what i want in any thread.
This is the crash i get
java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
this is my RealmObject class
public class PlaylistModel extends RealmObject implements Serializable {
public int id;
public String path;
public String fileName;
public String duration;
public RealmList<Note> notes;
this is my Note class
public class MidiModel extends RealmObject implements Serializable {
private String tag;
private long spaceTime = -1;
and this is how i get the data
public RealmResults<PlaylistModel> getPlaylist(){
realm.beginTransaction();
RealmResults<PlaylistModel> realmResults = realm.where(PlaylistModel.class).findAll();
realm.commitTransaction();
return realmResults;
}
and this is how i trying to read the RealmList in a diffrent thread
public void tick(){
Note model = noteList.get(index);
index++;
}
How can i make it works?
Do i need to convert RealmList to ArrayList Before manipulate?
Please help :)
Managed RealmObjects cannot be passed between threads, so you need to re-query it on the background thread by its primary key, from a Realm instance that was opened for that thread.
Executor executor = Executors.newSingleThreadedPool(); // like new Thread().start();
public void doSomething(final String pkValue) {
executor.execute(new Runnable() {
#Override
public void run() {
try(Realm realm = Realm.getDefaultInstance()) { // beginning of thread
doThingsWithMyObject(realm, pkValue);
} // end of thread
}
}
}
public void doThingsWithMyObject(Realm realm, String pkValue) { // <-- pass Realm instance
MyObject myObject = realm.where(MyObject.class).equalTo("id", pkValue).findFirst(); // <-- requery
RealmList<Note> notes = myObject.getNotes();
// ... do things with notes
}

Android - can't store a list in the Application class instance

I'm trying to store a list in the Application class instance as a global variable in one of my Android applications. Below is my Application class code:
public class DefectsApplication extends Application{
private NormalUser normalUser;
private ArrayList<Complaint> complaintList;
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
private String testString;
public NormalUser getNormalUser() {
return normalUser;
}
public void setNormalUser(NormalUser normalUser) {
this.normalUser = normalUser;
}
public ArrayList<Complaint> getComplaintList() {
return complaintList;
}
public void setComplaintList(ArrayList<Complaint> m_complaints) {
this.complaintList = complaintList;
}
}
Below is my code which is trying to access the fields from the Application class instance:
DefectsApplication defectsApplication = ((DefectsApplication)getApplicationContext());
defectsApplication.setComplaintList(m_complaints);
defectsApplication.setTestString("urghhhhhhhhh");
ArrayList<Complaint> complaintList = defectsApplication.getComplaintList();
String s = defectsApplication.getTestString();
In the above code, m_complaints is a list of objects. When I try to store a String, it works. But for a list, it doesn't. Please, help me to resolve this issue.
Probably, a typo is taking place:
public void setComplaintList(ArrayList<Complaint> m_complaints) {
this.complaintList = complaintList;
}
You're setting this.complaintList to itself which is initially null. Try
public void setComplaintList(ArrayList<Complaint> m_complaints) {
this.complaintList = m_complaints;
}

Categories