I have a Hashmap in a Fragment variable for a Position(int) to View Relationship.
sometimes I can read the Object in onCreate but with no data ( size 0) , However when I want to store it when onPause or onStop fires of the fragment to internal storage , it gives me a weird error without any clue what cause it:
android.widget.RelativeLayout
Fragment onPause:
#Override
protected void onPause() {
super.onPause();
try
{
FileOutputStream fileOutStream = getContext().openFileOutput("hashMap", Context.MODE_PRIVATE);
ObjectOutputStream objOutStream = new ObjectOutputStream(fileOutStream);
objOutStream.writeObject(HashMap);
objOutStream.close();
Log.v("Fragment:", "Done Writing "+HashMap.size());
}catch (IOException e)
{
Log.v("Fragment:","MainPage(onStop): "+e.getMessage()+e.getLocalizedMessage());
}
}
the following lines to read from internal storage when opening the app.
try {
FileInputStream f = getContext().openFileInput("hashMap");
ObjectInputStream s = new ObjectInputStream(f);
HashMap= (HashMap<Integer, View>) s.readObject();
s.close();
Log.v("Fragment:", "Done Reading " + HashMap.size());
} catch (ClassNotFoundException | IOException e) {
Log.v("Fragment:", "MainPage(onCreate): " + e.getMessage());
}
I think
Only objects that support the java.io.Serializable interface can be written to streams
according to ObjectOutputStream
The View does not seem to implement this interface.
Related
I am trying to save in cache response from server for certain time.
There are tne next data for saving in cache: I have a List<ProgrammeItem> which I am getting from server. While user is working, he can download up to ~230 List<ProgrammeItem> (but it is unreal to reach this, estimated is 10-50).
ProgrammeItem oblect including strings, int, int[].
That is how I am saving and getting the last downloaded List<ProgrammeItem>:
//saving / getting Programme items
public boolean saveObject(List<ProgrammeItem> obj) {
final File suspend_f=new File(android.os.Environment.getExternalStorageDirectory(), "test");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
boolean keep = true;
try {
fos = new FileOutputStream(suspend_f);
oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
} catch (Exception e) {
keep = false;
Log.e("catching exception ", "" + e.getMessage() + ";;;" + e);
} finally {
try {
if (oos != null) oos.close();
if (fos != null) fos.close();
if (keep == false) suspend_f.delete();
} catch (Exception e) { /* do nothing */ }
}
return keep;
}
public List<ProgrammeItem> getObject(Context c) {
final File suspend_f=new File(android.os.Environment.getExternalStorageDirectory(), "test");
List<ProgrammeItem> simpleClass= null;
FileInputStream fis = null;
ObjectInputStream is = null;
try {
fis = new FileInputStream(suspend_f);
is = new ObjectInputStream(fis);
simpleClass = (List<ProgrammeItem>) is.readObject();
} catch(Exception e) {
String val= e.getMessage();
} finally {
try {
if (fis != null) fis.close();
if (is != null) is.close();
} catch (Exception e) { }
}
return simpleClass;
}
That is how I am saving and getting object in activity:
PI = new ProgrammeItem();
List<ProgrammeItem> programmeItems = new ArrayList<>();
...
//filling programmeItems with data from server
...
boolean result = PI.saveObject(programmeItems); //Save object
ProgrammeItem m = new ProgrammeItem();
List<ProgrammeItem> c = m.getObject(getApplicationContext()); //Get object
The question is: how can I save a lot of my objects instead of only one?
I think I should done something like public boolean addObjectsInCache(List<ProgrammeItem> obj) for adding objects, not overriding them.
And change get method into public List<ProgrammeItem> getObject(Context c, String id), where id will be unique identifier, which will includes into every ProgrammeItem in the every List<ProgrammeItem>.
Am I right? And how I can achieve this? Maybe you will show me the other way to work with objects and cache?
You can use SharedPreference instead, while having a local database Android Room can also be an option. SharedPreference basically is stored in your device's cache while the local database is stored in your device's data hence in our apps we have clear cache and clear data function.
Additional Resources:
StackOverFlow: How Android SharedPreferences save/store object
Object based preference library: https://github.com/ShawnLin013/PreferencesManager I would suggest you go with this one, since it can easily save you time saving list based object and retrieving them. You can also add more to the persisted list object when needed.
Secured Preferences: https://github.com/scottyab/secure-preferences
An option could be to use Room database with inMemoryDatabaseBuilder:
db = Room.inMemoryDatabaseBuilder(context, ProgrammeDatabase::class.java)
.build()
if it all can fit in memory.
I am trying to write and read a text file which is full of words and add it to an ArrayList. The ArrayList later is used from another part of the program to display text in a TextView. But when i run the program and open the specific part of it, then there is nothing. The ArrayList is just empty. I don't get any exceptions but for some reason it doesn't work. Please help me.
I don't seem to have problems with the file writing:
TextView txt = (TextView)findViewById(R.id.testTxt);
safe = txt.getText().toString();
try {
FileOutputStream fOut = openFileOutput("test.txt", MODE_WORLD_READABLE);
OutputStreamWriter osw = new OutputStreamWriter(fOut);
try {
osw.write(safe);
osw.flush();
osw.close();
Toast.makeText(getBaseContext(), "Added to favorites", Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException ex){
Log.e("Exception", "File write failed: ");
}
} catch (IOException e) {
Log.e("Exception", "File write failed: ");
}
But I think the problem is in the file reading. I made some "Log.d" and found out that everything works fine till the InputStreamReader line:
public favHacks() {
testList = new ArrayList<String>();
try {
//Works fine till here
InputStreamReader inputStreamReader = new InputStreamReader(openFileInput("test.txt"));
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString = "";
while ( (receiveString = bufferedReader.readLine()) != null )
{
testList.add(receiveString);
}
bufferedReader.close();
}
catch (FileNotFoundException ex) {
Log.d("login activity", "File not found: ");
} catch (IOException ex) {
Log.d("login activity", "Can not read file: ");
}
}
If you have a relatively small collection of key-values that you'd like to save, you should use the SharedPreferences APIs.
http://developer.android.com/training/basics/data-storage/shared-preferences.html
also if you want to write/read files, or do any kind of operations that can block the Main Thread try to use another Thread like when you are trying to save the data in a file or use a Handler if you have multiple Threads (one for saving and one for reading).
https://developer.android.com/training/multiple-threads/index.html
In your code the method called favHacks can return an ArrayList with the list of all the strings Something like
//
public ArrayList<String> readFromFile(String file){
ArrayList<String> mArrayList= new ArrayList<String>();
//read from file here
return mArrayList;
}
but as I said before, you need to the operations that can block the UI thread in a new Thread.
https://developer.android.com/training/multiple-threads/communicate-ui.html
And also I think that the best way to do this is using Asynk task
Why and how to use asynctask
I have a custom object class Record that implements Parcelable and I'm creating a ListView via ArrayAdapter<Record> I want to be able to save that list so that it automatically loads the next time the user opens the app. The list is populated dynamically and I'm calling my save method everytime a record is added. Then I have a SharedPreference with a boolean value that I set to true so that I know the user has saved some data to load the next time the app is open. Here are my save and load methods:
public void writeRecordsToFile(ArrayAdapter<Record> records) {
String fileName = Environment.getExternalStorageDirectory().getPath() + "/records.dat";
try {
File file = new File(fileName);
if(!file.exists()){
file.createNewFile();
}
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file));
stream.writeObject(records);
stream.flush();
stream.close();
}
catch (IOException e){
Log.e("MyApp","IO Exception: " + e);
}
writeSavedState();
}
the writeSavedState() is for my SP
public void readRecordsList() {
String fileName = Environment.getExternalStorageDirectory().getPath() + "/records.dat";
try {
ObjectInputStream inputStream = new ObjectInputStream(getApplicationContext().openFileInput(fileName));
adapter = (ArrayAdapter<Record>)inputStream.readObject();
inputStream.close();
}
catch (Exception e){
Log.e("MyApp" , "File Not Found: " + e);
}
}
When I first open the app I get a message:
E/MyApp﹕ File Not Found: java.lang.IllegalArgumentException: File /storage/emulated/0/records.dat contains a path separator
and then when I add a Record to my list I get the message:
E/MyApp﹕ IO Exception: java.io.IOException: open failed: EACCES (Permission denied)
The second message I'm assuming I'm getting because of the first message. This is my first time working with I/O in Android so any help would be appreciated!
EDIT
After adding the permissions to the manifest I'm now only getting an error:
E/MyApp﹕ IO Exception: java.io.NotSerializableException: android.widget.ArrayAdapter
As I said, my custom object is Parcelable and the rest of this is being done in my MainActivity. Do I need to make a new class that is Serializable to build my ArrayAdapter?
I would suggest to save the records in internal storage in private mode,which can be accessed by your app only.If you store it in External storage, there is no guarantee that it will be available next time you load your app.
Also, you should save array of record objects rather than ArrayAdapter object.
Parcel and Parcelable are fantastically quick, but its documentation says you must not use it for general-purpose serialization to storage, since the implementation varies with different versions of Android (i.e. an OS update could break an app which relied on it). So use Serializable in this case instead of Parcalable (from this SO thread)
You can use global variables to pass data from one activity to another. Also you can read/ write records when app starts using global class which extends Applicaion class.
You can try following code,
public class GlobalClass extends Application {
public static Object objectToBePassed; // global variable
final static private RECORDS_FILENAME = "myRecords.txt"
#Override
public void onCreate(){
super.onCreate();
readRecordsFromFile(); // read records when app starts
}
public boolean writeRecordsToFile(ArrayList<Record> records){
FileOutputStream fos;
ObjectOutputStream oos=null;
try{
fos = getApplicationContext().openFileOutput(RECORDS_FILENAME, Context.MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(records);
oos.close();
return true;
}catch(Exception e){
Log.e(getClassName(), "Cant save records"+e.getMessage());
return false;
}
finally{
if(oos!=null)
try{
oos.close();
}catch(Exception e){
Log.e(getClassName(), "Error while closing stream "+e.getMessage());
}
}
}
private boolean readRecordsFromFile(){
FileInputStream fin;
ObjectInputStream ois=null;
try{
fin = getApplicationContext().openFileInput(RECORDS_FILENAME);
ois = new ObjectInputStream(fin);
ArrayList<Record> records = (ArrayList<Record>) ois.readObject();
ois.close();
Log.v(getClassName(), "Records read successfully");
return true;
}catch(Exception e){
Log.e(getClassName(), "Cant read saved records"+e.getMessage());
return false;
}
finally{
if(ois!=null)
try{
ois.close();
}catch(Exception e){
Log.e(getClassName(), "Error in closing stream while reading records"+e.getMessage());
}
}
}
}
So to pass any object from activity A to activity B, use following code in activity A ,
Intent intent = new Intent(this,B.class);
GlobalClass.objectToBePassed = obj;
startActivity(intent);
in activity B,
MyClass object = (MyClass) GlobalClass.objectToBePassed;
so to pass a Record class object, replace MyClass with Record.
At first I have strong Java knowledege, but however just started with Android.
My Android app is downloading some fairly complex data (texts, dates, images) which I am saving in a custom object. The data need to be refresh from time to time. However usually the data downloaded will not change.
In order to keep the data in memory I am using the Application Object. Unfortunately, it looks like the application object instance is destroyed when the app is killed.
Hence, I was wondering if it would be of good practice to serialize and save my custom object (which is contained in the application object) in the internal storage during onPause(). Obviously, I would then first read from the file in onResume() before reloading from the internet. The idea is also to enable offline viewing.
In longer term the plan is to move the code downloading the date in a background service. As there seems to be many different ways to keep application state in Android, I would like to be be sure that this is the correct way to go.
Try using those methods class to save the Object(s) (implements serialize) you need:
public synchronized boolean save(String fileName, Object objToSave)
{
try
{
// save to file
File file = new File(CONTEXT.getDir("filesdir", Context.MODE_PRIVATE) + "/file.file");
if (file.exists())
{
file.delete();
}
file.getParentFile().mkdirs();
file.createNewFile();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(objToSave);
oos.close();
return true;
}
catch (FileNotFoundException e)
{
e.printStackTrace();
return false;
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
}
public synchronized Object load(String fileName)
{
try
{
File file = new File(CONTEXT.getDir("filesdir", Context.MODE_PRIVATE) + "/file.file");
if (!file.exists())
{
return null;
}
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
savedObj = ois.readObject();
ois.close();
return savedObj;
}
catch (FileNotFoundException e)
{
e.printStackTrace();
return null;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
You'll need to cast the Object you load().
CONTEXT is an Activity or ApplicationContext to get access to the cachedir.
Your could use Environment.getExternalStorageState() instead to get a directory path. DOn't forget to add it "/filename".
I am trying to save ArrayLists(ArrayOne, ArrayTwo, and ArrayThree) of EditText's to the internal storage. As commented, it clearly shows that it attempts the save, but I never get another TOAST after that. Any help as of why it doesn't show "Save completed" or any error is appreciated.
public void save(Context c)
{
String fileName;
Toast.makeText(this, "Attempting Save", Toast.LENGTH_SHORT).show();//THIS SHOWS
if(semester.getText().toString().length() == 0)
{
Toast.makeText(c, "Please enter a filename", Toast.LENGTH_SHORT).show();
}
else
{
fileName = "test.dat";
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try
{
fos = this.openFileOutput(fileName, Context.MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(ArrayOne);
oos.writeObject(ArrayTwo);
oos.writeObject(ArrayThree);
Toast.makeText(c, "Save Completed", Toast.LENGTH_SHORT).show(); //THIS NEVER SHOWS
}
catch (FileNotFoundException e)
{
Toast.makeText(c, "Could not find " + fileName + " to save.", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if (oos != null)
oos.close();
if (fos != null)
fos.close();
}
catch (Exception e)
{ /* do nothing */ }
}
}
}
The problem is that the EditText class is not serializable
If you debug and put a break point at on the printStackTrace and examine the IOException it will tell you that
catch (IOException e
{
e.printStackTrace();
}
Classes have to use "implements Serializable" in order for them to be written out as objects, which EditText does not have.
You can not extend the class and add the serializable tag either because the underlying class will still throw the exception.
I suggest you either serialize the data via your own class or save whatever you are trying to do with some other method.
I think the error is beings swallowed in your first Try block because you're only catching FileNotFound and IOException - just for debugging purposes you could catch the generic Exception and printout the stacktrace.
If it also helps this is what I do:
java.io.File file = new java.io.File("/sdcard/mystorage/ArrayOne.bin");
ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
out.writeObject(obj);
out.close();
Best
-serkan
If nothing shows after the "Attempting save" you´re getting some exception in this block
catch (IOException e)
{
e.printStackTrace();
}
And you´re not viewing it in any Toast. Also you can be here in this way doing nothing with your exception:
catch (Exception e)
{ /* do nothing */ }
Instead of toasting your messages.. try to use LogCat for debbugging, is easy to use and also you don't need to put toast code in your code. Tell me how is going.