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.
Related
My Android app has a dedicated thread to make network api calls using retrofit.The thread makes a retrofit api call depending on the type of the message received. I would like to migrate this code to use Kotlin co-routines but I not sure how to implement message queues without using a Thread. Any help will be greatly appreciated.
Pseudo java code:
public class MyThread extends Thread {
private final Messenger mPushMessenger;
private Handler mHandler;
private Object mThreadLock;
private IMessage mMessage;
private MyThread(IMessage message) {
super(message.getThreadLink().name());
mMessage = message;
start();
mPushMessenger = new Messenger(getHandler());
mThreadLock = new Object();
}
public static MyThread getInstance(IMessage ISenThread)
{
return new MyThread(ISenThread);
}
public void NotifyThreadLock() {
synchronized (mThreadLock)
{
try {
mThreadLock.notify();
}
catch(Exception e)
{
}
}
}
private Handler getHandler() {
synchronized (this) {
while (mHandler == null) {
try {
wait();
} catch (InterruptedException ignored) {
}
}
}
return mHandler;
}
public void run() {
Looper.prepare();
synchronized (this) {
mHandler = new MyThread().MyHandler();
notifyAll();
}
Looper.loop();
}
public void pushMessage(Object message, int what) {
try {
android.os.Message msg = android.os.Message.obtain(null, what, message);
mPushMessenger.send(msg);
} catch (Exception ignored) {
}
}
public void EmptyQueue() {
}
/*
public void pushMessage(String message) {
pushMessage(message, SenFCMLink.SenEyeStatusSender.STATUS_GENERAL_MESSAGE);
}
*/
private class MyHandler extends Handler {
public void handleMessage(Message msg) {
switch(msg.Type)
{
case A:
// call retrofit API Method A
case B:
// call retrofit API Method B
case C:
// call retrofit API method C.
}
}
}
}
Retrofit methods can be declared for either synchronous or asynchronous execution
call.execute();
is a synchronous network call ,0
Synchronous requests are declared by defining a return type.Synchronous methods are executed on the main thread. That means the UI blocks during request execution and no interaction is possible for this period. Using the .execute() method on a call object will perform the synchronous request. The deserialized response body is available via the .body() method on the response object.
call.enqueue()
is a asynchronous network call, Asynchronous requests don’t have a return type. Instead, the defined method requires a typed callback as last method parameter.Using asynchronous requests forces you to implement a Callback with its two callback methods: success and failure. When calling the asynchronous getTasks() method from a service class, you have to implement a new Callback and define what should be done once the request finishes.
like below:
call.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
response = response.body();
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
}
I'm beginner in android and trying to show toast in the async task, for that purpose I wrote this code:
public class GetReading {
public GetReading() {
}
public List<ReadingModel> Get(String TokenKey, Context adapter) throws ExecutionException, InterruptedException {
GetReadingTask params = new GetReadingTask(TokenKey, adapter);
List List_result = (List)(new GetReading.AsyncRead()).execute(new GetReadingTask[]{params}).get();
return List_result;
}
private class AsyncRead extends AsyncTask<GetReadingTask, Void, List<ReadingModel>> {
ir.behineh.wepapiinterface.GETREADINGINTERFACE.ReadingModel.List x;
private AsyncRead() {
}
protected List<ReadingModel> doInBackground(GetReadingTask... getReadingTasks) {
final Context pos = getReadingTasks[0].adapter;
Handler handler = new Handler(pos.getMainLooper());
handler.post(new Runnable() {
public void run() {
Toast.makeText(pos, "Created a server socket", 1).show();
}
});
ir.behineh.wepapiinterface.GETREADINGINTERFACE.GetReading taskService = (ir.behineh.wepapiinterface.GETREADINGINTERFACE.GetReading)ServiceGenerator.createService(ir.behineh.wepapiinterface.GETREADINGINTERFACE.GetReading.class);
Call tasks = taskService.getReadings("application/x-www-form-urlencoded", "application/json", "bearer " + getReadingTasks[0].TokenKey);
try {
this.x = (ir.behineh.wepapiinterface.GETREADINGINTERFACE.ReadingModel.List)tasks.execute().body();
} catch (IOException var7) {
var7.printStackTrace();
}
return this.x;
}
}
}
and when i try to call that async task with this code:
GetReading reading=new GetReading();
List<ReadingModel> result= reading.Get("VQ",LineActivity.this);
after finishing doinbackground get the toast, but i want to show toast first to user, what happen? how can i solve that problem? thanks all.
Toasts as well as anything that has to do with the UI cannot be fired from any thread that runs in the background.
Move your code for displaying the toast to either onProgressUpdate or on onPostExecute.
All your troubles arise because you use the .get() in
execute(new GetReadingTask[]{params}).get();
Never use .get() as it kills the asynchonity of your task.
Instead: do the things you want to do with the result in onPostExecute().
I had 2 tables TimeStamps and Infraction, I want to do
something like that using retrofit with Rx Android :
Request-> I get TimeStamps (if it's changed)
-> I send new request to get Infractions
else I display infractions from database
this is what I did using Retrofit, is that correct ??
Observable<TimeStamps> callTimeStamp = apiInterface.getTimeStamp();
TimeStamps stamps = realm.where(TimeStamps.class).findFirst();
callTimeStamp.flatMap(new Function<TimeStamps, ObservableSource<List<Infraction>>>() {
#Override
public ObservableSource<List<Infraction>> apply(TimeStamps timeStamps) throws Exception {
if(!timeStamps.getInfractionTimeStamps().equalsIgnoreCase( stamps.getInfractionTimeStamps()))
return apiInterface.getInfractions();
else
return null;
}
}).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<List<Infraction>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Infraction> infractions) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
No
TimeStamps stamps = realm.where(TimeStamps.class).findFirst();
This obtains stamps on the current thread
if(!timeStamps.getInfractionTimeStamps().equalsIgnoreCase( stamps.getInfractionTimeStamps()))
This attempts to access that stamps instance on a different thread, so you'll get an IllegalStateException
return null;
Even if it did work, this line would make RxJava2 throw a NullPointerException
.subscribeOn(Schedulers.newThread())
This could easily be Schedulers.io() instead so that it wouldn't create too many threads (although then of course you should make sure you use try(Realm realm = ...) or finally { realm.close() })
.subscribe(new Observer>() {
This is wrong unless you "properly implement onSubscribe" which is not expected at all, this should be new DisposableObserver<List<Infraction>>().
In which case your Retrofit interface should probably expose Single<T>, as singles automatically unsubscribe when done.
Single<TimeStamps> callTimeStamp = apiInterface.getTimeStamp();
callTimeStamp.flatMap((timeStamps) -> {
try(Realm realm = Realm.getDefaultInstance()) {
TimeStamps stamps = realm.where(TimeStamps.class).findFirst();
if(!timeStamps.getInfractionTimeStamps().equalsIgnoreCase( stamps.getInfractionTimeStamps()))
return apiInterface.getInfractions();
else
return Single.never();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<List<Infraction>>() {
#Override
public void onNext(List<Infraction> infractions) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
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
I want to use RxAndroid in my project,
and i make the thread sleep for 50ms
but it caused anr,the code
public void getTypeAndCommodity() {
Observable.from(getCommodities())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Commodity>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Commodity commodity) {
}
});
}
and the getCommodities:
private ArrayList<Commodity> getCommodities() {
// some test info
ArrayList<Commodity> list = new ArrayList<>();
for (int i = 0; i < 99; i++) {
Commodity commodity = new Commodity();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
commodity.setName("name" + i);
commodity.setType("type" + (i + 1) / 10);
list.add(commodity);
}
return list;
}
why it cause anr?please help
This happens because getCommodities() is executed in main thread, and only the item emited is executed in io thread with subscribeOn(Schedulers.io()). If you want to execute getCommidities() in background thread too, you need to create an observable with defer() method:
Observable.defer(new Func0<Observable<Object>>() {
#Override public Observable<Object> call() {
return Observable.from(getCommodities());
}
}).subscribeOn(Schedulers.io())...
If you need more info: http://blog.danlew.net/2015/07/23/deferring-observable-code-until-subscription-in-rxjava/