Android, SharedPreference lost when phone reboot - android

I got a strange problem, that my app's SharedPreference seems lost some specific keys (not all) when the phone reboot.
Have you ever meet this problem? I used that key to store a serialized object and I did that in my own Application class.
public class Application extends android.app.Application {
static String key = "favs";
SharedPreferences settings;
public Favs favs;
#Override
public void onCreate() {
super.onCreate();
settings = PreferenceManager.getDefaultSharedPreferences(this);
String value = settings.getString(key, "");
if (value != null && value.length() > 0) {
try {
Favs = (Favs ) deSerialize(value.getBytes());
} catch (Exception ex) {
}
}
if(favs == null)
favs = new Favs ();
}
public void storeFavss() {
if (favs == null)
return;
try {
byte[] bytes = serialize(favs );
if(bytes != null)
{
String s = new String(bytes);
settings.edit().putString(key, s);
settings.edit().commit();
}
} catch (Exception ex) {
}
}

After debugging, I will show my own anwser here, hope it can help others.
the code below is bad. it seems the edit() method returns a new object each time.
settings.edit().putString(key, s);
settings.edit().commit();
If you are saving some serialized object bytes in the SharedPreference, Base64 it!

favs = (Favs ) deSerialize(value.getBytes());

Related

How to get package name from share preference?

I am working on appLocker in which I have to set the password on installed application of device.
for that I have created a list in which I am getting all the installed application. User can select the the application by using checkBox and set the password on that. for that I have to get the package name of that application. I am able to get the package name of selected application and save them using Share Preferences. now I have to get that package name in an another activity. please tell me how can I get that.
this is the code where I am getting the package name.
public void onClick(View v) {
if (R.id.select_done_btn == v.getId()) {
SharedPreferences prefs = getSharedPreferences(getPackageName(), MODE_PRIVATE);
int[] indexes = appListAdapter.getSelectedItemIndexes();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < indexes.length; ++i) {
AppInfo appInfo = installedApps.get(indexes[i]);
sb.append(appInfo.getPackageName()).append(";");
}
Editor editor = prefs.edit();
editor.putString("lock_apps", sb.toString());
editor.commit();
and this the code section where I have to get that package name to lock the selected application in another activity..
public void run() {
while (true) {
Log.i("lock", "lockerThread run.");
String packname = activityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
if ("**PACKAGE NAME**".equals(packname)) {
startActivity(pwdIntent);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
In the above question
SharedPreferences prefs = getSharedPreferences(getPackageName() , MODE_PRIVATE);
prefs.getstring("key");
will give your saved string from SharedPreferences where ever you want in Application.
If you like you can go through other solution given as below for optimization
public void addTask(Task t) {
if (null == currentTasks) {
currentTasks = new ArrayList<task>();
}
currentTasks.add(t);
//save the task list to preference
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE);
Editor editor = prefs.edit();
try {
editor.putString(TASKS, ObjectSerializer.serialize(currentTasks));
} catch (IOException e) {
e.printStackTrace();
}
editor.commit();
}
Similarly we can retrieve the list of tasks from the preference inside onCreate() method:
public void onCreate() {
super.onCreate();
if (null == currentTasks) {
currentTasks = new ArrayList<task>();
}
// load tasks from preference
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE);
try {
currentTasks = (ArrayList<task>) ObjectSerializer.deserialize(prefs.getString(TASKS, ObjectSerializer.serialize(new ArrayList<task>())));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

How to maintain session in android?

Can anybody tell me how to maintain session for a user login. For example when the user sign- in to an application they have to be signed in unless the user logouts or uninstall the application similar to gmail in android.
Make one class for your SharedPreferences
public class Session {
private SharedPreferences prefs;
public Session(Context cntx) {
// TODO Auto-generated constructor stub
prefs = PreferenceManager.getDefaultSharedPreferences(cntx);
}
public void setusename(String usename) {
prefs.edit().putString("usename", usename).commit();
}
public String getusename() {
String usename = prefs.getString("usename","");
return usename;
}
}
Now after making this class when you want to use it, use like this: make object of this class like
private Session session;//global variable
session = new Session(cntx); //in oncreate
//and now we set sharedpreference then use this like
session.setusename("USERNAME");
now whenever you want to get the username then same work is to be done for session object and call this
session.getusename();
Do same for password
You can achieve this by using AccountManager.
Code Sample
// method to add account..
private void addAccount(String username, String password) {
AccountManager accnt_manager = AccountManager
.get(getApplicationContext());
Account[] accounts = accnt_manager
.getAccountsByType(getString(R.string.account_type)); // account name identifier.
if (accounts.length > 0) {
return;
}
final Account account = new Account(username,
getString(R.string.account_type));
accnt_manager.addAccountExplicitly(account, password, null);
final Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, username);
intent.putExtra(AccountManager.KEY_PASSWORD, password);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,
getString(R.string.account_type));
// intent.putExtra(AccountManager.KEY_AUTH_TOKEN_LABEL,
// PARAM_AUTHTOKEN_TYPE);
intent.putExtra(AccountManager.KEY_AUTHTOKEN, "token");
this.setAccountAuthenticatorResult(intent.getExtras());
this.setResult(RESULT_OK, intent);
this.finish();
}
// method to retrieve account.
private boolean validateAccount() {
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> arg0) {
Log.e("calback", "msg");
try {
Bundle b = arg0.getResult();
if (b.getBoolean(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE)) {
//User account exists!!..
}
} catch (OperationCanceledException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AuthenticatorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
AccountManager accnt_manager = AccountManager
.get(getApplicationContext());
Account[] accounts = accnt_manager
.getAccountsByType(getString(R.string.account_type));
if (accounts.length <= 0) {
return false;
} else {
loginNameVal = accounts[0].name;
loginPswdVal = accnt_manager.getPassword(accounts[0]);
return true;
}
}
I have one simple way rather than maintain a session.
i.e. Just store one boolean variable with your username and password. by default set value equal to false.
After first successful login make its value to true.
Then just check its value on your Mainactivity, if it is true then jump to next activity otherwise jump to login activity.
You can use a boolean value in the SharedPreferences.
Load it before login to check if login is needed.
Save it after login.
Use SharedPreferences.
Code to save a value to sharedpreferences:
SharedPreferences sp=getSharedPreferences("key", Context.MODE_PRIVATE);
SharedPreferences.Editor ed=sp.edit();
ed.putInt("value", your_value);
ed.commit();
Code to get value from sharedpreferences:
SharedPreferences sp=getSharedPreferences("key", Context.MODE_PRIVATE);
int value = sp.getInt("value", default_value);
You can check login and logout by using this value.
You can obtain that behaivour in a few different ways, the one I prefer is setting a flag in the shared prefs. whe a user logs in an check it when the app is started if you get the default value the user is not loggend, else you should have your flag (i use the user name) set and avoid the log-in section.
save the user data in shared preferences till the user logs out.
once user logs out clear the data from shared preferences.
public class Session {
private SharedPreferences prefs;
public Session(Context cntx) {
// TODO Auto-generated constructor stub
prefs = PreferenceManager.getDefaultSharedPreferences(cntx);
editor = prefs.edit();
}
public void setusename(String usename) {
editor.putString("usename", usename).commit();
}
public String getusename() {
String usename = prefs.getString("usename","");
return usename;
}
}
Source Code
https://drive.google.com/open?id=0BzBKpZ4nzNzUcUZxeHo0UnJ5UHc
Fetch Previous Login ID in android
**After Login save Email ID is SharedPreferences**
emaidId = et_Email.getText().toString().trim();
SharedPreferences ss = getSharedPreferences("loginSession_key", 0);
Set<String> hs = ss.getStringSet("set", new HashSet<String>());
hs.add(emaidId);
SharedPreferences.Editor edit = ss.edit();
edit.clear();
edit.putStringSet("set", hs);
edit.commit();
===================onCreate()====================
===================AutoCompleteTextView set Adapter===================
**Fetch PRevious Login Email id in email EditText**
SharedPreferences sss = getSharedPreferences("loginSession_key", 0); // todo loginSession_key key name ALWAYS SAME
Log.i("chauster", "2.set = " + sss.getStringSet("set", new HashSet<String>()));
Log.e("Session", "Value->" + sss.getStringSet("set", new HashSet<String()));
ArrayList<String> al = new ArrayList<>();
al.addAll(sss.getStringSet("set", new HashSet<String>()));
//Creating the instance of ArrayAdapter containing list of language names
ArrayAdapter<String> adapter = new ArrayAdapter<String>
(this, android.R.layout.select_dialog_item, al);
//Getting the instance of AutoCompleteTextView
et_Email.setThreshold(1);//will start working from first character
et_Email.setAdapter(adapter);//setting the adapter data into the
Using this class will help you to store all types of sessions
public class Session {
private SharedPreferences prefs;
public Session(Context cntx) {
// TODO Auto-generated constructor stub
prefs = PreferenceManager.getDefaultSharedPreferences(cntx);
}
public void set(String key,String value) {
prefs.edit().putString(key, value).commit();
}
public String get(String key) {
String value = prefs.getString(key,"");
return value;
}
}
Through this format, you can set or get multiple objects of data, you don't need to create separate functions to store different models in SharedPreferences.
* Here in this format, you don't need to pass your object KEY for putString() and getString()
* Using the object class name you are able to identify your session object uniquely, for both setModel() and getModel()
public class Session {
private final SharedPreferences pref;
public Session(Context ctx) {
pref = PreferenceManager.getDefaultSharedPreferences(ctx);
//pref = ctx.getSharedPreferences("IDENTIFIED_NAME", Context.MODE_PRIVATE);
}
public <U> void setModel(U obj) {
pref.edit().putString(getClassName(obj), SerializeObject(obj)).apply();
}
/* Parameter Example: Class.class */
public <U> U getModel(Class<U> type) {
String user = pref.getString(type.getSimpleName(), null);
if (isEmptyOrNull(user)) {
return null;
}
return DeserializeObject(user, type);
}
/* The below functions are for support, You can move the below part to your own BaseUtil class. */
public static boolean isEmptyOrNull(String data) {
if (data == null) {
return true;
}
if (data.isEmpty() || data.trim().isEmpty()) {
return true;
}
return false;
}
public static String getClassName(Object obj) {
return obj.getClass().getSimpleName();
}
public static String SerializeObject(Object myObject) {
// serialize the object
try {
Gson gson = new Gson();
String json = gson.toJson(myObject);
return json;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static <U> U DeserializeObject(String serializedObject, Class<U> type) {
// serialize the object
try {
if (serializedObject == null || serializedObject.isEmpty()) {
return null;
}
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
U data = gson.fromJson(serializedObject, type);
return data;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
}
Create an object class (file_name: UserModel.java)
public class UserModel {
public String userId;
public String firstName;
public String lastName;
public String email;
public String phone;
}
How to use - For setModel() and getModel()
//creating an instance of the Session class
Session session = new Session(ctx); // Here you have to pass the Context(ctx)
// setting value in the UserModel
UserModel obj = new UserModel();
obj.firstName = "Apu";
obj.lastName = "Pradhan";
obj.email = "godfindapu#gmail.com";
obj.phone = "123456789";
// set UserModel in the Session
session.setModel(obj);
//getting UserModel data from the Session
UserModel userData = session.getModel(UserModel.class);

Activity restarted when i press home button in android

I am afraid that my activity is restarted when i open it through TaskManager....
and my DefaultHttpClient object treated as fresh one..so here i am loosing the
session.
I tried by overriding the onSaveInstanceState() method..but no use..
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); // the UI component values are saved here.
}
How i can get rid of this one...
when you press button Home, activity will pause and resume when reopen.
you shout put code to onCreate().
see activity lifecryde:
You could subclass Android Applications: You can init the HttpClient there and hold the reference.
Look here
Than you can access from activity your Application object with activity.getApplication()
If your session works with cookies than you may need a persistent cookie storeage (like database or shared preferences):
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.http.client.CookieStore;
import org.apache.http.cookie.Cookie;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
/**
* A persistent cookie store which implements the Apache HttpClient
* {#link CookieStore} interface. Cookies are stored and will persist on the
* user's device between application sessions since they are serialized and
* stored in {#link SharedPreferences}.
* <p>
*/
public class PersistentCookieStore implements CookieStore {
private static final String COOKIE_PREFS = "CookiePrefsFile";
private static final String COOKIE_NAME_STORE = "names";
private static final String COOKIE_NAME_PREFIX = "cookie_";
private final ConcurrentHashMap<String, Cookie> cookies;
private final SharedPreferences cookiePrefs;
/**
* Construct a persistent cookie store.
*/
public PersistentCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
cookies = new ConcurrentHashMap<String, Cookie>();
// Load any previously stored cookies into the store
String storedCookieNames = cookiePrefs.getString(COOKIE_NAME_STORE,
null);
if (storedCookieNames != null) {
String[] cookieNames = TextUtils.split(storedCookieNames, ",");
for (String name : cookieNames) {
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX
+ name, null);
if (encodedCookie != null) {
Cookie decodedCookie = decodeCookie(encodedCookie);
if (decodedCookie != null) {
cookies.put(name, decodedCookie);
}
}
}
// Clear out expired cookies
clearExpired(new Date());
}
}
#Override
public synchronized void addCookie(Cookie cookie) {
String name = cookie.getName() + cookie.getDomain();
// Save cookie into local store, or remove if expired
if (!cookie.isExpired(new Date())) {
cookies.put(name, cookie);
} else {
cookies.remove(name);
}
// Save cookie into persistent store
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(COOKIE_NAME_STORE,
TextUtils.join(",", cookies.keySet()));
prefsWriter.putString(COOKIE_NAME_PREFIX + name,
encodeCookie(new SerializableCookie(cookie)));
prefsWriter.commit();
}
#Override
public synchronized void clear() {
// Clear cookies from persistent store
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for (String name : cookies.keySet()) {
prefsWriter.remove(COOKIE_NAME_PREFIX + name);
}
prefsWriter.remove(COOKIE_NAME_STORE);
prefsWriter.commit();
// Clear cookies from local store
cookies.clear();
}
#Override
public synchronized boolean clearExpired(Date date) {
boolean clearedAny = false;
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for (ConcurrentHashMap.Entry<String, Cookie> entry : cookies.entrySet()) {
String name = entry.getKey();
Cookie cookie = entry.getValue();
if (cookie.isExpired(date)) {
// Clear cookies from local store
cookies.remove(name);
// Clear cookies from persistent store
prefsWriter.remove(COOKIE_NAME_PREFIX + name);
// We've cleared at least one
clearedAny = true;
}
}
// Update names in persistent store
if (clearedAny) {
prefsWriter.putString(COOKIE_NAME_STORE,
TextUtils.join(",", cookies.keySet()));
}
prefsWriter.commit();
return clearedAny;
}
#Override
public synchronized List<Cookie> getCookies() {
return new CopyOnWriteArrayList<Cookie>(cookies.values());
}
//
// Cookie serialization/deserialization
//
protected synchronized String encodeCookie(SerializableCookie cookie) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(cookie);
} catch (Exception e) {
return null;
}
return byteArrayToHexString(os.toByteArray());
}
protected synchronized Cookie decodeCookie(String cookieStr) {
byte[] bytes = hexStringToByteArray(cookieStr);
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
Cookie cookie = null;
try {
ObjectInputStream ois = new ObjectInputStream(is);
cookie = ((SerializableCookie) ois.readObject()).getCookie();
} catch (Exception e) {
e.printStackTrace();
}
return cookie;
}
// Using some super basic byte array <-> hex conversions so we don't have
// to rely on any large Base64 libraries. Can be overridden if you like!
protected synchronized String byteArrayToHexString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length * 2);
for (byte element : b) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
protected synchronized byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character
.digit(s.charAt(i + 1), 16));
}
return data;
}
}
Do something like this:
httpClient.setCookieStoreage(new PersistentCookieStore(this)) in your application subclass where you init the httpclient
You are probably seeing a nasty, long-standing Android bug that causes the symptoms you are describing. Have a look at my answer here: https://stackoverflow.com/a/16447508/769265
You probably have to add following code inside the onCreate() event of launcher Activity.
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String action = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
finish();//Launcher Activity is not the root. So,finish it instead of launching
return;
}
}
android:launchMode="singleInstance"
android:alwaysRetainTaskState="true"
Try adding this two attributes to your Activity in manifest, this will make sure newIntent is called when activity is resumed from background.

I cannot track referrer_install of google play

I am trying to report the install event to flurry.
I have implemented ReferralReceiver to be called when the app is being installed.
I have all the parameters in a map.
I am calling Flurry using my flurry code:
FlurryAgent.onStartSession(context, FlurryCode);
FlurryAgent.logEvent("Referral", referralParams);
FlurryAgent.onEndSession(context);
The data is being stored to the SharedPreferences but it is not being sent to flurry.
Has anyone encountered this problem.
#Override
public void onReceive(Context context, Intent intent)
{
LogManager.Info("ReferralReceiver intent.action=" + intent.getAction());
LogManager.Info("ReferralReceiver intent.DataString=" + intent.getDataString());
LogManager.Info("ReferralReceiver intent.intent=" + intent.toString());
// Workaround for Android security issue: http://code.google.com/p/android/issues/detail?id=16006
try
{
final Bundle extras = intent.getExtras();
if (extras != null) {
extras.containsKey(null);
}
}
catch (final Exception e) {
return;
}
Map<String, String> referralParams = new HashMap<String, String>();
// Return if this is not the right intent.
if (! intent.getAction().equals("com.android.vending.INSTALL_REFERRER")) { //$NON-NLS-1$
return;
}
String referrer = intent.getStringExtra("referrer"); //$NON-NLS-1$
if( referrer == null || referrer.length() == 0) {
return;
}
try
{
referrer = URLDecoder.decode(referrer, "UTF-8");
}
catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
LogManager.Info("ReferralReceiver intent.referrer=" + intent.getStringExtra("referrer"));
SharedPreferences storage = context.getSharedPreferences(ReferralReceiver.PREFS_FILE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = storage.edit();
editor.putString("referrer", referrer);
editor.commit();
try {
if (intent.hasExtra("referrer")) {
String referrers[] = referrer.split("&");
for (String referrerValue : referrers)
{
String keyValue[] = referrerValue.split("=");
if (keyValue.length==2)
{
referralParams.put(keyValue[0], keyValue[1]);
}
}
}
}
catch (Exception e)
{
LogManager.Error(e);
return;
}
ReferralReceiver.storeReferralParams(context, referralParams);
String FlurryCode = SaverrUtils.GetFlurryCode();
FlurryAgent.onStartSession(context, FlurryCode);
FlurryAgent.logEvent("Referral", referralParams);
FlurryAgent.onEndSession(context);
LogManager.Info("New_Install");
}
public final static String PREFS_FILE_NAME = "ReferralParamsFile";
/*
* Stores the referral parameters in the app's sharedPreferences.
* Rewrite this function and retrieveReferralParams() if a
* different storage mechanism is preferred.
*/
public static void storeReferralParams(Context context, Map<String, String> params)
{
SharedPreferences storage = context.getSharedPreferences(ReferralReceiver.PREFS_FILE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = storage.edit();
for(String key : SaverrUtils.EXPECTED_REFERRER_PARAMETERS)
{
String value = params.get(key);
if(value != null)
{
editor.putString(key, value);
}
}
editor.commit();
}
// Referral Parameters
public final static String[] EXPECTED_REFERRER_PARAMETERS = {
"utm_source",
"utm_medium",
"utm_term",
"utm_content",
"utm_campaign",
"referrer"
};
I think flurry has a minimum amount of seconds between start/end session for it to actually count. You can save all the data to shared preference or file, then when the user starts your activity check if it's been given to flurry. If it's new do FlurryAgent.logEvent(...).

How to serialize a Bundle?

I'd like to serialize a Bundle object, but can't seem to find a simple way of doing it. Using Parcel doesn't seem like an option, since I want to store the serialized data to file.
Any ideas on ways to do this?
The reason I want this is to save and restore the state of my activity, also when it's killed by the user. I already create a Bundle with the state I want to save in onSaveInstanceState. But android only keeps this Bundle when the activity is killed by the SYSTEM. When the user kills the activity, I need to store it myself. Hence i'd like to serialize and store it to file. Of course, if you have any other way of accomplishing the same thing, i'd be thankful for that too.
Edit:
I decided to encode my state as a JSONObject instead of a Bundle. The JSON object can then be put in a Bundle as a Serializable, or stored to file. Probably not the most efficient way, but it's simple, and it seems to work ok.
storing any Parcelable to a file is very easy:
FileOutputStream fos = context.openFileOutput(localFilename, Context.MODE_PRIVATE);
Parcel p = Parcel.obtain(); // i make an empty one here, but you can use yours
fos.write(p.marshall());
fos.flush();
fos.close();
enjoy!
I use SharedPreferences to get around that limitation, it uses the same putXXX() and getXXX() style of storing and retrieving data as the Bundle class does and is relatively simple to implement if you have used a Bundle before.
So in onCreate I have a check like this
if(savedInstanceState != null)
{
loadGameDataFromSavedInstanceState(savedInstanceState);
}
else
{
loadGameDataFromSharedPreferences(getPreferences(MODE_PRIVATE));
}
I save my game data to a Bundle in onSaveInstanceState(), and load data from a Bundle in onRestoreInstanceState()
AND
I also save game data to SharedPreferences in onPause(), and load data from SharedPreferences in onResume()
onPause()
{
// get a SharedPreferences editor for storing game data to
SharedPreferences.Editor mySharedPreferences = getPreferences(MODE_PRIVATE).edit();
// call a function to actually store the game data
saveGameDataToSharedPreferences(mySharedPreferences);
// make sure you call mySharedPreferences.commit() at the end of your function
}
onResume()
{
loadGameDataFromSharedPreferences(getPreferences(MODE_PRIVATE));
}
I wouldn't be surprised if some people feel this is an incorrect use of SharedPreferences, but it gets the job done. I have been using this method in all my games (nearly 2 million downloads) for over a year and it works.
Convert it to SharedPreferences:
private void saveToPreferences(Bundle in) {
Parcel parcel = Parcel.obtain();
String serialized = null;
try {
in.writeToParcel(parcel, 0);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
IOUtils.write(parcel.marshall(), bos);
serialized = Base64.encodeToString(bos.toByteArray(), 0);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.toString(), e);
} finally {
parcel.recycle();
}
if (serialized != null) {
SharedPreferences settings = getSharedPreferences(PREFS, 0);
Editor editor = settings.edit();
editor.putString("parcel", serialized);
editor.commit();
}
}
private Bundle restoreFromPreferences() {
Bundle bundle = null;
SharedPreferences settings = getSharedPreferences(PREFS, 0);
String serialized = settings.getString("parcel", null);
if (serialized != null) {
Parcel parcel = Parcel.obtain();
try {
byte[] data = Base64.decode(serialized, 0);
parcel.unmarshall(data, 0, data.length);
parcel.setDataPosition(0);
bundle = parcel.readBundle();
} finally {
parcel.recycle();
}
}
return bundle;
}
In case you want to store it in persistent storage you can't rely on parcelable nor serializable mechanism. You have to do it by yourself and below is the way how I usually do it:
private static final Gson sGson = new GsonBuilder().create();
private static final String CHARSET = "UTF-8";
// taken from http://www.javacamp.org/javaI/primitiveTypes.html
private static final int BOOLEAN_LEN = 1;
private static final int INTEGER_LEN = 4;
private static final int DOUBLE_LEN = 8;
public static byte[] serializeBundle(Bundle bundle) {
try {
List<SerializedItem> list = new ArrayList<>();
if (bundle != null) {
Set<String> keys = bundle.keySet();
for (String key : keys) {
Object value = bundle.get(key);
if (value == null) continue;
SerializedItem bis = new SerializedItem();
bis.setClassName(value.getClass().getCanonicalName());
bis.setKey(key);
if (value instanceof String)
bis.setValue(((String) value).getBytes(CHARSET));
else if (value instanceof SpannableString) {
String str = Html.toHtml((Spanned) value);
bis.setValue(str.getBytes(CHARSET));
} else if (value.getClass().isAssignableFrom(Integer.class)) {
ByteBuffer b = ByteBuffer.allocate(INTEGER_LEN);
b.putInt((Integer) value);
bis.setValue(b.array());
} else if (value.getClass().isAssignableFrom(Double.class)) {
ByteBuffer b = ByteBuffer.allocate(DOUBLE_LEN);
b.putDouble((Double) value);
bis.setValue(b.array());
} else if (value.getClass().isAssignableFrom(Boolean.class)) {
ByteBuffer b = ByteBuffer.allocate(INTEGER_LEN);
boolean v = (boolean) value;
b.putInt(v ? 1 : 0);
bis.setValue(b.array());
} else
continue; // we do nothing in this case since there is amazing amount of stuff you can put into bundle but if you want something specific you can still add it
// throw new IllegalStateException("Unable to serialize class + " + value.getClass().getCanonicalName());
list.add(bis);
}
return sGson.toJson(list).getBytes(CHARSET);
}
} catch (Exception e) {
e.printStackTrace();
}
throw new IllegalStateException("Unable to serialize " + bundle);
}
public static Bundle deserializeBundle(byte[] toDeserialize) {
try {
Bundle bundle = new Bundle();
if (toDeserialize != null) {
SerializedItem[] bundleItems = new Gson().fromJson(new String(toDeserialize, CHARSET), SerializedItem[].class);
for (SerializedItem bis : bundleItems) {
if (String.class.getCanonicalName().equals(bis.getClassName()))
bundle.putString(bis.getKey(), new String(bis.getValue()));
else if (Integer.class.getCanonicalName().equals(bis.getClassName()))
bundle.putInt(bis.getKey(), ByteBuffer.wrap(bis.getValue()).getInt());
else if (Double.class.getCanonicalName().equals(bis.getClassName()))
bundle.putDouble(bis.getKey(), ByteBuffer.wrap(bis.getValue()).getDouble());
else if (Boolean.class.getCanonicalName().equals(bis.getClassName())) {
int v = ByteBuffer.wrap(bis.getValue()).getInt();
bundle.putBoolean(bis.getKey(), v == 1);
} else
throw new IllegalStateException("Unable to deserialize class " + bis.getClassName());
}
}
return bundle;
} catch (Exception e) {
e.printStackTrace();
}
throw new IllegalStateException("Unable to deserialize " + Arrays.toString(toDeserialize));
}
You represent data as byte array which you can easily store to file, send via network or store to sql database using ormLite as follows:
#DatabaseField(dataType = DataType.BYTE_ARRAY)
private byte[] mRawBundle;
and SerializedItem:
public class SerializedItem {
private String mClassName;
private String mKey;
private byte[] mValue;
// + getters and setters
}
PS: the code above is dependent on Gson library (which is pretty common, just to let you know).

Categories

Resources