How to *not* use runOnUiThread - java

As you can see in the title, I'm trying to not use runOnUiThread each time I need it. I'll explain what I mean later, but first, here is my current code (part of):
private void filePaste()
{
final File src = new File(FileClip); //Source file (as a string)
final File dest = new File(myPath.getText()+"/"+src.getName()); //Destination file
final ProgressDialog dlg = new ProgressDialog(AppContext);
final Thread copythread = new Thread(new Runnable() {
public void run() {
if(FileClip==null)
Main.this.runOnUiThread(new Runnable() {
public void run(){
toast(getString(R.string.msg_EmptyClip));
}
});
else
{
if(src.canRead()){
if(!src.isDirectory())
{
try{
BufferedInputStream in = new BufferedInputStream(new FileInputStream(src), 8192);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
long pos = 0;
long read = 8192;
byte[] data = new byte[(int)read];
while(pos<src.length()){
int bytes = in.read(data, 0, (int)read);
if(bytes>-1) {
out.write(data,0,bytes);
in.skip(read);
in.mark((int)pos+bytes);
in.reset();
dlg.incrementProgressBy(bytes);
pos+=bytes;
}
}
Main.this.runOnUiThread(new Runnable() {
public void run(){
dlg.dismiss();
}
});
in.close();
out.close();
}catch(final Exception e)
{
Main.this.runOnUiThread(new Runnable() {
public void run(){
alert("filePaste():\n"+e);
}
});
}
if(Moving==true) {
boolean q = src.delete();
if(q==true)
Main.this.runOnUiThread(new Runnable() {
public void run(){
toast(getString(R.string.msg_MoveOK,src.getName()));
}
});
else
Main.this.runOnUiThread(new Runnable() {
public void run(){
toast(getString(R.string.msg_MoveNO,src.getName()),1);
}
});
Moving = false;
}
else {
Main.this.runOnUiThread(new Runnable() {
public void run(){
toast(getString(R.string.msg_CopyOK,src.getName()));
}
});
}
}
}
else
Main.this.runOnUiThread(new Runnable() {
public void run(){
toast(getString(R.string.msg_CopyNO,src.getName()));
}
});
FileClip = null;
Main.this.runOnUiThread(new Runnable() {
public void run(){
getDir(myPath.getText().toString());
}
});
}
}
});
dlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dlg.setTitle(Moving?getString(R.string.moving):getString(R.string.copying));
dlg.setMessage(src.getName());
dlg.setCancelable(true);
dlg.setButton(DialogInterface.BUTTON_NEGATIVE, getString(android.R.string.cancel), new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which) {
dlg.cancel();
}
});
dlg.setOnCancelListener(new DialogInterface.OnCancelListener()
{
#Override
public void onCancel(DialogInterface dialog) {
copythread.interrupt();
if(dest.exists())
dest.delete();
}
});
dlg.setMax((int)src.length());
dlg.show();
copythread.start();
}
What this code does is try to copy a given file (stored as a string in FileClip).
As you can see, the code is quite long, because the excessive use of runOnUiThread. I want to know how to move all of the
Main.this.runOnUiThread(new Thread(new Runnable()
public void run()
{
//Simple stuff for 6 lines
}
));
to a class or something. I was thinking of
public class runAtUI implements Runnable
But I stop at that point, I don't know how to make constructors. I hope you know what I mean with this; something like:
new runAtUI()
{
//This makes less lines IMO, and could be executed
}
*Note: I looked for topics about this but all saying the same, use the runOnUiThread. I know it's ok but for something short (like displaying a toast -using toast() with the same purpose- or an alert -again, but with alert()-) it's pointless.
PS: This is for a simple file manager that I am doing and will not be published on Google Play because of the lack of money, and I don't want to use ads either (breaking the "simplicity" in the name). Yes, I know that there are free and adsless (that word exists?) and better apps but I wanted to learn how to make one n_n;
Any information, idea or guide will be appreciated.
Edit #2: Thank you to all that answered quickly! The samples that you provided will work for now but I'd like it to be more flexible. Like eval("code as string") in javascript (without the code being a string).
I'll consider this question answered but leave it open to let people give some more ideas ;D
Edit #3: Okay, first, sorry for the long time no response. Second, I'm adding links to the current code as it is now, from 123 lines (this one) to 77 lines of code at pastebin. I'm also adding the code for uithread.
About the app: If you want it for testing, see how it is right now or anything, drop me a mail and I'll send it to you ;)

You could create a utility class to encapsulate the steps of creating a runnable to be run by the UI thread and invoke Toast. Here's a simple implementation:
public abstract class ToastUtils {
/**
* Displays a toast message, making sure that the toast is always invoked from the main (ui) thread.
* #param act Calling Activity
* #param text Toast text
* #param duration Toast duration
*/
public static void showToast(final Activity act, final String text, final int duration) {
act.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(act, text, duration).show();
}
});
}
}
Using this class you would do this to toast wherever necessary:
ToastUtils.showToast(this, "some text", Toast.LENGTH_SHORT);

You can create a Handler in activity:
Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case R.string.cancel:
//show toast
break;
default:
break;
}
}
};
And use it from your thread like:
handler.sendEmptyMessage(R.string.cancel);

From http://developer.android.com/guide/components/processes-and-threads.html:
However, as the complexity of the operation grows, this kind of code
can get complicated and difficult to maintain. To handle more complex
interactions with a worker thread, you might consider using a Handler
in your worker thread, to process messages delivered from the UI
thread. Perhaps the best solution, though, is to extend the AsyncTask
class, which simplifies the execution of worker thread tasks that need
to interact with the UI.
... skipping ahead ...
You can call publishProgress() at anytime in doInBackground() to
execute onProgressUpdate() on the UI thread
It looks to me like this is what you want to do.
With regard to your other question (you should try to ask only one question per topic), copying files in Java is kind of a solved problem. Among threads on SO, I think this one has some pretty good answers: Standard concise way to copy a file in Java?

Related

Google Glass Immersion - OnClick not working inside a separate thread

My problem is pretty simple. I am creating a card based on the result of a HTTP query performed inside a separate thread. The card also has an onclick method and is defined inside a runOnUiThread() located inside the separate thread. However, when the device is tapped, the onclick event isn't fired.
Here is my code:
private void login() {
Runnable r = new Runnable() {
#Override
public void run() {
// irrelevant code
runOnUiThread(new Runnable() {
#Override
public void run() {
setContentView(buildError(code));
}
}
}
Thread t = new Thread(r);
t.start();
}
private View buildError(String code) {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.ALERT);
card.setIcon(R.drawable.ic_warning_150);
if (code.equals("1"))
card.setText("Incorrect credientals");
else
card.setText("Unexpected error");
card.setFootnote("Tap to try again");
View cView = card.getView();
cView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("Event", "Clicked"); // This is what isn't triggering
}
});
cView.setFocusable(true);
cView.setFocusableInTouchMode(true);
return cView;
}
Even though the snippet of code contains an error (can't be compiled, missing ; at the Runnable statement), you were on the right track.
The View simply needs to request the focus in order to be clickable right away. Otherwise you'll have to move the focus manually.
cView.setFocusable(true);
cView.setFocusableInTouchMode(true);
cView.requestFocus();
Reference

Java runOnUiThread and Thread.sleep

I have this method from a separate class wherein when the call ends, the color of my ImageView changes from red to white. Sample code below:
public void endOfCall(){
((Activity)mContext).runOnUiThread(new Runnable(){
#Override
public void run(){
TargetDetails.oncall.setVisibility(View.VISIBLE);
TargetDetails.endcall.setVisibility(View.GONE);
}
});
try{
call.endCall();
}catch (SipException se) {}
call.close();
//this is just a representation; not the actual code
if(true){
Thread.sleep(10000);
}
//new intent here
}
The problem starts when it goes to the 'if' condition where I put the Thread.sleep. It waits 10seconds before the code below gets executed
TargetDetails.oncall.setVisibility(View.VISIBLE);
TargetDetails.endcall.setVisibility(View.GONE);
I think I am missing something here regarding the Thread.sleep. I just wanna get rid of it but I'm not sure of any alternative aside from that. Help. Thanks.
Use Handler instead of putting thread to sleep.
So instead of your if(true) {.....} try this:
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override public void run() {
//new intent here
}
}, 10000);

Editing a Button's colour in a nonUI thread (Android)

I've seen some similar questions and got some information but they stop shy of telling me enough to get it working.
What I'm trying to do is make a simple rhythm game where the player taps a button at regular intervals (ie. beats). I wanted to set up a way of signalling when to tap by having the button change colour, and since this would be a repeated task at regular intervals I want to use a timer object with a schedule method.
But when I try calling on this method it tells me that I can't change the UI in a non UI thread. I've tried a few ways to write a method in the main thread that I can call from the timer object but I get the same error every time. I'm assuming that I just have the wrong idea about what counts as being from the UI thread, so I was hoping someone could clear it up.
Here's a snippet of one way I tried it, just to show what my code looks like:
OnClickListener clickButton = new View.OnClickListener() {
public void onClick(View v) {
if (startBeat == 0){
startBeat = System.nanoTime();
timerStart.scheduleAtFixedRate((new TimerTask()
{
public void run()
{
flashButton();
}
}), 0, beatTime);
timerEnd.schedule(new TimerTask()
{
public void run()
{
unflashButton();
}
}, beatTolerance*2, beatTime);
return;
}
};
public void flashButton(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
public void unflashButton(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1));
}
To be clear, this is all contained within my MainActivity class along with the OnCreate class.
if you are in an activity all you need to do is use runOnUiThread() and then place the code to change the ui element in there
public void flashButton(){
runOnUiThread(new Runnable() {
#Override
public void run() {
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
});
}
You cannot, under any circumstances, touch a UI object from a non UI thread.
You can accomplish your intent using Handler.sendMessageDelayed
UI can only be touched by the main thread. You should post the actions you are performing on the ui thread via handler or via runOnUiThread
Try something similar to this
timerStart.scheduleAtFixedRate((new TimerTask()
{
public void run()
{
//replace MainActivity with your activity
//if inside a fragment use getActivity()
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
flashButton();
}
});
}
}), 0, beatTime);
If you are in an Activity you could surround flashButton() with an runOnUiThread.
...
runOnUiThread(new Runnable(){
public void run(){
flashButton();
}
});
...
use android.os.Handler Class. Change your code as follows:
private Handler handler=new Handler();
public void flashButton(){
handler.post(new Runnable(){
public void run(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
});
}
public void unflashButton(){
handler.post(new Runnable(){
public void run(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1));
}
});
}

app crash when android use sql web service [duplicate]

What does the following exception mean; how can I fix it?
This is the code:
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
This is the exception:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at android.widget.Toast.<init>(Toast.java:68)
at android.widget.Toast.makeText(Toast.java:231)
You need to call Toast.makeText(...) from the UI thread:
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
}
});
This is copy-pasted from another (duplicate) SO answer.
You're calling it from a worker thread. You need to call Toast.makeText() (and most other functions dealing with the UI) from within the main thread. You could use a handler, for example.
Look up Communicating with the UI Thread in the documentation. In a nutshell:
// Set this up in the UI thread.
mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message message) {
// This is where you do your work in the UI thread.
// Your worker tells you in the message what to do.
}
};
void workerThread() {
// And this is how you call it from the worker thread:
Message message = mHandler.obtainMessage(command, parameter);
message.sendToTarget();
}
Other options:
You could use Activity.runOnUiThread(). Straightforward if you have an Activity:
#WorkerThread
void workerThread() {
myActivity.runOnUiThread(() -> {
// This is where your UI code goes.
}
}
You could also post to the main looper. This works great if all you have is a Context.
#WorkerThread
void workerThread() {
ContextCompat.getMainExecutor(context).execute(() -> {
// This is where your UI code goes.
}
}
Deprecated:
You could use an AsyncTask, that works well for most things running in the background. It has hooks that you can call to indicate the progress, and when it's done.
It's convenient, but can leak contexts if not used correctly. It's been officially deprecated, and you shouldn't use it anymore.
UPDATE - 2016
The best alternative is to use RxAndroid (specific bindings for RxJava) for the P in MVP to take charge fo data.
Start by returning Observable from your existing method.
private Observable<PojoObject> getObservableItems() {
return Observable.create(subscriber -> {
for (PojoObject pojoObject: pojoObjects) {
subscriber.onNext(pojoObject);
}
subscriber.onCompleted();
});
}
Use this Observable like this -
getObservableItems().
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<PojoObject> () {
#Override
public void onCompleted() {
// Print Toast on completion
}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(PojoObject pojoObject) {
// Show Progress
}
});
}
----------------------------------------------------------------------------------------------------------------------------------
I know I am a little late but here goes.
Android basically works on two thread types namely UI thread and background thread. According to android documentation -
Do not access the Android UI toolkit from outside the UI thread to fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Now there are various methods to solve this problem.
I will explain it by code sample:
runOnUiThread
new Thread()
{
public void run()
{
myactivity.this.runOnUiThread(new Runnable()
{
public void run()
{
//Do your UI operations like dialog opening or Toast here
}
});
}
}.start();
LOOPER
Class used to run a message loop for a thread. Threads by default do
not have a message loop associated with them; to create one, call
prepare() in the thread that is to run the loop, and then loop() to
have it process messages until the loop is stopped.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
AsyncTask
AsyncTask allows you to perform asynchronous work on your user
interface. It performs the blocking operations in a worker thread and
then publishes the results on the UI thread, without requiring you to
handle threads and/or handlers yourself.
public void onClick(View v) {
new CustomTask().execute((Void[])null);
}
private class CustomTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... param) {
//Do some work
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
}
Handler
A Handler allows you to send and process Message and Runnable objects
associated with a thread's MessageQueue.
Message msg = new Message();
new Thread()
{
public void run()
{
msg.arg1=1;
handler.sendMessage(msg);
}
}.start();
Handler handler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if(msg.arg1==1)
{
//Print Toast or open dialog
}
return false;
}
});
Toast.makeText() can only be called from Main/UI thread. Looper.getMainLooper() helps you to achieve it:
JAVA
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// write your code here
}
});
KOTLIN
Handler(Looper.getMainLooper()).post {
// write your code here
}
An advantage of this method is that you can run UI code without Activity or Context.
Try this, when you see runtimeException due to Looper not prepared before handler.
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Run your task here
}
}, 1000 );
I ran into the same problem, and here is how I fixed it:
private final class UIHandler extends Handler
{
public static final int DISPLAY_UI_TOAST = 0;
public static final int DISPLAY_UI_DIALOG = 1;
public UIHandler(Looper looper)
{
super(looper);
}
#Override
public void handleMessage(Message msg)
{
switch(msg.what)
{
case UIHandler.DISPLAY_UI_TOAST:
{
Context context = getApplicationContext();
Toast t = Toast.makeText(context, (String)msg.obj, Toast.LENGTH_LONG);
t.show();
}
case UIHandler.DISPLAY_UI_DIALOG:
//TBD
default:
break;
}
}
}
protected void handleUIRequest(String message)
{
Message msg = uiHandler.obtainMessage(UIHandler.DISPLAY_UI_TOAST);
msg.obj = message;
uiHandler.sendMessage(msg);
}
To create the UIHandler, you'll need to perform the following:
HandlerThread uiThread = new HandlerThread("UIHandler");
uiThread.start();
uiHandler = new UIHandler((HandlerThread) uiThread.getLooper());
Hope this helps.
Reason for an error:
Worker threads are meant for doing background tasks and you can't show anything on UI within a worker thread unless you call method like runOnUiThread. If you try to show anything on UI thread without calling runOnUiThread, there will be a java.lang.RuntimeException.
So, if you are in an activity but calling Toast.makeText() from worker thread, do this:
runOnUiThread(new Runnable()
{
public void run()
{
Toast toast = Toast.makeText(getApplicationContext(), "Something", Toast.LENGTH_SHORT).show();
}
});
The above code ensures that you are showing the Toast message in a UI thread since you are calling it inside runOnUiThread method. So no more java.lang.RuntimeException.
that's what i did.
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast(...);
}
});
Visual components are "locked" to changes from outside threads.
So, since the toast shows stuff on the main screen that is managed by the main thread, you need to run this code on that thread.
Hope that helps:)
I was getting this error until I did the following.
public void somethingHappened(final Context context)
{
Handler handler = new Handler(Looper.getMainLooper());
handler.post(
new Runnable()
{
#Override
public void run()
{
Toast.makeText(context, "Something happened.", Toast.LENGTH_SHORT).show();
}
}
);
}
And made this into a singleton class:
public enum Toaster {
INSTANCE;
private final Handler handler = new Handler(Looper.getMainLooper());
public void postMessage(final String message) {
handler.post(
new Runnable() {
#Override
public void run() {
Toast.makeText(ApplicationHolder.INSTANCE.getCustomApplication(), message, Toast.LENGTH_SHORT)
.show();
}
}
);
}
}
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
}
});
Wonderful Kotlin solution:
runOnUiThread {
// Add your ui thread code here
}
first call Looper.prepare() and then call Toast.makeText().show() last call Looper.loop() like:
Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()
This is because Toast.makeText() is calling from a worker thread. It should be call from main UI thread like this
runOnUiThread(new Runnable() {
public void run() {
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
}
});
The answer by ChicoBird worked for me. The only change I made was in the creation of the UIHandler where I had to do
HandlerThread uiThread = new HandlerThread("UIHandler");
Eclipse refused to accept anything else. Makes sense I suppose.
Also the uiHandler is clearly a class global defined somewhere. I still don't claim to understand how Android is doing this and what is going on but I am glad it works. Now I will proceed to study it and see if I can understand what Android is doing and why one has to go through all these hoops and loops. Thanks for the help ChicoBird.
For Rxjava and RxAndroid User:
public static void shortToast(String msg) {
Observable.just(msg)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(message -> {
Toast.makeText(App.getInstance(), message, Toast.LENGTH_SHORT).show();
});
}
Coroutine will do it perfectly
CoroutineScope(Job() + Dispatchers.Main).launch {
Toast.makeText(context, "yourmessage",Toast.LENGTH_LONG).show()}
I was running into the same issue when my callbacks would try to show a dialog.
I solved it with dedicated methods in the Activity - at the Activity instance member level - that use runOnUiThread(..)
public void showAuthProgressDialog() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mAuthProgressDialog = DialogUtil.getVisibleProgressDialog(SignInActivity.this, "Loading ...");
}
});
}
public void dismissAuthProgressDialog() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (mAuthProgressDialog == null || ! mAuthProgressDialog.isShowing()) {
return;
}
mAuthProgressDialog.dismiss();
}
});
}
Java 8
new Handler(Looper.getMainLooper()).post(() -> {
// Work in the UI thread
});
Kotlin
Handler(Looper.getMainLooper()).post{
// Work in the UI thread
}
GL
Handler handler2;
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());
Now handler2 will use a different Thread to handle the messages than the main Thread.
To display a dialog or a toaster in a thread, the most concise way is to use the Activity object.
For example:
new Thread(new Runnable() {
#Override
public void run() {
myActivity.runOnUiThread(new Runnable() {
public void run() {
myActivity.this.processingWaitDialog = new ProgressDialog(myActivity.this.getContext());
myActivity.this.processingWaitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
myActivity.this.processingWaitDialog.setMessage("abc");
myActivity.this.processingWaitDialog.setIndeterminate(true);
myActivity.this.processingWaitDialog.show();
}
});
expenseClassify.serverPost(
new AsyncOperationCallback() {
public void operationCompleted(Object sender) {
myActivity.runOnUiThread(new Runnable() {
public void run() {
if (myActivity.this.processingWaitDialog != null
&& myActivity.this.processingWaitDialog.isShowing()) {
myActivity.this.processingWaitDialog.dismiss();
myActivity.this.processingWaitDialog = null;
}
}
}); // .runOnUiThread(new Runnable()
...
Using lambda:
activity.runOnUiThread(() -> Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show());
Toast, AlertDialogs needs to run on UI thread, you can use Asynctask to use them properly in android development.but some cases we need to customize the time outs, so we use Threads, but in threads we cannot use Toast,Alertdialogs like we using in AsyncTask.So we need separate Handler for popup those.
public void onSigned() {
Thread thread = new Thread(){
#Override
public void run() {
try{
sleep(3000);
Message message = new Message();
message.what = 2;
handler.sendMessage(message);
} catch (Exception e){
e.printStackTrace();
}
}
};
thread.start();
}
in Above example i want to sleep my thread in 3sec and after i want to show a Toast message,for that in your mainthread implement handler.
handler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what){
case 1:
Toast.makeText(getActivity(),"cool",Toast.LENGTH_SHORT).show();
break;
}
super.handleMessage(msg);
}
};
I used switch case here, because if you need to show different message in same way, you can use switch case within Handler class...hope this will help you
This usually happens when something on the main thread is called from any background thread. Lets look at an example , for instance.
private class MyTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
textView.setText("Any Text");
return null;
}
}
In the above example , we are setting text on the textview which is in the main UI thread from doInBackground() method , which operates only on a worker thread.
I had the same problem and I fixed it simply by putting the Toast in onPostExecute() override function of the Asynctask<> and it worked.
You need to create toast on UI thread. Find the example below.
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, "YOUR_MESSAGE", Toast.LENGTH_SHORT).show();
}
});
For displaying Toast message please refer to this article
Here is the solution for Kotlin using Coroutine:
Extend your class with CoroutineScope by MainScope():
class BootstrapActivity : CoroutineScope by MainScope() {}
Then simply do this:
launch {
// whatever you want to do in the main thread
}
Don't forget to add the dependencies for coroutine:
org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}
org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinCoroutines}
Create Handler outside the Thread
final Handler handler = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
try{
handler.post(new Runnable() {
#Override
public void run() {
showAlertDialog(p.getProviderName(), Token, p.getProviderId(), Amount);
}
});
}
}
catch (Exception e){
Log.d("ProvidersNullExp", e.getMessage());
}
}
}).start();
Recently, I encounter this problem - It was happening because I was trying to call a function that was to do some UI stuff from the constructor. Removing the initialization from the constructor solved the problem for me.
I got the same problem and this code is working fine for me now.
As an example this is my code to do a task in the background and UI thread.
Observe how the looper is used:
new Thread(new Runnable() {
#Override
public void run() {
Looper.prepare();
// your Background Task here
runOnUiThread(new Runnable() {
#Override
public void run() {
// update your UI here
Looper.loop();
}
});
}
}).start();
i use the following code to show message from non main thread "context",
#FunctionalInterface
public interface IShowMessage {
Context getContext();
default void showMessage(String message) {
final Thread mThread = new Thread() {
#Override
public void run() {
try {
Looper.prepare();
Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
Looper.loop();
} catch (Exception error) {
error.printStackTrace();
Log.e("IShowMessage", error.getMessage());
}
}
};
mThread.start();
}
}
then use as the following:
class myClass implements IShowMessage{
showMessage("your message!");
#Override
public Context getContext() {
return getApplicationContext();
}
}

Android - Setting a Timeout for an AsyncTask?

I have an AsyncTask class that I execute that downloads a big list of data from a website.
In the case that the end user has a very slow or spotty data connection at the time of use, I'd like to make the AsyncTask timeout after a period of time. My first approach to this is like so:
MyDownloader downloader = new MyDownloader();
downloader.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
#Override
public void run() {
if ( downloader.getStatus() == AsyncTask.Status.RUNNING )
downloader.cancel(true);
}
}, 30000 );
After starting the AsyncTask, a new handler is started that will cancel the AsyncTask after 30 seconds if it's still running.
Is this a good approach? Or is there something built into AsyncTask that is better suited for this purpose?
Yes, there is AsyncTask.get()
myDownloader.get(30000, TimeUnit.MILLISECONDS);
Note that by calling this in main thread (AKA. UI thread) will block execution, You probably need call it in a separate thread.
Use CountDownTimer Class in side the extended class for AsyncTask in the onPreExecute() method:
Main advantage, the Async monitoring done internally in the class.
public class YouExtendedClass extends AsyncTask<String,Integer,String> {
...
public YouExtendedClass asyncObject; // as CountDownTimer has similar method -> to prevent shadowing
...
#Override
protected void onPreExecute() {
asyncObject = this;
new CountDownTimer(7000, 7000) {
public void onTick(long millisUntilFinished) {
// You can monitor the progress here as well by changing the onTick() time
}
public void onFinish() {
// stop async task if not in progress
if (asyncObject.getStatus() == AsyncTask.Status.RUNNING) {
asyncObject.cancel(false);
// Add any specific task you wish to do as your extended class variable works here as well.
}
}
}.start();
...
change CountDownTimer(7000, 7000) -> CountDownTimer(7000, 1000) for example and it will call onTick() 6 times before calling onFinish(). This is good if you want to add some monitoring.
Thanks for all the good advice I got in this page :-)
In the case, your downloader is based upon an for an URL connection, you have a number of parameters that could help you to define a timeout without complex code:
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
urlc.setConnectTimeout(15000);
urlc.setReadTimeout(15000);
If you just bring this code into your async task, it is ok.
'Read Timeout' is to test a bad network all along the transfer.
'Connection Timeout' is only called at the beginning to test if the server is up or not.
I don't think there's anything like that built into AsyncTask. Your approach seems to be a good one. Just be sure to periodically check the value of isCancelled() in your AsyncTask's doInBackground method to end this method once the UI thread cancels it.
If you want to avoid using the handler for some reason, you could check System.currentTimeMillis periodically within your AsyncTask and exit on timeout, although I like your solution better since it can actually interrupt the thread.
Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
//async task
final RunTask tsk = new RunTask ();
tsk.execute();
//setting timeout thread for async task
Thread thread1 = new Thread(){
public void run(){
try {
tsk.get(30000, TimeUnit.MILLISECONDS); //set time in milisecond(in this timeout is 30 seconds
} catch (Exception e) {
tsk.cancel(true);
((Activity) mContext).runOnUiThread(new Runnable()
{
#SuppressLint("ShowToast")
public void run()
{
Toast.makeText(mContext, "Time Out.", Toast.LENGTH_LONG).show();
finish(); //will close the current activity comment if you don't want to close current activity.
}
});
}
}
};
thread1.start();
}
You can put one more condition to make cancellation more robust. e.g.,
if (downloader.getStatus() == AsyncTask.Status.RUNNING || downloader.getStatus() == AsyncTask.Status.PENDING)
downloader.cancel(true);
Inspiring from question I have written a method which do some background task via AsyncTask and if processing takes more then LOADING_TIMEOUT then an alert dialogue to retry will appear.
public void loadData()
{
final Load loadUserList=new Load();
loadUserList.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (loadUserList.getStatus() == AsyncTask.Status.RUNNING) {
loadUserList.cancel(true);
pDialog.cancel();
new AlertDialog.Builder(UserList.this)
.setTitle("Error..!")
.setMessage("Sorry you dont have proper net connectivity..!\nCheck your internet settings or retry.")
.setCancelable(false)
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
loadData();
}
})
.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
System.exit(0);
}
})
.show();
}
}
}, LOADING_TIMEOUT);
return;
}

Categories