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;
}
Related
The structure of my application is as follows:
MainActivity(Activity) containing Bottom Navigation View with three fragments nested below
HomeFragment(Fragment) containing TabLayout with ViewPager with following two tabs
Journal(Fragment)
Bookmarks(Fragment)
Fragment B(Fragment)
Fragment C(Fragment)
I am using Room to maintain all the records of journals. I'm observing one LiveData object each in Journal and Bookmarks fragment. These LiveData objects are returned by my JournalViewModel class.
JournalDatabase.java
public abstract class JournalDatabase extends RoomDatabase {
private static final int NUMBER_OF_THREADS = 4;
static final ExecutorService dbWriteExecutor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
private static JournalDatabase INSTANCE;
static synchronized JournalDatabase getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), JournalDatabase.class, "main_database")
.fallbackToDestructiveMigration()
.build();
}
return INSTANCE;
}
public abstract JournalDao journalDao();
}
JournalRepository.java
public class JournalRepository {
private JournalDao journalDao;
private LiveData<List<Journal>> allJournals;
private LiveData<List<Journal>> bookmarkedJournals;
public JournalRepository(Application application) {
JournalDatabase journalDatabase = JournalDatabase.getInstance(application);
journalDao = journalDatabase.journalDao();
allJournals = journalDao.getJournalsByDate();
bookmarkedJournals = journalDao.getBookmarkedJournals();
}
public void insert(Journal journal) {
JournalDatabase.dbWriteExecutor.execute(() -> {
journalDao.insert(journal);
});
}
public void update(Journal journal) {
JournalDatabase.dbWriteExecutor.execute(() -> {
journalDao.update(journal);
});
}
public void delete(Journal journal) {
JournalDatabase.dbWriteExecutor.execute(() -> {
journalDao.delete(journal);
});
}
public void deleteAll() {
JournalDatabase.dbWriteExecutor.execute(() -> {
journalDao.deleteAll();
});
}
public LiveData<List<Journal>> getAllJournals() {
return allJournals;
}
public LiveData<List<Journal>> getBookmarkedJournals() {
return bookmarkedJournals;
}
}
JournalViewModel.java
public class JournalViewModel extends AndroidViewModel {
private JournalRepository repository;
private LiveData<List<Journal>> journals;
private LiveData<List<Journal>> bookmarkedJournals;
public JournalViewModel(#NonNull Application application) {
super(application);
repository = new JournalRepository(application);
journals = repository.getAllJournals();
bookmarkedJournals = repository.getBookmarkedJournals();
}
public void insert(Journal journal) {
repository.insert(journal);
}
public void update(Journal journal) {
repository.update(journal);
}
public void delete(Journal journal) {
repository.delete(journal);
}
public void deleteAll() {
repository.deleteAll();
}
public LiveData<List<Journal>> getAllJournals() {
return journals;
}
public LiveData<List<Journal>> getBookmarkedJournals() {
return bookmarkedJournals;
}
}
I'm instantiating this ViewModel inside onActivityCreated() method of both Fragments.
JournalFragment.java
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
JournalFactory factory = new JournalFactory(requireActivity().getApplication());
journalViewModel = new ViewModelProvider(requireActivity(), factory).get(JournalViewModel.class);
journalViewModel.getAllJournals().observe(getViewLifecycleOwner(), new Observer<List<Journal>>() {
#Override
public void onChanged(List<Journal> list) {
journalAdapter.submitList(list);
}
});
}
BookmarksFragment.java
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
JournalFactory factory = new JournalFactory(requireActivity().getApplication());
journalViewModel = new ViewModelProvider(requireActivity(), factory).get(JournalViewModel.class);
journalViewModel.getBookmarkedJournals().observe(getViewLifecycleOwner(), new Observer<List<Journal>>() {
#Override
public void onChanged(List<Journal> list) {
adapter.submitList(list);
}
});
}
However, the problem when I use this approach is as I delete make some changes in any of the Fragment like delete or update some Journal some other Journal's date field changes randomly.
I was able to solve this issue by using single LiveData object and observe it in both fragments. The changes I had to make in BookmarkFragment is as follows:
BookmarksFragment.java
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
JournalFactory factory = new JournalFactory(requireActivity().getApplication());
journalViewModel = new ViewModelProvider(requireActivity(), factory).get(JournalViewModel.class);
journalViewModel.getAllJournals().observe(getViewLifecycleOwner(), new Observer<List<Journal>>() {
#Override
public void onChanged(List<Journal> list) {
List<Journal> bookmarkedJournals = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getBookmark() == 1)
bookmarkedJournals.add(list.get(i));
}
adapter.submitList(bookmarkedJournals);
}
});
}
It works properly now.
However, I want to know why it didn't work using my first approach which was to use two different LiveData objects and observe them in different fragments.
Are multiple LiveData objects not meant to be used in single ViewModel?
OR
Are two instances of same ViewModel not allowed to exist together while making changes and fetching different LiveData objects from the same table simultaneously?
I found out the reason causing this problem.
As I was using LiveData with getViewLifecycleOwner() as the LifecycleOwner, the observer I passed as parameter was never getting removed. So, after switching to a different tab, there were two active observers observing different LiveData objects of same ViewModel.
The way this issue can be solved is by storing the LiveData object in a variable then removing the observer as you switch to different fragment.
In my scenario, I solved this issue by doing the following:
//store LiveData object in a variable
LiveData<List<Journal>> currentLiveData = journalViewModel.getAllJournals();
//observe this livedata object
currentLiveData.observer(observer);
Then remove this observer in a suitable Lifecycle method or anywhere that suits your needs like
#Override
public void onDestroyView() {
super.onDestroyView();
//if you want to remove all observers
currentLiveData.removeObservers(getViewLifecycleOwner());
//if you want to remove particular observers
currentLiveData.removeObserver(observer);
}
Now, I am trying to understand how to use Realm with RxJava2. There aren't a lot of information. For example, I have a simple query:
CommentObject firstComments = realmForThisThread.where(CommentObject.class)
.equalTo(CommentObject.COMMENT,comment).findFirst();
How to get firstComment using RxJava2? As I understand asObservable method doesn't work now.
The same questions appear when I want to get RealmResults or set listener only for one model, not all Realm database.
Are there any documents about Realm + RxJava2. I found only RxJava, but it needn't. Before that I worked with Realm without Rx, but now it's important to use this concept
You can read my article on using Realm with RxJava2 in the Realm Academy.
Synchronous single-value query makes no sense to be exposed as Observable.
If you want to listen to whether there is 0 or 1 element existing of a RealmObject, then you should use RealmResults<T>.asFlowable().
Using RealmObject.asFlowable() is designed for listening to single-element object notifications that provides field change as well.
Anyways, using RxJava2 is to allow you to turn this
public class MyActivity extends AppCompatActivity {
Realm realm;
RealmResults<Task> results; // kept as strong reference!
RealmChangeListener<RealmResults<Task>> listener = new RealmChangeListener<RealmResults<Task>>() {
#Override
public void onChange(RealmResults<Task> results) {
if(results.isLoaded()) {
// results is always up to date here
// after a write to Realm from ANY thread!
updateUi(results);
}
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
ButterKnife.bind(this);
realm = Realm.getDefaultInstance();
results = realm.where(Task.class)
.findAllSortedAsync(TaskFields.ID);
// TaskFields is generated
// using https://github.com/cmelchior/realmfieldnameshelper
results.addChangeListener(listener);
}
protected void onDestroy() {
super.onDestroy();
results.removeChangeListener(listener);
results = null;
realm.close();
}
private void updateUi(List<MyObject> objects) {
// do something
}
}
into this
#Singleton
public class TaskRepository {
#Inject
public TaskRepository() {
}
// this implementation works on any thread.
public Flowable<List<Task>> getTasks(Realm realm) {
if(realm.isAutoRefresh()) { // for looper threads
return realm.where(Task.class)
.findAllSortedAsync(TaskFields.ID)
.asFlowable()
.filter(RealmResults::isLoaded);
} else { // for background threads
return Flowable.just(realm.where(Task.class).findAllSorted(TaskFields.ID));
}
}
}
and
public class MyActivity extends AppCompatActivity {
TaskRepository taskRepository;
Realm realm;
Disposable subscription;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
ButterKnife.bind(this);
realm = Realm.getDefaultInstance();
taskRepository = Injector.get().taskRepository();
subscription = taskRepository.getTasks(realm).subscribeWith(tasks -> {
updateUi(tasks);
});
}
protected void onDestroy() {
super.onDestroy();
subscription.dispose();
subscription = null;
realm.close();
}
private void updateUi(List<MyObject> objects) {
// do something
}
}
I have an Activity that manage multiples fragments and nested fragment, like that :
Activity -> RootFragment1 -> NestedFragment1, NestedFragment2 -> RootFragment2 -> NestedFragment3, NestedFragment4
...
I use to get a realm instance and close it in each nested fragment in onStart, onStop methods but sometimes I meet this exception :
Fatal Exception: java.lang.IllegalStateException: This Realm instance
has already been closed, making it unusable.
Is there a recommended way to get a Realm instance and close it ? In my case should I get an instance in Activity and pass it through my fragments ?
The docs say that you should open/close Realm in onCreateView()/onDestroyView(), but in my experience the fragment lifecycle is unusually erratic, so I can show you two other approaches.
1.) open/close the Realm in Activity.onCreate() and Activity.onDestroy(), then share it to the fragments (and even down the view hierarchy!) using getSystemService().
public class MyActivity extends AppCompatActivity {
Realm realm;
#Override
protected void onCreate(Bundle bundle) {
// ...
realm = Realm.getDefaultInstance();
}
#Override
protected void onDestroy() {
realm.close();
realm = null;
// ...
}
// -----------------------------
private static final String REALM_TAG = "__REALM__";
public static Realm getRealm(Context context) {
// noinspection ResourceType
return (Realm)context.getSystemService(REALM_TAG);
}
#Override
public Object getSystemService(#NonNull String name) {
if(REALM_TAG.equals(name)) {
return realm;
}
return super.getSystemService(name);
}
}
Then in fragment you can do
Realm realm = MyActivity.getRealm(getActivity());
And in views you can do
Realm realm = MyActivity.getRealm(getContext());
2.) manage the Realm lifecycle globally for the UI thread using retained fragment as lifecycle listener / activity reference counter.
/**
* Created by Zhuinden on 2016.08.16..
*/
public class RealmManager {
private static final String TAG = "RealmManager";
static Realm realm;
static RealmConfiguration realmConfiguration;
public static void init(Context context) {
Realm.init(context);
}
public static void initializeRealmConfig(Context appContext) {
if(realmConfiguration == null) {
Log.d(TAG, "Initializing Realm configuration.");
setRealmConfiguration(new RealmConfiguration.Builder(appContext).initialData(new RealmInitialData())
.deleteRealmIfMigrationNeeded()
.inMemory()
.build());
}
}
public static void setRealmConfiguration(RealmConfiguration realmConfiguration) {
RealmManager.realmConfiguration = realmConfiguration;
Realm.setDefaultConfiguration(realmConfiguration);
}
private static int activityCount = 0;
public static Realm getRealm() { // use on UI thread only!
return realm;
}
public static void incrementCount() {
if(activityCount == 0) {
if(realm != null) {
if(!realm.isClosed()) {
Log.w(TAG, "Unexpected open Realm found.");
realm.close();
}
}
Log.d(TAG, "Incrementing Activity Count [0]: opening Realm.");
realm = Realm.getDefaultInstance();
}
activityCount++;
Log.d(TAG, "Increment: Count [" + activityCount + "]");
}
public static void decrementCount() {
activityCount--;
Log.d(TAG, "Decrement: Count [" + activityCount + "]");
if(activityCount <= 0) {
Log.d(TAG, "Decrementing Activity Count: closing Realm.");
activityCount = 0;
realm.close();
if(Realm.compactRealm(realmConfiguration)) {
Log.d(TAG, "Realm compacted successfully.");
}
realm = null;
}
}
}
In conjunction with
public class RealmScopeListener
extends Fragment {
public RealmScopeListener() {
setRetainInstance(true);
RealmManager.incrementCount();
}
#Override
public void onDestroy() {
RealmManager.decrementCount();
super.onDestroy();
}
}
And
/**
* Created by Zhuinden on 2016.09.04..
*/
public class RealmActivity extends AppCompatActivity {
protected Realm realm;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
RealmManager.init(this);
RealmManager.initializeRealmConfig(getApplicationContext());
super.onCreate(savedInstanceState);
RealmScopeListener realmScopeListener = (RealmScopeListener)getSupportFragmentManager().findFragmentByTag("SCOPE_LISTENER");
if(realmScopeListener == null) {
realmScopeListener = new RealmScopeListener();
getSupportFragmentManager().beginTransaction().add(realmScopeListener, "SCOPE_LISTENER").commit();
}
realm = RealmManager.getRealm();
}
}
This allows you to call RealmManager.getRealm() for the UI thread, and its lifecycle is managed by retain fragments.
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.
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.
}
}