Just come to polishing my application and making it resume after the user has left. When the application restores I get an IllegalThreadStateException, which is quite annoying. This problem is present in the example google gives of Lunar Lander. Has anyone found a way to restore working when using surfaceView?
I believe this arises from a disparity in how the Surface and the Activity are handled. When you leave the LunarLander application the surface is destroyed (invoking surfaceDestroyed) but the Activity is only paused (invoking onPause). When the Activity is resumed the surface is created (invoking surfaceCreated) and it attempts to start the drawing thread again.
This means that creating the Thread happens with the Activity's lifecycle and destroying the thread happens with the SurfaceView's lifecycle, which do not always correspond, thus the IllegalThreadStateException. The solution would be to tie the thread to one lifecycle or the other, not both.
I think this thread proposes a possible solution, though I don't know if it works.
In my own test, I create the drawing thread in the surfaceCreated() method, and this solves the issue completely. This is my method implementation:
#Override
public void surfaceCreated(SurfaceHolder arg0) {
_thread = new DrawThread(getHolder());
_thread.setRunning(true);
_thread.start();
}
So in the code, when surfaceDestroyed() is called, it sets mRun to false and calls thread.join(). This causes the thread to complete and die. When the app is started again and surfaceCreated() is called, it calls thread.start(). This is invalid because the thread can not be started after it dies.
Two options to fix:
a) Start a new thread in surfaceCreated() - as above.
b) Or add a check in surfaceDestroyed() against Activity.isFinishing() to only end the thread if true. To do this, I surrounded the while(mRun) in the thread with another while loop that is only set to false if isFinishing() returns true.
Related
I'm making a thread the usual way and named it "Encoder". If the app closes and restarts again the object of the Thread is destroyed and recreated. Is there a way to get to know if the Thread that I started before is still running or not. ?
You're designing your app wrong.
If the Thread will carry on the work after the activity died, and a new activity should be able to "pick up where the last one left", you should be using a Service to do this work and bind your activity to it.
You can read more about it and how to implement it here: http://developer.android.com/guide/components/bound-services.html
You can use method .isAlive();
From doc:
Tests if this thread is alive. A thread is alive if it has been
started and has not yet died.
I am developing an app that contains a web view. A certain times during the app it does a call to Webview loadUrl.
Sometimes the call will come directly from an event on the UI thread and other times it comes from an event on a background worker thread. When it originates from the background thread I do a call to runOnUIThead() to ensure the actual call to loadURL happens on the UI thread.
What I am experiencing is that loadUrl() works fine when originating from the UI thread, however it fails to work when it comes from a worker thread (even though the actual call to loadUrl happens via a runnable I pass into runOnUIThread()).
Having set a break point I can see that in both instances loadUrl() is being called on the UI thread. Yet it works in one case but not the other.
I am currently sifting through the Android Webview source code to see if I can track down why sometimes it works and sometimes it doesn’t. If anyone can shed any light on the matter it would be greatly appreciated.
--- UPDATE ---
I have tried a few suggestions from this post here: WebView loadUrl works only once
Mainly doing the following before calling loadUrl:
webView.clearCache(true);
webView.loadUrl("Url");
And:
webView.clearCache(true);
webView.clearView();
webView.reload();
webView.loadUrl("about:blank");
webView.loadUrl("Url");
Unfortunately neither of them work.
In general, its not safe to create view outside of main thread.
In your particular case, this is not allowed, because WebView creates Handler() in its constructor for communication with UI thread. But since Handler's default constructor attaches itself to current thread, and current thread does not have Looper running, you're getting this exception.
You might think that creating a looper thread (that must be alive at least as long as WebView) might help you, but this actually a risky way to go. And I wouldn't recommend it.
You should stick with creating WebViews in main thread. All controls are usually optimized for fast construction, as they are almost always created in UI thread.
or You can call webview like this
runOnUiThread(new Runnable() {
#Override
public void run() {
// your webview method
}
});
Is there any way on Android to know, if the thread running my code, is the UI Thread or not ? In swing there was SwingUtilities.isEventDispatchThread() to tell me if i am on the UI Thread, or not. Is there any function in the Android SDK that lets me know this ?
Answer borrowed from here: How to check if current thread is not main thread
Looper.myLooper() == Looper.getMainLooper()
Any Android app has only one UI thread, so you could somewhere in the Activity callback like onCreate() check and store its ID and later just compare that thread's ID to the stored one.
mMainThreadId = Thread.currentThread().getId();
Anyway, you can omit checking if you want to do something on the UI thread and have any reference to Activity by using
mActivity.runOnUiThread( new Runnable() {
#Override
public void run() {
...
}
});
which is guaranteed to run on current thread, if it's UI, or queued in UI thread.
Yes, there is a way.
Check the current thread object against main lopper's thread object. Main looper is always in the UI thread.
boolean isOnUiThread = Thread.currentThread() == Looper.getMainLooper().getThread();
Hum actually due to Android architecture, all Activities run in the main thread, ie the UI thread. So when you are coding an activity, everything that is in your Activity is in the UI thread.
That is why in Honeycomb an error have been added when you are making network calls in the main thread : it totally blocks the UI.
So by default you are in fact always working in the UI thread.
Another thing : unless you explicitely ask it to be in another thread, a Service will operate on the same thread as the activities of its application.
So, what to do ?
When you have to do heavy calculation in your activity; one solution is to use an AsyncTask (a class designed to allow you to easily use another thread). The code in onExecute() is run in another thread (but be cautious postExecute runs in your main thread). Another one is to manually start a new thread when AsyncTask is not really adapted.
If you create a service that does costly background tasks, make it run in another thread with the android:process=":my_process" attribute of the manifest. You will need to create an AIDL to communicate with this separated service, but it is not a complicated task.
Many objects, like for example the MediaPlayer, have Async variations of their methods. Try to to always use them.
Put a breakpoint where you want to check and, when it gets hit, check if you can interact with your UI (ie, if the UI is not frozen). If you can't interact with the UI then you are in the UI Thread, otherwise you are in a background thread.
I have a class:
class RenderView implements Runnable {
Thread renderThread;
public void run() {
while(!Thread.currentThread().isInterrupted()) {
//does some work
}
}
//At some point in executed code, inside RenderView class (i'm sure it's executed)
renderThread = new Thread (this);
//When activity is closed (also, i'm sure this part is executed)
renderThread.interrupt();
And the renderThread really stops (at least, the run() method quits).
But for some reason, there is still some reference to renderView in my code, after i exit the activity. This is causing me a huge memory leak.
And a hprof dump tells me:
java.lang.Thread (this one is in GC Root)
has a reference to
target (mypackage.RenderView)
I have no idea why this Thread class is keeping a reference to my Thread, even though i've already finished the Thread! Any ideas?
EDIT: The renderView is referenced in, say, Activity B. So, when i exit the activity, no reference to renderThread should still be reachable. But still i tried setting renderThread = null : doesn't work. As i was able to find out via MAT Analyzer, the only thing that keeps renderView from being Garbage Collected is this weir reference from java.lang.Thread.
Why don't you just use an AsyncTask or runOnUIThread()?
I've never seen a need for what your doing. Perhaps there is one but otherwise just use AsyncTask or runOnUIThread. Don't reinvent the wheel.
Release your thread instance with
renderThread = null;
A thread instance is as any other instance, an object. Even if it finished running, you keep the reference to it, until you release it.
did you try to nullify renderThread?
Actually, renderThread.interrupt may not stop the thread, it is just setting a flag and singnals JVM that it can be interrupted.That is why may be your thread is still running.The best practice is gracefully exit from run method by just setting a flag in the while loop.
while(isRunning){
}
// at some point, say
isRunning = false;
The best practices on Android UI design can be had from the following link.
Android UI design pattern:
I'm writing a live wallpaper, and I'm forking off two separate threads in my main wallpaper service. One updates, and the other draws. I was under the impression that once you call thread.start(), it took care of everything for you, but after some trial and error, it seems that if I want my update and draw threads to keep running, I have to manually keep calling their run() methods? In other words, instead of calling start() on both threads and forgetting, I have to manually set up a delayed handler event that calls thread.run() on both the update and draw threads every 16 milliseconds. Is this the correct way of having a long running thread?
Also, to kill threads, I'm just setting them to be daemons, then nulling them out. Is this method ok? Most examples I see use some sort of join() / interrupt() in a while loop...I don't understand that one...
No
No
For #1, I believe your threads are terminating. Once the run() method is left, the thread is considered terminated. If you want the thread to run "forever", you need to repeat your actions.
For #2, the thread will continue running even if you lose all references to it. I would suggest a signal or condition to the worker thread, followed by a join() in the main thread.
Like Yann said, if you keep having to restart your thread(s), it means you are probably not looping correctly.
Say your wallpaper just has a ball moving around the screen, this would be a sample run() method:
boolean isAnimating;
public void run() {
isAnimating = true;
while(isAnimating) {
moveBall();
isAnimating = isWallpaperVisible(); // or whatever conditions apply to not keep animating
}
}
This way your run method will keep running indefinitely.