Passing references to Activity Intent - java

For quite some time I've had troubles passing variables from one Activity to another, and I've usually had to resolve to some pretty ugly Static-class-hacks to make it work.
Generally something along the lines of a static method that I call with the type of the Activity, along with the variables the Activity requires. These gets stored in a static variable, and retrieved in the constructor of said activity.
Like I said, pretty ugly. And there's no such thing as "myActivity.StartActivity(new Activity);". All of the overloads for StartActivity takes either an Intent, or a typeof(MyOtherActivity).
So my question is, have I completely misunderstood the concept of Activities, or am I simply missing a completely obvious way to pass arguments to them?
#Edit: The reason I want to pass an actual reference to an object, instead of simply a copy of the object, is because I'm trying to pass a View Model from an overlying Activity, down to the new Activity. And of course any changes made to this view model, should be reflected on the parent activity, which will only be possible if the the two activy's viewmodels points to the same instance.
I'm writing the app using Xamarin.Android, but the code is nearly identical between C# and Java, so answers in either those languages is fine.

The problem is that Android can kill the process hosting your app at any time (if it is in the background). When the user then returns to your app, Android will create a new process to host your app and will recreate the Activity at the top of the stack. In order to do this, Android keeps a "serialized" version of the Intent so that it can recreate the Intent to pass it to the Activity. This is why all "extras" in an Intent need to be Parcelable or Serializable.
This is also why you cannot pass a reference to an object. When Android recreates the process, none of these objects will exist anymore.
Another point to consider is that different activities may run in different processes. Even activities from the same application may be in different processes (if the manifest specifies this). Since object references don't work across process boundaries, this is another reason why you cannot pass a reference to an object in an Intent.

You can also use The Application class to store objects globally and retrieve them:
using Android.Runtime;
namespace SomeName
{
[Application]
public class App : Application
{
public string Name { get; set;}
public App (IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public override void OnCreate ()
{
base.OnCreate ();
Name = "";
}
}
}
And you can access the data with:
App application = (App)Application.Context;
application.Name = "something";
I choose to do this on the Application calss because this class is called on the App startup so you don't have to initiate it manually.
Keep in mind that variables which are scoped to the Application have their lifetime scoped to the application by extension.
This class will be Garbage Collected if the Android feels it is necessary so you have to modify the code to include this case also.
You can use SharedPreferences or a Database to save your variables in case they get deleted and retrieve them from the App class for faster results.
Don't be overly wasteful in how you use this approach though, as attaching too much information on this class it can lead to a degradation in performance. Only add information that you know will be needed by different parts of the application, and where the cost of retrieving that information exceeds the cost of storing it as an application variable.
Investigate which information you need to hold as application wide state, and what information can simply be retrieved straight from your database. There are cost implications for both and you need to ensure you get the balance right.
And don't forget to release resources as needed on OnStop and OnDestroy
I rarely use intents, i find this way better.

Related

Intra/Inter-activity communication

I am mostly a beginner to android development, and I find myself using a lot of global variables to share data between functions within the same activity, Intra-activity communication . This is mostly because no one calls onCreate () from within the activity, so I can't return UI elements and data that might be edited latter in the activity.
In addition for inter-activity communication I find myself using Intent extras for small data and external classes with static variables to pass large data, image strings, around when an activity dies. I read here that the application context can also be used to maintain global variables,so this might be a solution, however this keeps the variable for Intra-activity communication alive even after it dies, which is unnecessary. In addition some of that data passed may not be needed all the time for all the activities.
That seems like poor practice, so my questions :
1)For inter-activity communication is a constant use of intent extras and static variables to pass data ok?
2) For Intra-activity communication what can I use instead of global variables to pass data between different functions that don't call each other but share some value and the value dies with the activity ? Is there a danger to such use of global variables ?
If this is too opinionated or abstract I'll close it.
1) Usage of intent is ok. As for global variables I do not think so. The values can be lost when your application would be recreated after android system decides to free some memory. Why not persist data in SharedPreferences or SQLite?
2) Fields (and classes) variable is a normal way to use (and pretty good practice for the cases like not using findViewById all the time). If you want to persist data between activity recreation why not to use provided by android framework (https://developer.android.com/training/basics/activity-lifecycle/recreating.html)?
Save the needed data (like item ids, values, etc) in bundle and restore them afterwards.
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstance.putInt(some_key,value);
}
and restore in onCreate or onRestoreInstanceState.

Parcelable, carrying information through activities

So, I'm trying to make something like an rpg for the android to practice programming. I have a hero object which I'm trying to pass using parcelable as recommended by others, but I'm not sure how to pass it back.
In one activity, I'll have
myIntent.putExtra("heroData", hero);
And then, in myIntent, the activity started in the original activity, I'll have
hero = (Protag) getIntent().getParcelableExtra("heroData");
note: Protag is the class of the hero object
So, the first activity does successfully pass the object to the second activity, but such that the second activity doesn't affect the object in the first activity. Like, if something happens to the object in the first activity, it'll preserve to the second activity, but if something happens to the object in the second activity, the object in the first activity is still the same.
How would I make an object that can be changed by any activity such that the changes are preserved through other activities?
you are now dealing with concurrency. Since you have 2 'activities' possibly acting upon a single 'data'. However, Parcelable is a serialization technique, which means that you will lose reference once you pass a parcelable object(via parcelable). which means that you can't use a parcelable object as a central point of synchronization (both updating it with data).
If you will ONLY EVER plan on having a single object of your class, then you can make the object have static values. Otherwise this is the wrong mechanism to do this.
An AIDL one-way ascync service that notifies each thread of changes to a 'registered' object's values, is viable. (AIDL one-way is actually not hard to write, just takes some practice) Here is a project I wrote that makes use of it, to show both sync and async service usage. (My PhD Adviser's REPO for the MOOC he teaches)
Update to explain why I say this is 'concurrency'.
First of all I take a 'wide breadth' approach to what is 'concurrent' vs what you might be thinking. Your working definition of 'concurrent' is a valid one. However, it is somewhat limited in scope compared to what I'm thinking about. (usually doesn't matter, but the nuance of Android's lifecycle actually makes it important)
Android Activities have 6 life-cycle states they could possibly be in.
Created
Started (Visible)
Resumed (Visible)
Paused (partially visible)
Stopped (hidden)
Destroyed
Now here is where the issue of concurrency comes up.... When you have 2 or more Activities that are not in the 'Destroyed' state.
Plus there are a lot of things that add unpredictability to your logic that you have to think out very carefully...
Android's non-deterministic behavior.. User could press power/home/back or some other button, a phone call comes in/AMBER Alert takes priority over the phone, and/or the Garbage Collector "magic divine/unholy ritualistically" decides who lives or dies (exaggerating, but you get the point).
But lets assume that A doesn't get killed (which is actually the 'problem' scenario here).
So A makes an X object (hero or whatever) and passes by value (parcelable+intent) to B the value of X. At this time, the value(and reference) of X is in A. but the value of X is in B. Therefore, we are already in concurrency. Because the life-cycles of A and B overlap. Not just the "visible" portions of the life-cycles. So this means... where do you put the logic on "when to pass back the value of X"? Do you pass it back on onPause()? (but how? an intent wouldn't work if the User presses the back button)
Short answer: There isn't a 'great' way to do this. (at least with only Activities and Intents/Parcelables.)
You need some concurrency mechanism in place that allows the changes to X in A to propagate to change the values of X in B. (And this needs to be done reliably, correctly, and efficiently.)
The base goals of concurrent programming include correctness, performance and robustness. (from WIKI:Concurrency(CS) )
Ideally you wouldn't pass the data by value, but instead by reference, and when one class updated X (be it A or B) there only ever existed one value of X. That way when A or B was re-started it would have a 'valid' value of X. But you can't do this 'nicely' through parcelable data.
Therefore I would use a service to be the authoritative keeper of the 'value of X', any time A or B wants to update X, then they have to go through synchronized methods to get/set X. (I've never really ran into this scenario where two activities want to have the same 'live data' like this, so perhaps there is a better solution, possibly making use of handlers(but I can't think of it offhand)) With a service you will have slight time delays from when A is telling Service that X is updated to when B can get that info. but its the best I can think of off the top of my head right now.
Also the SQLite DB is designed to promote data storage like this, and has built in support for monitors to block access to the DB when another thread is accessing it. (there is nuance here I won't get into, such as 'setLockingEnabled(boolean)')
Make it static on the top class
All the activity can call it. Like
MyObject hero = topActivity.Hero
but but but
I'm not sure at all it is the correct way or the best way to do it. So maybe wait for other response
Sincerely
A possibility is:
From Activity1 launch the Activity2 using the method startActivityForResult(). In the Activity2 you will do all the modification needed to the object "hero" and then you can put it in an intent and send it back using setResult (int resultCode, Intent data). This way, when Activity1 is resumed, using onActivityResult(int requestCode, int resultCode, Intent data), you will be able to catch the object "hero" modified.
Here you can find more information on this mechanism.
http://developer.android.com/training/basics/intents/result.html
You could write object data in shared prefs. That way there is no need for a service. You need to save the progress of the game anyway. I think changes like that should be saved in memory once the app closes the progress is back to where it was.
Edit:
Or you could save in Internal Storage. Save the Protag object as a file.
It all depends how often the object changes.
Tip: You should not be writing every time you make a change to the object. Save the data in some data structure and then when everything is finalized such as the level ends or something then you write in storage or shared prefs. That way you keep it efficient otherwise all that writing in storage will slow the app down.

Android How to handle multiple instances data/identities and JNI

This is a question of good practice and a smart solution, I need an advice.
I have an app that (and as far as I can read here in Stackoverflow and by Google search):
The app handles kind of documents and I like it possible to
handle more than one document at the same time. (I am used to Win32 where there is a program segment and one segment of data for each instance but that is obviously not the case in Android/Java.)
I see one instance starting the app from the app storage (the tablet) another opening a Gmail or email with an appended document file, a third instance by opening a file from a File handling app like ES file explorer. And I like them all be possible to be flipped in between. The user might like to read more than one document at a time. (correct me if I use the word instance wrong in the Android/Java environment)
The app is built in a JNI section that contains all the data and logics and a Java Android user interface. (The JNI section is designed to be OS independent for implementations in different OS, has a glue c-file.)
The Android section recreates every time the screen is flipped or instances are flipped between
There is only one JNI instance and that is kept even when the Android Java part is recreated and all Java data is wiped out, right now it shows the last read file in all cases flipping in-between pushing the running app button
There are no problems making different instances within the JNI section as long as it is possible to bind them to each Java instance, with an identity or something that I can use as a parameter in the interchange with the JNI section, but how?
I can't save for instance the FilePathName in each instance to identify the instance in the Java section because it will be wiped when the Java section is recreated.
First question is if I am right in my observations reading Stackoverflow and Googled articles?
Second question, any good suggestions in solving the issue? I need an advice
Is there a possibility to identify the instance in all situations as long it is alive?
Any other possible paths, both to the general issue of separating data for each instance or identifying the instances for the JNI to handle the data for each instance?
Jan
We have similar problems with JNI objects in our application. The problem is that JNI link isn't work as ordinary Java object and has to be relesed explicitly. At the same time we have activity that can be destroyed at any moment by Android.
Our current solution is to store JNI objects on Application level with posibility to manage refereces and drop objects as soon as reference is zero. And also destroyed JNI reference if activity is going to be destroyed forever. So this is similar like you did in previous post.
However if you would like to have your application scalable after some time you might understand that this solution isn't ideal.
The first thing that Android system sometimes temprorary destroys activity to save memory. In your case all JNI objects with documents will still consume memory. So the best solution here is to be able documents on JNI level saves its state to bundle. This is especually important if your documents can be changed by user. In that case by saving state of JNI object in onSaveInstanceState you can destroy your JNI object and recreate in onCreate. However here it is important to analize how much time is required to destroy/create JNI object with saved to bundle document as we have to support quickly activity recreation in some case (portrait/landscape mode for example) with some limited bundle (not more 1Mb). In case of long process this solution might be not good.
Also as you would like to have one task - one document system. You should consider case when you have several activities in one task.
The second item that Android isn't call onDestroy() always. And if you do some save operation here data might be lost sometimes.
Hope this information helps you.
I made something working but I don't know if it is good practice?
I am getting an int-instance-tag from JNI and tagging it on the intent by
public void onCreate(Bundle savedInstanceState) {
....
if (savedInstanceState == null) {
// Creating the JNI task and get the JNI task ID
int iInstance = initProgram(...);
// and store the JNI task ID in the intent
getIntent().putExtra(Intent.EXTRA_TEXT, iInstance);
...
}
...
public void onResume() {
super.onResume();
if (JniManagement.resumeInstance(iTask)) {
...
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
iTask =
savedInstanceState.getInt(AndroidApp.STATE_TASK_ID);
}
Then we are talking about the lifespan of a task, the user is flip/flopping between windows/tasks with the home button. The issue is to synchronise the JNI data with the task of Java.
Re-appearing in th else section of if (savedInstanceState == null) { we get the JNI task ID from the intent and synchronise the JNI task with it.
And onDestroy() with if(isFinishing()) freeing the instance set of memory in the JNI.
#Override
public void onDestroy() {
super.onDestroy(); // Always call the superclass
if(isFinishing())
Commands.destroyInstance(getIntent().getExtras().getInt(Intent.EXTRA_TEXT, 0));
// Extinguishing the JNI task started during onCreate()
}
The JNI-side
In the JNI-side all memory used by an instance will be put together in a structure. This structure could be pointed at, in an array of pointers to get the right set of data for the right instance integer. The pointer array is realloced when new instances are created and can go on as long there is memory left for a new instance.
This works actually pretty good, always getting the right data to the right activity/instance. And using a File Manager app starting one activity after another by calling work data files, there will be a stack of activities/instances. When the user is leaving them with the end button are pealed off one by one and its memory is extinguished real smooth. Open a file in a Gmail works fine too the same way, however appears as a different activity by the activity button.
As an old Win32 C-fox I love my pointers and set them in all the methods/functions this feels a bit clumsy (only handle the active window screen data). But the Android OS do not have active overlapping windows.
So just synchronising the JNI this way to the right Java activity/instance is simply working real smooth.
But is it good practice? Are there any other smooth and good looking solutions?

A basic way to have cache/data handled by manager class that my Android Activities can all access?

So what I'm wanting to do is have a few different single manager classes that handle the caching of data that I've pulled down from the internet. I want the data in these classes/objects to be accessible from different activities in my application, whereby any of the activities can manipulate data or call functions as needed.
Eg, I have a UserDatabasemanager where I will store the initial raw JSON data I pull down from the internet and an array of User objects which is a distilled version of that JSON data.
My app at various times will pull data down from the web and will update the User array as data changes. Eg, initially pull down a list of users that fit a criteria. So I then cache all the usernames and IDs into the User objects. Should the application then focus in on a particular user page, it will then download that user's description text and store the new description into User object. (All of which I think I cant do currently with my knowledge of programming)
I've been reading up on sending objects via Intents with Parcelable and Serializable which seems a bit of a round about way of doing things for shared data between Activities.
How to send an object from one Android Activity to another using Intents?
Passing data through intent using Serializable
Since my intention is to use these classes/managers/objects (whatever we want to call them) as single instances I'm not too fussed about following encapsulation to the letter as their job is to provide and update my cache data based on the needs of the Activities.
I'm still very green with all this Android/Java stuff, but it seems this sort of thing would be fairly straight forward in C/C++ with pointers. It seems that even if I was programming directly with Java I could pass around references to objects fairly easily, but the structure of Activities seem to be in such a way that it doesn't lend itself to easy passing data around in my own program?
One easy (but not great) way to achieve this is to use the Android Application class to store (the singleton) instances of your managers. You can always retrieve a reference to your application by calling getApplication() on any activity.
You need to provide your own implementation of the Application class so that you create the managers in the onCreate() method. You should also create strongly typed getters for your managers.
Also, consider what you need to do for the system callbacks the Application may receive such as onLowMemory().
So what I ended up doing in the end is making a Globals.java file with the following contents
public class Globals {
public static ArrayList<User> userObjArray = new ArrayList<User>();
//Weird magic class stuff
private static Globals instance;
private Globals() {
}
public static synchronized Globals getInstance() {
if (instance == null) {
instance = new Globals();
}
return instance;
}
Then whenever I wanted to use data from the Globals I would do something like...
Globals.getInstance().userObjArray
This seems to be working exactly how I want it to. Its not great for encapsulation, but that wasn't the goal for what I needed to get done.
I think you should use android service and create binders in your activities.
http://developer.android.com/guide/components/services.html

Is it possible, and what is the best strategy, to pass objects by reference from one Activity to the next?

I have a simple Android application that uses an instance of a class, let's call it DataManager, to manage access to the domain classes in a Façade-like way. I initially designed it as a singleton which could be retrieved using static methods but I eventually got irritated with the messiness of my implementation and refactored to what I reckoned was a simpler and cleaner idea.
Now the idea is that for each file that is opened, one DataManager is created, which they handles both file I/O and modification of the domain classes (e.g. Book). When starting a new Activity, I pass this one instance as a Serializable extra (I haven't got on to using Parcelable yet, but expect I will when I have the basic concept working), and then I grab the DataManager from the Intent in the onCreate() method of the new Activity.
Yet comparison of the objects indicates that the object sent from one activity is not identical (different references) to the object retrieved from the Bundle in the second Activity. Reading up on Bundle (on StackOverflow, etc.) suggests that Bundles cannot do anything other than pass-by-value.
So what is likely to be the cleanest and safest strategy for passing an object between Activities? As I see it I could
Forget about passing by reference and live with each Activity having its own DataManager object. Pass back the new DataManager every time I close an activity so that the underlying activity can use it. (The simple solution, I think.)
Go back to using a singleton DataManager and use a static method to get it from each Activity. (Not keen on using singletons again.)
Extend Application to create a sort of global reference to DataManager. (Again, not keen on the idea of globals.)
Is that a fair summary? Is there some other healthy strategy I could use?
Another approach would be to create a service. The first activity would start the service and bind to it, when you launch a new intent, unbind the first activity and when second activity starts, bind to the service.
This way you don't have to ever stop the service or worry about passing data between activities.
Java does not have pass by reference so that option is out, I would suggest dependency injection for passing data between the activities. Otherwise definetely the singleton would be the way to go.
The prescribed one is Going by implementing Parcellable interface, thats the way to pass Objects between Activities.. and the 2nd and better choice is to make a Singleton to be sure its single Object.
Create your DataManager as a Singleton that implements Service. Bind the service to your application in the manifest xml (see the link), and you will have a persistent singleton your activities can access without issues.
Passing parcellable arguments can quickly get very messy if you need to get a lot of data. The singleton approach, although usually considered an anti-pattern, works like a charm in cases like these. Just remember to not create multiple singletons that interact with one another.
I would suggest using an Application Subclass. It allows you to hold a single reference to the DataManger class and is persistent as long as your app lives.
A singleton with a static field will also work, but there are some place in the documentation where it says that the content of static fields is not a safe place to store your data. As I understand static fields should persist as long as your ClassLoader stays in memory. Therefore a singleton should only vanish if the whole process leaves the memory and in that case the application class will also leave the memory, but it will call the onDestroy methods of the Application and that enables you to safely close your DataManager and persist important data to memory.
That said to your two variations.
The Android way to go would be to make your DataManager a ContentProvider. This will make it possible to access your Data from every Activity without holding a global reference. I'm not sure how you would build a caching Content Provider that stays in memory and is not reinstantiated too often.

Categories