Resource leak warning on file stream - android

private void SaveLog(boolean externalStorage)
{
String s = tv_log.getText().toString();
File file;
FileOutputStream fos;
if ( externalStorage )
{
try
{
file = new File(getExternalFilesDir(null), FILE_LOG);
fos = new FileOutputStream(file); // Warning: Resource leak: 'fos' is never closed
}
catch(FileNotFoundException e)
{
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
return;
}
}
else
{
try
{
fos = openFileOutput(FILE_LOG, Context.MODE_PRIVATE);
}
catch(FileNotFoundException e)
{
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
return;
}
}
try
{
fos.write(s.getBytes());
fos.close();
}
catch(IOException e)
{
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
return;
}
}
Why the warning is shown in the line fos = new FileOutputStream(file)? Interesting, that if I remove if ( externalStorage ) and leave only the first branch, the warning is not shown:
private void SaveLog(boolean externalStorage)
{
String s = tv_log.getText().toString();
File file;
FileOutputStream fos;
try
{
file = new File(getExternalFilesDir(null), FILE_LOG);
fos = new FileOutputStream(file); // OK!
}
catch(FileNotFoundException e)
{
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
return;
}
try
{
fos.write(s.getBytes());
fos.close();
}
catch(IOException e)
{
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
return;
}
}

I believe that in your specific case there isn't any possibility of resource leak as the line fos = new FileOutputStream(file);is the last line before the end of the try group, and if you have an exception in here the resource fos wouldn´t be created.
However, if you would have a statement after that line, that statement could genetare a exception, the execution would move to the catch group that is terminated with a return without releasing resources allocated in the trygroup.
The easiest way to avoid the warning is to add the following in the catch:
catch(FileNotFoundException e)
{
try { if(fos != null) fos.close();} catch (Exception e2) {} //Add this line
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
return;
}
This would ensure that resource will be released if there is an exception raised.

In every catch block, close the stream using fos.close().
Otherwise, the program might hit the try block, generate an exception and go to the catch block without ever closing the stream. This might be causing the error.

Related

Android Studio : How to save text files from EditText into a specific directory

I am currently making a journal app, so the users type their entry into an EditText and it saves in their phone and they can load it up later. At first I used just getFilesDir() but recently there is this weird rList file that shows up every time I open the app and I couldn't figure it out(I wrote a question about it). So now I want to save these files in this specific directory called TextEntries
Here is the code for my save funcction:
public void save(View v) {
textFile = inputTitle.getText().toString();
String text = inputFeelings.getText().toString();
FileOutputStream fos = null;
try {
String rootPath = getFilesDir().getAbsolutePath() + "/TextEntries/";
File root = new File(rootPath);
if (!root.exists()) {
root.mkdirs();
}
fos = openFileOutput(textFile, MODE_PRIVATE);
fos.write(text.getBytes());
inputFeelings.getText().clear();
Toast.makeText(this, "Saved to " + getFilesDir() + "/TextEntries/" + textFile,
Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
All help is welcome and thank you in advance.
replace
openFileOutput(textFile, MODE_PRIVATE);
with
new FileOutputStream(rootPath + textFile)

Save an object or list of objects in Android

I have a list of a custom object class that I called Students, and I would like to save it in a way such that it won't be affected when we close the app completely. The class: (of course I have a constructor and stuff, but these are the fields that may be important)
public class Students implements Serializable{
private int ID;
private int imageId;
private String firstName;
private String lastName;
private Map<Date, Boolean> Attendance;
My attempt in saving and reading it (inside an activity):
public Boolean saveStudent(Students s) {//saves student into fileName (data.bin)
ObjectOutputStream oos = null;
try {
File file = new File(this.getFilesDir().toString(), fileName);
file.createNewFile();
if(!file.mkdir()){ //just to check whats the reason behind the failed attempt
Toast.makeText(this, "Security Issue", Toast.LENGTH_SHORT).show();
}
FileOutputStream fos = this.openFileOutput(fileName, this.MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(s);
oos.close();
fos.close();
return true;
} catch (FileNotFoundException err) {
Toast.makeText(this, "Something went wrong while saving", Toast.LENGTH_SHORT).show();
return false;
} catch (Exception abcd) {
Toast.makeText(this, "Ooops, I don't know what's the problem. Sorry about that!", Toast.LENGTH_SHORT).show();
return false;
}
finally {//makes sure to close the ObjectOutputStream
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public Students readStudent() {//reads Student object from(data.bin) and returns it.
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("data.bin"));
ois.close();
return (Students) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
When I run, it gives me the "Something went wrong while saving" and "Security Issue". Can some one help me solve this problem?
PS: I really don't wanna use an SQLlite database because I think that it's an overkill for just 3 or 4 entries. I hope it's not one of those stupid mistakes that take several hours of staring to find out.
Please check this source. I have tested it, and it is working well.
public class LoginActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Students students = new Students();
students.setID(1234);
students.setFirstName("hello");
Log.d("test", "pre students="+students);
saveStudent(students);
Students s = readStudent();
Log.d("test", "after students="+s);
}
String fileName = "data.bin";
public Boolean saveStudent(Students s) {//saves student into fileName (data.bin)
ObjectOutputStream oos = null;
try {
/* File file = new File(this.getFilesDir().toString(), fileName);
file.createNewFile();
if(!file.mkdir()){ //just to check whats the reason behind the failed attempt
Toast.makeText(this, "Security Issue", Toast.LENGTH_SHORT).show();
}*/
FileOutputStream fos = this.openFileOutput(fileName, this.MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(s);
oos.close();
fos.close();
return true;
} catch (FileNotFoundException err) {
Toast.makeText(this, "Something went wrong while saving", Toast.LENGTH_SHORT).show();
return false;
} catch (Exception abcd) {
Toast.makeText(this, "Ooops, I don't know what's the problem. Sorry about that!", Toast.LENGTH_SHORT).show();
return false;
}
finally {//makes sure to close the ObjectOutputStream
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public Students readStudent() {//reads Student object from(data.bin) and returns it.
ObjectInputStream ois = null;
Students students = new Students();
try {
/*ois = new ObjectInputStream(new FileInputStream("data.bin"));*/
ois = new ObjectInputStream(openFileInput(fileName));
students = (Students) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return students;
}
}
And for saving and loading multiple objects, please check this link How do I save and multiple objects from a single file?
No need to file.mkdir()
Give openFileOutput an absolutePath may be good.
Check here:
File file = new File(this.getFilesDir().toString(), fileName);
file.createNewFile();
//if(!file.mkdir()){ //just to check whats the reason behind the failed attempt
// Toast.makeText(this, "Security Issue", Toast.LENGTH_SHORT).show();
//}
// Use file.getAbsolutePath()
FileOutputStream fos = this.openFileOutput(file.getAbsolutePath(), this.MODE_PRIVATE);
oos =oos = new ObjectOutputStream(fos);
oos.writeObject(s);
oos.close();
fos.close();
return true;
And in the read method you should give an absolute file path
check this line : ois = new ObjectInputStream(new FileInputStream("data.bin"));
pass more information to the new FileInputStream("data.bin")
change to :new FileInputStream(new File(getContext().getFilesDir(),"data.bin"));
Do not forget the parent dir.

Can't create file in the internal storage

i am trying to create a file in the internal storage, i followed the steps in android developers website but when i run the below code there is no file created
please let me know what i am missing in the code
code:
File file = new File(this.getFilesDir(), "myfile");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
FileOutputStream fOut = null;
try {
fOut = openFileOutput("myfile",Context.MODE_PRIVATE);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
fOut.write("SSDD".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
By default these files are private and are accessed by only your application and get deleted , when user delete your application
For saving file:
public void writeToFile(String data) {
try {
FileOutputStream fou = openFileOutput("data.txt", MODE_APPEND);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fou);
outputStreamWriter.write(data);
outputStreamWriter.close();
}
catch (IOException e) {
Log.e("Exception", "File write failed: " + e.toString());
}
}
For loading file:
public String readFromFile() {
String ret = "";
try {
InputStream inputStream = openFileInput("data.txt");
if ( inputStream != null ) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString = "";
StringBuilder stringBuilder = new StringBuilder();
while ( (receiveString = bufferedReader.readLine()) != null ) {
stringBuilder.append(receiveString);
}
inputStream.close();
ret = stringBuilder.toString();
}
}
catch (FileNotFoundException e) {
Log.e("login activity", "File not found: " + e.toString());
} catch (IOException e) {
Log.e("login activity", "Can not read file: " + e.toString());
}
return ret;
}
Try to get the path for storing files were the app has been installed.The below snippet will give app folder location and add the required permission as well.
File dir = context.getExternalFilesDir(null)+"/"+"folder_name";
If you are handling files that are not intended for other apps to use, you should use a private storage directory on the external storage by calling getExternalFilesDir(). This method also takes a type argument to specify the type of subdirectory (such as DIRECTORY_MOVIES). If you don't need a specific media directory, pass null to receive the root directory of your app's private directory.
Probably, this would be the best practice.
Use this method to create folder
public static void appendLog(String text, String fileName) {
File sdCard=new File(Environment.getExternalStorageDirectory().getPath());
if(!sdCard.exists()){
sdCard.mkdirs();
}
File logFile = new File(sdCard, fileName + ".txt");
if (logFile.exists()) {
logFile.delete();
}
try {
logFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
//BufferedWriter for performance, true to set append to file flag
BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
buf.write(text);
buf.newLine();
buf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
In this method, you have to pass your data string as a first parameter and file name which you want to create as second parameter.

Resource leak: stream is never closed

Short way to reproduce the problem in my real project. Environment: Android SDK 1.16, Eclipse 4.2.0, Windows. Create default Android application and add the following code to MainActivity.java:
private void Save1(boolean externalStorage)
{
String s = "12345";
File file;
FileOutputStream fos = null;
if ( externalStorage )
{
try
{
file = new File(getExternalFilesDir(null), "log");
fos = new FileOutputStream(file); // Resource leak: 'fos' is never closed
}
catch(FileNotFoundException e)
{
return;
}
}
else
{
try
{
fos = openFileOutput("log", Context.MODE_PRIVATE);
}
catch(FileNotFoundException e)
{
return;
}
}
try
{
fos.write(s.getBytes());
fos.close();
}
catch(IOException e)
{
return;
}
}
private void Save2(boolean externalStorage)
{
String s = "12345";
File file;
FileOutputStream fos = null;
try
{
file = new File(getExternalFilesDir(null), "log");
fos = new FileOutputStream(file); // OK
}
catch(FileNotFoundException e)
{
return;
}
try
{
fos.write(s.getBytes());
fos.close();
}
catch(IOException e)
{
return;
}
}
Line fos = new FileOutputStream(file) in Save1 function, warning: Resource leak: 'fos' is never closed
The same line in Save2 function: no warnings.
Please don't send untested answers, the problem is not so simple as it looks. Adding fos.close() to different parts of the function doesn't help.
It also go away if i add an finally block to the try in the if block like this:
if (externalStorage) {
try {
fos = new FileOutputStream(new File(getExternalFilesDir(null),
"log"));
} catch (FileNotFoundException e) {
return;
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
try {
fos = openFileOutput("log", Context.MODE_PRIVATE);
} catch (FileNotFoundException e) {
return;
}
}
It is getting interesting...
So my guess would be, So if you are opening an Stream in a try block and catch block has an return statement then there should be a finally block that close the stream.
Something like that..
A tried the same code in a simple java project in eclipse and still got the warning. So it looks like is not related to lint or android. It looks like the eclipse compiler issue. Following is the code, I had to create a dummy openFileOutput() method since it is not available n java:
private void Save1(boolean externalStorage) {
String s = "12345";
FileOutputStream fos = null;
if (externalStorage) {
try {
fos = new FileOutputStream(new File("c://", "log"));
} catch (FileNotFoundException e) {
return;
}
} else {
try {
fos = openFileOutput("log", -1);
} catch (FileNotFoundException e) {
return;
}
}
try {
fos.write(s.getBytes());
fos.close();
} catch (IOException e) {
return;
}
}
/**
* #param string
* #param i
* #return
*/
private FileOutputStream openFileOutput(String string, int i)
throws FileNotFoundException {
return null;
}
This isn't an answer, but added here for clarity to the OP and other readers rather than a comment.
I've tested this in IDEA 11.2 API 15 using the current versions of the platform tool chain (Rev 14 Oct 2012) and there is no lint warning, compile error or runtime error. I forced the method to go through each path by creating exceptions and setting useExternalStorage both true and false.
My guess is this is a lint/compile error in your tool chain or possibly Eclipse (although unlikely, does Eclipse itself do any checking like this?).
[EDIT]
Just a thought, (and I would test but I've forgotten how to use Eclipse) but FileInputStream(file) might throw a SecurityException which would be thrown to somewhere up in your call stack. What happens if you catch it?
[EDIT]
This is the closest warning I get, and not at all relevant. I'm convinced the warning is not down to you.
In case of an Exception, fos wont be closed. Adding a finally to the try-catch would solve this issue.
try
{
fos = openFileOutput("log", Context.MODE_PRIVATE);
}
catch(FileNotFoundException e)
{
return;
}
//add this:
finally {
if (fos != null) {
fos.close();
}
}

How to write to a textfile in one Activity & read that file in another Activity?

I am able to write and then read a text file in the SAME activity, but I am unable to read a text file after writing to it from another Activity.
Ex: Activity A creates and writes to a text file. Activity B reads that text file.
I use this code to write to the text file in Activity A:
FileOutputStream fos = null;
OutputStreamWriter osw = null;
try
{
fos = openFileOutput("user_info.txt", Context.MODE_WORLD_WRITEABLE);
osw = new OutputStreamWriter(fos);
osw.write("text here");
osw.close();
fos.close();
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And then I use this code to try and read the same text file created by Activity A, but I get a FileNotFoundException:
try
{
FileInputStream fis = openFileInput("user_info.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader buff = new BufferedReader(isr);
String line;
while((line = buff.readLine()) != null)
{
Toast.makeText(this, line, Toast.LENGTH_LONG).show();
}
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Does anyone know why I am getting the FileNotFoundException?
Is it a path issue?
Don't really know how is built your application, but, the error you get does seem like a path issue, are you sure both Activities are in the same folder ?
If not, you'll need to set either an abolute path (like : "/home/user/text.txt") for the text file or a relative path (like : "../text.txt").
If you're not sure, try to print the current path for the Activity using some command like
new File(".").getAbsolutePath();
And, although I can't say I'm expert with Android, are you sure you need the Context.MODE_WORLD_WRITEABLE for your file ? If no other application than yours is reading or writing from/to it, it should not be necessary, right ?
it is surealy a path issue.
you can write like this
fpath=Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+"yourdirectory";
File custdir=new File(fpath);
if(!custdir.exists())
{
custdir.mkdirs();
}
File savedir=new File(custdir.getAbsolutePath());
File file = new File(savedir, filename);
if(file.exists())
{
file.delete();
}
FileOutputStream fos;
byte[] data = texttosave.getBytes();
try {
fos = new FileOutputStream(file);
fos.write(data);
fos.flush();
fos.close();
Toast.makeText(getBaseContext(), "File Saved", Toast.LENGTH_LONG).show();
finish();
} catch (FileNotFoundException e) {
Toast.makeText(getBaseContext(), "Error File Not Found", Toast.LENGTH_LONG).show();
Log.e("fnf", ""+e.getMessage());
// handle exception
} catch (IOException e) {
// handle exception
Toast.makeText(getBaseContext(), "Error IO Exception", Toast.LENGTH_LONG).show();
}
and you can read like
String locatefile=Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+"yourdirectory"+"/filename";
try {
br=new BufferedReader(new FileReader(locatefile));
while((text=br.readLine())!=null)
{
body.append(text);
body.append("\n");
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Categories

Resources