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.
Related
I have an android app with one "home" or "main" activity that relies on fragments to accomplish several tasks. This data relies on information retrieved from a server (mine, and presumably a substantial amount by google maps).
I would like to structure my code such that several other activities (ie. preferences) can temporarily take focus before returning to the main activity.
Currently android destroys and recreates the main activity, which means bandwidth is wasted every time.
There are several notable intent flags which 'solve' this problem (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT and Intent.FLAG_ACTIVITY_CLEAR_TOP), however they only appear to be useful when transitioning back to the main activity, which means I have to #override the system behaviour for both onKeyDown() and onBackPressed(). I would really prefer not to do this in case it causes other issues or eventually becomes deprecated.
Is this safe? Or is the better solution to force my application to create a serialization (savedInstanceState) of the main activity and all fragments anytime another activity temporarily takes the foreground?
Using saved instance state is the proper approach here. That will let you persist the information retrieved from the server when the activity gets re-created.
You only need to implement code to save / restore the instance state in the fragment or activity that holds the data. If you want to share that data across all your fragments, you can place it in the activity and add code to save the instance state of the activity. Then you can access the data that's stored in the activity from your fragments with ((MainActivity)getActivity()).getData().
For code to save and restore instance state, take a look at:
http://developer.android.com/training/basics/activity-lifecycle/recreating.html#SaveState
http://developer.android.com/training/basics/activity-lifecycle/recreating.html#RestoreState
If the data you need to persist is really large, you can use a retained fragment instead, as explained here:
http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject
Another trick for large data objects is to use a singleton class to store the data while the orientation change takes place.
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?
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.
I have created a SettingsActivity for my app. In this Activity I am using the SharedPreferences class to do handle the user editable preferences.
While setting up the SharedPreferences, I have to load them in the onCreate of my main activity and then again in the SettingsActivity. The probably was that both calls to the getXXXX() methods require defaults and I figured that it would not be good to hard-code the default values into both places because I would imagine it would be problematic in the future if I ever changed them.
Which is the best/most popular (or accepted standard) of doing this?
Create a global variables class in which I import into each activity and define my default constants in there?
Use putExtra and getExtra to pass the data from the main activity to the settings activity?
Any other suggestions?
I think Squonk has a good answer, but if you're looking for an alternative, consider creating a Settings class with all of your settings as members. It could have a static method like loadFromPreferences(Context) that would return a Settings object constructed from SharedPreferences, using whatever defaults you need. It could also have a saveSettings(Context) method to save your edits. Hope that helps.
Personally, in this situation, I'd put the default values in a resource file. In that way there's no need to use a global variables class or a helper class. Android resources already do that for you.
See:
Providing resources
More resource types
Instead of using a class with static values why dont you extend the Application class which will always live when the application's process lives. you can keep shared methods and variables in it
I would highly recommend opening the SharedPreference in the onCreate of both activities. Every time I've tried to use global variables, the values disappear in a way that's difficult to detect and fix. Activities are destroyed when they are closed. Services can be removed from memory at any time. The application context will be destroyed if your services are sleeping and don't have activities in memory.
That being said, putting a variable in the application context is probably the best place. Create a class that extends Application and set AndroidManifest.xml to use this. Just don't expect the value to be there if you try to use it from services or broadcast receivers.
Also, unless you're having problems with the activities loading too slowly, you're better off spending time on features than optimization.
You can declare objects as public static and reference them from another class. ActivityA:
public static int testIntegerA = 42;
Intent intentInteger = new Intent(getActivityContext(), ActivityB.class);
intentInteger.putExtra("INTENT_EXTRA", testIntegerA);
startActivity(intentInteger);
ActivityB:
public static int intentInt, staticInt;
staticInt = ActivityA.testIntegerA;
intentInt = getIntent().getExtras().getInt("INTENT_EXTRA");
Now both intentInt and staticInt equal 42;
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.