ObjectInputStream readObject() erratic behavior? - android

I am currently making an app for our group in high school. We decided to make a personal financing app that organizes your spendings.
I am trying to use internal storage for storing data. I have created an InternalStorage class just for that (it was someone else's answer to a question like mine but I forgot from where. Whoops.), having write and read methods accordingly.
However, while debugging I found some implausible behavior.
public final class InternalStorage {
private static String key = "billify";
public static void write(Context context, List<Bill> billList){
try{
FileOutputStream fos = context.openFileOutput(key, Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(billList);
oos.close();
fos.close();
}catch(IOException e){
Toast.makeText(context,"Internal storage write error: IOException", Toast.LENGTH_LONG).show();
}
}
public static List<Bill> read(Context context){
try {
FileInputStream fis = context.openFileInput(key);
ObjectInputStream ois = new ObjectInputStream(fis);
List<Bill> a = (List<Bill>) ois.readObject();//jumps from here
return a;
}catch(Exception e){
e.printStackTrace();
Toast.makeText(context,"Internal storage read error",Toast.LENGTH_LONG).show();
return null;//jumps to here w/o triggering previous two lines.
}
}
}
I have put breakpoints in all the lines of code from read(), and from what I saw, the code ran ois.readObject(), then without running e.printStackTrace() and Toast, jumped straight back to return null.
Does anybody know what's going on?
Edit:
if(z.getClass() == cls){
ArrayList<Bill> a = (ArrayList<Bill>) z;//Jumps from here now
return a;
}
return null;
Doing a checked cast still makes the same thing happen-the classes are same, but doing the cast still makes it jump to the previous, last null.

Related

Saving objects in cache

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.

Can't get path to /data/data/my_app/files

I'm working on an app which stores small amounts of data in /data/data/my_app/files using this code:
private void buildFileFromPreset(String fileName) {
fileName = fileName.toLowerCase();
StandardData data = StandardData.getInstance();
String[] list = data.getDataByName(fileName);
FileOutputStream fos = null;
try {
fos = openFileOutput(fileName, Context.MODE_PRIVATE);
PrintWriter writer = new PrintWriter(fos);
for (int i = 0; i < list.length; i++) {
writer.println(list[i]);
}
writer.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
All works fine when the app gets started, in the onCreate() function of the main activity, I want to check if the files that were created last time are still present:
private String getAppFilesDir() {
ContextWrapper c = new ContextWrapper(this);
return c.getFilesDir().toString();
}
which returns something like:
/data/user/0/my_app/files
I've read some older posts (2012) suggesting this method must work but it doesn't, probably since jellybean.
So my question:
How can I check if the files I created using FileOutputStream and PrintWriter in a previous session still exist?
I hope I provided enough info for you guys to answer (:
I still have not found a solution for this specific problem.
Instead I am now using SQLite so I don't have to worry about these kinds of things.

Null reference error reading application data with a background service

I'm saving and object
saveObject(myKey, object);
static public void saveObject(String key, Object value){
try{
FileOutputStream fos = MainActivity.getAppContext().openFileOutput(key, Context.MODE_PRIVATE);
ObjectOutputStream oos= new ObjectOutputStream(fos);
oos.writeObject(value);
oos.flush();
oos.close();
fos.flush();
fos.close();
}catch(IOException ioe){
Log.e("TAG", "Stack trace is " + Log.getStackTraceString(ioe));
}
}
While my application is open, I can load the object
Object object = loadObject("myKey")
static public Object loadObject(String key){
try {
FileInputStream fis = MainActivity.getAppContext().openFileInput(key);
ObjectInputStream is = new ObjectInputStream(fis);
Object object = is.readObject();
is.close();
fis.close();
return object;
}catch(IOException ioe){
Log.e("TAG","Stack trace is "+Log.getStackTraceString(ioe));
} catch (ClassNotFoundException e) {
Log.e("TAG", "Stack trace is "+Log.getStackTraceString(e));
}
return null;
}
When my application is closed, I'm using a broadcast receiver to load the object when the receiver receives a certain intent, but I get a null reference error in my loadObject method
//Error receiving broadcast Intent....
//Attempt to invoke virtual method 'java.io.FileInputStream android.content.Context.openFileInput(java.lang.String)' on a null object reference
FileInputStream fis = MainActivity.getAppContext().openFileInput(key);
I think this may have to do with the fact that I use a background service to register my receiver, as I cannot reigster it in the manifest.
Any idea what the problem could be?
FileInputStream fis = MainActivity.getAppContext().openFileInput(key);
For this line MainActivity.getAppContext() part seems to your problem.
What is MainActivity? Is it an instance of your Activity? You cannot reach activities like that, especially when the app is closed.
You need to use Application Context of your Application class.
Like this Application.instance().getApplicationContext()
Instead of FileInputStream fis = MainActivity.getAppContext().openFileInput(key);
You could do this instead (we do this for a background service we have):
Context context = getApplicationContext();
FileInputStream fis = context.openFileInput(key);
You have to run the MainActivity application at least once to create the service and get the application context.

Android writing custom objects to file

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.

Serialization of Application Object

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".

Categories

Resources