I am using the Sumup SDK to create a bridge to React Native. Most of the hard work is done but I am trying to call a specific function to wake up the card reader before a transaction is processed.
The original code I had was this:
#ReactMethod
public void prepareCardTerminal() {
SumUpAPI.prepareForCheckout();
}
}
The RN bridge then calls this function like this:
static prepareCardTerminal() {
NativeRNSumup.prepareCardTerminal();
}
How ever this gives me a React Native error of:
Must be called on main thread
I read that this could mean it needs to be run on the UI thread so I rewrote the function to be:
#ReactMethod
public void prepareCardTerminal() {
new Thread(new Runnable() {
public void run() {
SumUpAPI.prepareForCheckout();
}
});
}
However this doesn't have the intended results (even though it doesn't show any errors).
Any tips would be much appreciated.
Edit: I found a solution to this issue. I used UiThreadUtil:
import com.facebook.react.bridge.UiThreadUtil;
...
#ReactMethod
public void prepareCardTerminal() {
UiThreadUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
});
}
You can do something like this:
#ReactMethod
public void prepareCardTerminal() {
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
};
mainHandler.post(myRunnable);
}
Or even simpler:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
});
The answer posted as an edit to the question is correct. The answer by #waquar-ulhaq is technically correct but using UiThreadUtil is way simpler and internally does use a Handler
import com.facebook.react.bridge.UiThreadUtil;
...
#ReactMethod
public void prepareCardTerminal() {
UiThreadUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
});
}
Related
I want to execute below methods in order. That means, if A methods ends, B starts. If B ends C starts and so forth.
public void InitializeAPI {
public static void init_A(Context mContext) {
Realm.init(mContext);
Realm realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Some working is here.
}
}, new Realm.Transaction.OnSuccess() {
// Some proceesing lines are here.
}, new Realm.Transaction.OnError() {
Log.d("AsyncTransaction", "ERROR");
});
// And there are lots of other TransactionAsync() line in this methods.
}
public static void init_B(Context mContext) {...}
public static void init_C(Context mContext) {...}
// and So many initialize methods.
}
As you can see, when I execute init_A(Context), Async Transaction will be in background.
But the problem is init_B needs to be executed after init_A ending. init_C also. How can I design this matter??
Of course, I know that there is Realm.Transaction.OnSuccess() But, If I write init_B in Realm.Transaction.OnSuccess() at init_A, I think that codes won't be neat or good to Maintenance.
Why don't you use something like this:
realm.beginTransaction();
//... add or update objects here ...
realm.commitTransaction();
instead of a async call.
It will ensure that code will work synchronously.
Hope it helps..!
you could send a message though handler in the OnSuccess to trigger the init_B. But that doesn't look much better. So if you want to do the initialization without blocking the UI, creating your own thread might a good choice.
public void InitializeAPI {
public static void init_A(Context mContext) {
Realm.init(mContext);
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
// Write data
realm.commitTransaction();
realm.close();
}
public static void init_B(Context mContext) {...}
public static void init_C(Context mContext) {...}
// and So many initialize methods.
public static void init() {
init_A();
init_B();
init_C();
//...
}
}
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
InitializeAPI.init();
// Notify the main thread that initialization is done.
handler.post(initialziedRunnable);
}
});
thread.start();
I've just started playing around with CountDownLatch in my Android app. Currently I am trying to make two Volley requests to my api, and wait until the data has been retrieved and stored before continuing with thread execution.
This is a sample of my code:
// new CountDownLatch for 2 requests
final CountDownLatch allDoneSignal = new CountDownLatch(2);
transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
#Override
public void onSuccess(ArrayList<Contact> resources, String resourceId) {
transactions.createRealmObject(resources, Contact.class);
allDoneSignal.countDown();
}
#Override
public void onFail(ArrayList<Contact> resources) {
}
});
transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
#Override
public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
transactions.createRealmObject(resources, Meeting.class);
allDoneSignal.countDown();
}
#Override
public void onFail(ArrayList<Meeting> resources) {
}
});
try {
allDoneSignal.await();
// continue executing code
// ...
} catch (InterruptedException e) {
e.printStackTrace();
}
The issue is that it doesn't seem to "complete" the countdown and therefore freezes because the latch is never released. I have confirmed that the API requests are working and the onSuccess callback is hit successfully, but the thread hangs.
UPDATE
I've just noticed that with the CountDownLatch set to 0, it hits onSuccess, but when I set it to anything greater than 0, it freezes and onSuccess is never called. Seems like something's funky with the threading.
Your code is too error prone, you need to call countDown() in a finally block and call it also in onFail otherwise in case of failure your application will freeze for ever. So your code should rather be something like:
transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
#Override
public void onSuccess(ArrayList<Contact> resources, String resourceId) {
try {
transactions.createRealmObject(resources, Contact.class);
} finally {
allDoneSignal.countDown();
}
}
#Override
public void onFail(ArrayList<Contact> resources) {
allDoneSignal.countDown();
}
});
transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
#Override
public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
try {
transactions.createRealmObject(resources, Meeting.class);
} finally {
allDoneSignal.countDown();
}
}
#Override
public void onFail(ArrayList<Meeting> resources) {
allDoneSignal.countDown();
}
});
Sorry for the late answer, but if it's still any help to anyone:
You need to do the ".await" in a separate thread because it blocks the current thread.
Example:
final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
#Override
public void run() {
allDoneSignal.await();
mainThreadHandler.post(new Runnable() {
doSomethingWhenAllDone();
});
}
}).start()
I want to make downloader, which download data and then call function in UI thread. I have this in main activity
onCreate(){
...
dataRepository.downloadIfNewOrEmpty(new DownloadResponse() {
#Override
public void SuccessResponse(Response response) {
// do something in UI
}
});
}
My function downloadIfNewOrEmpty looks for now only simple with sleep()
public void downloadIfNewOrEmpty(final DownloadResponse response){
//final Handler handler = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(5000);
response.SuccessResponse(ResponseCode.SUCCESS);
/*handler.post(new Runnable() {
#Override
public void run() {
response.SuccessResponse(ResponseCode.SUCCESS);
}
});*/
}catch (Exception e){
// Log...
}
}
}).start();
}
If I run this code, it normally does the job and update my UI. I found this solution with Handler (android.os.Handler) but if I run it without or with Handler (commented version) it works same.
Although without handler function SuccessResponse is run in UI thread?
Thank you
Although without handler function SuccessResponse is run in UI thread?
Yes, because response is instance of DownloadResponse which is passed from UI Thread as parameter to downloadIfNewOrEmpty.
I've write the following example:
public class MyThread extends Thread{
MyThread(Runnable r){
super(r);
}
public void run(){
System.out.println("run");
}
}
public static void main(String[] args)
{
Thread t = new MyThread(new Runnable() {
#Override
public void run() {
System.out.println("rrrrrrrrrruuuuuuuuuuuun");
}
});
t.start(); //run
}
Why does run methdo defined in MyThread was called instead?
Because the default behavior of a thread constructed with a Runnable is to delegate to the runnable passed as argument to the constructor. But you overrode run() in the thread itself, so instead of delegating to the runnable, it executes the code inside the overridden run() method.
For the record, here's the default implementation of Thread.run(), that you overrode:
private Runnable target;
public void run() {
if (target != null) {
target.run();
}
}
Because you the MyThread.run is not override, but the Runnable.run is. Now if you look at your implementation of MyThread.run, the stored Runnable plays no part in it. In other words, it doesn't matter what kind of runnable you give with the constructor. You should use:
public static void main(String[] args)
{
Thread t = new MyThread() {
#Override
public void run() {
System.out.println("rrrrrrrrrruuuuuuuuuuuun");
}
});
t.start(); //run
}
As #BorisTheSpider notes, overriding a Thread is in general not good practice: a Thread has the responsibility to start a Thread and give control to a runnable. A better implementation would be:
public static void main(String[] args)
{
Thread t = new Thread(new MyThread() {
#Override
public void run() {
System.out.println("rrrrrrrrrruuuuuuuuuuuun");
}
}));
t.start(); //run
}
In the last days I have found myself using this approach for asynchronously performing some long operation (several seconds), and then return some value via a callback that must execute on the caller thread, which is typically but not necessarily the UI thread.
public abstract class DoSomethingCallback
{
public abstract void done(Object result);
}
public void doSomething(final Object param, final DoSomethingCallback doSomethingCallback)
{
// Instantiate a handler for the calling thread
final Handler handler = new Handler();
// Start running the long operation in another thread
new Thread(new Runnable() {
#Override
public void run() {
// Do a long operation using "param" as input...
Object result = longOperation(param);
// Return result via a callback, which will run in the caller thread
handler.post(new Runnable() {
#Override
public void run() {
doSomethingCallback.done(clearBytes);
}
});
}
}).start();
}
This seems to work pretty well and is very simple to use. However, I somehow suspect it might have some problems I'm not aware of. So the question is, what are the potential issues of this approach? What are better alternatives than manually creating and running a thread? I'm seeking for simplicity and robustness.
The only problem is that such approach breaks encapsulation: the second thread not only computes the result, but also dictates what the caller thread should do with it. So I'd better refactor your code as follows:
public abstract class DoSomethingCallback {
final Handler handler = new Handler();
public void post(final Object result) {
handler.post(new Runnable() {
#Override
public void run() {
doSomethingCallback.done(result);
}
});
}
public abstract void done(Object result);
}
public void doSomething(final Object param, final DoSomethingCallback doSomethingCallback) {
// Instantiate a handler for the calling thread
final DoSomethingCallback handler = new DoSomethingCallback () {
void done(Object result) {
...
}
};
// Start running the long operation in another thread
new Thread(new Runnable() {
#Override
public void run() {
// Do a long operation using "param" as input...
Object result = longOperation(param);
// Return result via a callback, which will run in the caller thread
handler.post(result);
});
}
}).start();
}