I've been experiencing this problem on some devices, especially Samsung.
I have an activity that has 7 fragments. In most of them, I start an async task for getting some data. I handle the exceptions by creating a handler in onCreateView().
handler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if (msg.what == 0) {
Toast.makeText(getActivity().getApplicationContext(), getActivity().getResources().getString(R.string.connection_timed_out), Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
When making the API call, in catch blocks, if exceptions occur, I cancel the async task and in the onCancelled() callback, I notify the handler.
new AsyncTask<Void, Void, ArrayList<UserProfile>>() {
#Override
protected ArrayList<UserProfile> doInBackground(Void... params) {
try {
return new APIService().GetUserProfiles(EnloopApplication.getCurrent().getAuthenticationToken(), thisUserProfile.getEnloop_friends());
} catch (WampNetworkException e) {
cancel(true);
e.printStackTrace();
} catch (WampApiException e) {
e.printStackTrace();
} catch (SocketTimeoutException e) {
cancel(true);
}
return null;
}
#Override
protected void onCancelled() {
super.onCancelled();
handler.sendEmptyMessage(0);
}
On most devices, it works, but on Samsung devices sometimes I get null pointer exception since getActivity() returns nothing, as if the activity was destroyed and never got created again. Problem is the activity is not destroyed in code, since only thing that is happening is switching between fragments in same activity.
getActivity() returning null is a perfectly valid scenario which you should expect as well.
This happens because by creating anonymous Handler in your onCreateView you're referencing Fragment which was already detached from Activity (therefore getActivity() returns null). Same goes for your AsyncTask - if you're creating it as an anonymous class inside Fragment then it also references same detached Fragment.
I would suggest you to revise your architecture. How? I believe, is that your UI components should not perform asynchronous operations by themselves. Instead, you should use any sort of publish/subscribe mechanism out there (there are solutions which are using LoaderManager, HandlerThread, RxJava, RoboSpice and other 3rd party libraries).
Related
I am trying to review the concept of Handler. So I simply create a Thread to update a ProgressBar and see if it would throw an exception.
Here's my code
#Override
public void onResume() {
super.onResume();
new Thread(){
#Override
public void run() {
super.run();
while (progressBar.getProgress() <100){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
progressBar.setProgress(pb.getProgress()+10);
}
}
}.start();
}
My question is simple. Why it doesn't throw any CalledFromWrongThreadException?
If it meant to be this way, should we forget about Handler when we deal with progress updating?
https://cs.android.com/android/platform/superproject/+/android-10.0.0_r30:frameworks/base/core/java/android/widget/ProgressBar.java;l=1610
Bad luck--ProgressBar has special support for updating its progress from another thread. That's why it doesn't throw something.
But because it's so specifically this operation that has this extra support, no to the second question. You wouldn't, in general, stop using a handler. For cases where, for example, you need to do one other little UI thing while updating the progress.
Is it possible that the android OS may delay or suspend my asynctask for some reason. If so what can I do to ensure that my task (e.g fetching data from database) is not delayed or suspend. Run it on a UI thread maybe but I have read that it is not a good idea as it can freeze the UI and cause lag.
Is it possible that the android OS may delay or suspend my asynctask for some reason.
It might be blocked by another instance of an AsyncTask. By default they run on a single background thread: https://developer.android.com/reference/android/os/AsyncTask.html
If so what can I do to ensure that my task (e.g fetching data from database) is not delayed or suspend.
Investigate what exactly causes the delay. Is it something inside doInBackground method or maybe delay happens before or after the background operation. It can be anything, hard to tell without debugging the code.
Run it on a UI thread maybe but I have read that it is not a good idea as it can freeze the UI and cause lag.
Although AsyncTask is not a good solution in this case, it should work fine if you implement it correctly. Other approaches to consider will be Loaders, or AsyncQueryHandler with ContentProvider. Depends on your use case.
#Override
protected void onPreExecute() {
holdWait = false;
stop = false;
}
#Override
protected Void doInBackground(Void... params) {
synchronized (this) {
while(true) {
if(Work) return null;
if(HaveWork) {
//make some
} else {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
return null;
}
public void mynotify() {
synchronized (this) {
if(holdWait) {
notify();
holdWait = false;
}
}
}
public void setStopWork() {
synchronized (this) {
stop= false;
if(holdWait) {
notify();
holdWait = false;
}
}
}
}
I'm trying to update the UI multiple times in an AsyncTask.
First of all the UI should update, if a request was accepted and later it should run the publishProgress, but If I return a value in the requestAccepted method the other acceptedFiles method will never be executed, but I want it to execute and update the UI before from this task
#Override
protected Void doInBackground(FileInformationHandler... params) {
try {
handler.createSecureSocket("192.168.3.29", 7431);
ProtocolHandler phandler = new ProtocolHandler(handler.getInputStream(), handler.getOutputStream());
phandler.sendInitialisation();
ConfirmationHandler cHandler = new ConfirmationHandler(handler.getInputStream(), handler.getOutputStream());
cHandler.addListener(new ConfirmationReceivedListener() {
#Override
public void requestAccepted(boolean b) {
// Update UI without stopping the asynctask
}
#Override
public void acceptedFiles(int[] ints) {
fileSender.addListener(new ProcessListener() {
#Override
public void processChanged(int i, long l) {
publishProgress(i);
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
First of all you don't need to use an Async task if you are anyways doing the long running operation in a separate thread and have callbacks for different states.
So in your case why can't you simply so all of this in the main thread and in the callbacks just update the UI.
But if you still want to use the Async task then since the doInBackground executes in a separate thread, you can do whatever long running operation is in the method serially and keep using the publishProgress method to update the UI whenever you want. since you are using the callback interface in your case the method will return null and the control with go into the 'onPostExecute'.
IMO using a callback interface in the doInBackground method is not the right approach and defeats the purpose of asyncTask async task.
I must use Thread in an Android project. Sometimes, it works corectly, however sometimes does not; it does not start (does not call SendToServer() method)or it starts but return to another function suddenly (return updated; line)before the thread does not finish.
Note: affected value is bigger than 0, it gives condition and it goes to if statement.
Here is the my code sample;
public static Boolean MyUpdateFunction(MyObject myobject){
Boolean updated=false;
//Code for updating local database
int affected= SqliteDb.update(....);
if(affected>0)
{
//Send updated data to server
//For this I must use Thread(I can't use AsyncThread)
updated=true;
SendToServer();
}
return updated;
}
public static void SendToServer()
{
try{
;
Thread th=new Thread(new Runnable() {
public void run() {
try {
//Create data and send it to server
//.......
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
th.start();
th.join();
}
catch(SQLException e)
{
Toast.makeText(myContext,"ERROR: "+e.getMessage(), Toast.LENGTH_LONG).show();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Other people are correct in that an AsyncTask is the way forward, but the direct problem due to what you're experiencing is this (and as such, I would recommend reading up on how Threading works):
When you start the thread, it begins a new process. The UI thread (which is generally where the majority of your code is) continues. So your code will fire the thread with SendToServer(), and then by definition will immediately return updated, as the UI thread immediately goes to the next line.
What you need is a callback from your Thread, which is handled in the onPostExecute() method of an AsyncTask. There's a good tutorial on how to use them and what they do here
Edit:
I've just seen from a comment above that you can't use Asynctasks, fair enough, but you still need a callback/event fired from your Thread to return any results
Instead of using threads and your variables (updated and affected), you can use AsyncTasks: see: http://developer.android.com/reference/android/os/AsyncTask.html
With AsyncTask, you have some methods which are doing exactly what you want:
onPreExecute
doInBackground
onPostExecute
So, what you can do is to check your condition in onPreExecute, then do your SendToServer in the doInBackground and onPostExecute do what you need.
Just wondered what's happened with IceCream Sandwich...
I had a simple AsyncTask reading data from a server. When the Disconnect button was clicked, then lServerTask.cancel(true); was called which was successfully calling onCancelled(), setting m_Running = false; thus breaking out of the while loop in protected void doInBackground(Void...params).
With my Galaxy Nexus, I click the disconnect button, but the task does not quit. And is still connected to the server. Does anyone have any ideas?
Many Thanks
Mark
Code FYI -
// automatically done on worker thread (separate from UI thread)
protected Void doInBackground(Void...params)
{
while(m_Running)
{
try
{
lRDS.readSocket();
}
catch (IOException e)
{
lRDS.disconnectFromServer();
publishProgress(e.toString());
return null;
}
lDataBuffer = lRDS.getDataBuffer();
publishProgress(lDataBuffer);
}
return null;
}
#Override
protected void onCancelled()
{
m_Running = false;
connectRDSButton.setEnabled(true);
disconnectRDSButton.setEnabled(false);
}
and from the UI side:
private class disconnectRDSButtonHandler implements View.OnClickListener
{
public void onClick(View v)
{
editRobotData.setText("Disconnect...");
if (lRobotDataServerTask.cancel(true) == true)
{
editRobotData.setText("Disconnected...");
}
}
}
Okay it seems I've found the issue. I needed to check isCancelled() in the while loop of the doInBackground() method and then call the onCancelled() method from there. Not sure why I didn't see this as a problem on android 2.2 on HTC desire.
protected Void doInBackground(Void...params)
{
while(m_Running)
{
if (isCancelled () == true)
{
onCancelled();
}
Hope this helps someone.
Mark
There are two approaches to cancel an AsyncTask:
Checking isCancelled inside your doInBackground method to try to terminate as soon as possible.
Calling cancel(true), then AsyncTask's background thread will be interrupted. For this to work, you'll need to strategically place Thread.sleep calls in your doInBackground method, and when you catch the InterruptedException, you return.
Of these two approaches, the latter is the most unreliable by far, since any call to a method catching an InterruptedException other than yours will screw the cancellation. For instance, if you call a method such as SystemClock.sleep that swallows the exception, you'll never hit your catch clause.
In both cases, long operations in between isCancelled checks or Thread.sleep calls will run to completion.
Now about your question, you were using the second approach, but there were not any Thread.sleep call, so it wont work unless some of the methods you are calling catched the Interrupted exception and throwed the IOException. Probably changes in implementation from a version to another make the difference.