I am getting an Android error, even though the error message is quite obvious, i can't figure out how to make it work properly.
The error message is:
java.lang.IllegalStateException: Must be called from main thread
at android.app.Activity.recreate(Activity.java:4193)
In my app, a notification is sent to log out a user (when his token expires).
On older Android versions i am having no problems to do so, however from SDK 11 and up, i have to use the recreate() method. I get the error that it has to be called from the Main thread.
I moved the recreate() statement to the MainActivity class, this doesn't work when i call the method from the IntentService. I still get the same error. The messaging part is working just fine, just the handling of the logout message is resulting in this error.
Here are some snippets:
inside GcmIntentService.java
if (logout!=null) {
VarControl.ma.logout();
}
inside MainActivity.java
public void logout() {
deleteToken();
closeWebView();
restartApp();
}
public void restartApp() {
if (Build.VERSION.SDK_INT >= 11) {
this.recreate(); // THE ERROR OCCURS HERE
}
else{
//left out this part because its not relevant
}
}
How can i call recreate from the Main thread (but the code has to be handled on receiving the intent) ??
If you want to run sthg on the main thread you can still do :
public void restartApp() {
if (Build.VERSION.SDK_INT >= 11) {
runOnUiThread(new Runnable() {
#Override
public void run() {
recreate();
}
});
}
else{
//left out this part because its not relevant
}
}
Make a List that is accessible by both threads. Secure it using synchronized or anything like that. Whenever the Intent is received add a Runnable with the fitting code to the List.
In the main thread check if the list is non-empty on a regular basis. If it's not empty, pop the first Runnable from the list and run it. Do that until the List is empty.
That is generally a good way to handle passing code from one thread to the other.
Related
My scenario is an onCreate() activity method which executes the following code (simplified):
dialog.show(); //loading wheel
try {
remote.sendRequest(myData, new MyHandler());
}
catch (Exception e) {
dialog.dismiss();
//log and react
}
class MyHandler extends SDKSpecificCompiledHandler {
#Override
public void failure() {
dialog.dismiss();
//do stuff
}
#override
public void success() {
dialog.dismiss();
//do stuff
}
}
//I have read-only access to this handler!
public abstract class SDKSpecificCompiledHandler {
public abstract void success(JSONObject successData);
public abstract void failure(JSONObject errorData);
}
Explanation: A remote service is called passing an handler that gets called when he's done. A loading wheel (dialog) is shown to the user until a success, failure or exception happens.
The problem is when the service gets successfully called but no response ever comes. In that case dialog.dismiss() doesn't get called and the loading wheel keeps spinning for ever.
What I need is a sort of timeout which dismisses the dialog (and possibly takes other actions) after some seconds if the server doesn't get back.
My first though would be to create a new thread for the service call, and right after the launch set a timer which dismisses the dialog.
Would it be a good idea?
Thank you,
EDIT:
The service is third-party/not editable. I'm using a pre-compiled artifact.
Still not really sure what you're trying to achieve but if you want to run some code after some time on main thread (i.e. your code will do stuff to the UI), you can use a android.os.Handler
mHandler = new Handler(getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
// do stuff on UI thread
}
},10000);
When your call returned from the server, simply cancel the messages on the queue:
mHandler.removeCallbacksAndMessages(null);
It is better to use time out in service call itself, You can set the time out with service , If you need know how to set the time out then I should know what kind of service you are using ?
One more thing is that if you are using a loader you should make that loader in such a way that it can be cancel by the client.
Hey guys I am having an issue that a method I am trying to run every thirty seconds is causing my toggle button to crash. My goal is to send data to a database based on the button click, and while the toggle button is on to continue sending that data through a method every thirty seconds. When I click the button, I get the Unfortunately error and the app crashes.
To save the length of this post, this button to send the data only one time works fine:
uploadOnce.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view) {
try {
SendCode(socket);
}catch(Exception e){
String stackTrace = Log.getStackTraceString(e);
sendEmail(stackTrace);
}
}
});
Notice that the above button click uses the SendCode method and it works correctly.
The error that I am having is using that same method with a timer on it, like so:
This is the toggle button onClick:
toggle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
if (toggle.isChecked()) {
makeToast("On");
sendForever();
}
}catch (Exception e){
String stackTrace = Log.getStackTraceString(e);
sendEmail(stackTrace);
}
}
});
If I take the sendForever() method out, the toggle button works fine as expected.
This is the sendForever method in the toggle button:
public void sendForever(){
if(toggle.isChecked()) {
while (toggle.isChecked()) {
try {
new Handler().postDelayed(new Runnable() {
// Runs a timer to send code to database every 30 seconds
#Override
public void run() {
try {
SendCode(socket);
} catch (Exception e2) {
String stackTrace = Log.getStackTraceString(e2);
sendEmail(stackTrace);
}
}
}, 30000);
} catch (Exception e) {
String stackTrace = Log.getStackTraceString(e);
sendEmail(stackTrace);
}
sendForever();
}
}
}
So the goal is that when this method is called, it checks to see if the toggle button is in the "ON" state, then while it is on it will run the SendCode method (which works fine in the button to send it only once) then wait 30 seconds. After the 30 seconds is over I am going to call the method again until the toggle button is hit again and breaks the loop.
My problem I am having is that I am using this on an OBD2 sensor in my car, and it is not hitting the sendEmail methods to shoot me a stacktrace of the error, so I am not able to post the stacktrace as of now.
If anybody has any advice on what is going wrong or what I can look at to fix this, it would be greatly appreciated. Once again, sorry for not being able to put the stacktrace up right now, I will edit the post if I am able to acquire it.
You call sendForever() from the method itself (in a loop, even). That will result in a stack overflow as the recursion is limited only by the toggle condition. Just remove the recursive call, since you already have the loop handling the repetition (well, that would solve the stack overflow, but see next paragraph for further issues).
Note also that you have a blocking task running in the UI thread. Since you block the UI thread, the toolkit will have no chance to make the button not toggled anymore, essentially locking up your application. Simplest is probably checking the condition after the previous sending is done, and schedule a new one if needed.
A side note: It's needless to do an if (toggle.isChecked()) check in sendForever() since you have the same condition in the while loop.
try to dump the stack trace on file system instead of email... may be your whole application is crashing
or may be you should post the code of SendCode(socket) that function might be locking some resources that are not released in 30 sec i.e. before the next function call is made
How do I within Android, if a function is called which is already running, cancel/stop that function then start it from the beginning?
My code looks like the below, basically throughout my application a given function( Important(); ) is called but rather than have that function run several times or prevent users from running the function again if presently running. I'd like to Cancel/stop the currently running function, then start it fresh?
How can I go about doing this?
Code:
public void Important() {
//Do lots of stuff
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Important();
}
#Override
protected void onResume() {
super.onResume();
Important();
}
I've already attempted the below making use of booleans. But it just stops a user from running the function again if already running? (which isn't what I'm trying to do)
I want, when the function is called again. The running function is stopped. Then freshly executed
Code:
Boolean runningCode = false;
public void Important() {
if (runningCode== false) {
runningCode = true;
//Do lots of stuff
runningCode = false;
}
F.Y.I
Important() is called many times throughout my application (in addition to the above), sometimes whilst Important() is still running. Hence the need for a solution to my question :-)
Tasks like resetting variables and formatting the page are carried out within Important();, it is not running on a separate thread. These are tasks that need to be completed on the main UI thread
You can use a Handler on the UI thread and post messages or Runnables to it. Make every discrete action in the Important() function be a message or Runnable containing a function (e.g., resetVariables(), formatThePage(), etc). Whenever you call the function again, just call removeCallbacksAndMessages on the Handler and it will drop all actions that have not yet been executed.
Well, I don't understand why you want to do something like this?
There is not one simple answer to that. Probably working solution would be something like this
public void Important() {
if (runningCode == false) {
runningCode = true;
//Do lots of stuff
runningCode = false;
} else {
// stop current code from execution
runningCode = false;
Important();
}
But you must figure your own code to stop current execution as it may result in broken state of objects etc...
I have an app that needs to pull data from a server and insert it into an SQLite database in response to user input. I thought this would be pretty simple - the code that pulls the data from the server is a fairly straightforward subclass of AsyncTask, and it works exactly as I expect it to without hanging the UI thread. I implemented callback functionality for it with a simple interface and wrapped it in a static class, so my code looks like this:
MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
#Override
public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
// do something with contents
}
}
All still good. Even if the server takes an hour to retrieve the data, the UI still runs smoothly, because the code in getFolderContents is running in the doInBackground method of an AsyncTask (which is in a separate thread from the UI). At the very end of the getFolderContents method, the onFolderContentsResponse is called and passed the list of FilesystemEntry's that was received from the server. I only really say all this so that it's hopefully clear that my problem is not in the getFolderContents method or in any of my networking code, because it doesn't ever occur there.
The problem arises when I try to insert into a database via my subclass of ContentProvider within the onFolderContentsResponse method; the UI always hangs while that code is executing, leading me to believe that despite being called from the doInBackground method of an AsyncTask, the inserts are somehow still running on the UI thread. Here's what the problematic code looks like:
MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
#Override
public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
insertContentsIntoDB(contents);
}
}
And the insertContentsIntoDB method:
void insertContentsIntoDB(final List<FilesystemEntry> contents) {
for (FilesystemEntry entry : contents) {
ContentValues values = new ContentValues();
values.put(COLUMN_1, entry.attr1);
values.put(COLUMN_2, entry.attr2);
// etc.
mContentResolver.insert(MyContentProvider.CONTENT_URI, values);
}
}
where mContentResolver has been previously set to the result of the getContentResolver() method.
I've tried putting insertContentsIntoDB in its own Thread, like so:
MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
#Override
public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
new Thread(new Runnable() {
#Override
public void run() {
insertContentsIntoDB(contents);
}
}).run();
}
}
I've also tried running each individual insert in its own thread (the insert method in MyContentProvider is synchronized, so this shouldn't cause any issues there):
void insertContentsIntoDB(final List<FilesystemEntry> contents) {
for (FilesystemEntry entry : contents) {
new Thread(new Runnable() {
#Override
public void run() {
ContentValues values = new ContentValues();
values.put(COLUMN_1, entry.attr1);
values.put(COLUMN_2, entry.attr2);
// etc.
mContentResolver.insert(MyContentProvider.CONTENT_URI, values);
}
}).run();
}
}
And just for good measure, I've also tried both of those solutions with the relevant code in the doInBackground method of another AsyncTask. Finally, I've explicitly defined MyContentProvider as living in a separate process in my AndroidManifest.xml:
<provider android:name=".MyContentProvider" android:process=":remote"/>
It runs fine, but it still seems to run in the UI thread. That's the point where I really started tearing my hair out over this, because that doesn't make any sense at all to me. No matter what I do, the UI always hangs during the inserts. Is there any way to get them not to?
Instead of calling mContentResolver.insert(), use AsyncQueryHandler and its startInsert() method. AsyncQueryHandler is designed to facilitate asynchronous ContentResolver queries.
I think your original problem may have been that you are calling the run method on your new thread (which causes execution to continue on the current thread) instead of calling the start method. I think this is what Bright Great was trying to say in his/her answer. See Difference between running and starting a thread. It's a common mistake.
Man.Relax yourself.And anything would looks better.
At first,Start a Thread is Func start not Func run,if you want to start the new Thread
not only call the func run.
new Thread(Runnable runnable).start();
Then I bet use Handler sometimes would be better than AsyncTask.
You can run the query in the doInBackground(Integer int) overridden method of the AsynTask, and update the main UI on the onPostExecute(Integer int) method.
I have a WebView in my activity, which have a JSInterface :
mWebView.addJavascriptInterface(new JSInterface(mWebView, this), "interfaceWebsite");
When I call a function in my interface, I would like to see some Views (Textview/Button) modified.
public void changeStep(int newStep){
step = newStep;
tvStep.setText("Etape "+step);
step_info = getString(R.string.step3_info);
}
}
step_info works (my options menu change), it's just a string var, but not my TextView, it throw VM aborting.
I call the function this way (where addWebsiteActivity is "this" in the code above) :
addWebsiteActivity.changeStep(step);
Is this possible to do that in a proper way ?
Thanks
Only the original thread that created a view hierarchy can touch its views
Taht's why I can call changeStep from JSInterface but I can inside my activity class... How can I do that then ?
And the solution is...
runOnUiThread(new Runnable() {
public void run() {
step = newStep;
tvStep.setText("Etape "+step);
step_info = getString(R.string.step3_info);
}
});
Like this it knows it's a code for the main thread. But it's strange Eclipse throw me VM aborting exept that the explicit error above...
changeStep method must be called inside UI thread. You can achieved this with runOnUiThread:
addWebsiteActivity.runOnUiThread(new Runnable() {
public void run() {
changeStep(step);
}
})
change step to newStep in your code