java - Can I call a Async task inside a runnable postdelayed? - android

I have done a counter, who handler a limit time for my loading.
Its something like: If the process spend 10s, and my loading minimum time is 15s, this counter will execute a postdelayed with 5s.
But something is wrong, cause my app is crashing.
Here is what Ive done so far, in resume:
new MyHandler(secondsLeft, new LoadingFinishedCallBack() {
#Override
public void timeFinished() {
//CALLING A ASYNC TASK HERE
}).execute();
class MyHandler {
public void execute(){
runnable = new Runnable() {
#Override
public void run() {
handler.removeCallbacks(runnable);
callback.onTimeLeftFinished();
}
};
handler.postDelayed(runnable,timeLeft);
}
}
But for some reason, my app is crashing when doInBackground is calling, inside the async task.
I dont know why, cuz the error log doesnt show anything.
Can you guys help me pls

Related

runOnUiThread() no executing when using Thread.sleep()

I'm using code that looks like this :
_thread = new Thread(){
#Override
public void run() {
try {
while (true) {
operate();
Thread.sleep(DELAY);
}
} catch (InterruptedException e) {
// Doesn't matters...
}
}
};
operate function looks like this :
// does things....
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen
}
});
// does other things...
At the bottom line, what i wanted to achieve is an operation that happens once in a while, without interrupting the main thread and the UI, something like a game-loop.
In the first 2 times that operate() runs, it adds the ImageView and everything is alright, but after 2 or 3 times it stops adding the ImageViews, but the UI is still running as usual. When i debugged the problem, i found out that after 3 times the run() method of the Runnable isn't called anymore, even thought the operate function was called.
The wired thing (for me) was that when i removed the Thread.sleep, everything worked fine (much faster of course...). I tried to replace it with a very long for loop (just for checking) and it worked, but of course it is not an appropriate solution to the problem.
I read about the problem, most of the people that asked this question did a thread.sleep or an infinite loop on the main thread, but, as i see it, i didn't do such thing. Many people wrote that you should replace the Thread.sleep with Handler.postDelayed. I tried to do it but it didn't work, maybe I did it wrong. I even tried replacing the runOnUiThread with other options I found on the internet, but all of them gave me the same exact results. I tried to replace the method that I'm adding the view to the activity, but all of them, again, gave the same result.
The waiting is crucial for this application. I got to find a way to wait sometime and then execute a function on the UI thread, cause this pattern returns at least a couple of times in my application.
It sounds like you want a post delay so that you can do the code on the UI thread after some delay. Handler Post Delay.
private static final int DELAY = 500;
private Handler mHandler;
private Runnable mRunnable;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
}
private void start()
{
mHandler = new Handler();
mRunnable = new MyRunnable(this);
mHandler.postDelayed(mRunnable, DELAY);
}
private void stop()
{
mHandler.removeCallbacks(mRunnable);
}
private void doSomething()
{
// Do your stuff here.
// Reschedule.
mHandler.postDelayed(mRunnable, DELAY);
}
Recommended way of creating a Runnable.
private static class MyRunnable implements Runnable
{
private WeakReference<MainActivity> mRef;
// In here you can pass any object that you need.
MyRunnable(MainActivity activity)
{
mRef = new WeakReference<MainActivity>(activity);
}
#Override
public void run()
{
// Safety check to avoid leaking.
MainActivity activity = mRef.get();
if(activity == null)
{
return;
}
// Do something here.
activity.doSomething();
}
}
There could be several reasons why the UI Runnable isn't being executed. Probably the activity variable has something messed up with it or it's referencing the context incorrectly, or as you said the Thread.sleep() could be causing an issue. At this point more parts of the code needs to viewed to better solve the problem.
A better way of implementing your logic is to use a scheduled Timer instead of using an infinite loop with a Thread.sleep() in it. It will execute the code within a background thread. And then use a Handler to update the UI instead of activity.runOnUiThread(). Here's an example:
// Global variable within the activity
private Handler handler;
// Activity's onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
handler = new Handler(getMainLooper());
Timer timer = new Timer("ScheduledTask");
// Timer must be started on the Main UI thread as such.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
operate();
}
}, 0L, DELAY);
}
private void operate() {
// does things in background....
handler.post(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen from within the Main UI thread
}
});
// does other things in the background...
}

Can Runnable be called immediately?

Is it possible to call Runnable without execute-ing it?
AsyncTask.execute(new Runnable() {
#Override
public void run() {
//TODO your background code
}
});
In iOS a closure can be executed, or just called like a method. In first case there is a small delay, other code chunk can come first to the loop, in second case right at the time when call is done code is Runnable is performed. How is it with Android?
Runnable runnable = new Runnable() {...}
runnable.run();
and if you need to be able to decide where to execute it depending on your current thread: UI or non-UI, you can do something like this
if (Looper.getMainLooper().isCurrentThread()) { ... } else { ... }
Some useful information can be found here Runnable

Running progress bar while another task is being run android

I have an activity class where when a button is clicked i calls a function which updates some data. Below is the function
"startDIImport"
This function internally uses Thread.sleep(10000) to wait for another task to be completed.
Hence while this function is in progress i am planning to show a progress bar.
"showProgress" is the function which shows the progress bar.
I have written below code to show progress bar while my task runs in oncreate function
new Thread(new Runnable() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
showProgress();
}
});
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
startDIImport();
}
}).start();
But my progressbar hangs when i click the button. Can you please help in resolving this issue.
I have checked AsyncTask but came to know that this has to be used only if the background task runs for few seconds. My background task may run to few hours.
Can you please let me know the best solution to handle this.
Use this code to run code on the ui thread. Also, you should never Thread.sleep(), use a proper callback mechanism instead. Show more code, it's hard to see what you're trying to do.
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
showProgress();
}
});

How can I run my code after Activity is made visible?

I have an Activity with 3 spinners. These spinners get their data from a web-service by a method that takes about 1 minute to be completed.
I want to load the Activity first and after it is made visible, call that web-service method and load data. I have tested the following codes separately but none of them solved my problem. In these samples application goes into a black screen and when the web-service operation completed, it is made visible.
#Override
protected void onCreate() {
//.........
final Runnable r = new Runnable()
{
public void run()
{
loadMyData();
}
};
Utilities.performOnBackgroundThread(r);
}
#Override
protected void onResume() {
new Thread() {
#Override
public void run() {
loadMyData();
}
}.start();
super.onResume();
}
#Override
protected void onStart() {
if (comesFromOnCreateMethod)
{
final Runnable r = new Runnable()
{
public void run()
{
loadMyData();
}
};
Utilities.performOnBackgroundThread(r);
}
comesFromOnCreateMethod = false;
super.onStart();
}
#Override
protected void onResume() {
if (comesFromOnCreateMethod)
{
final Runnable r = new Runnable()
{
public void run()
{
loadMyData();
}
};
Utilities.performOnBackgroundThread(r);
}
comesFromOnCreateMethod = false;
}
If you are getting a black screen, then I would assume your code is being run on the UI thread and not on the background, causing the UI to hang until the work is completed.
One of the best solutions to doing background work is an AsyncTask. Using this, you can call it in your onCreate() method, and when its done, it will post a callback to the UI thread for you in which you can display you data.
If you want this method to run everytime this Activity displays, then call it in onResume(). Otherwise, call it in onCreate().
In your onCreate, make the async tasks as the others have advised. Make sure you generate the content for the app first and then call the asyncTasks. You can control your spinners from the callback.
First of all, you might want to increase your accept rate, 39% is pretty low.
Anyway, you might want to check AsyncTask, it should do the thing. http://developer.android.com/reference/android/os/AsyncTask.html
Typically, you will want to initialize in onPreExecute, do the networking in the doInBackGround, and set the result to the UI thread on the OnPostExecute. Hope this will help.
Use AssynchTask() and you should call super.onResume() or any lifecycle method in respective life cycle method first then other specific method you want to do....

Android unit tests with multiple threads

I have a problem with unit tests in Android.
My object MyObject has a method start() like this :
public void start() {
final Handler onStartHandler = new Handler();
new Thread() {
#Override
public void run() {
super.run();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
}.start();
}
And I want to test that onStart() is called.
So I tried something like that :
public void testOnStartIsCalled() {
assertFalse("onStart() should not be called", mMyObject.isRunning());
mMyObject.start();
assertTrue("onStart() should be called", mMyObject.isRunning());
mMyObject.stop();
assertFalse("onStop() should be called", mMyObject.isRunning());
}
But it doesn't work, I guess it's because it's in a Handler and a new Thread.
My test class extends AndroidTestCase.
What should I do ? What is the best practice for this case ?
Regards.
When I deal with testing some multi-threaded code I try to let the program take as much of its natural flow as possible. Additionally, I avoid the use of sleep statements since you don't get any guarantees that the sleep interval you've chosen is enough to allow the subject of your test to finish what it's doing; you often end up having to choose sleep intervals that are too large and it forces a much slower execution of your test cases.
I would recommend that you try to add some code into the class you're testing, in this case MyObject, which call a listener whenever something happens. It seems that you already have callback methods for onStart() and onStop()(if those are events/callbacks), so those should be getting invoked and you should use them to control the flow of your test. When you get an onStart() event, you should then call stop() and wait for an onStop() event.
Update
First and foremost, you have redundant code:
public void start() {
final Handler onStartHandler = new Handler();
new Thread() {
#Override
public void run() {
super.run();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
}.start();
}
Either start a new thread to call onStart() or schedule the runnable on the Handler's thread queue.
Version 1- remove the handler and just let the code be executed in a new thread:
public void start() {
new Thread() {
#Override
public void run() {
super.run();
mIsRunning = true;
onStart();
}
}.start();
}
Version 2- only use the handler to asynchronously execute the callback:
public void start() {
final Handler onStartHandler = new Handler();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
And second: I noticed is that if you don't have a Looper, then whatever you post with the Handler will be ignored (thus it will never be called). For more information on the Looper-Handler pattern see the article: Android Guts: Intro to Loopers and Handlers. The Looper and the Handler are supposed to be attached to the same thread (usually the main thread). Additionally, if you're creating the Handler on a separate thread as your Looper, then you'll run into the same problem: anything you post with the Handler will be ignored.
Here are a few more good questions and articles on loopers and handlers:
Just do IT: looper and handler in android
Handler-Looper implementation in Android
The relationships between Looper, Handler and MessageQueue is shown below:
The problem here is that you are calling onStart() which invokes a new thread, and then immediately ask if it is started. There is startup time for the new thread and while that is happening, your test is asking if it is started -- it's not YET.
I bet if you waited by using Thread.sleep(), or a loop, you'd find it is started "eventually".
What is it you're actually trying to test?
If you need the new thread, you might want to read up on threads, synchronize, etc.
http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html

Categories

Resources