How do I preserve a complex object across Activity restarts? - android

Say I have a Java Bean object which is serializable. I want to store it away safely when an Activity goes through onDestroy() on purpose (i.e. onSaveInstanceState() is not called).
I am looking for a way which doesn't involve creating a database and write the object to that (mostly since a) Android's DB API is horrible and b) since databases make application updates a nightmare, because there is no decent support for applying migrations).
I thought about serializing the object to a ByteArrayOutputStream, base64 encode that and write it to a SharedPreferences file as a string. Or is that too far off?
UPDATE
Maybe that serialize-to-string idea wasn't that bad after all, seems to work out pretty well. Here's what I'm doing now:
public static String objectToString(Serializable object) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
new ObjectOutputStream(out).writeObject(object);
byte[] data = out.toByteArray();
out.close();
out = new ByteArrayOutputStream();
Base64OutputStream b64 = new Base64OutputStream(out);
b64.write(data);
b64.close();
out.close();
return new String(out.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Object stringToObject(String encodedObject) {
try {
return new ObjectInputStream(new Base64InputStream(
new ByteArrayInputStream(encodedObject.getBytes()))).readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
in onDestroy() I can then simply write the Base64 string to a preference file, where it's safe until I read it again during the next activity launch. It's a lot faster than I expected and unless your beans carry huge amounts of data, it works pretty well. And even better, you don't have to maintain a DB schema.
Still, I'm curious about how others do this.

I am looking for a way which doesn't
involve creating a database and write
the object to that (mostly since a)
Android's DB API is horrible and b)
since databases make application
updates a nightmare, because there is
no decent support for applying
migrations).
Android's API is actually fairly reasonable, mostly because it's a thin wrapper over the SQLite API, and the SQLite API is fairly reasonable for an embedded database. Moreover, Android does provide assistance for schema upgrades on app upgrades, via SQLiteOpenHelper.
It's a lot faster than I expected and
unless your beans carry huge amounts
of data, it works pretty well.
I have heard of many more developers running away screaming from serialization than I have heard of people having long term success with it. Just within the past few days, here on SO #android, I had an exchange with somebody trying desperately to rip serialization out of his app by the roots.
And even better, you don't have to maintain a DB schema.
Oh yes you do. What do you think is going to happen when you update your application and your class is modified? Doing the bookkeeping to figure out how to deserialize old versions of the class from a new version of a class is a chore and is one of the reasons developers abandon serialization. Also, do not forget that serialization is not transactional, whereas SQLite is.

I was also looking for a nice approach of un/marshalling any beans or activity states. We all know how much Activity's onStoreInstanceState() and onRestoreInstanceState() is a pain.
My acitivities simply store their states in onPause() and restore them in onCreate() lifecycle hooks via direct object serialization.
Serialization via a String like you do, is of course possible but less suitable for big data and causes a lot of overhead. Moreover Preferences are actually there to store preferences, not data :) Unfortunately, the Parcelable / Parcel what we could use for this purpose, does not recommend to store to persistent storage.
So what's left over is a simple object serialization - fortunately android SDK has implementation of the ObjectInputStream and ObjectOutputStream classes with all drawbacks and benefits - like we would also do in a non-android Java world, a simple:
ObjectOutputStream.writeObject(yourPojo)
would do the magic for us, (remember to implement the Serializable marker-interface)
Also, you may want to look in following APIs of a Context - ContextWrapper - Activity, which are very useful to cache local data (such as images) etc:
.getCacheDir()
.getDir()
.openFileInput()
.openFileOutput()
happy hacking :)

Related

Parceling objects in android to transfer from one activity to another

Recently an interviewer asked me a very tricky question.
There are several parts of the question.
Why (question is why and not how) do you need to parcel objects while sending from one activity to another and not send directly
Answer I gave -
Parcelable gives the capability to developers to restrict object
creation which in a way makes it faster to use.
I was confused on the part, so decided to site difference between using serializable and parcelable :p (clever huuuhhh !),
http://www.developerphil.com/parcelable-vs-serializable/ used this reference.
While using Bundle, when we use String, int we do not need to parcel the data, so do you think the String/int is by default internally parcelled ?
Answer I gave -
because String/int is a primitive data-type, if we had used the
Wrapper class directly, might be possible we had to use parcelable(I
am not sure on that part)
I did not get any useful link after googling, also I or the interviewer is not quite satisfied with the answer.
If you guys can help, would be wonderful !
Why (question is why and not how) do you need to parcel objects while sending from one activity to another and not send directly
Parcelling/serializing objects isn't for speed as you had guessed.
When you're sending data between Activities, and especially between different applications (remember that Intent objects aren't only meant for communication between your own Activities, but are also for between yours and those of other apps as well), you cannot expect the sender and the receiver to have access to the same memory address spaces.
Android's documentation states that applications run in their own discrete memory spaces. Here's a quote to that effect from the docs:
Each process has its own virtual machine (VM), so an app's code runs in isolation from other apps.
So when you want to send an object myObject to some receiving Activity, you can't send its reference/pointer because the receiver won't necessarily have access to the location specified by the pointer. Instead you'll have to send some representation of myObject that the receiver can access and use -- this is why you need to marshall the data into a form that can be unmarshalled, and the easiest way to do so is to simply have the class of the object implement Serializable which lets Java do its best to convert the object into an array of bytes that can be easily sent to and unmarshalled by the receiver. But since Serializable uses reflection, this is slow.
You can use other ways that are faster to marshall the data -- one, for example, is converting the object into its JSON representation using a library like Gson and just sending it across since any JSON document can be represented as a String and easily converted back to a Java Object. Another way, which is probably faster in pretty much all cases is using the Parcelable interface which lets you specify exactly how you want to marshall the data and exactly how it should be unmarshalled. It basically gives you more control on the transmission of the object.
The tl:dr: Parcelling/Serializing etc is used because you can't send memory addresses across, so you have to send the actual data of the object and it has to be represented in some form.
While using Bundle, when we use String, int we do not need to parcel the data, so do you think the String/int is by default internally parcelled ?
How Bundle works internally is that it puts everything into a Map and parcels/unparcels the data as needed (ie when get/put is called). For putting Objects into a Bundle, the object's class needs to implement Serializable or Parcelable because it needs to tell the Bundle how it should be marshalled/unmarshalled internally.
But primitive types and Strings are simple enough and used often enough that the developer doesn't need to specify how that needs to happen and Bundle provides convenience methods for it. I can't give you a solid answer at the lowest level of how they works because a lot of the Parcel code is natively implemented and I couldn't find it online, but they must certainly be straightforward to convert to their representation in bytes.
Just to add what #uj- said, Parcelling/Serializing is needed as #uj- said it will be sent across JVMs so they need to be converted into some format so that the other party will be able to understand.
Let me take an example to explain why serializing/parcelling is needed,
you are sending data from an application written in "C++" to an application written in java, so the following are the classes,
In C++,
class Android {
public: int dataToSend; //for example purpose making field public and omitting setter/getters
}
In Java,
class Android{
public int dataToSend;
}
suppose the C++ code generates dynamic library (which will be generated by compiling using the standard C++ compiler and then linked), and Java code generates a jar (by compiling using the javac).
When the C++ application sends data (object of Android class) to the java application the way it is compiled and linked in C++ is completely different as compared to the way its compiled in java and hence java will be wondering what has this C++ application sent to me.
Hence to get rid of such problems serialisation/parcelling is needed which will make sure that both of the application know how the data is converting while transmitting through network (in case of android how it is transmitted to another activity, may be in same or different application).
And yea when we start comparing Serialisation and Parcelling, Parcelling gets the upper hand as we will be specifying the way the data must be converted when sending the data, else in the case of serialisation the object is converted to string using reflection and reflection always takes time. Hence Parcelling is faster compared to Serialisation.
For your second question,
if we consider the above example itself then we can say that String and int being primitive types (no user defined fields in them) and hence android will be able to handle the marshalling and unmarshalling of the data which will be sent.
I tried going through the code when we go on digging deeper we end up getting native code as said by #uj-.
Some extract from the android source code:
while writing the parcel:
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
parcel.writeArrayMapInternal(mMap);
int endPos = parcel.dataPosition();
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
while reading the parcel,
int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
//noinspection ThrowableInstanceNeverThrown
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
int offset = parcel.dataPosition();
parcel.setDataPosition(offset + length);
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
p.setDataPosition(0);
mParcelledData = p;
set the magic number which will identify the start of the parcel while writing and the same will be used while we read the parcel.
Hope I answered your question.

Reading from SharedPreferences vs. keeping an instance of the object

THE SCENARIO
I have a class that makes use of a request list set by the user. The request list is stored in SharedPreferences. The dilemma I'm facing is to whether to keep an instance of the request list or to read from SharedPreferences every time the request list is needed (which is very frequent).
Also not that Gson is used to deserialize the object.
The code goes like this:
public List<PrayerTimesCalculator.Time> getDefaultRequestList() {
if (mRequestList != null) return mRequestList;
// Try getting request list from preferences;
Gson gson = new Gson();
String json = mSharedPref.getString(KEY_PREF_REQUEST_LIST, null);
Type listType = new TypeToken<List<Time>>() {
}.getType();
mRequestList = gson.fromJson(json, listType);
if (mRequestList != null) return mRequestList;
// Create default list;
mRequestList = Arrays.asList(
Time.DAWN,
Time.MORNING,
Time.AFTERNOON,
Time.EVENING,
Time.MID_NIGHT);
return mRequestList;
}
THE GOAL
My concern is that if I keep around an instance of the request list, and there are multiple instances of this class, an update to the request list in one instance of the class would not be reflected in the rest of the instances until they are recreated.
Thus, I'm leaning towards reading from SharedPreferences unless there is a better way to keep the request list objected updated in all instances.
THE QUESTION
(1) So, how efficient is it to read the same key from SharedPreferences quite frequently by multiple instances of the object? and (2) Is there a better way to keep the request list objected updated in all instances?
So there are a couple of approaches you can take to this.
First, your object is small - re-reading SharedPreferences thousands of times would hardly be noticeable. It's not like SharedPreferences is on a remote drive or has a "bad connection."
Second, if you don't like that answer, then you need a DAO (Data Access Object). SharedPreferences is a form of this already. It provides a means to store and retrieve data with confidence that you have the most recent data available. But, if you feel like you can improve on it's optimization (because it's generic, and this is your app), then you can provide access to you data through a static object that performs both "read" and "write" operations. This will guarantee that access to the object is done with the most recent data. Of course, you will need to be thread aware, etc. (something that is not always guaranteed by SharedPreferences).
Next, you could persist your data in a database and use Cursors or other built-in or custom DAOs. This requires another level of complexity and a lot of overhead, but is useful when several components of your app might need access to the data, provide updates or needs real-time monitoring of changes because background threads or other objects may make modifications that will change your app behavior or result in UI updates.
Last, you could use more complex data stores like a Content Provider. This is really required for cases where you want/need other apps to access data provided by your app (and your app may also consume the data). That's a complex solution and implementation is well outside the scope of this question.
But I mention it because you seem interested in being certain that frequent reads of SharedPreferences is acceptable. It definitely is acceptable - otherwise there would be something else besides it, databases and Content Providers.

How to transmit an Android Object over a Socket?

I would like to transmit an Android Object over a Socket with low overhead.
Should I use standard java.io.Serializable or android.os.Parcel?
Thank you!
Neither.
Parcel is designed for IPC, and other uses are albeit arguably efficient, dangerous. The documentation says
Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport. As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable.
This also means that you need to be sure that the remote side has got the exact same version of Android and of your code to be sure it will work.
Serializable has similar problems, and in the (android) dcumentation there is even a hint towards the solution:
Warning: this interface limits how its implementing classes can change in the future. By implementing Serializable you expose your flexible in-memory implementation details as a rigid binary representation. Simple code changes--like renaming private fields--are not safe when the changed class is serializable.
[...]
Recommended Alternatives: JSON is concise, human-readable and efficient. Android includes both a streaming API and a tree API to read and write JSON. Use a binding library like GSON to read and write Java objects directly.
If you want to move data from one device to another, I think you are better off creating a protocol for the data, ship it over, and populate the remote objects with that data. JSON is probably a good place to start.
Good luck!
If your message is longer, more complex or otherwise you need more efficiency, I would recommend to try use Google Protocol Buffers. They would allow some reasonable amount of alterations like adding new fields, or removing previously optional fields, or adding a completely new structure as a field. However you will also have the real classes with the real setters and getters (can be immutable versions or mutable builders, how do you prefer). Stricter control over that do you put is not as bad thing as may appear.
think XML, but smaller, faster, and simpler
The major drawback of serialized objects is not that you cannot alter the protocol details later. Differently, you can do many compatible changes if you introduce versions numbers and you cannot rename the property name in JSON either if you want legacy client to find that property.
However serialized objects are Java specific and you cannot have a more efficient C++ server client or use Python for prototyping, for instance, if you decide in the future. Protocol buffers are language neutral.
Transmitting an object through a socket depends a lot on what will be receiving the object. Usually such a transfer relies on a text-based serialisation such as XML or JSON.
First, define an object to send. As an example, we can define a class called Message to encapsulate our communications:
public class Message implements Serializable {
private static final long serialVersionUID = 1L; // Your version number
private int senderID;
private String messageText;
public Message(int id, String text) {
senderID = id;
messageText = text;
}
public String getText() {
return messageText;
}
}
Next, instantiate the object, wrap the socket's streams in object streams, then send the message across the socket:
Message sayhey = new Message("123456789", "Hello");
Socket socket = new Socket(host, port);
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(sayhey);
On the other side of the socket, the message can be retrieved and used by invoking methods on the returned object:
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Message messageObject = (Message) in.readObject();
String messageText = messageObject.getText();
you can implement Serializable.

pros and cons for implemeting a global object in Android/Java as singleton or DataClass

There are many questions and answers on how to implement a global variable in Android/Java.
So it seems one can either implement a singleton or use a data class itself with static variables.
I am about to start a larger project and would like to start on the right foot.
I am just not sure which one to use.
Pro singleton/con Data Class
supposedly "cleaner" way (but I really don't know why)
ensures that there is really always just one representation
creates a new instance should the old one be "cleaned away" (whenever this may happen?)
Con singleton/pro Data Class
not recommendet by some (but did not find convincng reasons)
ensures that there is only one representation by design
very easy to access just by writing MyDataClass.x (vs accessing singleton requires getting access to it first somehow)
no need to pass it as a parameter
So in summary I tend to use DataClass but I am unsure because I read that this is supposedly not good programming style.
I like to add
the data this global object has to hold is quite big, more than 30k strings/keys. And this should not be cleaned at any stage so that when the app return it may crash because of that - as I read in other places eg Singletons vs. Application Context in Android? (the 3rd answer)
it's not a web application, I use only one classloader
it is multithread but only one thread is actually accessing this data
one may certainly also use this approach How to declare global variables in Android?, but isn't an ObjectClass just easier to use and access in this case?
And checking this http://developer.android.com/resources/faq/framework.html, esp under "Persistent Objects", implies that there is no real advantage for on or the other in those cases anyway.
Many thanks
Best way to implement singleton is to use enum.
public enum Singleton
{
INSTANCE;
public void someMethod()
{
// your code here
}
}
For more details you can read Effective Java (2nd Edition)
First of all: There's not much difference between a class with public static member variables and a singleton class. A lot of developers prefer the singleton pattern because the code looks more natural and more Java. E.g. Singleton.Data looks like a constant access and Singleton.getData() looks like you're accessing some kind of static data.
Personally I use the static Application pattern: See Accessing resources without an Activity or Context reference
You can use onCreate to setup any kind of static data or even other singletons. E.g. I prefer to setup a singleton SQLite database like that and access it then via App.getDb(). You can use this pattern to access the application context or resources.
While using static data you should think about memory leeks. I would recommend to take a look at this article then.

Best method for saving data - preferences, sqlite, serializable or other?

I've been investigating alternative methods for saving my game's data between turns, and wonder if anyone can point me in the right direction.
I have approximately 32k of data which must be saved during onPause. I ruled out preferences due to the sheer quantity of data. I spent a few days playing around with SQLite but couldn't get the data to save in less than two seconds (although the time certainly hasn't been wasted).
I've decided that I'll use the database for loading constant data at the beginning of the game. This will certainly make it easier to tweak various parameters and default values in the game. But this still leaves me looking for the ideal method for writing data.
The data that needs to be saved is basically nine occurrences of class A and nine occurrences of class B. I'm an intensive month into the learning curve of Android (and the nuances of Java, coming from a C++ background) and have been googling like crazy. This brought two possibilities to mind -
1) Serialization (ObjectOutputStream)
I thought this would be the perfect solution but, having read several other posts regarding the subject, gather that it isn't highly recommended on the Android platform due to speed and memory allocations provoking the garbage collector into a potential rage.
2) DataOutputStream class
My current thought is to add Load and Save functions to both classes and to use DataOutputStream and DataInputStream calls in them to write and read the data respectively.
The data in the classes are primitives (strings and ints mostly) and arrays of primitives, so there's nothing too complicated in there to break down. Would this second solution seem a good, viable one? Or are there other solutions that I am unaware of as yet?
You should use an Async task to save the data, I used this method to fetch highscores at the start a game:
new HighscoreTask().execute(this);
the Async task looks like this:
public class HighscoreTask extends AsyncTask<MainView, Void, Void> {
protected void onPreExecute() {
}
protected void onPostExecute(final Void unused) {
}
#Override
protected Void doInBackground(MainView... params) {
HighScoreFactory.syncScores();
return null;
}
}
All the database interaction happens in HighScoreFactory.syncScores() this can take as long as it needs because it happens in the background. In my case it sends an HTTP request to an external server and loads these into a database. It's never caused any problems and works seamlessly.
Why do you have a 2 second limit on your database write? If it is purely for the sake of UI responsiveness, then there is another approach you can take.
You don't actually have to perform the save within your onPause method itself, you could just kick off a new Thread that actually does the save for you.
private void backgroundSave(){
Thread backgroundThread = new Thread() {
#Override
public void run() {
//do save here
}
};
backgroundThread.start();
}
#Override
protected void onPause() {
super.onPause();
backgroundSave();
}
You could alternatively use an AsyncTask for this.
You might have to consider the case when a user attempts to restart your app before the save is complete, but that shouldn't be too hard to take into account.
Have you tried insert data to the database in transaction?
try{
db.beginTransaction();
//here insert data to database
db.setTransactionSuccessful();
} finally {
db.endTranscation();
}
That can speed up operation.
Create a new Thread that does the data writing using Context.openFileOutput(String name, int mode) with this as the context. You can then write it in the background with the new thread and retrieve it with: Context.openFileInput(String name) again with this as the context. Hopefully this helps.

Categories

Resources