I have a question, why we using Parcelable/Serializable when passing data between activities. As I read in the documents around internet, Parcelable/Serializable is used when we need to pass data between processes. But all the activities of the same application run on the same process. Even Service started by an Activity run on the same process too. Then why we need to implement Parcelable/Serializable in this case ?
The app containing the parcel receiving activity might get destroyed and later be recreated so the data must be persisted to some kind of temp file.
Example:
parcelSenderActivity s is active
s calls parcelReceiverActivity r with parcable intent-extra
r is interrupted due to a phone call,
android removes app containing r and s from memory
after phone call is finished r is recreated by reloading it with parcable intent-extra in a differend process/thread
this recreation is only possible if the intentparameter can be persisted to a femp-file.
Related
Android 7.0 start throwing TransactionTooLargeException while restoring instance state if the parcel size is over the limit.
But my application have to save the loaded content which has no size limit.
So it's very easy to cause this error.
Is it safe to save instance state in Application instance?
Will it be deleted while the Activity is in background?
Is there a way to save large data without throwing this exception?
To answers these questions one by one :
Is it safe to save instance state in Application instance?
Not if want to retrieve that data after your app's process has been killed by the OS and later restored. For that, you need to persist data to disk somehow. There are always two parts to properly handle saving state : handling configuration changes (like rotations) which do not involve process death and actual process death and restoration. Putting state in the Application instance (or any singleton) solves the first problem but not the second.
Will it be deleted while the Activity is in background?
Depends what you mean by "in the background". That can sometimes mean "the Activity exists but is in the stopped state". In that case, your data is fine. But your entire app process can be killed while the app is backgrounded and later restored when you return. In that case, your data will be gone if it is not properly saved to disk somewhere.
Is there a way to save large data without throwing this exception?
Yes, write it to a database and restore the data from there instead. Alternatively, you can use this library that automatically handles persisting / restoring your Bundles to / from disk for you : https://github.com/livefront/bridge .
Now I'm facing some problem in Android when memory is low or the application is killed by system.
Scenario 1:
I set some static members in a class, I found in some situation , it will be deleted by system when the application is still running.
My problem to this is : when does this kind of GC run?
Scenario 2:
If I switch to another large application and then switch back to my application ( named App_A). App_A sometimes will be recycled by system and restart the last activity when it be switched back.
But there are some application-wide data (like login info) I saved in a singleton.
My problem to this is : Dose the application-wide data saved in singleton will be deleted?
If so, is there a appropriate way to restore the data?
My effort is:
To Scenario 1, I will avoid to use static member directly.
To Scenario 2, I will save those data into file , after it be deleted, I pass Context to each public function to let each of them have the ability to restore the data. But I think it will be unfriendly when the function is used in some situation which need run quickly.
I can only answer about Scenario 2.
Android will try to keep recently used apps in memory, but if the user switches to another app and memory starts running low, the OS has the option to kill the recently used app to make more memory available to running applications.
I had the same problem, where I had some user-context data like username in a static singleton. This data would disappear when returning to the app after using a number of other apps.
The way I solved this problem was to use the activity's intent. Since the user data was retrieved at the beginning of the app, I would simply pass this data to subsequent activities in their intents. Because the OS stores the intent and uses it to recreate an activity not in memory, my data was no longer vulnerable to being garbage-collected.
Also consider other means of persisting data: Shared Preferences, file system, SQLite database. But never count on static data from previous execution being available at the start of an activity.
It is generally bad idea to use singleton to save some data.
Best practice is using any persistent storage - SQLite, Realm,JSON, or any file.
Easiest way is saving your login data for ex. in JSON - then in Application class parse it in onCreate method into POJO - then you can get it from any place of your app. And store to file when app is closing or on any change.
Anyway I suggest you to read Android guides about persistence, memory management and performance tips.
I found and read a lot of articles talking about global variables in Android, some of them suggests using an subclass of Application + declare it in the manifest file as a glbal variable container.
But some articles mentioned that This class could also be killed when system memory gets low, is this correct?
So, is it 100% reliable to use an Application subclass as a global variable container? And could somebody give me a link to some documents explaining the life cycle of an application in Android (not activity)?
EDIT:
Thanks for the answers, I think I need to explain a bit more of my question.
The situation is, I just want to share a global String variable, Activity A modifies it, and activity B reads it.
When B is currently visible and user receives a call,
If A and B are killed but Application keep untouched (is this what Google calls an empty process?), I'm OK with it.
If A, B, and Application class are all killed and when user come back my app gets a clean start, I'm OK with it.
Which I'm not OK with it is, everything was killed including the Application class, when user come back my app doesn't start fresh, I mean, it starts from Activity B, will this happen? then should I start A manually or let Application class to do the initiation? none of these ideas looks good to me...
The answer is both "YES" and "NO" - the Application class can be used as a "global variable container" in that all activities and classes can rely on it.
However, you cannot rely on the Application class (or any other class) to persist indefinitely. In other words, if the user answers their phone, your application could be destroyed and then re-created when the user completes the call and returns to your app. So, you definitely cannot rely on it to persist data, but you can rely on it to provide global access to data when you app is active.
This is the Android documentation:
http://developer.android.com/training/basics/activity-lifecycle/index.html
This is a really good post on it - read the SECOND highest voted response, in addition to the "accepted" response:
Using the Android Application class to persist data
It explains pretty clearly how your app can be killed/destroyed whether you expect it or not.
EDIT:
To clarify, if you have a variable (call it "myVar") in the Application class and a user sets it in Activity A, then proceeds to Activity B, Activity B will be able to read the change.
If Android "destroys" the application class, which can occur anytime the user is not in your app (and in rare instances even if they are...), the app will be reconstructed so that the Activity Stack is still valid but "myVar" is not set, unless you persist the data.
In other words, Android will recreate the Application class and Activity B, but there is no guarantee that it will recreate Activity A until the user does something to destroy Activity B. Also, it will certainly not "replay" the user actions in Activity A in order to recreate the app state, and in your case that means "myVar" is not reliable.
If you read the references provided, you will see that this is the case (and also I have tested it).
EDIT 2:
To persist data, consider SQLite (which is pretty complicated and there are many references) or SharedPreferences:
How to use SharedPreferences in Android to store, fetch and edit values
This class could also be killed when system memory gets low, is this correct?
Yes.
So, is it 100% reliable to use an Application subclass as a global
variable container?
If you want to use it to pass values it is not reliable.
why?
EDIT:
Which I'm not OK with it is, everything was killed including the
Application class, when user come back my app doesn't start fresh, I
mean, it starts from Activity B, will this happen?
yes. It is possible that you set a value in your application from activity A and then when you are in Activity B user leaves your app and after a while android kills your process. then user comes back wants to look at your app. android again recreates application and activity B but not Activity A and instead of fill the application variable with what has passed Activity A to it, it recreates it from default initialization. So simply you missed what has passed or set by Activity A.
Don’t Store Data in the Application Object
I'm building a new android app which uses data from a server through different activities.
currently I have 2 activities, the main one connects to the server and creates the singleton object (which is shared for all activities) using the data from the server.
obviously I have an instance of the data held in the singleton class, and get the object from a static method.
I know that this is not the best way to implement a shared data between activities - I can pass them through intents (but I'm planning to create more 3-4 activities which uses the same data so why to pass them when you can have them in a static context global for the app).
What this implementation got me into is this problem:
when the user switches to another app at the second activity, and my app stays for some time on background, android frees the memory used by the app, and when returning to it, I get null pointer exception when using one of the fields of the singleton object!
I solved the problem by returning to the main activity (if the object is null) to recreate the data, but this makes the activity reconnect every time it's destroyed (it's not optimal since the data on the server doesn't change much).
I know that I have to save the server data onDestroy and to recreate the object every time I return to the activity but this must happen on every activity, and the data is about 4-5KB meaning it needs to be written to a file, and parsing it takes time (accessing phones SD card).
I'm starting to think on using Parcelable object to share the data through intents - I think android can save and restore the data of the intents automatically and in a optimal way.
(ref: http://bimbim.in/post/2010/09/27/Android-Passing-object-from-one-activity-to-another.aspx)
The question is, will intents solve my problem?
Is there another ways? Better, faster ways?
Thanx!
I am sure the problem relates to the life cycle of activities. When Android kills your activity, you can save your data as a bundle by using your main activity's onRestoreInstanceState() method, so when the activity gets recreated again, you can restore your data on the activity's onCreate() method (see here as an example). You could also use SharedPreferences in your activity's onResume() and onPause() methods. With both approaches, the idea is the same: that you save your object/data temporarily on disk, and restore it later.
I would like to know where the bundle "outState" of the method onSaveInstanceState(Bundle outState) is stored.
Is it stored in memory or in the device storage?
I am concerned about the security of the data which is stored in the bundle.
To store data only for application lifetime (ie temporarily), use the onSaveInstanceState(Bundle) activity event
This data will only be held in memory until the application is closed, the data will be available any time that this activity starts within the current lifetime of the application.
Explanation: if data is stored here by activity A then the application shows a different activity or rotates the screen (hence closing A) and then returns to A the data can be retrieved to populate the controls. However if the application is closed and opened again the data will be gone and the controls will revert to their default values.
Example of use: storing text typed in by user and selections making up an order, blog entry, message, etc...
Note:
It’s important to notice that only the Activity is destroyed and recreated, not your whole application! An Android application can consist of many Activities, Services and ContentProviders! If the application is closed (for example by pressing the “Back” Button, then all values will be gone. savedInstaceState is only there to preserve data temporary when an Activity is destroyed/recreated, not the application itself.
If you want to preserve data permanently, you need to save it either as Preferences or in a ContentProvider/database.
Here is a detailed answer for where the outState Bundle data is saved:
...Bundles are an IPC mechanism, so it's not going to the filesystem. But now there's a P involved – which process is it? And what is that process doing with this data? And do I need to be worried about it? It turns out that these instance state bundles are stored in the Activity Manager service. This service is implemented under the package com.android.server.am in the Android source code. Recall that Activities are stacked one on top of another and that Android calls these stacks “Tasks”... Each of these tasks is represented internally with an object of class TaskRecord. This class contains an array of ActivityRecord objects, each of which manages the state of an Activity. ActivityRecord contains a member of type Bundle named icicle. This icicle-bundle is the saved instance state and it is actually stored in the memory space of the Activity Manager service.
Source: https://www.linkedin.com/pulse/android-onsaveinstancestate-bundle-secret-safe-daniel-pietsch/
The documentation has been updated and indicates precisely that the state is serialized to disk:
Saved instance state bundles persist both configuration changes and process death, but are limited by amount of storage and speed because onSavedInstanceState() serializes data to disk.
You can also found a table comparing the differents approches to preserving UI state
Source: https://developer.android.com/topic/libraries/architecture/saving-states
I don't think there's any way that any malicious background process can get at the bundle data of your application. It is not documented how Android treats the Bundle data. It may or may not be written to disk in the event that your app is cleaned, while backgrounded. However, given that we don't know whether or not this data is saved to disk, and if it is, given that we have no clue where, and almost certainly don't have read access to that part of the disk, I wouldn't worry about some third party process being able to recover that data.
Consequently I'm not clear what you might think the exposure is. Though I may be missing something.
However, in answer to your question, it is absolutely in memory while your app is alive, and if your app is backgrounded it may or may not be written somewhere hidden, but we dont' know because Google hasn't told us.
It's destroyed along with the application when the memory is collected.
My guess would be in memory, but the best way to protect your data would be not to trust the system and encrypt it. Never trust the client (in this case the client being the OS).
EDIT:
To be clear, I'm not saying encrypt the bundle. Rather I'm saying that any sensitive data should not be put into the bundle. If you must put custom data in the bundle, then encrypt it.
But ultimately you should keep as little sensitive data on the client as possible. This is the same reason a e-commerce site would only show the last 4 digits of a credit card.