Is it possible to write a whole shared_preferences.xml at once?
I want to realize a kind of settings import/export, so i need to read and write the whole file without loosing the xml-tags.
Reading the file is easy, but when i write my values (using PrintWriter) the old values stored in memory overwrite them seconds later.
what can i do to prevent that without writing single values using preference editor.
Now I read it from a file designed like Android's own preferences.xml and write it successively in my own function like this:
public static void preferencesImport(String PreferenceFilepath) {
preferencesImportPreferenceFilepath = PreferenceFilepath;
try {
// Parsing
// see http://theopentutorials.com/tutorials/android/xml/android-simple-xml-dom-parser/
XMLParserHelper parser = new XMLParserHelper(); // reference to described XMLDOMParser helper class
BufferedInputStream stream;
try {
stream = new BufferedInputStream(new FileInputStream(preferencesImportPreferenceFilepath));
org.w3c.dom.Document doc = parser.getDocument(stream);
// string value
NodeList nodeListString = doc.getElementsByTagName("string");
for (int i = 0; i < nodeListString.getLength(); i++) {
Element eString = (Element) nodeListString.item(i);
Pref.setString(eString.getAttribute("name"), eString.getTextContent()); // Own getter/setter -> use Android's preference manager instead in similar way
}
// repeat code above for boolean, long, int, float values
stream.close();
} catch (IOException e1) {
// output IOException
} catch (Throwable t1) {
// output Throwable1
}
writer.close();
} catch (Throwable t2) {
// output Throwable2
}
}
Related
I previously used external storage to store specific data that I would like to share between my applications (without having any contentprovider "host")
File folder = new File(Environment.getExternalStorageDirectory(), "FOLDER_NAME");
File file = new File(folder, "FILE_NAME.dat");
FileOutputStream outputStream = new FileOutputStream(file);
That is why I am trying to use BlobStoreManager, as suggested in google's recommendation for targeting 30 (https://developer.android.com/training/data-storage/shared/datasets)
The read & write are based on a BlobHandle with 4 parameters, one being MessageDigest based on a "content". BlobHandle must use the same 4 parameters, or read will fail (SecurityException).
I managed to write data, and to read it, but it makes no sense:
It seems that in order to write, I need to use the data I want to write to generate the BlobHandle.
Then, to read, as BlobHandle must use the same 4 parameters, I also need the data I wrote to be able to read.
Totally illogic, as I wanted to read this data, I don't have it!
I must miss something or just do not understand how it work. If someone can help :)
Here are my sample:
If I set the following:
createBlobHandle: content = "mydata"
write: data = "mydata"
Then write will success, and read will success too. But it I can not know the value before reading it in a normal usecase :(
If I set the following (which would be logic, at least to me):
createBlobHandle: content = "somekey"
write: data = "mydata"
Then write will fail :(
#RequiresApi(api = Build.VERSION_CODES.R)
private BlobHandle createBlobHandle() {
//Transfer object
String content = "SomeContentToWrite";
String label = "label123";
String tag = "test";
//Sha256 summary of the transmission object
try {
byte[] contentByte = content.getBytes("utf-8");
MessageDigest md = MessageDigest.getInstance("sha256");
byte[] contentHash = md.digest(contentByte);
return BlobHandle.createWithSha256(contentHash, label,0, tag);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
private void write() {
String data = "SomeContentToWrite";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
//Generate the session of this operation
try {
BlobHandle blobHandle = createBlobHandle();
if (blobHandle == null)
return;
long sessionId = blobStoreManager.createSession(blobHandle);
try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
try (OutputStream pfd = new ParcelFileDescriptor.AutoCloseOutputStream(session.openWrite(0, data.getBytes().length))) {
//The abstract of the written object must be consistent with the above, otherwise it will report SecurityException
Log.d(TAG, "writeFile: >>>>>>>>>>text = " + data);
pfd.write(data.getBytes());
pfd.flush();
//Allow public access
session.allowPublicAccess();
session.commit(applicationContext.getMainExecutor(), new Consumer<Integer>() {
#Override
public void accept(Integer integer) {
//0 success 1 failure
Log.d(TAG, "accept: >>>>>>>>" + integer);
}
});
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String read() {
String data = "";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
BlobHandle blobHandle = createBlobHandle();
if (blobHandle != null) {
try (InputStream pfd = new ParcelFileDescriptor.AutoCloseInputStream(blobStoreManager.openBlob(createBlobHandle()))) {
//Read data
byte[] buffer = new byte[pfd.available()];
pfd.read(buffer);
String text = new String(buffer, Charset.forName("UTF-8"));
Log.d(TAG, "readFile: >>>>>>>>>>>>>>>>>>>>" + text);
} catch (IOException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
return data;
}
According to the official training documentation linked in the question, the missing piece of information, at the time of the question having been asked, is that the four pieces of data contained in the BlobHandler need to be uploaded to a server owned by the client application then subsequently downloaded by which ever other application wants to access the blob via the BlobStorageManager.
So it would seem that on-device blob discovery is not supported. There could also be a solution possible using a Content Provider which could offer up the four required pieces of data, thus circumventing the need for the server infrastructure.
Here I try to use getClassLoader().getResources() to get my .model file, however it returns null. I'm not sure where goes wrong!
And when I try to print out the urls, it gives me java.lang.TwoEnumerationsInOne#5fd1900, what does this means?
public Activity(MainActivity activity) {
MainActivity activity = new MainActivity();
try {
// Open stream to read trained model from file
InputStream is = null;
// this .model file is save under my /project/app/src/main/res/
Enumeration<URL> urls = Activity.class.getClassLoader().getResources("file.model");
// System.out.println("url:"+urls);
if (urls.hasMoreElements()) {
URL element = urls.nextElement();
is = element.openStream();
}
// deserialize the model
classifier = (J48) SerializationHelper.read(is);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
In Android, resources put under src/main/res are not visible in the class path and can only be accessed via the android resources API. Try to put the file into src/main/resources.
I just started to learn developping android and I have a (probably) basic questions, but I didn't find anything clear.
I'm trying to store data in a JSON file, well, I've understood the logic to store it, my way is:
public boolean writeFileJson(JSONObject jobj) {
try {
FileOutputStream fOut = openFileOutput(file, Context.MODE_PRIVATE);
fOut.write(jobj.toString().getBytes());
fOut.close();
Toast.makeText(getBaseContext(), "file saved", Toast.LENGTH_SHORT).show();
} catch (Exception e1) {
e1.printStackTrace();
}
return true;
}
But my problem is to read, and concretely for the first time, because the way I do it is:
public String readFileJson() {
int c;
String temp = "";
try {
FileInputStream fin = openFileInput(file);
while ((c = fin.read()) != -1) {
temp = temp + Character.toString((char) c);
}
Toast.makeText(getBaseContext(), "file read", Toast.LENGTH_SHORT).show();
} catch (Exception e2) {
}
return temp;
}
So wen I read it for the first time and I want to acces to a parameter of my JSON is obvious that any JSON Object already exist in the file.
So I try to save a first JSON Object with my parameters in onCreate() method and save it in the file, but wen I run the app, and I stop it, it returns again to execute onCreate() and deletes all data stored during the run time.
So my question is: There is any way to init only for one time the parameters of the JSON file to could access for the first time unlike it's empty???
I hope that I'd explained well!!
Thanxxxx!!!!
You can create your own flag boolean and check when you start.
Well I don't understand well why you can use a flag if the flag is set to init value in onCreate(), but I've tried a basic method: check each time if the json file is null. But it's like so basic no? Is there any ther way, or trying to understand how to use flags without reset their values?
msgjson = readFileJson();
if(msgjson == "") {
json.put("ARRAY", jsonArray);
}else{
json = new JSONObject(msgjson);
}
Thanx!!
I am desperatly trying to fix a bug that:
always happens in my emulator for Android versions 2.2, 2.3
never happens in emulator android versions 4.*
never happens in a real device (android 4.*)
It is the following IndexOutOfBoundsException exception:
java.lang.RuntimeException: Unable to start activity
ComponentInfo{<myapppackage>}: java.lang.IndexOutOfBoundsException:
Invalid index 39, size is 0
In my app I am fecthing data from a json file that I am displaying as text. I've isoleted where the bug is coming from, it is when I call this method:
public String getItemValue(int id, String s) {
List<JsonItems> list = new ArrayList<JsonItems>();
try {
// CONVERT RESPONSE STRING TO JSON ARRAY
JSONArray ja = new JSONArray(s);
// ITERATE THROUGH AND RETRIEVE
int n = ja.length();
for (int i = 0; i < n; i++) {
// GET INDIVIDUAL JSON OBJECT FROM JSON ARRAY
JSONObject jo = ja.getJSONObject(i);
// RETRIEVE EACH JSON OBJECT'S FIELDS
JsonItems ji = new JsonItems();
ji.id = jo.getInt("id");
ji.text= jo.getString("text");
list.add(ji);
}
} catch (Exception e) {
e.printStackTrace();
}
return list.get(id).text;
}
My class JsonItems is very basic:
public class JsonItems{
int id;
String text;
}
Sample from my json file:
[
{"id":0,"text":"some text 0"},
{"id":1,"text":"some text 1"},
{"id":2,"text":"some text 2"}
]
Here is how I process content of my json file into a String
public static String fromJsonFileToString(String fileName, Context c) {
//JSONArray jArray = null;
String text = "";
try {
InputStream is = c.getAssets().open(fileName);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
text = new String(buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
return text;
}
Once again I repeat: the IndexOutOfBoundsException NEVER happens on a device with Android 4.* , it only happens when I test the app on emulators with Android 2.*
Any idea where it is coming from?
Thanks
First problem:
You are not reading your input stream correctly. Calling available literally does just that - it returns you the amount of data that is available to be read, when the call is made. This number may, or may not represent the entire content of the file.
Reading material for you:
How to Read a File in Java.
Writing and Creating Files
Note that there are helper libraries like Apache Commons IO that make it possible to read file contents in a single line of code (IOUtils.toString(inputStream)). Android doesn't support Java 7 yet but a noteworthy alternative is available in that release, with the Files.readAllLines method. In any case, you can make the below shown changes to your file reading code and it should work better:
public static String fromFileToString(String fileName, Context context) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
context.getAssets().open(fileName)));
String line = null;
StringBuilder builder = new StringBuilder(1024);
while ((line = reader.readLine()) != null) {
builder.append(line);
}
return builder.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Second problem:
You do not do any bound check, to make sure that the argument you pass into your 'search' method:
public String getItemValue(int id, String s)
Does not exceed the length of the list of items you eventually calculate:
return list.get(id).text;
// ^
// -------------
// 'id' could be larger than the list size!
In any case, your current design doesn't at all match what you are really trying to do, aka, to determine the element in the JSON array that has an 'id' field matching what you supply to the method. You need to process the JSON data as a map, in order to be able to do this.
public String getItemValue(int id, String json) {
if(json == null || json.trim().equals("")) return null;
Map<Integer, JsonItems> map = new HashMap<Integer, JsonItems>(4);
try {
JSONArray ja = new JSONArray(json);
int n = ja.length();
for (int i = 0; i < n; i++) {
JSONObject jo = ja.getJSONObject(i);
JsonItems ji = new JsonItems();
ji.id = jo.getInt("id");
ji.text = jo.getString("text");
map.put(Integer.valueOf(ji.id, ji);
}
} catch(NumberFormatException nfe) {
throw new IllegalArgumentException("Invalid JSON format");
} catch (Exception e) {
throw new RuntimeException(e);
}
JsonItems item = map.get(id);
return item != null ? item.text : null;
}
Some quick notes:
JsonItems should be called JsonItem, to conform to good Java naming standards
You should really parse and store your JSON just once, to improve performance
You are really only using a minimal subset of your JSON data, you could actually determine the matching node within your for loop and directly return its value, without having to use an intermedia Java bean object
I want to load a 2D array like this one:
[
[false, true, false, false],
[true, false, false, false],
[false, false, false, true],
[false, false, true, false],
[false, false, true, false]
]
Actually, these are the radio button states, false indicating radiobutton is uncheck while true indicating that the radio button is checked.
To explain the whole scenario, I am creating a quiz, which has a question and options for this i have created a radiobuttonStates[][] 2D array. The first [] indicating number of questions versus number of options in second []. There is a save and exit button which saves the quiz (saving the question number from where the user left and the selected radio buttons, previous as well as present). So the 2D array which i have created in the first attempt of the quiz, I want to load this same array when the user again comes back and resumes the quiz. Right now i am thinking to store this array in some persistant storage (Database or any kind). But I am not getting any way on how to store. The user clicks the resume button and I am showing the current question number from the database but not able to show the radio button selection. Please help me.
Well you can always transform your boolean array into a string(or more strings), and store it in SharedPreferences.
For example: ResultString = "false,true,false,false";
When you need the result back from the SharedPreference just split your strings, using a certain separator (for instance ",") and reuse your saved data.
Like this you get faster store/restore data backup. Using SQLite to store this kind of data is not a good idea.
Good luck,
Arkde
Well, you can make some kind of table structure where you put each individual array value in a specific row/column. However, if your array contains a small number of elements (like the one you showcase in your question), you can just serialize the instances and save the bytes in the database. This way you won't have to make all the boiler-plate code that thakes each array value and puts it in some row/column and then builds an array out of various row/column values.
An easy and instant way is to store the array or multiple 2d arrays is to use file to read and write your array object.
here is the code sample below which reds my custom objects from a file and writes them to the file. the only thing in the methods below are when reading it reds all objects and when writing it writes all objects again means you can not append single object with previous file or read single object from file. so if you hae to append another array you have to read all previous ones and then write them again with the increment of one you want to append.
public static boolean writeBlockedMessagesInFile(Context context,
ArrayList<BlockedMessages> blockedMessages) {
boolean status = false;
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = context.openFileOutput("BlockedMessagesFile.dat",
Context.MODE_WORLD_WRITEABLE);
oos = new ObjectOutputStream(fos);
if (blockedMessages != null && blockedMessages.size() != 0) {
for (int i = 0; i < blockedMessages.size(); i++)
oos.writeObject(blockedMessages.get(i));
}
oos.flush();
oos.close();
fos.close();
status = true;
} catch (IOException e) {
e.printStackTrace();
}
return status;
}
public static ArrayList<BlockedMessages> readBlockedMessagesFromFile(Context context) {
ArrayList<BlockedMessages> blockedMessages = new ArrayList<BlockedMessages>();
FileInputStream fis = null;
ObjectInputStream ois = null;
Object object = null;
try {
fis = context.openFileInput("BlockedMessagesFile.dat");
ois = new ObjectInputStream(fis);
BlockedMessages blo;
Object temp;
try {
while ((blo = (BlockedMessages) ois.readObject()) != null) {
blockedMessages.add(blo);
}
} catch (NullPointerException npe) {
npe.printStackTrace();
} catch (EOFException eof) {
eof.printStackTrace();
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return blockedMessages;
}