Can someone please share the implementation of a background thread with a Looper that i can pass to the subscribeOn(AndroidScheduler.from(/backgroundThreadWithLooper/)).
I need this because i am trying to implement a DBService class that runs all of its operations in the background while still getting live objects updates. So when i apply an addChangeListener, an exception is thrown:
java.lang.IllegalStateException: Your Realm is opened from a thread without a Looper. Async queries need a Handler to send results of your query
or if i use findAll() instead of findAllAsync():
java.lang.IllegalStateException: You can't register a listener from a non-Looper thread or IntentService thread.
DBService code:
public Observable<List> getAll(Class clazz) {
return Observable.defer(() -> {
Realm realm = Realm.getDefaultInstance();
return realm.where(clazz).findAll().asObservable()
.map(o -> realm.copyFromRealm((RealmResults) o))
.doOnUnsubscribe(() -> closeRealm(realm))
.doOnTerminate(() -> closeRealm(realm));
});
}
HandlerThread does the job.
HandlerThread handlerThread = new HandlerThread("backgroundThread");
if (!handlerThread.isAlive())
handlerThread.start();
AndroidSchedulers.from(handlerThread.getLooper());
this is an example of Thread with Looper:
public class GameLoop extends Thread {
#Override
public void run() {
super.run();
Looper.prepare(); // at first write this line of code
//do something
Looper.loop(); //and at the end write this line
}
}
This seems to be working for me
public class MainScopeListener
extends Fragment {
Realm realm;
HandlerThread handlerThread;
Scheduler looperScheduler;
Observable<Realm> realmObservable;
Subscription realmSubscription;
Handler handler = new Handler(Looper.getMainLooper());
public MainScopeListener() {
setRetainInstance(true);
realm = Realm.getDefaultInstance();
Injector.INSTANCE.initializeComponent(realm);
handlerThread = new HandlerThread("REALM_LOOPER") {
#Override
protected void onLooperPrepared() {
super.onLooperPrepared();
Log.i(getName(), "ON LOOPER PREPARED");
handler.post(() -> {
looperScheduler = AndroidSchedulers.from(handlerThread.getLooper());
realmObservable = Observable.create(new Observable.OnSubscribe<Realm>() {
#Override
public void call(Subscriber<? super Realm> subscriber) {
final Realm observableRealm = Realm.getDefaultInstance();
observableRealm.setAutoRefresh(true);
final RealmChangeListener<Realm> listener = realm1 -> {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(observableRealm);
}
};
subscriber.add(Subscriptions.create(() -> {
observableRealm.removeChangeListener(listener);
observableRealm.setAutoRefresh(false);
observableRealm.close();
}));
observableRealm.addChangeListener(listener);
// Immediately call onNext with the current value, as due to Realm's auto-update, it will be the latest
// value.
subscriber.onNext(observableRealm);
}
});
realmSubscription = realmObservable.unsubscribeOn(looperScheduler).subscribeOn(looperScheduler).subscribe(realm12 -> {
Log.i("REALM SUBSCRIPTION", "An event occurred on background thread!");
});
});
}
};
handlerThread.start();
}
public void configureRealmHolder(MainActivity.RealmHolder realmHolder) {
realmHolder.realm = this.realm;
}
#Override
public void onDestroy() {
if(realmSubscription != null && !realmSubscription.isUnsubscribed() ) {
realmSubscription.unsubscribe();
}
handlerThread.quit();
realm.close();
super.onDestroy();
}
}
and
#SuppressWarnings("NewApi")
private Subscription writePeriodic() {
return Observable.interval(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) //
.takeWhile(aLong -> counter < DogNames.values().length) //
.observeOn(Schedulers.io())
.doOnNext(aLong -> {
try(Realm bgRealm = Realm.getDefaultInstance()) {
bgRealm.executeTransaction(realm1 -> {
long currentIndex = realm1.where(Dog.class).max(Dog.Fields.ID.getField()).longValue();
Dog dog = new Dog();
dog.setId(currentIndex + 1);
dog.setName(DogNames.values()[((Long) dog.getId()).intValue() % DogNames.values().length].name());
dog = realm1.copyToRealmOrUpdate(dog);
Log.i(TAG, "Realm write successful [" + counter + "] :: [" + dog.getName() + "].");
counter++;
});
}
}).subscribe();
}
Results in
01-21 00:58:51.672 2094-2127/com.zhuinden.rxrealm I/DogView: Realm write successful [1] :: [Munch].
01-21 00:58:51.672 2094-2115/com.zhuinden.rxrealm I/REALMÂ SUBSCRIPTION: An event occurred on background thread!
So the HandlerThread Realm is able to receive automatic updates.
Copying from the Realm still results in eager evaluation of results, so it is not an efficient way of handling large data sets.
Related
I have this code and it is not working as expected.
get_device_info_work = new OneTimeWorkRequest.Builder(DeviceInfo.class).build();
ListenableFuture<WorkInfo> future = WorkManager.getInstance(getContext()).getWorkInfoById(get_device_info_work.getId());
WorkManager.getInstance(getContext()).enqueue(get_device_info_work);
Futures.addCallback(
future,
new FutureCallback<WorkInfo>() {
public void onSuccess(WorkInfo result) {
Log.d(TAG, "Future success");
}
public void onFailure(#NonNull Throwable thrown) {
Log.d(TAG, "Future failure");
}
},
// causes the callbacks to be executed on the main (UI) thread
getContext().getMainExecutor()
);
I have long running task in doWork() of DeviceInfo class.
public class DeviceInfo extends Worker {
...
public Result doWork() {
int count = 60;
while (count > 0) { Log.d(TAG, "Still working hard"); Thread.sleep(1000); --count; }
return Result.success();
}
}
The onSuccess() callback gets called immediately while doWork is still working.
Why the callback is not waiting for future job done?
Thanks
I'm try to add a simple RxJava call into a runnable thread so I can update the UI once the thread is completed. How do I go about doing that? Here is my Activity code:
public class PrintActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_printer);
printZpl("^XA^LL360^POI^FO20,20^A0N,25,25^FDThis is a test of the ZPL file printing on " + Helper.getCurrDateTime() + "^FS^XZ");
}
}
Here is the class which performs the runnable thread:
public class PrinterManager {
private static void printZpl(final String body, final String footer) {
new Thread(new Runnable() {
public void run() {
try {
Connection btCon = new BluetoothConnectionInsecure("AC:3F:A4:0E:22:05");
btCon.open();
btCon.write(body.getBytes());
Thread.sleep(500);
btCon.close();
// Insert RxJava return here to update the UI in the activity once the thread is completed.
} catch (Exception e) {
Timber.e(e.getMessage());
}
}
}).start();
}
}
I simplified the code for this posting. The actual code is much, much more complex...
Using RxJava2:
Completable.fromAction(() -> {
Connection btCon = new BluetoothConnectionInsecure("AC:3F:A4:0E:22:05");
btCon.open();
btCon.write(body.getBytes());
Thread.sleep(500);
btCon.close();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
Use a Completable instead of an Observable since you do not emit anything but the completion event.
Wrap the asynchronous part of your code in an Observable like this:
public class PrinterManager {
public static Observable<Void> printZpl(final String body, final String footer) {
return Observable.fromCallable(new Callable<Void>() {
#Override
public Void call() throws Exception {
try {
Connection btCon = new BluetoothConnectionInsecure("AC:3F:A4:0E:22:05");
btCon.open();
btCon.write(body.getBytes());
Thread.sleep(500);
btCon.close();
} catch (Exception e) {
Timber.e(e.getMessage());
}
return null;
}
});
}
}
Then, in your Activity, subscribe to it, triggering the code inside:
public class PrintActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_printer);
PrinterManager.printZpl("^XA^LL360^POI^FO20,20^A0N,25,25^FDThis is a test of the ZPL file printing on " + Helper.getCurrDateTime() + "^FS^XZ")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
}
}
If you haven't already, you will need to add the dependencies to your app.gradle file:
compile "io.reactivex:rxandroid:1.2.0"
compile "io.reactivex:rxjava:1.2.0"
If you want to update the UI, then pass an Observer to the subscribe method, instead of just using the empty one as in my example above.
I am trying to implement a repository pattern that use realm and still keep the live objects feature while handling the creating and closing of the realm instances. This my current approach, sadly it does not work. I run all that code on my custom background thread.
public Observable<List> getAll(Class clazz) {
Realm realm = Realm.getDefaultInstance();
SerializedSubject relay = new SerializedSubject<>(PublishSubject.create());
try {
realm.where(clazz).findAllAsync().addChangeListener(new RealmChangeListener<RealmResults>() {
#Override
public void onChange(RealmResults element) {
relay.onNext(realm.copyFromRealm(element));
}
});
return relay.asObservable();
} finally {
relay.onCompleted();
try {
if (!realm.isClosed())
realm.close();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}
Background Thread:
public class JobExecutor implements ThreadExecutor {
private static final int INITIAL_POOL_SIZE = Runtime.getRuntime().availableProcessors();
// Sets the amount of time an idle thread waits before terminating
private static final int KEEP_ALIVE_TIME = 10;
// Sets the Time Unit to seconds
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
#NonNull
private final ThreadPoolExecutor threadPoolExecutor;
public JobExecutor() {
threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, INITIAL_POOL_SIZE,
KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, new LinkedBlockingQueue<>(), new JobThreadFactory());
}
#Override
public void execute(#NonNull Runnable runnable) {
this.threadPoolExecutor.execute(runnable);
}
private static class JobThreadFactory implements ThreadFactory {
private static final String THREAD_NAME = "android_";
private int counter = 0;
#NonNull
#Override
public Thread newThread(#NonNull Runnable runnable) {
return new Thread(runnable, THREAD_NAME + counter++);
}
}
I use it like this:
private <T> Observable.Transformer<T, T> applySchedulers() {
return observable -> observable.subscribeOn(Schedulers.from(mThreadExecutor)) // my background thread
.observeOn(mPostExecutionThread.getScheduler()); // main thread
}
This exception is thrown:
java.lang.IllegalStateException: Your Realm is opened from a thread without a Looper. Async queries need a Handler to send results of your query
When i use .findAll() instead of .findAllAsync() this exception is thrown:
java.lang.IllegalStateException: You can't register a listener from a non-Looper thread or IntentService thread.
Thanks in advance and looking forward to your responses.
You need a Thread that has a Looper/Handler, as stated by the IllegalStateException. Right now, it looks like you're just using the normal Schedulers class, and not the AndroidSchedulers. Check out this repo for Android specific schedulers.
https://github.com/ReactiveX/RxAndroid/tree/1.x/rxandroid/src/main/java/rx/android/schedulers
Is there any way to call Realm queries from AsyncTask?
I have so many queries that are doing join, So i want to call them from a separate One AsyncTask to avoid the load on UI Thread. For now i am using DefaultInstance of Realm everywhere. I get this error
Realm objects can only be accessed on the thread they where created
P.S I know Realm has its own Async for every query, but as i just mentioned i have alot of separate calls that are further doing joins and for loops.
EDIT
here's my code for an Async
#Override
protected Object doInBackground(Object[] params) {
//Step 1: Find All quote_taxes
Realm realm = Realm.getDefaultInstance();
listTaxData = new ArrayList<TaxData>();
try {
RealmResults<quote_taxes> listQuoteTaxes = quote_taxes.get_from_quotes(realm, quote.getId());
if (listQuoteTaxes != null && listQuoteTaxes.size() > 0) {
for (quote_taxes quoteTax : listQuoteTaxes) {
TaxData taxData = new TaxData();
taxData.setTaxName(quoteTax.getTaxName());
taxData.setAccountNumber("" + quoteTax.getAccountNumber());
taxData.setTaxRate("" + quoteTax.getTaxRate() + "%");
double total = quote_taxes.total(realm, quoteTax);
showLog("Total = " + total);
}
}
}catch (Exception ex)
{
}finally {
realm.close();
}
return null;
}
You just have to do what the docs say:
For AsyncTask this is a good pattern:
protected Void doInBackground(Void... params) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ... Use the Realm instance ...
} finally {
if (realm != null) {
realm.close();
}
}
return null;
}
More importantly, you can use try-with-resources:
protected Void doInBackground(Void... params) {
try(Realm realm = Realm.getDefaultInstance()) {
// ... Use the Realm instance ...
}
return null;
}
If you are using Thread or Runnable for short-lived tasks, the follow pattern is recommended:
// Run a non-Looper thread with a Realm instance.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ... Use the Realm instance ...
} finally {
if (realm != null) {
realm.close();
}
}
}
});
thread.start();
And use a RealmChangeListener on the UI thread to be notified of successful transactions in background threads.
EDIT: Oh, you want to perform asynchronous queries.
I have so many queries that are doing join, So i want to call them from a separate One AsyncTask to avoid the load on UI Thread.
...while I truly doubt you have any "join"s considering Realm is not a relational database and the concept of joins doesn't exist in Realm; if you want asynchronous queries, you shouldn't overcomplicate your design with nonsense like AsyncTask. Just use the asynchronous query methods.
RealmResults<Something> results;
RealmChangeListener realmChangeListener = new RealmChangeListener() {
#Override
public void onChange(Object element) {
if(results != null && results.isValid() && results.isLoaded()) {
updateUI(results);
}
}
};
//...
results = realm.where(Something.class)./*...*/.findAllAsync(); // <-- async query
results.addChangeListener(realmChangeListener);
Realm has its Async load default functionality :-
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Use the Realm instance
}
});
Above execution done on background thread, and it's gives callback when db changes like.
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// success callback
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// error callback
}
});
I believe that you create Realm objects in doInBackground and then process results in onPostExecute? To avoid this you can use IntentService instead of AsyncTask. If you still want to use AsyncTask, you can process query results in doInBackgroundas well and then return needed data (Lists, POJOs etc.) to onPostExecute.
I'm very new to RXJava.
I have a function called politelyrefresh() that concats two observables together, but the functions in these two observables only run the first time I called politeRefresh, I'm not sure this is the right way to do it. What I want is run this function inside the observables everytime.
public void politelyRefresh() {
Observable.concat(refreshStoreDataObservable, refreshProjectDataObservable)
.finallyDo(()-> {
try {
//someother other long runnning-network requests
} catch (Exception e) {
Log.e(TAG, "politelyRefresh finallyDo Error", e);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(reloadUiFromLocalStorageSubscriber);
}
//the other observable is pretty much the same but making another request
Observable<String> refreshStoreDataObservable = Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
//DOESN'T GET HERE SECOND TIME!
Store.syncStores(new ListCallback() {
#Override
public void syncSuccess() {
getSyncStateManager().setStoresRefreshed();
subscriber.onCompleted();
}
#Override
public void syncError() {
subscriber.onError(new Throwable("SYNC STORES ERROR"));
getSyncStateManager().setStoresSyncCompleted();
}
});
}
});
Subscriber<String> reloadUiFromLocalStorageSubscriber = new Subscriber<String>() {
#Override
public void onCompleted() {
if (mStoreRefreshLayout != null){
mStoreRefreshLayout.setRefreshing(false);
}
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "reloadUiFromLocalStorageSubscriber: onError", e);
if (mStoreRefreshLayout != null){
mStoreRefreshLayout.setRefreshing(false);
}
}
#Override
public void onNext(String s) {
Log.d(TAG, "reloadUiFromLocalStorageSubscriber: onNext " + s);
}
};
I think you're looking for Observable.defer(). What this basically does is defer the creation of the Observable to when it is being subscribed to.
Here's a quick example:
public class Refresher {
Refresher() {
politelyRefresh();
politelyRefresh();
}
public void politelyRefresh() {
Observable.defer(() -> Observable.concat(refreshProjectData(), refreshStoreData()))
.map(this::processData)
.subscribe(this::printData);
}
private Observable<String> refreshStoreData() {
System.out.println("StoreData Refreshed");
return Observable.just("data1","data2","data3");
}
private Observable<String> refreshProjectData() {
System.out.println("ProjectData Refreshed");
return Observable.just("Project1","Project2", "Project3");
}
private String processData(String data) {
return data + " processed";
}
private void printData(String data) {
System.out.println(data);
}
}
If you instantiate our refresher object, you'll get
StoreData Refreshed
StoreData Refreshed
Project1 processed
Project2 processed
Project3 processed
data1 processed
data2 processed
data3 processed
StoreData Refreshed
StoreData Refreshed
Project1 processed
Project2 processed
Project3 processed
data1 processed
data2 processed
data3 processed
If you'd like something to run on a different thread, you'd specify that on the specific observable you're looking to run on a non-ui thread.
So, for example, you might want to run the Observable in politelyRefresh on a background thread and subscribe to it on the UI thread. The creation of the other Observables will happen in a background thread too!
I finally got this to work by move the subscriber from an class instance to inside the .subscribe() function(). I have no idea why this is happening.
Observable.concat(refreshStoreDataObservable, refreshProjectDataObservable)
.finallyDo(()-> {
try {
//someother other long runnning-network requests
} catch (Exception e) {
Log.e(TAG, "politelyRefresh finallyDo Error", e);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( new Subscriber<String>() { /*rest of code */}); //**here