I have successfully serialized my custom object but when I deserialize it this happens:
-Custom object is NOT NULL
-All fields are NULL
I know that I have successfully serialized my custom object because I have read the serialization file and it look fine.
Here is my code:
public class Preferences implements Serializable {
private static Preferences instance;
public static final long serialVersionUID = 3358037972944864859L;
public String accessToken;
protected Object readResolve() {
return getInstance();
}
private Preferences() {
}
private synchronized static void synchronize() {
if(instance == null) {
instance = new Preferences();
}
}
public static Preferences getInstance() {
if(instance == null) {
Preferences.synchronize();
}
return instance;
}
public void save(File file) {
try {
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fos);
Preferences tempInstance = Preferences.getInstance();
out.writeObject(tempInstance);
out.close();
fos.close();
}catch(IOException e) {
e.printStackTrace();
}
}
public void load(File file) {
try {
FileInputStream fis = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fis);
if(file.length() > 0) {
Preferences tempInstance = (Preferences) in.readObject();
Log.e("", String.valueOf(tempInstance == null)); //prints FALSE
Log.e("", String.valueOf(tempInstance.accessToken == null)); //prints TRUE
}
in.close();
fis.close();
}catch(IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Here is my testing code:
public class CustomActivity extends AppCompatActivity {
private File dir = new File(Environment.getExternalStorageDirectory(), ".app");
private File backup = new File(dir, "backup.ser");
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
Log.e("APPLICATION", "START");
super.onCreate(savedInstanceState);
if(this instanceof ActivityLogin) {
if(!dir.exists()) {
dir.mkdirs();
}
Preferences.getInstance().load(backup);
}
}
#Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
try {
backup.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
Preferences.getInstance().save(backup);
Log.e("APPLICATION", "STOP");
}
}
Any thoughts on what might be a problem?
You have this method in your class:
protected Object readResolve() {
return getInstance();
}
This tells the serialization mechanism: whenever you deserialize a Preferences instance, replace it by the one returned by getInstance(). So, if youc call load() and the instance of your Preferences has a null accessToken, then the deserialized preferences will have a null accessToken too, since they are the same object.
Add
System.out.println(tempInstance == this);
to your logging statements (or whatever you use in android to log), and you'll see.
Related
Expected: When i run application in debug mode and pulling the endpoint, bytes still appear to be null however i did implement ApplicationEvent and passed ApplicationStartedEvent, then I have override onApplicationEvent and called my method there, which should lead to code execution once application started and bytes should already have a value. Have I missed something
public class FaqAttachment implements ApplicationListener<ApplicationStartedEvent> {
private final String fileName = "FAQ.pdf";
private byte[] bytes;
public Attachment asAttachment() {
return new Attachment(pdfToBytes(), fileName);
}
private byte[] pdfToBytes() {
if (bytes == null) {
try (FileInputStream inputStream = new FileInputStream(new File(ClassLoader.getSystemResource(fileName).getFile()))) {
this.bytes = ByteStreams.toByteArray(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return bytes;
}
#Override
public void onApplicationEvent(ApplicationStartedEvent event) {
pdfToBytes();
}
This should work :
#Component
public class FaqAttachment {
private final String fileName = "FAQ.pdf";
private byte[] bytes;
public Attachment asAttachment() {
return new Attachment(pdfToBytes(), fileName);
}
private byte[] pdfToBytes() {
if (bytes == null) {
try (FileInputStream inputStream = new FileInputStream(new File(ClassLoader.getSystemResource(fileName).getFile()))) {
this.bytes = ByteStreams.toByteArray(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return bytes;
}
#EventListener
public void onApplicationStartedEvent(ApplicationStartedEvent event) {
pdfToBytes();
}
}
My app is saving a hashmap before it stops and when it starts again loads the same hashmap so changes could be made to it. I am using Serialization.
Storage class:
public class Storage {
private Map<String, String> storage;
private String projectStorageFilePath;
public Storage() {
this.storage = new ConcurrentHashMap<String, String>();
makeDir();
}
/**
* If the file in which the map objects will be saved doesn't exist in the
* user home directory it creates it.
*/
private void makeDir() {
File projectHomeDir = new File(System.getProperty("user.home"), ".TestMap");
String projectHomeDirPath = projectHomeDir.getAbsolutePath();
File projectStorageFile = new File(projectHomeDirPath, "storage.save");
projectStorageFilePath = projectStorageFile.getAbsolutePath();
if (!projectHomeDir.exists()) {
projectHomeDir.mkdir();
try {
projectStorageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("unchecked")
public boolean load() {
boolean isLoaded = false;
ObjectInputStream ois = null;
try {
File file = new File(projectStorageFilePath);
if (file.length() != 0) {
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage = (ConcurrentHashMap<String, String>) ois.readObject();
isLoaded = true;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (null != ois) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isLoaded;
}
public boolean save() {
boolean isSaved = false;
ObjectOutputStream oos = null;
try {
//saving
oos = new ObjectOutputStream(new FileOutputStream(projectStorageFilePath));
oos.writeObject(storage);
isSaved = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != oos) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isSaved;
}
public Map<String, String> getStorage() {
return this.storage;
}
}
The class in which I am trying to do something with that hashmap:
public class DoSomethingWithMap {
private Map<String, String> storage;
public DoSomethingWithMap(Map<String, String> storage) {
this.storage = storage;
}
public void addToMap(String key, String value) {
this.storage.put(key, value);
}
public void printMap() {
System.out.println(this.storage);
}
}
When I run it the first time it works fine:
public class Main {
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.addToMap("2", "test2");
something.addToMap("4", "test4");
something.addToMap("5", "test5");
if (s.save()) {
System.out.println(s.getStorage());
}
}
}
Output:
{} //empty map which is ok because it has never been saved before
{3=test3, 4=test4, 5=test5} //changes during runtime are saved
The problem is when I start Main again and try to make changes to the saved map:
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.printMap();
something.addToMap("6", "newTest");
something.addToMap("7", "newTest");
something.addToMap("8", "newTest");
something.printMap();
if (s.save()) {
System.out.println(s.getStorage());
}
}
Output:
{3=test3, 4=test4, 5=test5} //loading the map works fine
{} //here it should be same as previous line but is not
{6=newTest, 7=newTest, 8=newTest} //DoSomethingWithMap.printMap is printing only the changes during runtime
{3=test3, 4=test4, 5=test5} // changes during runtime are not saved
It is obvious DoSomethingWithMap class is not using the map which was given to it. Why? Which map is using? How I can fix that?
Thank you.
You are creating a new instance of the Map in your load method:
storage = (ConcurrentHashMap<String, String>) ois.readObject();
To fix you can clear the current map and then add all the values from the loaded one:
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage.clear();
storage.putAll((ConcurrentHashMap<String, String>) ois.readObject());
To prevent such error in the future, you could make those fields final and thus you will get error reports.
I am using a global variables "GlobalVariables" in a separated class and I am try to use it in the following code but it is always gives me the error :
The method getApplication() is undefined for the type UploadPicture
I tried the following but still have error:
((GlobalVariables) this.getApplication()).set_FileUploading(false);
The qustion was already asked here but unfortunatlly all the answors didn't work with me and gave me same error! any suggestion please?
public class UploadPicture extends AsyncTask<Void, Long, Boolean> {
private DropboxAPI<?> mApi;
private String mPath;
private File mFile;
private long mFileLen;
private UploadRequest mRequest;
private Context mContext;
private String mErrorMsg;
private File outFiles;
public UploadPicture(Context context, DropboxAPI<?> api, String dropboxPath, File file) {
mContext = context.getApplicationContext();
mFileLen = file.length();
mApi = api;
mPath = dropboxPath;
mFile = file;
}
#Override
protected Boolean doInBackground(Void... params) {
try {
FileInputStream fis = new FileInputStream(mFile);
String path = mPath + outFiles.getName();
mRequest = mApi.putFileOverwriteRequest(path, fis, mFile.length(),
new ProgressListener() {
#Override
public long progressInterval() {
return 500;
}
#Override
public void onProgress(long bytes, long total) {
//publishProgress(bytes);
}
}
);
if (mRequest != null) {
mRequest.upload();
((GlobalVariables) UploadPicture.this.getApplication()).set_FileUploading(false);
return true;
}
} catch (DropboxUnlinkedException e) {
// This session wasn't authenticated properly or user unlinked
mErrorMsg = "This app wasn't authenticated properly.";
} catch (DropboxFileSizeException e) {
// File size too big to upload via the API
mErrorMsg = "This file is too big to upload";
} catch (DropboxPartialFileException e) {
// We canceled the operation
mErrorMsg = "Upload canceled";
} catch (DropboxServerException e) {
// Server-side exception. These are examples of what could happen,
// but we don't do anything special with them here.
if (e.error == DropboxServerException._401_UNAUTHORIZED) {
// Unauthorized, so we should unlink them. You may want to
// automatically log the user out in this case.
} else if (e.error == DropboxServerException._403_FORBIDDEN) {
// Not allowed to access this
} else if (e.error == DropboxServerException._404_NOT_FOUND) {
// path not found (or if it was the thumbnail, can't be
// thumbnailed)
} else if (e.error == DropboxServerException._507_INSUFFICIENT_STORAGE) {
// user is over quota
} else {
// Something else
}
// This gets the Dropbox error, translated into the user's language
mErrorMsg = e.body.userError;
if (mErrorMsg == null) {
mErrorMsg = e.body.error;
}
} catch (DropboxIOException e) {
// Happens all the time, probably want to retry automatically.
mErrorMsg = "Network error. Try again.";
} catch (DropboxParseException e) {
// Probably due to Dropbox server restarting, should retry
mErrorMsg = "Dropbox error. Try again.";
} catch (DropboxException e) {
// Unknown error
mErrorMsg = "Unknown error. Try again.";
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return false;
}
}
Edit: I am adding now my "VariableGlobales" calss:
public class GlobalVariables extends Application {
private Boolean _IsIOIORunning=false;
private Boolean _FileUploading=false;
public Boolean get_IsIOIORunning()
{
return _IsIOIORunning;
}
public void set_IsIOIORunning(Boolean _IsIOIORunning)
{
this._IsIOIORunning = _IsIOIORunning;
}
public Boolean get_FileUploading()
{
return _FileUploading;
}
public void set_FileUploading(Boolean _FileUploading)
{
this._FileUploading = _FileUploading;
}
It's normal UploadPicture doesn't extend GlobalVariables but it extend AsyncTask.
That it's my "GlobalVariables "
public class AppInfo extends Application {
private static Context context;
private static String user;
public void onCreate(){
super.onCreate();
AppInfo.context = getApplicationContext();
user = null;
}
public static Context getAppContext() {return AppInfo.context;}
public static String getUser() {return user;}
public static void setUser(String user) {AppInfo.user = user;}
}
And I call it everywhere like that:
AppInfo.getUser();
Edit:
GlobalVariables should use static method and variables:
public class GlobalVariables extends Application {
private static Boolean _IsIOIORunning=false;
private static Boolean _FileUploading=false;
public static Boolean get_IsIOIORunning() {
return _IsIOIORunning;
}
public static void set_IsIOIORunning(Boolean _IsIOIORunning) {
GlobalVariables._IsIOIORunning = _IsIOIORunning;
}
public static Boolean get_FileUploading(){
return _FileUploading;
}
public static void set_FileUploading(Boolean _FileUploading){
GlobalVariables._FileUploading = _FileUploading;
}
}
I have a simple singleton:
class Test1 implements Serializable {
private static Test1 instance;
public String a = "a";
public String b = "b";
public String c = null;
public String d = null;
public String e;
public String f;
private Test1() {
e = "e";
}
public static Test1 getInstance() {
if (instance == null) {
instance = new Test1();
}
return instance;
}
// http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-with-examples
protected Object readResolve() {
return getInstance();
}
public String toString() {
return String.format("Test1{ a:%s, b:%s, c:%s, d:%s, e:%s, f:%s}", a, b, c, d, e, f);
}
}
My main():
if ((new File("t1.obj").exists() == false)) {
Test1 t1 = Test1.getInstance();
t1.b = "bb";
t1.d = "dd";
t1.f = "ff";
serialize("t1.obj", t1);
}
else {
Test1 t2 = deserialize("t1.obj");
}
First run looks well
Serialized hu.fehergeri13.abptc.server.Test1 object to t1.obj file.
Test1{ a:a, b:bb, c:null, d:dd, e:e, f:ff}
after second run:
Deserialized hu.fehergeri13.abptc.server.Test1 object from t1.obj file.
Test1{ a:a, b:b, c:null, d:null, e:e, f:null}
My serialize/deserialize:
public static void serialize(String filePath, Object o) {
try {
FileOutputStream fos = new FileOutputStream(filePath);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(o);
System.out.println(String.format("Serialized %s object to %s file.", o.getClass().getName(), filePath));
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static <T> T deserialize(String filePath) {
try {
FileInputStream is = new FileInputStream(filePath);
ObjectInputStream ois = new ObjectInputStream(is);
T o = (T) ois.readObject();
System.out.println(String.format("Deserialized %s object from %s file.", o.getClass().getName(), filePath));
ois.close();
is.close();
return o;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Why are the values keep default and not bb,dd,ff?
My serializable singleton:
public class MySingleton implements Serializable {
private static final long serialVersionUID = -5909418239300111453L;
private static MySingleton instance = null;
protected MySingleton() {
}
public static MySingleton getInstance() {
if (instance == null) {
instance = new MySingleton();
}
return instance;
}
protected Object readResolve() {
return getInstance();
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
instance = this;
}
private String attr;
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
}
tl;dr:
I have two threads, one of them serealizes object, and another tries to read object. How do I avoid collisions, i.e. synchronize access to file?
More info:
I have Service which uses AsyncTask to fetch data and then serializes object:
#Override
protected Boolean doInBackground(Void... params) {
FeedItem currentItem = mParser.parseFeed();
Util.saveItem(UpdateService.this, currentItem);
}
Object serialization:
public class Util {
private static final String sFileName = "feedobject";
public static FeedItem loadItem(Context context) {
FeedItem result = null;
try {
FileInputStream fis = context.openFileInput(sFileName);
ObjectInputStream is = new ObjectInputStream(fis);
result = (FeedItem) is.readObject();
is.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
public static void saveItem(Context context, FeedItem item) {
FileOutputStream fos;
try {
fos = context.openFileOutput(sFileName, Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(item);
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I also try to read object from Activity, i.e. from UI thread.
mTextView.setText(Html.fromHtml(Util.loadItem(this).message));
If you can rename files under Android, then you could write the object to a temporary file "feedobject.t" and then rename it to be "feedobject" which should be atomic.
private static final String sFileName = "feedobject";
private static final String tempFileName = "feedobject.t";
...
File tempFile = new File(tempFileName);
... // write the file
tempFile.renameTo(new File(sFileName));
If this doesn't work then you will be forced to have a synchronized lock. The consumer should lock and wait for the producer to finish writing. The producer needs to lock to make sure it is writing only when the consumer is not reading.
You could just make the saveItem and loadItem methods be synchronized which would lock the class:
public static synchronized FeedItem loadItem(Context context) {
...
public static synchronized void saveItem(Context context, FeedItem item) {
...
Better would be to create a lock object and lock on that:
private static final Object itemLock = new Object();
Then in the loadItem and saveItem methods you'd lock on that object:
public static FeedItem loadItem(Context context) {
synchronized (itemLock) {
// do the reading...
}
Hope this helps.