How to save and load presets of Android activity? - android

I have an Activity that consists of multiple containers (FrameLayouts, more specifically) in order to display Fragments. These Fragments are actually audio effects, so once I choose a number of fragments to show in their corresponding containers and adjust their knobs (typical guitar effects stuff), I would like to save and load their presets.
In another words, I want to save the view of my Activity (with data of which effect is in which container, what values are the knobs etc.) to a file, and then load the file. On load, I would like my Activity to switch to the view I saved in file.
I have made the interface for saving and loading, but I can't find a way to save and load my views. I've found numerous examples of saving and loading views on orientation change, but not one example of saving and loading in files.
Here are the snippets of my code for saving and loading, too bad it doesn't work. Actually, this was just "mushed" by me via some marginal instructions I've found on the internet, perhaps I couldn't be farther from the answer:
public void save_file (Object file, String fileName, Context context) {
try {
FileOutputStream fos = context.openFileOutput(fileName, context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(MainActivity.this);
oos.writeObject(file);
oos.flush();
oos.close();
fos.close();
if (fileName != null) {
Toast.makeText(context, "Save successful", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Enter name", Toast.LENGTH_LONG).show();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static MainActivity load_file(String fileName, Context context) {
MainActivity Main = null;
try {
FileInputStream fis = context.openFileInput(fileName);
ObjectInputStream is = new ObjectInputStream(fis);
Main = (MainActivity) is.readObject();
is.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return Main;
}
I also don't know a way to switch a view to the loaded one. Thank you for your help.

So right now it looks like all you're doing is creating a new instance of MainActivity in load_file, but that's not really going to do you any good. In Android, you don't deal with creating instances of your Activity classes, the system will do that for you.
It sounds to me like for what you're trying to do, you should be using the shared preferences to store the information about your views, then re-render the Views with all the proper values in the onCreate method of MainActivity. If you're unfamiliar with SharedPreferences, you can look at the docs here: http://developer.android.com/reference/android/content/SharedPreferences.html
Basically, it's just writing to a file like you're already doing, but abstracted a little so that it's more convenient. You want to have a field in MainActivity called mPrefs, initialize it in onCreate like this:
mPrefs = getSharedPreferences(<unique name here>, Context.MODE_PRIVATE);
For example, say a user adjusts a guitar knob to a tension of 6 (I'm imagining that gets stored as an int)
SharedPreferences.Editor editor = mPrefs.edit();
//you should actually store the key string "guitar_knob_1" as a static final global variable rather than hard-coding
editor.putInt("guitar_knob_1", 6);
editor.commit();
Now, back in onCreate, you can have something like this:
if(mPrefs.getInt("guitar_knob_1", -1) != -1) { //-1 is the value we get if nothing is stored there
//set the knob to the value we read
}
The way it would make sense to me to do what you're asking would be to store a String of the names of the Views you have associated with each FrameLayout, then write some functions to render the correct Views in onCreate. You don't really need to "save" the views, just save all the values associated with them and rebuild them correctly when the activity opens. Hope that helps!

Related

How to restore data in any activity after returning from another activity?

When i start my android app, it loads large amount of data from server in mainactivity and adapt it with listView and when listview is clicked , new activity will launch . When return back to mainactivity from current activity , my app again load data from server that was previously loaded? How can i use 1st time loaded data after returning back to same activity next time??
There are several possible approaches to your problem depending on what you want to do. First of all, in the activity where you download and process the data, you should override the onSaveInstanceState and onRestoreInstanceState methods so that your data is persisted and survives orientation changes (and not loose the work you did processing the data).
If your application simply opens a detail activity, from the main activity, then when you press back, your data will be available. You do not have to reload anything. If this is not the case, then there may be some problem with how you load the data in the activity's lifecycle (e.g. avoid loading and processing data in the onStart/onResume methods).
If you want to persist data after your application has died, you can either use http caching (e.g. with OkHttp) or use an Sqlite database, as others have pointed out. These approaches can also be combined for additional performance gains.
You can cache the server response in shared preference in form of JSON and store to avoid making server calls each time. (However this is recommended only when data is small)
Example :
public <T> void putJsonIntoSharedPreferences(String key, T object) {
SharedPreferences sharedPreferences = getSharedPreferences();
SharedPreferences.Editor edit = sharedPreferences.edit();
String json = convertToJson(object);
edit.putString(key, json);
edit.commit();
}
public <T> T getFromSharedPreferences(String key, Class<T> clazz) {
SharedPreferences sharedPreferences = getSharedPreferences();
final String fromSharedPreferences = sharedPreferences.getString(key, "");
T object = null;
if (!fromSharedPreferences.equals("")) {
try {
object = JsonConverter.fromJson(fromSharedPreferences, clazz);
} catch (IOException e) {
return null;
}
} else {
try {
return clazz.newInstance();
} catch (Exception e) {
return null;
}
}
return object;
}
private <T> T fromJson(String json, Class<T> clazz) throws IOException {
ObjectMapper objectMapper = getObjectMapper();
if(json!=null)
return objectMapper.readValue(json,clazz);
return null;
}
private String convertToJson(Object object) {
ObjectMapper objectMapper = new ObjectMapper();
String string = null;
try {
string = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
Log.d(ComponentConstants.JSON_PARSE_ERROR_TAG,e.getMessage(),e);
}
return string;
}
You can simply use an http client with caching like OkHttp. more complicated yet more appropriate in some cases is to manage an internal Sqlite Database.
As a Side note You can save all your data and load it when activity is reloaded but you should not. as no metter what way you choose, IO will be involved and its going to be a heavy operation if your data is big enough.
Its good practice to load exactly what the activity needs to show when its being loaded, and not the full data. You should use CursorLoader or CursorAdapter to load dynamically what your activity needs as user interacts with it...

Saving data upon closing app and retrieving that data

I know, there are plenty of questions in regards to saving/retrieving data on here. I was doing find looking things up on my own and really thought I could manage to find my answers without having to "ask a question", but I began to wonder something that I haven't seen an answer for on here.
MY SITUATION:
Naturally, I'm making an app. Upon closing the app, I want to save a simple array of numbers (0 or 1) or boolean values as it were. Upon starting the app, I want to search for that array, if it exists, and retrieve it for use within the app.
I began placing my code into the activity in which the array would be used. But, I started wondering if I would have to copy/paste the overridden onStop() function into all of my activities? Or do I do it in the main activity and somehow link the other activities.
Basically, no matter what state/activity the app is currently on when the app is closed, I want to be able to save the array of int/bool and open it back up when the app is started.
Maybe I didn't know how to search for what I wanted, so explaining it felt like the right thing to do.
I don't mind doing more searching, but if someone would point me in the right direction at the very least, I'd be extremely grateful.
EDIT: If there's a better way to do what I want than what I described (i.e. using a different state instead of onStop(), for instance), please feel free to throw out ideas. This is my first time actually having to deal with the activities' lifecycles and I'm a bit confused even after looking through the android development tutorials. I really think they're poorly done in most cases.
When you application needs to save some persistent data you should always do it in onPause() method and rather than onStop(). Because if android OS kills your process then onStop() and onDestroy() methods are never called. Similarly retrieve data in onResume() method.
Looking at the purpose you want to fulfill, SharedPreferences is all you want.
The documentation states:
"SharePreferences provides a general framework that allows you to save
and retrieve persistent key-value pairs of primitive data types. You
can use SharedPreferences to save any primitive data: booleans,
floats, ints, longs, and strings. This data will persist across user
sessions (even if your application is killed)."
Use SharedPreference to store small amount of data or use SQLite to store large amount of data.
See this link
http://developer.android.com/guide/topics/data/data-storage.html
Serialize an object and pass it around which is more dependable than shared preferences (had lots of trouble with consistency with shared preferences):
public class SharedVariables {
public static <S extends Serializable> void writeObject(
final Context context, String key, S serializableObject) {
ObjectOutputStream objectOut = null;
try {
FileOutputStream fileOut = context.getApplicationContext().openFileOutput(key, Activity.MODE_PRIVATE);
objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(serializableObject);
fileOut.getFD().sync();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
} finally {
if (objectOut != null) {
try {
objectOut.close();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
}
}
}
}
Then use a class to use:
public class Timestamps implements Serializable {
private float timestampServer;
public float getTimestampServer() {
return timestampServer;
}
public void setTimestampServer(float timestampServer) {
this.timestampServer = timestampServer;
}
}
Then wherever you want to write to the variable use:
SharedVariables.writeObject(getApplicationContext(), "Timestamps", timestampsData);
Best way to achieve that is:
create a class. Call it MySettings, or whatever suits you
in this class, define the array of ints / booleans you are going to share, as static. Create getter & setter method (property) to access that (also as static methods)
add a static load() method to MySettings that reads from SharedPreferences. When you launch the app (in your first activity or better in a subclass of Application) call MySettings.load(). This load method sets the array
add a static save() method. Public also. Now you can save from anywhere in you app. This save() method reads the array and writes in SharedPreferences
Code sample:
public class MySettings {
private static List<Integer> data;
public static void load() {
data = new ArrayList<Integer>();
// use SharedPreferences to retrieve all your data
}
public static void save() {
// save all contents from data
}
public static List<Integer> getData() {
return data;
}
public static void setData(List<Integer> data) {
MySettings.data = data;
}
}

Passing arrayadapter and arraylist between fragments

I have an activity that contains two fragments. I would like to pass data (an ArrayAdapter and an ArrayList) between the two fragments. User operations in Fragment 1 modifies both datatypes, which then need to be passed onto Fragment 2. Similarly, user operations in Fragement 2 also modify the two datatypes, which then need to be passed back to Fragment 1.
Can you please guide on the most elegant way to do it? I have been looking into parcelable and interface. Since, I do not have much experience with Java (let alone android) I was not able to discern the limitations of two approach.
I'd suggest holding a reference to your data object in each fragment (as I'm sure you are) and do something like the following:
public void onResume()
{
mDataObject = getFragmentManager.getFragmentByTag("Fragment1").getDataObject1();
super.onResume();
}
you can run this in Frag 1 and Frag 2 and it should update the model. If you have sub objects you will need to compare them and determine if the sub-objects are different in a function something like this.
public void determineIfDifferent(DataObject mData1)
{
Field mData1Fields[] = mData1.getClass().getFields();
Field mData2Fields[] = mData2.getClass().getFields();
for (int i = 0; i < mData1Fields.length; i++)
{
try
{
if (mDataFields[i].get(mData) != null && tempFields[i].get(PS)!= null)
{
String mDataValue = mDataFields[i].get(mData).toString().trim();
String tempValue = tempFields[i].get(PS).toString().trim();
if (!mDataValue.equals(tempValue))
{
differenceList.add(tempValue);
}
}
}
catch (IllegalArgumentException e)
{
Logger.logStackTrace(getClass().getSimpleName(), e);
}
catch (IllegalAccessException e)
{
Logger.logStackTrace(getClass().getSimpleName(), e);
}
}
}
This obviously can be modified if the type is not a String - this is just what I had at hand
You can put them into Intent.putExtra() and vice versa

Serializing Android Views - causes InvalidClassException from IllegalAccessException

I'm trying to make an Android app that will store widgets (represented as Views) in a database, and then be able to recreate them later on.
Here is the gist of it:
public class DBSerializer<T extends View & Serializable> {
public int storeWidget(T widget) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bytes);
out.writeObject(widget);
byte[] data = bytes.toByteArray();
bytes.close();
out.close();
<store "data" in database>;
return <unique database id>;
}
public T getWidget(int id) {
byte[] data = <get data from database for the given id>;
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
return (T)in.readObject();
}
}
Unfortunately, the in.readObject() throws an InvalidClassException with the message android.view.View; IllegalAccessException.
Since Android's View directly extends Object, and Object has a no-argument constructor, shouldn't this work? Or is it trying to call View with a no-argument constructor? The error message is not being very clear about what the exact cause of the exception is.
Does anyone know what is causing this and how to fix it?
Just because the base class has a parameterless constructor, that does not mean that subclasses will. TextView does not, nor do most other views.
Serializing a View in its entirety and retrieving it later doesn't seem like a good idea, since a View has a context, which may have been destroyed in the time between you commit the serialized version and the time your retrieve it.
Perhaps you could store only the data you need in a Parcel, and use that to get a byte array, which you could then store in your database as a blob. Chances are, you might find this approach useful anyways in conjunction with the onSaveInstanceState() method.

I am trying to update a textview from a different activity and it keeps crashing

On a button click I am reading some information from a file and I need to display this in a textview, this code works while I am in the activity with the layout but if I am in a different screen the same code doesn't work.
String sdcard = Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(sdcard + "/Mult/","boardname.cfg");
//Read text from file
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
Global.defaultboard = line;
}
TextView defaultboard = (TextView) findViewById(R.id.currentboard);
defaultboard.setText(Global.defaultboard);
}
catch (IOException e) {
//You'll need to add proper error handling here
}
finish();
Can anyone see whats wrong? if I remove the lines aor comment the lines of the textview it doesn't crash.
Thanks
MrC
OK
My Solution is - my value is stored in a global variable so I use the onResume() like this to refresh the textview.
public void onResume()
{
super.onResume();
TextView defaultboard = (TextView) findViewById(R.id.currentboard);
defaultboard.setText(Global.defaultboard);
}
MrC
you can only update UI views when running in the main thread. If you do stuff in a different thread use the runOnUIThread method of the activity passing a little Runnable that does the actual work.
Well, each activities has his own layout so you can't update items through different activities directly.
What you need is to take a look at "Starting Activities and Getting Results"
Have you ever thought of using SharedPreferences?
Make a string for the text you want to display in this class.
Write this string to shared preferences using putString("key", value).
In the activity that has the TextView you need to update, initialize it to something as to not get a NullPointerException. Then load the string from your SharedPreferences and set the text from there.
If you want to be able to set it dynamically, you can make a updateText() method in the activity that has the TextView to change it, because if the activity is still running, it will not update as onCreate() is not called. Just be sure to check what state it's in, because if that activity is not currently in existence (onDestroy() has been called), you will get another exception.
Hope this helps. I have found that SharedPreferences are the answer to a lot of my problems. If you need help, just let me know and I'll try to help you out.
OK
My Solution is - my value is stored in a global variable so I use the onresume like this to refresh the textview
public void onResume()
{
super.onResume();
TextView defaultboard = (TextView) findViewById(R.id.currentboard);
defaultboard.setText(Global.defaultboard);
}
Thanks to everyone for answering, I like some of these ideas and am looking into how they work!

Categories

Resources