The best way to caching json - android

My application should work not only in online but also in offline mode. For that reason I am considering find the best way for cashing data. I't like use SharedPreference for store data but in android documentation writen Maximum size in characters allowed for a preferences value is 8192. I don't know this is ok or not? I tried to pass out of this idea trying to use FileCashing or sqLite cashing.
So what you think guys what is the best SharedPreference vs FileCashing or vs SqLiteCaching?

Save the json in cache directory as file....
Save:
// Instantiate a JSON object from the request response
JSONObject jsonObject = new JSONObject(json);
// Save the JSONOvject
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File(getCacheDir(),"")+"cacheFile.srl"));
out.writeObject( jsonObject );
out.close();
Retrieve:
// Load in an object
ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(new File(getCacheDir(),"")+"cacheFile.srl")));
JSONObject jsonObject = (JSONObject) in.readObject();
in.close();

I personally like to do this the following way. Create a SQLite database that can hold your content. Then, bind the user interface directly to the database using Adapters & Content Providers that send a notification whenever the data is changed so that the UI can update itself. The last piece in the equation is some form of synchronization service that downloads content and saves it to the database asynchronously. That way, you can manage your data very easily because it is all in the same place. The only part you'll have to figure out for your app is how you decide when to update or remove the data from the database.
Adapters
ContentProvider
Synchronization

Based on your requirement I would recommend SQLite data base.
Since shared preference is suitable for configuration storage - often small data/strings.
File cache is hard to manage, so I recommend SQLite - easy to manage and ability to store mass data.
Considering the performance, if the number of index is not that huge, SQLite database should have the acceptable performance. E.g. only several ms slower than a file cache.
You might be able to combine these two approaches together. Use random access file with index-offset stored in SQLite.

I have used Internal Storage which store file in Application package directory that can't be accessible by not rooted device.
Here the class which can create, read and delete the file
public class ReadWriteJsonFileUtils {
Activity activity;
Context context;
public ReadWriteJsonFileUtils(Context context) {
this.context = context;
}
public void createJsonFileData(String filename, String mJsonResponse) {
try {
File checkFile = new File(context.getApplicationInfo().dataDir + "/new_directory_name/");
if (!checkFile.exists()) {
checkFile.mkdir();
}
FileWriter file = new FileWriter(checkFile.getAbsolutePath() + "/" + filename);
file.write(mJsonResponse);
file.flush();
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public String readJsonFileData(String filename) {
try {
File f = new File(context.getApplicationInfo().dataDir + "/new_directory_name/" + filename);
if (!f.exists()) {
onNoResult();
return null;
}
FileInputStream is = new FileInputStream(f);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
return new String(buffer);
} catch (IOException e) {
e.printStackTrace();
}
onNoResult();
return null;
}
public void deleteFile() {
File f = new File(context.getApplicationInfo().dataDir + "/new_directory_name/");
File[] files = f.listFiles();
for (File fInDir : files) {
fInDir.delete();
}
}
public void deleteFile(String fileName) {
File f = new File(context.getApplicationInfo().dataDir + "/new_directory_name/" + fileName);
if (f.exists()) {
f.delete();
}
}
}
You can create, read and delete the file by calling ReadWriteJsonFileUtils class methods as follows:
For creating file:
try {
new ReadWriteJsonFileUtils(context).createJsonFileData(file_name, data);
} catch (Exception e) {
e.printStackTrace();
}
For reading file:
String jsonString = new ReadWriteJsonFileUtils(context).readJsonFileData(file_name);
For deleting single file
new ReadWriteJsonFileUtils(context).deleteFile(file_name);
For deleting all file
new ReadWriteJsonFileUtils(context).deleteFile();

Related

Use external storage to write file used by multiple applications on Android 11

I previously used external storage to store specific data that I would like to share between my applications (without having any contentprovider "host"), by using WRITE_EXTERNAL_STORAGE.
Not a media file, it is more like an encoded string in it.
It does not seem to be possible anymore on Android 11, without requesting MANAGE_EXTERNAL_STORAGE.
But this permission will not be granted by Google to all applications, and will require to fill a form, like everry "restricted permissions" (READ_CALL_LOG, READ_SMS, ACCESS_FINE_LOCATION, etc...) See support.google.com/googleplay/android-developer/answer/9888170
Exemple :
By having XX applications, each one could be the first one to write a file (the first app used by the user basically), and the 3 other applications would read this file when started.
Any idea on how this can be achieved on Android 11?
BlobManager seems to be appropriate but documentation is terrible (I tried it without success: new BlobStoreManager read write on Android 11)
private void writeFile(String data) {
try {
File f = new File(Environment.getExternalStorageDirectory(), FOLDER_NAME);
if (!f.exists()) {
boolean mkdirs = f.mkdirs();
if (!mkdirs) {
return;
}
}
File file = new File(f, FILE_NAME);
FileOutputStream outputStream = new FileOutputStream(file);
String encoded = Base64.encodeToString(data.getBytes(), Base64.DEFAULT);
outputStream.write(encoded.getBytes());
outputStream.close();
} catch (IOException e) {
Logger.e(TAG, "writeFile: IOException", e);
} catch (Exception e) {
Logger.e(TAG, "writeFile: Basic exception", e);
}
}
private String readFile() {
String data;
try {
File file = new File(Environment.getExternalStorageDirectory(), FOLDER_NAME + "/" + FILE_NAME);
if (!file.exists()) {
return "";
}
InputStream is = new FileInputStream(file);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
String text = new String(buffer, Charset.forName("UTF-8"));
data = new String(Base64.decode(text, Base64.DEFAULT));
Logger.d(TAG, "readFile: decoded = " + data);
} catch (IOException e) {
Logger.e(TAG, "readFile: IOException", e);
return "";
} catch (IllegalArgumentException e) {
Logger.e(TAG, "readFile: Illegal Base64 import preset", e);
return "";
} catch (Exception e) {
Logger.e(TAG, "readFile: Basic exception", e);
return "";
}
return data;
}
EDIT:
I tried some others solutions:
The External Public Storage way
An application "A" can write, and then read the file. But an other application "B" can not read the file written by "A"
I only get an access error:
NotificationHelper - readFile: IOException
java.io.FileNotFoundException: /storage/emulated/0/Download/myfolder/settings.bin: open failed: EACCES (Permission denied)
at libcore.io.IoBridge.open(IoBridge.java:492)
at java.io.FileInputStream.(FileInputStream.java:160)
file = new File (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), FOLDER_NAME + "/" + FILE_NAME);
The mediastore way
But just with one app, I have issues: The app can not override a file writtend earlier, it creates multiple instance "my_file", "myfile(1), ..."
And I have error when trying to read it:
java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:151)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:781)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1986)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1801)
at android.content.ContentResolver.openInputStream(ContentResolver.java:1478)
at fr.gg.frameworkmobile.utils.NotificationHelper.readFile(NotificationHelper.java:388)
private void writeFile(String data) {
String outputFilename = "my_file";
String outputDirectory = "my_sub_directory"; // The folder within the Downloads folder, because we use `DIRECTORY_DOWNLOADS`
ContentResolver resolver = AbstractMobileApplication.getInstance().getApplicationContext().getContentResolver();
ContentValues values = new ContentValues();
// save to a folder
values.put(MediaStore.Files.FileColumns.DISPLAY_NAME, outputFilename);
values.put(MediaStore.Files.FileColumns.MIME_TYPE, "application/my-custom-type");
values.put(MediaStore.Files.FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + outputDirectory);
values.put(MediaStore.Files.FileColumns.IS_PENDING, 1);
Uri uri = resolver.insert(MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), values);
// You can use this outputStream to write whatever file you want:
OutputStream outputStream = null;
Log.d(TAG, "writeFile: >>>>>>>>" + uri.getPath());
try {
outputStream = resolver.openOutputStream(uri);
String encoded = Base64.encodeToString(data.getBytes(), Base64.DEFAULT);
outputStream.write(encoded.getBytes());
outputStream.close();
values.clear();
values.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
resolver.update(uri, values, null, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private String readFile() {
String data;
String outputFilename = "my_file";
String outputDirectory = "my_sub_directory"; // The folder within the Downloads folder, because we use `DIRECTORY_DOWNLOADS`
ContentResolver resolver = AbstractMobileApplication.getInstance().getApplicationContext().getContentResolver();
ContentValues values = new ContentValues();
// save to a folder
values.put(MediaStore.Files.FileColumns.DISPLAY_NAME, outputFilename);
values.put(MediaStore.Files.FileColumns.MIME_TYPE, "application/my-custom-type");
values.put(MediaStore.Files.FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + outputDirectory);
Uri uri = resolver.insert(MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), values);
// You can use this outputStream to write whatever file you want:
Log.d(TAG, "readFile: >>>>>>>>" + uri.getPath());
try {
InputStream is = resolver.openInputStream(uri);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
String text = new String(buffer, Charset.forName("UTF-8"));
data = new String(Base64.decode(text, Base64.DEFAULT));
Logger.d(TAG, "readFile: decoded = " + data);
} catch (FileNotFoundException e) {
data = "";
e.printStackTrace();
} catch (IOException e) {
data = "";
e.printStackTrace();
}
return data;
}
Content Providers is not a solution either because none of the apps is a "host"
They all are the host. You will need to maintain N copies of the data, one per app, with some sort of coordination mechanism between them for handling modifications to that data. You already needed a coordination mechanism, if multiple of the apps might modify your common file in your old solution.
For example, if the data changes infrequently:
The first app in your suite, when first run, sends a secured "can I get a copy of the data?" broadcast, which nobody responds to, since it is the first app in your suite
The first app sets up the data
Subsequent apps, when first run, send a the same "can I get a copy of the data?" broadcast
Each app has a receiver for the broadcast, and if they have the data, sends a "here is a copy of the data" broadcast in reply, which either has the data itself (if it is small) or has a Uri to a ContentProvider that can supply the data. Ideally, the data has a timestamp or some other versioning information in it.
Each app, if it modifies the data, sends that "here is a copy of the data" broadcast.
Each app has a receiver for the "here is a copy of the data" broadcast and uses that to grab the data if it is newer than what they have (or grabs it for the first time if they do not already have the data).
This is complex, with risks of collisions if two apps try modifying the data around the same time.
You could consider an election protocol and have a single app be the "owner" of the data, with the other apps just having backup copies, and with a new election if the current owner app is uninstalled. Done properly, this could reduce the risks of collisions, at the cost of even more complexity.
The simple solution is to allow the user to specify where this shared content resides, via ACTION_CREATE_DOCUMENT (for your first app) and ACTION_OPEN_DOCUMENT (for subsequent apps). However, you rejected this ("And it requires to be "invisible" for the users: no file picker"). My recommendation would be for you to relax this requirement. And, you still need some coordination mechanism, if multiple of the apps might modify your common content, just as you did with the common file approach you took originally.
And, you could always consider eliminating the suite, merging the functionality into a single app. Or, adopt more of a "host-and-plugins" model for the suite, such that each plugin app does not need independent access to the data.
Write your files to the public directories DCIM or Pictures.
Or to a subfolder in those directories.
The files should have the .jpg or .png extension.
You used .dat which you can make .dat.jpg to recognise the files.

How can we use cache for stored json data in android [duplicate]

My application should work not only in online but also in offline mode. For that reason I am considering find the best way for cashing data. I't like use SharedPreference for store data but in android documentation writen Maximum size in characters allowed for a preferences value is 8192. I don't know this is ok or not? I tried to pass out of this idea trying to use FileCashing or sqLite cashing.
So what you think guys what is the best SharedPreference vs FileCashing or vs SqLiteCaching?
Save the json in cache directory as file....
Save:
// Instantiate a JSON object from the request response
JSONObject jsonObject = new JSONObject(json);
// Save the JSONOvject
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File(getCacheDir(),"")+"cacheFile.srl"));
out.writeObject( jsonObject );
out.close();
Retrieve:
// Load in an object
ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(new File(getCacheDir(),"")+"cacheFile.srl")));
JSONObject jsonObject = (JSONObject) in.readObject();
in.close();
I personally like to do this the following way. Create a SQLite database that can hold your content. Then, bind the user interface directly to the database using Adapters & Content Providers that send a notification whenever the data is changed so that the UI can update itself. The last piece in the equation is some form of synchronization service that downloads content and saves it to the database asynchronously. That way, you can manage your data very easily because it is all in the same place. The only part you'll have to figure out for your app is how you decide when to update or remove the data from the database.
Adapters
ContentProvider
Synchronization
Based on your requirement I would recommend SQLite data base.
Since shared preference is suitable for configuration storage - often small data/strings.
File cache is hard to manage, so I recommend SQLite - easy to manage and ability to store mass data.
Considering the performance, if the number of index is not that huge, SQLite database should have the acceptable performance. E.g. only several ms slower than a file cache.
You might be able to combine these two approaches together. Use random access file with index-offset stored in SQLite.
I have used Internal Storage which store file in Application package directory that can't be accessible by not rooted device.
Here the class which can create, read and delete the file
public class ReadWriteJsonFileUtils {
Activity activity;
Context context;
public ReadWriteJsonFileUtils(Context context) {
this.context = context;
}
public void createJsonFileData(String filename, String mJsonResponse) {
try {
File checkFile = new File(context.getApplicationInfo().dataDir + "/new_directory_name/");
if (!checkFile.exists()) {
checkFile.mkdir();
}
FileWriter file = new FileWriter(checkFile.getAbsolutePath() + "/" + filename);
file.write(mJsonResponse);
file.flush();
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public String readJsonFileData(String filename) {
try {
File f = new File(context.getApplicationInfo().dataDir + "/new_directory_name/" + filename);
if (!f.exists()) {
onNoResult();
return null;
}
FileInputStream is = new FileInputStream(f);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
return new String(buffer);
} catch (IOException e) {
e.printStackTrace();
}
onNoResult();
return null;
}
public void deleteFile() {
File f = new File(context.getApplicationInfo().dataDir + "/new_directory_name/");
File[] files = f.listFiles();
for (File fInDir : files) {
fInDir.delete();
}
}
public void deleteFile(String fileName) {
File f = new File(context.getApplicationInfo().dataDir + "/new_directory_name/" + fileName);
if (f.exists()) {
f.delete();
}
}
}
You can create, read and delete the file by calling ReadWriteJsonFileUtils class methods as follows:
For creating file:
try {
new ReadWriteJsonFileUtils(context).createJsonFileData(file_name, data);
} catch (Exception e) {
e.printStackTrace();
}
For reading file:
String jsonString = new ReadWriteJsonFileUtils(context).readJsonFileData(file_name);
For deleting single file
new ReadWriteJsonFileUtils(context).deleteFile(file_name);
For deleting all file
new ReadWriteJsonFileUtils(context).deleteFile();

What path does "getApplicationContext().getFilesDir()" return?

I'm doing a simple app in Android and in a certain part of the app I would like to create an Excel file and write in it. I've already prepared everything to use jexcel library to edit an excel using Java, but the thing is I can't find the Excel file I created. I've tried to find it in my own device executing the app, but I couldn't.
String fileName = "hours.xls";
File file = new File(getApplicationContext().getFilesDir() + fileName);
Can anybody help me please?
Thanks in advance :)
On Android KitKat, it returns /data/data/{your package name}/files, however I imagine this could change depending on your platform version. Thus if you're just trying to dig through your filesystem and see a file, it's safe to use this path, but if you're using this path for some functionality across multiple platform versions, you should only reference it using getFilesDir().
What are you planning on using this file for? Do you want it usable by other apps too? Using getApplicationContext().getFilesDir() will give you /data/data/com.package/files but if you want a file that's easily accessible by yourself and other apps, you're better off using something like getExternalFilesDir()
If you want to access your file via your PC (with an usb cable) or via a file manager on your device, prefer:
new File(getExternalFilesDir(null), fileName);
This folder is created in .../Android/data/ ... com.yoursociety.yourapp/files ...
null means that you do not want to store files in predefined folders like Movies, Pictures and so on.
(See documentation for more info)
This worked:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
b = (Brain)load("brain.txt");
if (b == null) {
b = new Brain();
}
vocabulary = (ArrayList <String>) load("vocabulary.txt");
if (vocabulary == null) {
vocabulary = new ArrayList <String> ();
vocabulary.add("I love you.");
vocabulary.add("Hi!");
}
b.setRunning(true);
}
public Object load(String fileName) {
File file = new File("/storage/emulated/0/Android/data/com.cobalttechnology.myfirstapplication/files/" + fileName);
if (!file.exists()) {
return null;
}
try {
Object o;
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
o = ois.readObject();
if (o == null) {
System.out.println(fileName + " = null");
}
ois.close();
fis.close();
System.out.println("Loaded: " + fileName);
return o;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
return null;
}
public void save(Object o, String fileName) {
File file = new File("/storage/emulated/0/Android/data/com.cobalttechnology.myfirstapplication/files/" + fileName);
try {
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(o);
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Read the documentation, this method reads the files stored in the internal storage that were created with
with openFileOutput():
getFilesDir()
Returns the absolute path to the directory on the filesystem where
files created with openFileOutput(String, int) are stored.

How can I get informations within code about the cache of my app?

I want to check where my apps cache is raising all the time.
So I need a method to access informations about the current cache at any time.
I think there must be such a method, because I could see the size of the cache in the App-Info of my android-smartphone.
The app is running in the background and I want to log when the size of the cache is raising.
Use below code snippets for your solution.
public static byte[] retrieveData(Context context, String name) throws IOException {
File cacheDir = context.getCacheDir();
File file = new File(cacheDir, name);
if (!file.exists()) {
// Data doesn't exist
return null;
}
byte[] data = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
try {
is.read(data);
}
finally {
is.close();
}
return data;
}
Here is complete CacheManager class.
When to clear the cache dir in Android?

How do I store data into a flat file in Android?

I want to store a few values in the form of high scores. But since I'm not going to be storing more than 5 values, using SQLite doesn't seem appropriate. Another option I was considering was a flat file, but I'm not sure how to go about that...
See here for your Data Storage options. I suppose that in your case the easiest will be to use SharedPreferences.
You could also use Internal Storage to save data in a file that is private to your application. I wouldn't recommend to use External Storage for storing high scores.
If it's an array you can use this:
public void saveArray(String filename, String[] output_field) {
try {
FileOutputStream fos = new FileOutputStream(filename);
GZIPOutputStream gzos = new GZIPOutputStream(fos);
ObjectOutputStream out = new ObjectOutputStream(gzos);
out.writeObject(output_field);
out.flush();
out.close();
}
catch (IOException e) {
e.getStackTrace();
}
}
#SuppressWarnings("unchecked")
public String[] loadArray(String filename) {
try {
FileInputStream fis = new FileInputStream(filename);
GZIPInputStream gzis = new GZIPInputStream(fis);
ObjectInputStream in = new ObjectInputStream(gzis);
String[] read_field = (String[])in.readObject();
in.close();
return read_field;
}
catch (Exception e) {
e.getStackTrace();
}
return null;
}
You just call it like this:
Save Array: saveArray("/sdcard/.mydata/data.dat", MyArray);
Load Array: String[] MyArray = loadArray("/sdcard/.mydata/data.dat");
You can see an example at http://androidworkz.com/2010/07/06/source-code-imageview-flipper-sd-card-scanner/

Categories

Resources