Difference between PendingIntent.send(...) and Activity.startIntentSenderForResult(...) - android

There seem to be (at least) two ways to send Intents in Android:
PendingIntent.send(...)
Activity.startIntentSenderForResult(PendingIntent.getIntentSender(), ...)
Other than the fact that the latter only works starting API level 5 and that the results are passed back in a different way (via PendingIntent.OnFinished vs. Activity.onActivityResult(...)) is there any fundamental difference between the two?
I find the first one a lot more convenient as it can be entirely encapsulated inside a library without requiring the calling activity to override onActivityResult(...) to forward the result (like this: yuck!). Is it ok to still use that approach?
A quick clarification, because I've seen someone complain about this on another question: The methods above are not static methods. I wrote them that way simply for readability.

Seems like these two approaches are very different:
The start...forResult(...) methods start an intent or sub-activity in a way that allows for a result to be returned to the activity that executed the start...forResult(...). The result will be passed back to the activity's onActivityResult(...) method.
All other ways of launching intents or sub-activities (including PendingIntent.send(...)) act in a fire-and-forget-manner and don't allow for any results to be returned. The OnFinished handler is called as soon as the launch is sent, whether or not it takes a while to complete. The data passed into this handler, therefore, does not necessarily have anything to do with what you would otherwise receive via onActivityResult(...). In fact, in my case, the OnFinished handler is always called right away, before the dialog of the sub-activity even shows up, with a resultCode of Activity.RESULT_CANCELED.
What a mess...

Related

is android lifecycle methods order garanted?

I know this place is full of questions about the lifecycle of androids components.
I would like to know if the order of lifecycle events IN DIFFERENT ACTIVITIES can be garanted.
With an example is easier to understand. Lets say A and B are activies, let's suppose i do:
a.finish();
a.startActivity(new Intent(a,B.class));
Does Android scheculer garantee that A.onStop(), A.onDestroy() will be called before B.onCreate(), B.onStart()?
I know those methods are not called imediatally after the invoke of finish(); nor startActivity();, but i want to know about the order... is it possible to assure the order those methods will be called?
There is no promise of the ordering of calls between two activities. Particularly the onDestroy call. The start/stop/pause/resume have some implicit ordering based on their meaning (pause means you're no longer the foreground activity, stop means you're completely off screen, so it makes no sense for stop to be called before B's onStart, as it may otherwise be incorrect). But onDestroy can definitely be delayed until convenient for the system, there's no hard ordering to it other than after A's onStop.

Why is there no Android API for getting the current Activity?

The question, How can I get the current Activity? has been asked dozens of times on Stackoverflow and other sites and there are many proposed approaches. However, all of them have drawbacks in one form or another.
In this posting, I am assuming that there is no solution provided for this in Android's APIs, e.g., something like: Application.getTask().getRootActivity().
Wouldn't it be nice if there was :-)?
So, to be clear, I'm not asking for an answer to How can I get the current Activity?
Instead, I am asking for the reason that such a capability has not been provided. Given that each running app has a task (assuming that the task hasn't been emptied) and each such task has a root Activity, it would seem to be easy to provide access to that root Activity.
The fact that that such access is not provided, when it is so clearly desired, implies to me that there is something fundamental about the Android architecture that I don't understand.
What is it that I'm missing? Why is this information not provided by the Android APIs?
For background, here is a section summarizing some of the approaches that have been proposed. I found the following two links particularly informative (each of the approaches below is presented at one or both of the links).
Links
How to get current foreground activity context in android?
Android: How can I get the current foreground activity (from a service)?
Approaches
Static Hook
Reflection
ActivityManager
Other (Instrumentation, AccessibilityService, UsageStatsManager)`
ActivityManager
The ActivityManager approach only provides the name of the Activity class, not the current Activity instance. E.g., for a Context instance c:
c.getSystemService().getActivityManager()
.getAppTasks().get(0).getTaskInfo()
.topActivity().getClassName()
Reflection
My favorite is reflection, as proposed by _AZ, but that approach is fragile, given that it relies on internals. What I would like to see from Android is this approach provided via a standard API that developers could then safely rely on.
Static Hook
The most common approach is using a static hook to save a reference to the currently running Activity. The hook can be either per-Activity or per-Application. Memory leaks can be avoided by saving/destroying the hook's value (e.g., in onCreate()/onDestroy(), onStart()/onStop(), onPause()/onResume()). However, issues can arise when multiple Activities are involved (e.g., due to overlapping lifecycles -- see below).
I implemented a static hook approach which does the following (to be perfectly transparent, I haven't implemented #1 yet -- I am currently using a per-Activity static hook, which is a bug).
Provides a class that extends Application to provide the hook. The hook contains a Stack; each node in the stack is a simple ActivityInfo class which contains a reference to an Activity instance as well as the state of that instance (CREATED, STARTED, RESUMED).
Provides a class called ActivityTracker that extends Activity. I then extend each of my Activities with ActivityTracker. ActivityTracker uses its lifecycle callbacks to push/pop itself to/from the stack and to update its state -- my other Activities don't have to do anything.
In theory, this would allow me to always know the full state of the task's back stack -- the full set of Activities, including the root Activity, as well as their current state. In practice, however, there is a twist -- when one Activity starts another Activity, their lifecycles overlap. During that period, peeking at the stop of the stack can yield an unexpected Activity instance.
From: https://developer.android.com/guide/components/activities/activity-lifecycle.html#soafa, "Coordinating activities":
Here's the order of operations that occur when Activity A starts
Acivity B:
Activity A's onPause() method executes.
Activity B's onCreate(), onStart(), and onResume() methods execute in sequence. (Activity B now has user focus.)
Then, if Activity A is no longer visible on screen, its onStop() method executes
Of course, this could be managed also. The bottom line is that we do have a global context available for storing information (the Application) and we do have full information about Activity lifecycle transitions, so with enough effort I believe that this static stack-based approach could probably be made pretty bullet-proof.
But in the End
But in the end it feels like I am simply rewriting code which probably already exists internally for managing an Activity back stack, which is why I ask (in case you've forgotten):
Why is there no Android API for getting the current Activity?
UPDATE
In this update, I'll summarize what I've learned from this thread and my own experiments and research. Hopefully, this summary will be useful to others.
Definitions
I'm going to use the following definitions for "Activity Visibility States", based on the Activity State definitions at https://developer.android.com/guide/components/activities/activity-lifecycle.html.
-----------------------------------
Visibility State Definition
-----------------------------------
Not Visible Created+Stopped
Partially Visible Started+Paused
Fully Visible Resumed
-----------------------------------
Issues
The very definition of "Current Activity" is murky. When I use it, I mean the single Activity in the Fully Visible state. At any given instant, there may or may not be such an Activity. In particular, when Activity A starts Activity B, A's onPause() gets called and then B's onCreate(), onStart() and onResume(), followed by A's onStop(). There is a stretch between A's onPause() and B's onResume() where neither is in the Fully Visible state, so there is no Current Activity (as I define it). Of course, there are also situations where a background thread may want to access a Current Activity and there may or may not be an Activity at all, much less a Current Activity.
I've also realized that I may not always need a Current ("Fully Visible") Activity. In many cases, I may simply need a reference to an existing Activity, whether or not it is currently visible. In addition, that reference might be to just any Activity (for situations where I need to pass a generic Activity reference to some API method) or it might be to a specific Activity subclass instance (so that I can trigger some code specific to that Activity subclass).
Finally, there is the need to understand when Activity lifecycle callbacks are called by the main UI looper and how events like configuration changes are handled. For example, if I create a DialogFragment using an Activity intance which is currently in the "Not Visible" state, will it ever get displayed and, if so, when? Along similar lines, it turns out that the onDestroy() and onCreate() methods caused by a configuration change are contained in the same message in the UI's message queue (see Android UI Thread Message Queue dispatch order), so no other messages will be processed between those two callbacks (during a configuration change). Understanding this level of processing seems to be critical, but documentation on it is sorely lacking, if not missing completely.
Approaches
Here is a collection of approaches that can be used to address most of the above situations.
Background
For discussion, assume Activity A and Activity B, where A creates B.
Generally speaking, a "global" variable can be created by making it
"public static" on pretty much any class. Conceptually, extending
the Application class and adding it to the extended class would be
good, but if that's too much work it could be included (for
instance) in one of the Activity classes.
Generic Activity Reference
Useful whenever a generic Activity is needed.
Create a global variable. In both A and B, have onCreate() set it to "this" and onDestroy() set it to null.
Topmost Activity Reference
Useful whenever you want to access the currently visible Activity.
Create a global variable. In both A and B, have onResume() set it to "this". This approach works fine unless all Activities exit, in which case you may need to create a separate flag to indicate that situation. (That flag could be the Generic Activity Reference implementation mentioned above.)
Specific Activity Reference
Useful whenever a handle to a specific Activity subclass instance is needed.
In both A and B: create a global variable in the Activity subclass itself. Have onCreate() set it to "this and onDestroy() set it to null.
Application Context
Useful whenever a Context spanning the lifecycle of the entire app is needed or when you don't care about using a specific Activity Context (e.g., to create a Toast from a background thread).
You can get this from Activity's getApplication() and store it on a static hook.
Handling Configuration Changes
There may be times when you want to stop/start a background thread only across an Activity "session", where I define "session" to include the series of Activity instances which may be created and destroyed due to configuration changes. In my particular case, I have a Bluetooth Chat Activity and an associated background thread to handle the network connection. I don't want to have the connection destroyed and created each time the user rotates the device, so I need to create it only when one doesn't exist and destroy it only if a configuration change isn't underway. The key here is understand when onDestroy() is called due to a configuration change. This can be done with or without fragments. As is often the case, I prefer the non-fragment approach since the fragment approach doesn't seem worth the extra complexity to me.
Approach 1: Without Fragments
In onCreate(), create the background thread if it doesn't exist yet. In onDestroy(), destroy the background thread only if isFinally() returns false.
Approach 2: With Fragments
This works well because the FragmentManager will store fragment instances across configuration changes if setRetainInstance(true) is used. For an excellent example of this, see http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html. The example is for AsyncTasks, but can also be applied to managing a background thread (just create the thread instead of an AsyncTask in the fragment's onCreate() and then destroy the thread in the fragment's onDestroy()).
Closing
Fully understanding these issues requires a deep understanding of how the UI looper processes its message queue -- when Activity callbacks are called, how other messages are interleaved with them, when display updates occur, etc. For instance, if a DialogFragment is created using an instance of a non-visible Activity, will it get displayed at all and, if so, when?
Perhaps some day Android will provide a deeper API to Tasks and their associated backstacks, along with documentation describing the UI's message processing and associated mechanisms in more detail. Until then, more "source code and/or ... empirical analysis" :-).
Thanks,
Barry
If all you want you want to know is which Activity is foremost and accepting user interactions, just create a BaseActivity that extends Activity and override onResume() and save a reference to "this" in a static variable. All of your other activities should extend BaseActivity. You're done.
The short answer I would guess is that only one activity can ever be active at a time in a given app, and that activity obviously knows who it is (it is itself) -- so the only answer any activity can get to "what activity is currently active" can only ever be "you are, silly".
For simple apps with a clear division between the different activity classes, this works fine, and so that's a great percentage of most of the apps in the play store. It doesn't work so hot when you're getting real clever with encapsulation and polymorphism, as I'm sure you've discovered, but I don't think Google is really targeting those types of developers.
Just my $0.02, I don't think you'll get an "official" answer here.

Is it okay for me to implement a switch statement in the onCreate method?

I currently have an activity's onCreate() method set to capture an intent the first thing it does. The intent will always have an extra int "ACTIONCODE" that determines what the activity should do.
Activity A might want activity Z to set up variables for the first time, so it calls startActivity(includedIntent) which has some extra int ActivityZ.SET_UP_FIRST_TIME (which is a constant in Activity Z.) Activity B might want to change the variables around a bit, so it does a startActivity(includedIntent) with the intent now including an extra int ActivityZ.CHANGE_VARIABLES as well as other data to change those variables.
Activity Z could just be a bunch of textviews that display what its variables are. Depending on what ACTIONCODE it receives from getIntent(), it will perform things just as it needs to.
I feel like I have a lot more control over the activities in my app my doing this, yet I fear as though it might be a really naive and inefficient implementation. I basically do not trust (nor fully understand) onStart(),onResume(),onPause(),and onStop(). From what I've heard, there is no guarantee that an activity will always return back to onResume(). While it was in its onPause() or onStop() state, it could have been killed or completely destroyed by the system, and thus would only return back to onCreate() again. It's the only method that I trust.
I even do all of my data saving from onCreate(). Why? I heard that if an activity is in onPause() or onStop(), it is liable to be killed, and may not even finish running through all the lines included in the overridden onPause() or onStop() method. I don't want to perform a data saving function from within a method that could abruptly stop!
Is my thinking wrong here? Are my fears irrational? If so, what should I do instead?
Here are my app screenshots by the way:
Thanks, Luksprog. After some reading, I think I can trust the activity lifecycle better now.
I will implement setting up in onCreate, loading in onResume, and saving in onPause! Hopefully that's correct.
Now all I need is some advice on the right way to set up activities and fragments. I just need some general rule of thumb for deciding when and when not to start a new activity, or if it may be better to use fragments.

Synchronous and asynchronous activities

Can anyone help me to understand synchronous and asynchronous activities in Android?
What is exactly meant by synchronous and asynchronous activity in Android?
StartActivity, StartSubActivity and StartAcivityForResult start an activity synchronously or asynchronously, or can they behave in both ways?
Please explain as I have gone through many articles but could not find any proper explaination over this.
First of all, only one activity can be running at a time on Android, so you'll never have two activities running at the same time. Use startActivity() when you want to "fire and forget", that is, you want to launch an activity but are not expecting it to return a value to your activity. In that case, the new activity will start and your activity will be paused; you might eventually regain control once the user returns to your activity.
Use startActivityForResult() when you are expecing a result from the activity you are launching. In this case, the calling activity should override onActivityResult(), which will be called when the launched activity exits and has a result to return to you (which it sets with setResult()).
In both cases, since the calling activity and the called activity are in the same task, it's "synchronous" in a certain sense (although I think using the terms "synchronous" and "asynchronous" can be confusing in this context). The calling activity won't appear on the screen until the called activity finishes.
A useful read to know more is:
* http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html
-
Bruno Oliveira (Android Developer Relations, Google)

Same Activity called twice... Issue with Multiple AsyncTasks?

I have three simultaneous instances of an AsyncTask for download three files. When two particular ones finish, at the end of onPostExecute() I check a flag set by each, and if both are true, I call startActivity() for the next Activity.
I am currently seeing the activity called twice, or something that resembles this type of behavior. Since the screen does that 'swipe left' kind of transition to the next activity, it sometimes does it twice (and when I hit back, it goes back to the same activity). It's obvious two versions of the activity that SHOULD only get called once are being put on the Activity stack.
Could this be from both onPostExecute()s executing simultaneously and both checking the flags each other set at the exact same time? This seems extremely unlikely since two processes would have to be running line-by-line in parallel...
*****EDIT*** A lot removed from this question since I was way off in what I thought was wrong. Nonetheless I found the answer here quite useful, so I have edited the question to reflect the useful parts.
The only way I can find that this is
possible is if both AsyncTasks'
onPostExecute() executed SO
simultaneously that they were
virtually running the same lines at
the same time, since I set the
'itemXdownloaded' flag to true right
before I check for both and call
startActivity().
Since they are both called on the main application thread, that's not possible, unless you're doing something really strange.
I would introduce some Log calls to ensure that you are not misreading the symptoms.
Beyond that, it is difficult to see any problems from your pseudocode, unless there's a possibility of other downloadID values beyond the three shown. For example, if there is a DL4, and DL4 completed after DL1 and DL2, DL4 would trigger your activity.

Categories

Resources