I want to connect to the data in the database and display it in list view
but I have Error in object Realm and stopped my application
How can solve the problem??
protected void onResume() {
super.onResume();
Realm realm = Realm.getInstance(getApplicationContext());
realm.beginTransaction();
List<Car> cars = realm.allObjects(Car.class);
String[] names = new String[cars.size()];
for (int i = 0; i < names.length; i++) {
names[i] = cars.get(i).getName();
}
ListView listView = (ListView) findViewById(R.id.listView);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
}
}
Apart from that you did not specify the logcat, Realm probably tells you that you've left a transaction open and crashes instead.
You only need transactions to write into RealmObjects that are attached to a realm. You also need to close every realm instance that you open. You also must note that you cannot read from a closed realm.
For example, this works:
Realm realm = null;
try {
Cat cat = new Cat(); //public class Cat extends RealmObject {
cat.setName("Meowmeow"); //cat is not yet attached to a realm, therefore you can modify it
realm = Realm.getInstance(context); //open instance of default realm
realm.beginTransaction();
realm.copyToRealmOrUpdate(cat); //cat is now attached to the realm,
//and cannot be written outside the transaction.
realm.commitTransaction();
} catch(Exception e) {
if(realm != null) {
try { //newer versions of Realm like 0.84.0+ have `realm.isInTransaction()`
realm.cancelTransaction();
} catch(IllegalStateException e) {
//realm not in transaction
}
}
throw e;
} finally {
if(realm != null) {
realm.close(); //every open realm must be closed
}
}
If you're using a newer version of Realm, you can also do this on a background thread without all the manual opening and closing.
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// begin and end transaction calls are done for you
Dog dog = realm.where(Dog.class).equals("age", 1).findFirst();
d.setAge(3);
}
}, new Realm.Transaction.Callback() {
#Override
public void onSuccess() {
// Original RealmResults<T> objects and Realm objects
// are automatically updated
// ON THREADS THAT HAVE A LOOPER ASSOCIATED WITH THEM (main thread)
//the realm is written and data is updated, do whatever you want
}
});
And for displaying your data, this works (0.83.0+):
public class HelloWorldActivity extends AppCompatActivity {
protected Realm realm;
ListView listView;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
realm = Realm.getInstance(this);
setContentView(R.layout.activity_hello_world);
this.listView = (ListView)findViewById(R.id.list_view);
listView.setAdapter(new CarAdapter(this, realm.where(Car.class).findAll(), true);
}
#Override
public void onDestroy() {
realm.close();
}
}
And you'll need an adapter for your... listView...
public class CarAdapter extends RealmBaseAdapter<Car> {
public RealmModelAdapter(Context context, RealmResults<Car> realmResults, boolean automaticUpdate) {
super(context, realmResults, automaticUpdate);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO
//implement viewholder pattern here:
//http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder
//Listview is obsolete so I won't bother,
//use RecyclerView when you get the chance.
}
}
Related
I have an Android app which uses firestore as its database. I have followed this series of blog posts to set up my firestore database in my app : https://firebase.googleblog.com/2017/12/using-android-architecture-components.html and then followed this stackoverflow entry to change my code to work for firestore: Android Architecture Components with Firebase specifically Firestore.
After this I was successful to display the result of my query in a recycler view, however when I added the swap to update (I do soft delete by setting a isActive flag to false) action in my app, LiveData was inconsistent in refreshing the RecyclerView. Here is my code snippets:
MainActivity.java
TaskViewModel viewModel =
ViewModelProviders.of(this).get(TaskViewModel.class);
LiveData<LinkedList<TaskProperties>> liveData = viewModel.getTaskPropertiesLiveData();
final MainActivity mainActivityReference = this;
liveData.observe(this, new Observer<LinkedList<TaskProperties>>() {
#Override
public void onChanged(#Nullable LinkedList<TaskProperties> taskProperties) {
if (taskProperties != null) {
// Get a handle to the RecyclerView.
mRecyclerView = findViewById(R.id.recyclerview);
// Create an adapter and supply the data to be displayed.
mAdapter = new TaskListAdapter(mainActivityReference, taskProperties);
// Connect the adapter with the RecyclerView.
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(mAdapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(mRecyclerView);
mRecyclerView.setAdapter(mAdapter);
// Give the RecyclerView a default layout manager.
mRecyclerView.setLayoutManager(new LinearLayoutManager(mainActivityReference));
}
}
});
View Model:
public class TaskViewModel extends ViewModel {
private LinkedList<TaskProperties> taskProperties;
private static final Query PROJECT_REF = FirebaseFirestore.getInstance().collection("project").whereEqualTo("active", true);
private final FirebaseQueryLiveData liveData = new FirebaseQueryLiveData(PROJECT_REF);
public TaskViewModel() {
taskPropertiesLiveData.addSource(liveData, new Observer<QuerySnapshot>() {
#Override
public void onChanged(#Nullable final QuerySnapshot querySnapshot) {
if (querySnapshot != null) {
new Thread(new Runnable() {
#Override
public void run() {
taskProperties = new LinkedList<TaskProperties>();
for (DocumentSnapshot document : querySnapshot.getDocuments()) {
taskProperties.addLast(document.toObject(TaskProperties.class));
}
taskPropertiesLiveData.postValue(taskProperties);
}
}).start();
} else {
taskPropertiesLiveData.setValue(null);
}
}
});
}
#NonNull
public LiveData<LinkedList<TaskProperties>> getTaskPropertiesLiveData() {
return taskPropertiesLiveData;
}
}
Code in the callback class to remove :
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
Constructor in Adapter:-
public TaskListAdapter(Context context,LinkedList<TaskProperties> taskList) {
mInflater = LayoutInflater.from(context);
this.taskList = taskList;
}
Code in Adapter to remove:-
public void onItemDismiss(int position) {
TaskDao taskDao = new TaskDao();
taskDao.softDeleteTaskInDB(taskList.get(position));
}
Code in DAO class to update( soft delete) :-
public void softDeleteTaskInDB(TaskProperties taskProperties){
taskProperties.setActive(false);
database.collection("project")
.document(taskProperties.getTask())
.set(taskProperties).
addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(DEBUG_TAG, "DocumentSnapshot successfully written!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(DEBUG_TAG, "Error writing document", e);
}
});
Log.i(DEBUG_TAG,taskProperties.getTask());
}
I have observed that LiveData was able to refresh the view when I was deleting one component from the end of the list, however when I deleted from the middle of the list the view sometimes does not refresh properly. From the logs I found that the position that is being passed into the adapter class is working fine, however the tasklist array does not have the most updated value.
For example if the task list contains :-
Cat
Dog
Mouse
Rabbit
Tiger
and if delete Mouse and then Rabbit in quick succession, the onItemDismiss in adapter class receives position 3 in both cases, but the taskList variable in the Adapter class still contains Mouse at position 3. This means the LiveData might not have refreshed the RecyclerView.
Can someone please tell me where am I going wrong?
Thanks,
Sangho
I have a messaging application which uses RealmDB to store messages and threads for users. The message threads are RealmObjects. I'm trying to figure out how to add a RealmChangeListener which will trigger when any of the threads are updated (i.e a new message is received). The threads are initially retrieved and subsequently updated from my MessageThreadList activity via a syncMessages function in the API. It seems like no matter where I try to add the listener I get the
Cannot add listener from unmanaged object
error.
My code:
MessageThread.java
public class MessageThread extends RealmObject {
//Some member variables here - Id, title, etc
//This is the field that would indicate a new message if changed
private Date mLatestSentTimeStamp;
//Empty constructor
public MessageThread() { }
public MessageThread(JSONObject jsonOb) {
//Filling fields from JSON here
}
}
ThreadListActivity.java
public class ThreadListActivity {
private Realm realm;
#Override
public void onCreate() {
Realm.init();
realm = Realm.getDefaultInstance();
MyAPI.syncMessages();
}
MyAPI.java
static protected void syncMessages() {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
JSONArray jsonMsgThreads = getJSONFromServer();
for (int i = 0; i < jsonMsgThreads.length(); i++) {
MessageThread m = new MessageThread(jsonMsgThreads.getJSONObject(i));
realm.copyToRealmOrUpdate(m);
}
realm.commitTransaction();
}
If it's not possible to add it anywhere here, is there anything that I could do to have a listener on each MessageThread?
public class ThreadListActivity {
private Realm realm;
private RealmResults<MessageThread> messageThreads;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Realm.init(this);
realm = Realm.getDefaultInstance();
messageThreads = realm.where(MessageThread.class).findAll();
messageThreads.addChangeListener((results) -> {
// any message thread was modified
}
MyAPI.syncMessages();
}
using this code:
public class App extends Application {
private static App instance;
#Override
public void onCreate() {
super.onCreate();
initRealmDB();
}
private void initRealmDB() {
instance = this;
Realm.init(this);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().modules(new SimpleRealmModule()).name("RealmSample.realm").build();
Realm realm = null;
try {
realm = Realm.getInstance(realmConfiguration);
realm.setDefaultConfiguration(realmConfiguration);
} finally {
if (realm != null) {
realm.close();
}
}
}
}
**In use:**
Realm realm = Realm.getDefaultInstance();
RealmResults<OrganizationModelClass> results = realm.where(OrganizationModelClass.class).findAll();
if(realm.isInTransaction())
{
realm.cancelTransaction();
}
realm.beginTransaction();
if (results != null) {
if(membershipList != null)
{
membershipList.clear();
}
for (int i = 0; i < results.size(); i++) {
Log.d(OrganizationActivity.class.getName(), " i :" + results.get(i).getCurrent_membership_uuid());
}
}
Is this best way to use?
Should i use singleton approach?
If there is another good approach to fulfill this task, please share with me.
i followed this https://dzone.com/articles/realm-practical-use-in-android
but this code is not working with this dependency: classpath "io.realm:realm-gradle-plugin:3.3.1"
Realm realm = Realm.getInstance(SimpleRealmApp.getInstance());
Is this best way to use?
No
Realm realm = Realm.getDefaultInstance(); // <-- opens Realm
RealmResults<OrganizationModelClass> results = realm.where(OrganizationModelClass.class).findAll();
if(realm.isInTransaction())
{
realm.cancelTransaction(); // <-- what if that transaction was important?
}
realm.beginTransaction();
if (results != null) {
if(membershipList != null)
{
membershipList.clear(); // <-- ??
}
for (int i = 0; i < results.size(); i++) {
Log.d(OrganizationActivity.class.getName(), " i :" + results.get(i).getCurrent_membership_uuid()); // <-- if the result set was modified here because of the transaction, then the RealmResults will update, and you'll skip elements
}
// <-- where is the commit?
} // <-- where is realm.close()?
Instead
try(Realm r = Realm.getDefaultInstance()) {
r.executeTransaction((realm) -> { // AS 3.0+ desugar
RealmResults<OrganizationModelClass> results = realm.where(OrganizationModelClass.class).findAll(); // <-- get in transaction
for (OrganizationModelClass model : results) { // uses snapshot() internally
Log.i(model.getClass().getName(), getCurrentMembershipUuid());
}
}
} // <-- auto-close because of try-with-resources
Should i use singleton approach?
Realm instances you open with getInstance()/getDefaultInstance() are thread-local and reference counted, so it is NOT suitable for being used as a singleton across the application. You need to open thread-local instances.
So on UI Thread, based on documentation:
// Setup Realm in your Application
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
Realm.init(this);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
//.deleteIfMigrationNeeded()
.migration(new MyMigration())
.build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
// onCreate()/onDestroy() overlap when switching between activities.
// Activity2.onCreate() will be called before Activity1.onDestroy()
// so the call to getDefaultInstance in Activity2 will be fast.
public class MyActivity extends Activity {
private Realm realm;
private RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setAdapter(
new MyRecyclerViewAdapter(this, realm.where(MyModel.class).findAllSortedAsync(MyModelFields.ID)));
// ...
}
#Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
}
// Use onCreateView()/onDestroyView() for Fragments.
// Note that if the db is large, getting the Realm instance may, briefly, block rendering.
// In that case it may be preferable to manage the Realm instance and RecyclerView from
// onStart/onStop instead. Returning a view, immediately, from onCreateView allows the
// fragment frame to be rendered while the instance is initialized and the view loaded.
public class MyFragment extends Fragment {
private Realm realm;
private RecyclerView recyclerView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
realm = Realm.getDefaultInstance();
View root = inflater.inflate(R.layout.fragment_view, container, false);
recyclerView = (RecyclerView) root.findViewById(R.id.recycler_view);
recyclerView.setAdapter(
new MyRecyclerViewAdapter(getActivity(), realm.where(MyModel.class).findAllSortedAsync(MyModelFields.ID)));
// ...
return root;
}
#Override
public void onDestroyView() {
super.onDestroyView();
realm.close();
}
}
For background thread, see the docs:
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try (Realm realm = Realm.getDefaultInstance()) {
// No need to close the Realm instance manually
}
}
});
thread.start();
If you want to use Realm as a singleton, you have to use a class that can increment, decrement, and get instance without incrementing ref count for thread local Realms, kinda like this experiment here.
public RealmController(Context context) {
realm = Realm.getDefaultInstance();
}
public static RealmController with(Activity activity) {
if (instance == null) {
instance = new RealmController(activity.getApplication());
}
return instance;
}
public static RealmController with(Application application) {
if (instance == null) {
instance = new RealmController(application);
}
return instance;
}
public static RealmController getInstance() {
if (instance == null) {
instance = new RealmController(SysApplication.getAppContext());
}
return instance;
}
I have a table which stores the list of products. I want to get notified only when any of the row gets updated.Can I use RealmChangeListener. Below id my code
public void updateProducts(final List<Product> products) {
Realm realmObj = Realm.getDefaultInstance();
for (Product product : products) {
if (product.shouldBeDeleted()) {
delete(product.getBarcode());
} else {
realmObj.beginTransaction();
realmObj.copyToRealmOrUpdate(product);
realmObj.commitTransaction();
}
}
realmObj.close();
}
Yes you can use realm change listener...
I am using in my project like this
product.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel realmModel) {
//Your code when any row update or insert new record
}
});
For detail you can refer this link
https://realm.io/docs/java/latest/api/io/realm/RealmChangeListener.html
You can use our fine-grained collection notifications, which will report what kind of modification are done:
RealmResults<Product> products = realm.where(Product.class).findAll();
products.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Product>>() {
#Override
public void onChange(RealmResults<Product> products, OrderedCollectionChangeSet changeSet) {
for (int i : changeSet.getChanges()) {
// Item at index i was updated
}
}
});
public void updateProducts(final List<Product> products) {
try(Realm r = Realm.getDefaultInstance()) {
r.executeTransaction((realm) -> {
for (Product product : products) {
if (product.shouldBeDeleted()) {
delete(product.getBarcode());
} else {
realm.insertOrUpdate(product);
}
}
});
}
}
And elsewhere on UI thread:
private Realm realm;
private RealmResults<Product> results;
private RealmChangeListener<RealmResults<Product>> listener = (element) -> {
if(element.isLoaded()) {
// results are loaded, or a change occurred!
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.some_layout);
realm = Realm.getDefaultInstance();
results = realm.where(Product.class).findAllAsync();
results.addChangeListener(listener);
}
#Override
public void onDestroy() {
super.onDestroy();
results.removeChangeListener(listener);
results = null;
realm.close();
}
P.S. you'll want to be notified on inserts and deletes as well, or your UI will get desynchronized. Thankfully that is the default behavior.
experiencing an error when trying to make new objects through a button with a relationship (one-to-many) from another page, using realm with recycler view to connect multiple to tasks to one note(the list of tasks)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task_activity);
final Realm mrealm = Realm.getDefaultInstance();
RealmResults<tItem> results = mrealm.where(tItem.class).findAll();
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
final tData data = new tData();
fbtn1 = (FloatingActionButton) findViewById(R.id.fbtn1);
recView = (RecyclerView)findViewById(R.id.task_list);
recView.setLayoutManager(layoutManager);
adapter = new tAdapter(results,this);
recView.setAdapter(adapter);
adapter.setIconClickCallback(this);
fbtn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mrealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
final RealmResults<tItem> item2 = realm.where(tItem.class).findAll();
tItem item = mrealm.createObject(tItem.class);
UUID.randomUUID()
.toString();
taskData.add(item);
item.setTasks("to do list 1");
mrealm.copyToRealm(item);
}
});
}
});
adapter.notifyDataSetChanged();
RealmResults<tItem> tItem = mrealm.where(tItem.class).findAll();
Log.d("john", "new task ");
Log.d("", "path: " + mrealm.getPath());
}
});
}
the error points to this part of the code
tItem item = mrealm.createObject(tItem.class);
Realm instance is thread - confined - you cannot make queries from another thread. This error occures, because you make query on a main thred and then attempt to use that query in async transaction, which will be executed on another thread:
final RealmResults<tItem> item2 = realm.where(tItem.class).findAll();
Try creating tItem class using the realm instance instead of mRealm instance since that is the realm instance created for the async transaction
tItem item = realm.createObject(tItem.class);
Your code is supposed to look like this:
Realm realm;
RealmResults<tItem> results;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task_activity);
realm = Realm.getDefaultInstance();
results = realm.where(tItem.class).findAll();
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
fbtn1 = (FloatingActionButton) findViewById(R.id.fbtn1);
recView = (RecyclerView)findViewById(R.id.task_list);
recView.setLayoutManager(layoutManager);
adapter = new tAdapter(results,this); // tAdapter extends RealmRecyclerViewAdapter
recView.setAdapter(adapter);
adapter.setIconClickCallback(this);
fbtn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fbtn1Clicked();
}
});
}
private void fbtn1Clicked() {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
final RealmResults<tItem> items = realm.where(tItem.class).findAll();
tItem item = realm.createObject(tItem.class, UUID.randomUUID().toString();
item.setTasks("to do list 1");
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
if(realm != null) {
realm.close();
realm = null;
}
}
But your original error is that instead of
tItem item = mrealm.createObject(tItem.class);
you should have
tItem item = realm.createObject(tItem.class); // <-- background thread realm
The best idea is to make a copy on you thread if you want to have it on this one.
Also, you should try to make request in a DAO or REPOSITORY pattern.
I made a repository pattern like this :
public Observable<List<T>> query(final Specification spec) {
return Observable.create(new ObservableOnSubscribe<List<T>>() {
#Override
public void subscribe(#NonNull ObservableEmitter<List<T>> emitter) throws Exception {
Log.d(TAG, "OnSubscribe :: Thread id :"+Thread.currentThread().getId() + " name : "+Thread.currentThread().getName());
Realm realm = Realm.getDefaultInstance();
final RealmSpecification realmSpecification = (RealmSpecification) spec;
RealmResults<T> res = realmSpecification.toRealmResults(realm);
List<T> resList = realm.copyFromRealm(res);
realm.close();
if(res != null)
emitter.onNext(resList);
emitter.onComplete();
}
});
}
like that i can query my base from a thread to the main thread. All my queries uses these functions.