I currently have a thread/AsyncTask that I start in Activity A. Before that AsyncTask is started though, Activity A makes an object which takes in Activity A's Context so it can instantiate a NotificationCompat.Builder. The object uses this builder to make/show a notification.
This object is then passed to the AsyncTask as the AsyncTask is created and started. This allows the AsyncTask to update the object (and the notification) with its progress.
Once the AsyncTask has been started though, Activity A calls finish(), and the activity that started it, Activity B, resumes. What I would now like to happen is to have the object send/attempt to send a message to Activity B once the AsyncTask has said it is 100% complete. How can I achieve this?
PS: let me know if a picture would make the scenario more clear.
As far as I have understood your question Activity A is started from Activity B. You can start A from B via startActivityForResult then rather finishing A when AsyncTask starts you finish() A when 100% task is loaded by AsyncTask and send result to A. You can implement callback interface to send result to Activity A from AsyncTask. Now call finish() to Activity A with setResult() and pass the result in intent to Activity B.
You can use the onPostExecute() method of the AsyncTask.
private class DownloadFilesTask extends AsyncTask<Void, Void, MyObject> {
protected Void doInBackground(Void... params) {
//do whatever you need to do
}
protected void onPostExecute(MyObject myObject) {
myObject.sendMessage(activityB);
}
}
Note that onPostExecute() runs on the main UI thread, so it will be able to handle UI updates.
Typing up my own answer both because it was the result of discussion that happened in the comments of my question, and so there is a thorough overview of what I did for a solution.
I ended up going with callbacks as suggested by rusted brain. It is important to note that I am referring to the use of android.os.Handler to create a callback, not the callback pattern that is commonly used with things such as buttons. Anyway, onto the solution.
Step 1 was to set up a handler in Activity B:
messageHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
refreshList();
}
};
Whatever is in handleMessage is what will be called when a message is sent to the handler.
Step 2 is to send a android.os.Messenger object to Activity A. This can easily be done with the Intentused to start Activity A:
Intent intent = new Intent(AlbumGalleryActivity.this, CreateNewPhotoHuntActivity.class);
intent.putExtra("callbackMessenger", new Messenger(messageHandler));
startActivityForResult(intent, Constants.REQUEST_CREATE_NEW_PHOTO_HUNT);
Now, any messages sent by the android.os.Messenger will go to our android.os.Handler.
Step 3, the final step is to create a android.os.Message from the android.os.Messenger which can be sent back to the handler. In the context of my question, this is done inside of the object that controls updating the progress of the notification:
if(totalProgress >= 100) {
notificationBuilder.setContentText("Upload complete! ");
notificationBuilder.setSmallIcon(R.drawable.ic_done_black_24dp);
notificationBuilder.setProgress(0, 0, false);
notificationManager.notify(1, notificationBuilder.build());
Message msg = Message.obtain();
msg.arg1 = 1;
try {
callbackMessenger.send(msg);
}
catch (android.os.RemoteException e1) {
Log.d(Constants.UploadProgressNotificationTag, e1.toString());
}
}
So yep that's it. Feel free to comment if you have a Q or if you see a mistake in what I've posted. Also, I think using Broadcasts would also solve this issue, but that would require a bit more time/effort to implement. Thank you to everyone who suggested a solution!
Related
Under some conditions, when my app starts, it displays an AlertDialog. However, the alert never gets displayed. I discovered that if I add a delay, it works (i.e. gets displayed).
More specifically: on app startup, it executes the main activity onCreate() which under a certain condition starts a 2nd activity. In the 2nd activity, through a separate thread, it makes a check for some web server status. If the Android device doesn't have Internet connectivity, HttpURLConnection returns an error instantly and my enclosing function executes a callback to the 2nd activity. My code then uses post() to attempt to display an alert to the user (using post allows displaying the alert on the UI thread, which is required).
Apparently it tries to display the alert before any of the either activity's UI has been created. If I use postDelayed() in the 2nd activity, the problem still persists. However, if I use the following block of code in the main activity, the alert shows properly:
new Handler().postDelayed (new Runnable ()
{
#Override public void run()
{
Intent intent = new Intent (app, MyClass.class);
app.startActivityForResult (intent, requestCode);
}
}, 3000);
My solution is a hack that happens to work at the moment. I don't mind having a little delay on start-up for this particular situation but I don't want a delay that's longer than necessary or one that may sometimes fail.
What is the proper solution?
Ok, here's a workaround. First, I'll speculate that the problem is that the attempt to display the alert is happening before the looper for the UI thread has been started. Just a speculation.
To work around the problem I added a recursive post which gets called from onResume(), like this:
private boolean paused = true;
#Override public void onResume ()
{
super.onResume();
paused = false;
checkForAlert();
}
#Override public void onPause ()
{
super.onPause();
paused = true;
}
And here's the function that does the post:
private AlertInfo alertInfo = null;
private void checkForAlert()
{
if (alertInfo != null)
{
...code to build alert goes here...
alertInfo = null;
}
if (!paused)
contentView.postDelayed (new Runnable()
{
#Override public void run() { checkForAlert(); }
}, 200);
}
AlertInfo is a simple class where the thread needing the alert can put the relevant info, e.g. title, message.
So, how does this work? checkForAlert() gets called during onResume() and will continue to get called every 200ms until "paused" is false, which happens in onPause(). It's guaranteed to be recurring whenever the activity is displayed. The alert will get built and displayed if alertInfo is not null. In the secondary thread, I simply create an AlertInfo instance and then within 200ms the alert gets displayed. 200ms is short enough that most people won't notice the delay. It could be shorter but then battery use goes up.
Why did I start checkForAlert() in onResume instead of onCreate()? Simply because there's no need for it to run unless the activity is currently "on top". This also helps with battery life.
I have a Service with registered ContentObserver. When my ContentObserver detects changes it sets Service's boolean variable to true. I also have a Thread running in the service which sleeps for some time and wakes up to check that variable.
When it detects change it needs some time to process some other code and I need to show ProgressDialog during the delay. How can I do this?
You should use AsyncTask instead.
Here is the link to the library. It is fairly simple:
1) onPreExecute() = show ProgressDialog
2) doInBackground() = execute your code
3) onPostExecute() = dismiss ProgressDialog
DONE :-)
The essence of your question is that you want your service to send a message of some kind to your UI (to show a loading dialog).
There are four (or more) ways of going about this:
Intents: have your service send an intent to your activity
AIDL
Using the service object itself (as singleton)
Having your activity be a broadcast receiver
These options may seem familiar: How to have Android Service communicate with Activity
You'll have to read up on those options and take your pick.
AsyncTask is a good alternative, but still if you decided to go with threads, then in order to show the ProgressDialog on UI you will need to call runOnUiThread() method of the activity.
Let suppose you want to display the ProgressDialog in the MainActivity. Inside your Thread from Service you should have something like this:
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// Display ProgressDialog here
}
});
Thanks everyone for answers.
I solve the problem using these steps
- broadcast Intent when my variable was changed
- create BroadcastReceiver for the intent( in Activity )
- inside BroadcastReceiver's method onReceive call runOnUiThread for my activity
I know this is an old thread but I have exactly what you needed because I just implemented this from a thread here. Please read Rachit Mishra's answer further down the page talking about a ProgressBar:
Communication between Activity and Service
I have this in my service:
public void sendMessage(int state) {
Message message = Message.obtain();
switch (state) {
case 1://SHOW:
message.arg1 = 1;
break;
case 0:
message.arg1 = 0;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
Call sendMessage() with 1 or 0 to show or dismiss the ProgressDialog within your service.
And this is in my Main Activity:
private ProgressDialog progress;
public class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case 0://HIDE
progress.dismiss();
break;
case 1://SHOW
progress = ProgressDialog.show(MainActivity.this, (getResources().getString(R.string.CONNECTING) + "..."), (getResources().getString(R.string.PLEASE_WAIT) + "!")); //show a progress dialog
break;
}
}
}
The ProgressDialog cannot be shown from the service, it must be called from the activity or fragment. I hope I added all the code you need and that it works well for your needs. To be honest I'm not sure how the message handler works but it works for me! The naming is probably not the best either lol. Sorry.
What i am trying to do over here is that I want to call a webservice and based on its response i might invoke another webservice or start an activity.I am already writing the web service in a separate thread but the issue is that i am calling the activity in a worker thread,
To make myself more clear i have put my pseudo code.
if (User ID and Password present in the shared preference) THEN
Utils.checkauthorisation(API) //Web Service Call
if(respsonse is Paswordexpired)
erase password from DB
Goto (LOGIN SCREEN)//startActivity Call
else if(download of images hasnt happened today)) THEN
UTILS.DownloadImages//Web service call
if(response==connectivityorOtherError)
Toast respective Message
GOTO (GALLERY SCREEN)//startActivity Call
else if (response==confilicted Data)
Goto (CHANGES SCREEN)//startActivity Call
endif
endif
endif
I was planning to show a progress bar and do all these events in a thread like this
progressDialog = ProgressDialog.show(this, "Loading",
"Authenticating Please wait.");
new Thread() {
public void run() {
///execute the pseudo code
startActivity(intent); //Is this a good practice
Message msg = Message.obtain();
msg.what = 1;
messagHandler.sendMessage(msg);
}
}.start();
private static Handler messagHandler = new Handler() {
public void handleMessage(Message message) {
super.handleMessage(message);
switch (message.what) {
case 1:
progressDialog.dismiss();
break;
default:
break;
}
}
};
But something that disturbs me is that I have to start an activity in a worker thread here. Is this good practice? I initially thought that we could only start an activity from the UI thread. What is the process happening in the back end here(in the thread sense)? If this is not a good practice what are the other alternatives to implement my pseudocode?
P.S.:Sorry about asking this question again.I had asked the same question earlier but had failed to convey what i wanted exactly so had received answers for alternatives to starting activity on worker thread(part of my question) but didn't recieve any answer on "is this a good practice"
Thanks
I can't fully understand where you're starting the activity, but assuming it's in the "GOTO" lines of your pseudo-code, I would advise you to set the msg.what with different values for each activity you want to launch and then launch it on the handleMessage(Message).
This is assumes that the Handler is created in the UI thread. If it's not, you shouldn't dismiss the ProgressDialog as well since both things should be done from the UI thread. Create one on the UI thread and get it there.
Well, please don't ask me why, but I need to start a synchronous and blocking activity so that the program flow won't continue until it's finished. I know how to do a synchronous Dialog, but how can I do a synchronous activity?
Following are two approaches I tried but failed:
// In the 1st activity, start the 2nd activity in the usual way
startActivity(intent);
Looper.loop(); // but pause the program here
// Program continuse running afer Looper.loop() returns
....
// Then, in the second activity's onBackPressed method:
public void onBackPressed() {
// I was hoping the following quit() will terminate the loop() call in
// the first activity. But it caused an exception "Main thread not allowed
// to quit." Understandable.
new Hanlder().getLooper().quit();
}
I also tried to use another thread to achieve this:
// In the first activity, start a thread
SecondThread m2ndThread = new SecondThread(this);
Thread th = new Thread(m2ndThread, "Second Thread");
th.run();
synchronized(m2ndThread) {
m2ndThread.wait();
}
class SecondThread implements Runnable {
Activity m1stActivity;
SecondThread(Activity a) {
m1stActivity = a;
}
public void run() {
Looper.prepare();
Handler h = new Handler() {
public void handleMessage() {
Intent intent = new Intent(m1stActivity, SecondActivity.class);
m1stActivity.startActivity(intent); // !!!!!!!!!!!!!!
}
}
h.sendEmptyMessage(10); // let it run
Looper.quit();
}
}
However, this approach doesn't work because when I'm using the 1st activity to start the 2nd activity, the main thread is already in wait state and doing nothing. So the 2nd activity didn't even get created. (This is ironical: You have to use an activity to start an activity, but how can you do that when it's already in wait state?)
You can't.
That is all there is to it. You just can't do this. Activities execute on the main thread, always. Your main thread must be actively running its event loop. It is ALWAYS broken to block the main thread's event loop. Always.
All activities are asynchronous. But if you want to block the rest of your application flow until the activity finishes, you can inject a dependency on a flag that the activity sets. The most encapsulated way to do so would be to carry the flag along with the intent/activity/return flow. You could:
Declare the flag in that object's global scope:
boolean syncBlock = false;
int ASYNC_ACTIVITY_REQUEST_CODE = 1; // Arbitrary unique int
In the object's scope that starts the new activity include:
Intent asyncIntent = new Intent(this, AsyncActivity.class);
syncBlock = true;
startActivityForResult(asyncIntent, ASYNC_ACTIVITY_REQUEST_CODE);
while(syncBlock) {}
And in in the object that started the activity:
onActivityResult(int requestCode, int resultCode, Intent data)
{
switch(requestCode)
{
case ASYNC_ACTIVITY_REQUEST_CODE:
{
syncBlock = false;
break;
}
[...]
}
It's a crude hack, and if you're blocking in your UI thread (eg. in your MAIN activity) you're blocking everything, including other UI features your users will probably expect to respond but won't. It's a big nono, and you should learn to go with the async flow that makes apps work the Android way. But if you absolutely must...
I have a service which sends continously values to an activity through some custom event listeners.
Here everything works fine. Certain values are displayed in my activity as expected, but some others make the application to crash. This is because some of the incoming data is calculated inside a normal thread (that I cannot have access for changing it), and I know I have to use a handler here, but as far as I tried the app still crashing.
more graphically I would like to do the following
onValuesChanged(float val) {
myTextView.setText( Float.toString(val) )
}
where val is calculated in a normal thread, but of course it makes crash the app when doing the setText.
Any suggestions?
Use AsyncTask instead of Thread and in the onPostExecute() you can update the UI.
or use Activity.runOnUiThread(new Runnable() {
void run() {
// do something interesting.
}
});
hey u can send a custom broadcast from your service like this
Intent mintent = new Intent();
mintent.setAction("com.action");
mintent.putExtra("name", "value");
sendBroadcast(mintent);
and register a receiver in your activity which will get the value from incoming intent and then call the handler like this to update the UI ..plese parse the int to string at receiving
myTextView.post(new Runnable() {
public void run() {
myTextView.setText( Float.toString(val) )
}
});
Every time you send a broadcast to your activity and it will update the ui ..
However the above mentioned way is also right but if you have to stay with service then go for this way else above......