Returning value from inside Runnable - android

I am using Android Room, I have tried to build a utility method where I retrieve values from the Database. The issue I am having is trying to pass the values back from inside the Runnable which is being executed asynchronously.
public static List<Genre> getAll(final Context context){
AsyncTask.execute(new Runnable() {
#Override
public void run() {
AppDatabase db = AppDatabase.getAppDatabase(context.getApplicationContext());
db.genreDao().getAll();
}
});
return null;
}
What methods do I have to pass this value back but still run in 'not' on the Main Thread?

As already mentioned, you can't return a value asynchronously. And I would advise against using static utility methods for working with the db. Also, AsyncTasks are cumbersome to use. Another option is Rx. For example:
#Dao
public interface GenreDao {
#Query("SELECT * FROM Genre")
List<Genre> getAll();
}
public class GenreRepository {
//...
#WorkerThread
public List<Genre> getAll() {
//Any other source management logic could be placed here, i.e. retrieving from cache or network
return genreDao.getAll();
}
}
public class GenreInteractor {
//...
public Single<List<Genre>> getAll() {
return Single
.fromCallable(genreRepository::getAll)
.subscribeOn(Schedulers.io());
}
}
Then you retrieve your genres list like this:
genreInteractor.getAll().subscribe(genres -> {});
Or if you want your callback to be on main thread:
genreInteractor.getAll().observeOn(AndroidSchedulers.mainThread()).subscribe(genres -> {});

Related

Android - Best way to access Room Database Asynchronously

So I have a Room database with some Users stored in it. This is the current method im using to access the database and get data.
User user;
userDAO = AppDatabase.getAppDatabase(getApplicationContext()).userDao();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
user = userDAO.getUser("testuser1");
runOnUiThread(new Runnable() {
#Override
public void run() {
String name = user.getName();
}
});
}
});
thread.start();
My question is there a better way to do this. I'm doing all this in my Activity code and I'd like to clean it up.
How would you go around implementing something like a static or abstract class which has callbacks to the main thread/activity so I can listen for responses. I'm still new to asynchronous tasks, so apologies if im being stupid.
I also need to be able to access the database from my Service which is always running. Also I sometimes need to save some data when the OnDestroy() method is called, and if I run an thread from there, it results in an crash. Maybe something like a IntentService? But I need to be able to call different methods to get and save different objects.
in AppDatabase class you have to use below code:
#Database(entities = {User.class},version = 1)
public abstract class AppDatabase extends RoomDatabase
{
public abstract userDAO userDao();
}
in UserDAo class you have to use below codes:
#Query("select * from User where user =:search")
User getUser(String search);
for use these methods you can use below codes:
User user;
public static AppDatabase database=null;
if (database==null)
{
database= Room.databaseBuilder(getApplicationContext(),AppDatabase.class,"nameOfDatabase")
.allowMainThreadQueries().build();
}
user=database.userDao.getUser("testuser1");

Instrumented test with Realm failing

I'm implementing an instrumented test to test a Database Data Source class that is using Realm. So now I'm facing some problems about how to use fixtures and how to mock Realm.
My database data source looks like:
public class DatabaseDataSource {
private Realm realm;
public DatabaseDataSource(Realm realm) {
this.realm = realm;
}
public Observable<RealmResults> getContacts(String firstName, String lastName, String city, String zipCode) {
final RealmQuery realmQuery = realm.where(Contact.class);
if(!TextUtils.isEmpty(firstName)) {
realmQuery.contains("firstName", firstName);
}
if(!TextUtils.isEmpty(lastName)) {
realmQuery.contains("lastName", lastName));
}
if(!TextUtils.isEmpty(city)) {
realmQuery.contains("city", city);
}
if(!TextUtils.isEmpty(zipCode)) {
realmQuery.contains("zipCode", zipCode);
}
return realmQuery.findAll()
.asObservable();
}
}
I want to have a list of contacts in my mocked realm so I can check that filtering is working fine. How can I do that?
I've tried doing:
#RunWith(AndroidJUnit4.class)
public class DatabaseDataSourceTest extends BaseInstrumentedTest{
private DatabaseDataSource databaseDataSource;
private List<Contact> contacts;
#Before
public void setup() {
Realm.init(InstrumentationRegistry.getTargetContext());
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());
databaseDataSource = new DatabaseDataSource(new DatabaseClient());
}
#Test
public void trial() throws Exception {
subscribeContactsListObservable(databaseDataSource.getContacts("firstName", null, null, null));
assertEquals(2, contacts.size());
}
private void subscribeContactsListObservable(final Observable<RealmResults> observable) {
notaries = null;
observable.map(new Func1<RealmResults, List<Contact>>() {
#Override
public List<Notary> call(RealmResults realmResults) {
return realmResults != null? new ArrayList<>(realmResults) : null;
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Contact>>() {
#Override
public void onCompleted() {
contacts = null;
}
#Override
public void onError(Throwable e) {
contacts = null;
}
#Override
public void onNext(List<Contact> contactsList) {
contacts = contactsList;
}
});
}
}
But the test is failing when doing the Observable.subscribe with the following exception:
You can't register a listener from a non-Looper thread or IntentService thread.
What may I do?
Thanks in advance
Well it specifically tells you the solution to your problem in that error message:
You can't register a listener from **a non-Looper thread** or IntentService thread.
This is because asObservable() needs to register a RealmChangeListener in order to listen to changes in the Realm.
The instrumentation thread is a non-looper thread, so that means you can't listen to changes in it.
Solution, you need to either use a Looper thread (like the main thread), or create a Looper thread, and create the Realm instance in that looper thread. Conveniently, RxAndroid features a so-called LooperScheduler which you can create using AndroidSchedulers.from(Looper), which allows you to execute logic on an arbitrary looper thread.
A possibility is looking into how Realm already tests their looper-related stuff with this RunInLooperThread test rule.
Apparently your getContacts() methods run on a non-looper background thread which doesn't work with our change listeners (and thus our asObservable() method).
You can just create the observable instead, but keep in mind that it will emit your list once and then complete. For continuous updates you need to be on a Looper thread.
return Observable.just(realmQuery.findAll());
Here is fragment of one of my tests:
public class PlaybackDatabaseTest extends ApplicationTestCase<Application> {
public PlaybackDao dao;
public PlaybackDatabaseTest(){ super(Application.class);}
#Override
protected void setUp() throws Exception {
super.setUp();
DatabaseModule module = new DatabaseModule();
RealmConfiguration config = module.providePlaybackRealmConfiguration(getContext());
dao = module.providePlaybackDatabase(config);
}
public void testAddingPlaylistAndDeletingDatabase() {
dao.purgeDatabase();
int id1 = 0;
Playlist playlist = createTestPlaylist(id1, 0, 100);
dao.addPlaylist(playlist);
boolean exist1 = dao.isPlaylistExist(String.valueOf(id1));
assertTrue(exist1);
dao.purgeDatabase();
exist1 = dao.isPlaylistExist(String.valueOf(id1));
assertFalse(exist1);
}
}
But, it use Dagger2 to create database 'data access object' .
Realm can work from main thread, use Observable.toblocking() so your
test thread will wait until jub is done.
Realm use Android's Handler for concurrency (.map(), .flatmap() operators return results on Schedulers.computation() by default), so to solve Handler issue use ApplicationTestCase.

How to create an Observable in Android?

What I want to do is to create a simple in-memory cache just to try Observables out. However I got stuck because I don't understand how to create an observable. This is the code I have gotten so far:
public class MovieCache {
MovieWrapper movieWrapper;
public Observable<MovieWrapper> getMovies() {
//How to create and return an Observable<MovieWrapper> here?
}
public void setCache(MovieWrapper wrapper) {
movieWrapper = wrapper;
}
public void clearCache() {
movieWrapper = null;
}
}
In the getMovies() method I want to create an Observable and return my local field movieWrapper to the subscriber. How can I do this? I tried with using new Observable.just(movieWrapper) but it results in a null exception.
Take a look at this tutorial as it does exactly what you are looking for. Basically you use defer() to make sure you always get the latest version of your cached object:
public class MovieCache {
MovieWrapper movieWrapper;
public Observable<MovieWrapper> getMovies() {
return Observable.defer(new Func0<Observable<MovieWrapper>>() {
#Override
public Observable<MovieWrapper> call() {
return Observable.just(movieWrapper);
}
});
}
public void setCache(MovieWrapper wrapper) {
movieWrapper = wrapper;
}
public void clearCache() {
movieWrapper = null;
}
}
defer() makes sure that you will get the object upon subscription to the Observable not on creation.
Note however that, according to the author of the post:
The only downside to defer() is that it creates a new Observable each
time you get a subscriber. create() can use the same function for each
subscriber, so it's more efficient. As always, measure performance and
optimize if necessary.
As already said, accepted answer has downside
it creates a new Observable each time you get a subscriber
But it is not the only one.
Consumer won't receive any value if he calls getMovies().subscribe(...) before setCache(...) is called.
Consumer should resubscribe if he want to receive any updates (let's say setCache() can be called multiple times.
Of course all of them can be irrelevant in your scenario. I just want to show you another way (I'm sure there are many more).
You can use BehaviorSubject in order to eliminate all these disadvantages.
public class MovieCache {
private BehaviorSubject<MovieWrapper> mMovieCache = BehaviorSubject.create();
public void setCache(MovieWrapper wrapper) {
mMovieCache.onNext(wrapper);
}
public Observable<MovieWrapper> getMovieObservable() {
//use this if consumer want to receive all updates
return mMovieCache.asObservable();
}
public MovieWrapper getMovie() {
//use this if consumer want to get only current value
//and not interested in updates
return mMovieCache.getValue();
}
public void clearCache() {
//CAUTION consumer should be ready to receive null value
mMovieCache.onNext(null);
//another way is to call mMovieCache.onCompleted();
//in this case consumer should be ready to resubcribe
}
public static class MovieWrapper {}
}
Take a look at BehaviorSubject marble diagram.

how to make sure that only one AsyncTask runs in the background

I have a class, DownloadAndSave that extends from AsyncTask. In its doInBackground method, it retrieves data from an http connection and saves the data using OrmLite, but also cleans the old entries from the database. So, something like this:
doInBackground()
{
clearDb();
dataList = fetchDataFromHttp();
saveToDb(dataList);
}
I frequently get a DB exception:
attempt to re-open an already-closed object:SQLiteDatabase
in the clearDb() and saveToDb() functions.
And this is bad since old data from the previous call of DownloadAndSave is mixed with the new data from DownloadAndSave.
In my opinion, I need to make sure that when I start a thread, all of the other treads from the DownloadAndSave class have finished, or in other words I need to run at most one instance of DownloadAndSave at a time. So the question is: how do I make sure that only one instance of DownloadAndSave will run in any point of time?
Option 1. Move above:
clearDb();
dataList = fetchDataFromHttp();
saveToDb(dataList);
in a separate class that synchronizes against the class object:
public class WorkerClass {
private WorkerListener workerListener;
public static interface WorkerListener {
public void publishWorkProgress(String data);
}
public WorkerClass(WorkerListener workerListener) {
this.workerListener = workerListener;
}
public void performWork() {
synchronized (WorkerClass.class) {
clearDb();
publish("Cleared DB");
dataList = fetchDataFromHttp();
publish("Got http data");
saveToDb(dataList);
publish("There! saved!");
}
}
private void publish(String message) {
if(workerListener != null) {
workerListener.publishWorkProgress(message);
}
}
}
While from your activity:
public class SampleActivity extends Activity {
public void doTheThing() {
new MyAsyncTask().execute();
}
private static class MyAsyncTask extends AsyncTask<Void, String, Void> implements WorkerListener {
#Override
protected Void doInBackground(Void... params) {
new WorkerClass(this).performWork();
return null;
}
#Override
public void publishWorkProgress(String data) {
publishProgress(data);
}
}
}
Option 2: Move above code to an IntentService:
public class WorkerIntentService extends IntentService {
public WorkerIntentService() {
super(null);
}
#Override
protected void onHandleIntent(Intent intent) {
clearDb();
dataList = fetchDataFromHttp();
saveToDb(dataList);
}
}
Using an IntentService guarantees that tasks are executed serially.
Since API version 11 (HONEYCOMB) of the Android API, an AsyncTask can be executed on a given Executor. You can use the default SerialExecutor to execute tasks sequentially.
If you use db operation in doInBackground, you should be locked db for one thread.
public void insertToDb(){
SQliteDatabase db;
...
db.beginTransaction();
...//operation
db.yieldIfContendedSafely();
db.setTransactionSuccessful();
db.endTransaction();
}
I believe that the issue you are facing is that you are starting the AsyncTask from an activity. Your activity is extending ORMLiteBaseActivity which opens the helper (and with that the database) onCreate and closes it onDestroy. When you exit the activity and the background task still hasn't finished then when trying to do write to the database you end up with a closed DB.
ORMLite handles synchronizations internally and i have never needed to do synchronized blocks with it. I use it in all my projects that require a database.
Also for the other answers, the error is a closed database and not concurrent write operations, so synchronization doesn't make sense.

Android - howto pass data to the Runnable in runOnUiThread?

I need to update some UI and do it inside of the UI thread by using runOnUiThread
Now the data for the UI comes from the other Thread, represented by data here.
How can i pass the data to the Runnable, so tht they can be used to update the UI?
Android doesn't seem to allow using data directly. Is there an elegant way to do this?
public void OnNewSensorData(Data data) {
runOnUiThread(new Runnable() {
public void run() {
//use data
}
});
}
My solution was creating a fioeld private Data sensordata inside of the runnable, and assigning data to it. This works only, if the original Data data is final.
public void OnNewSensorData(final Data data) {
runOnUiThread(new Runnable() {
private Data sensordata = data;
public void run() {
//use sensordata which is equal to data
}
});
}
The problem you found is that
Inner classes in Java capture ("close over") the lexical scope in which
they are defined. But they only capture variables that are declared "final".
If this is clear as mud, there's a good discussion of the details here:
Cannot refer to a non-final variable inside an inner class defined in a different method
But your solution looks fine. In addition, provided that data is final, you could simplify the code to this:
public void OnNewSensorData(final Data data) {
runOnUiThread(new Runnable() {
public void run() {
// use data here
data.doSomething();
}
});
}
If you want to avoid using an intermediate final variable (as described by Dan S), you can implement Runnable with an additional method to set Data:
public class MyRunnable implements Runnable {
private Data data;
public void setData(Data _data) {
this.data = _data;
}
public void run() {
// do whatever you want with data
}
}
You can then call the method like this:
public void OnNewSensorData(Data data) {
MyRunnable runnable = new MyRunnable();
runnable.setData(data);
runOnUiThread(runnable);
}
you could also make MyRunnable's constructor take in the Data instance as an argument:
public class MyRunnable implements Runnable {
private Data data;
public MyRunnable(Data _data) {
this.data = _data;
}
public void run() {
...
}
}
and then just say runOnUiThread(new MyRunnable(data));
I had a similar problem where I wanted to pass information into the thread. To solve it with the android system, I modifying corsiKa's answer in: Runnable with a parameter?
You can declare a class right in the method and pass the param as shown below:
void Foo(String str) {
class OneShotTask implements Runnable {
String str;
OneShotTask(String s) { str = s; }
public void run() {
someFunc(str);
}
}
runOnUiThread(new OneShotTask(str));
}
You'll need to update every time your program has new Data it wants to show. Your second code listing here is the standard way to accomplish this. There can be some catches if you're continuing to update Data in the thread. If this is the case consider blocking the thread until the UI finishes updating or copying the data to another Data object.
What's happening internally is that the JVM is copying the reference to the Data object for when the anonymous class will run. Data stored inside can still be changed. If your method requires additional changes to Data just use another variable (object reference) such as: final Data finalData = data;. You can also remove the line private Data sensordata = data; and use data directly in your run method.
It may not look elegant but this is the way Java passes object variables to anonymous classes. There is newer syntax in Java Language version 7 but Android is compatible with Java Language version 5 and 6.
Here is a typical case where service callback is called to update a UI status string (TextView textStatus). The service may be threaded.
The sample combines checking if thread redirection is needed and the actual redirection:
// service callbacks
public void service_StatusTextChanged(final String s) {
if( isOnUiThread() ) {
textStatus.setText(s);
} else {
runOnUiThread(new Runnable() {
#Override
public void run() {
textStatus.setText(s);
}
});
}
}
static public boolean isOnUiThread() {
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
See also How to check if running on UI thread in Android?
public static Activity globalContext = null;
CommonSetting.globalContext = this;// put this in MainACtivity.onCreate()
public void createToastShort(final String message) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(CommonSetting.globalContext, message, Toast.LENGTH_SHORT).show();
}
});
}

Categories

Resources