Opening a 12kb text file takes WAY too long....? - android

The following code works, but takes way too long (over a minute) to open a small file. The LogCat shows a lot of instances of "GC_FOR_MALLOC freed #### objects / ###### bytes in ##ms". Any suggestions?
File dirPath = new File(Environment.getExternalStorageDirectory(), "MyFolder");
String content = getFile("test.txt");
public String getFile(String file){
String content = "";
try {
File dirPathFile = new File(dirPath, file);
FileInputStream fis = new FileInputStream(dirPathFile);
int c;
while((c = fis.read()) != -1) {
content += (char)c;
}
fis.close();
} catch (Exception e) {
getLog("Error (" + e.toString() + ") with: " + file);
}
return content;
}
Update:
This is what it looks like now:
File dirPath = new File(Environment.getExternalStorageDirectory(), "MyFolder");
String content = getFile("test.txt");
public String getFile(String file){
String content = "";
File dirPathFile = new File(dirPath, file);
try {
StringBuilder text = new StringBuilder();
BufferedReader br = new BufferedReader(new FileReader(dirPathFile));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
content = new String(text);
} catch (Exception e) {
getLog("Error (" + e.toString() + ") with: " + file);
}
return content;
}
Thank you all!!

Using += on a String is extremely inefficient - it will constantly allocate and deallocate memory, something you need to avoid!
If you need to constantly add characters, use a StringBuilder and give it a sufficiently big buffer up front.
However, it's even better to just read the entire file as a byte array and then create a string from that byte array. Use the String(byte[]) constructor.

content += (char)c;
Well, here's your problem. String concatenation is slow if you have to do it repeatedly. And you're reading the file one character at a time, which is also really slow.
You want to be using the read(byte[] buffer) method to read the file into a buffer efficiently. And then you can stringify the buffer if need be.

Rather than reading a single byte at a time, you should read multiple using read(byte[]).
Also, Strings are immutable, so every time you do String s = s + "a"; there is the possibility that you are creating a new String object. You can use StringBuilder instead to build up a larger string.

Schlemiel the painter strikes again!

try to read with buffer read(byte[] buff)

The reasons are:
You are creating too many String objects with content += (char)c; - use StringBuilder instead to append with read data, then in the end call toString() on the StringBuilder.
You don't use a byte[] (or char[], it depends on implementation) buffer to read from file. Usually 1KB buffer is optimal instead of reading one by one byte.

Related

Async Socket Server Listen Again

I am using the sample from Microsoft's Async Socket Listener. I got everything working fine, I am able to send files and data etc. However, I am struggling with trying to keep the socket open. I do not want to close the socket and re-open for every file I am sending. I want to keep it open until all files are sent. I assume the issue is in the 'SendCallback' but I can not seem to get it to work.
.Net Code
Private Shared Sub SendCallback(ar As IAsyncResult)
Try
' Retrieve the socket from the state object.
Dim handler As Socket = DirectCast(ar.AsyncState, Socket)
' Complete sending the data to the remote device.
Dim bytesSent As Integer = handler.EndSend(ar)
'trying to receive again - I added this after commenting out the below lines but am missing something.
Dim state As StateObject = DirectCast(ar.AsyncState, StateObject)
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
'The below lines are what was there, I tried to comment out and use the above lines trying to receive again but it does not work. I assume I am missing something simple but can not find anything useful. Any help would be appreciated.
'handler.Shutdown(SocketShutdown.Both)
'handler.Close()
Catch e As Exception
PubVars.ServerStatus = e.ToString
End Try
End Sub
I know I do not want to close the socket until all is sent but I am not sure what i am missing. I want to send the first file, then send back to the client a status update which all works well. Then when the client receives the status update, I want to send the next file and so on.
Android Code:
Socket socket = null;
try {
socket = new Socket("localhost", 11000);
OutputStream out = socket.getOutputStream();
PrintWriter output = new PrintWriter(out);
String FileName = "my.jpg";
String FilePath= Environment.getExternalStorageDirectory() + "/mydir/" + FileName;
File f= new File(FilePath);
byte[] data= readFileToByteArray(f);
String strbase64 = Base64.encodeToString(data, Base64.DEFAULT);
//I chose to pass the filename as part of my header string then parse it out on server
output.println("<HEADER>" + FileName + "</HEADER>" + strbase64 + "<EOF>");
output.flush();
InputStream in = socket.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(in));
StringBuilder returnString = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
returnString.append(line).append('\n');
}
Log.i("INPUT",returnString.toString());
//If true run the next one
//SEND NEW FILE
Log.i("NEXT","STARTING NEXT FILE");
FileName = "my2.png";
FilePath= Environment.getExternalStorageDirectory() + "/mydir/" + FileName;
f= new File(FilePath);
data= readFileToByteArray(f);
strbase64 = Base64.encodeToString(data, Base64.DEFAULT);
output.println("<HEADER>" + FileName + "</HEADER>" + strbase64 + "<EOF>");
output.flush();
in = socket.getInputStream();
r = new BufferedReader(new InputStreamReader(in));
returnString = new StringBuilder();
while ((line = r.readLine()) != null) {
returnString.append(line).append('\n');
}
Log.i("INPUT",returnString.toString());
//END ANOTHER FILE
output.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}

separate appended data from the file android

I am writing data in file continuously in append mode using FileOutputStream. Everything is working fine but I want to separate each appended stream from file while reading it.
Here is how I created file and writing it in Android
FileOutputStream outputStream = service.openFileOutput("text.txt", Context.MODE_APPEND);
outputStream.write(measurement.toString().getBytes());
outputStream.close();
It is appending data successfully but when I am reading it I do not know how to find end point between appended strings.
Here is my code to read the string from file
StringBuilder total = new StringBuilder();
FileInputStream inputStream = service.openFileInput("text.txt");
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = r.readLine()) != null) {
total.append(line);
}
r.close();
inputStream.close();
Log.d(TAG, "File Size: "+total.length());
For future references, it is silly that I have not tried but thanks to #greenapps
I just had to add another write statement to append
FileOutputStream outputStream = service.openFileOutput("text.txt", Context.MODE_APPEND);
outputStream.write(measurement.toString().getBytes());
outputStream.write("\n".getBytes());
outputStream.close();
for reading saparated string I simply used split for java
String[] parts = total.toString().split("\n");
for(int i = 0; i < parts.length; i++) {
Log.d(TAG, parts[i]);
}
deleteFile("text.txt");

Android very slow to read file from internal storage

On my application I need save and load a small xml file. I'd like save and load it on internal storage but I have speed problem for read this file.
This file is very small (about 20/30 lines).
I have try this code:
try {
FileInputStream file = openFileInput("map.xml");
int c;
String xml = "";
while( (c = file.read()) != -1){
xml = xml + Character.toString((char)c);
}
readXMLdata(xml);
mapRestore = true;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
then I have try to save and load the same file to external storage with this code:
String data = "";
try {
File myFile = new File(file_xml);
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader myReader = new BufferedReader(new InputStreamReader(fIn));
String aDataRow = "";
String aBuffer = "";
while ((aDataRow = myReader.readLine()) != null) {
aBuffer += aDataRow + "\n";
}
data = aBuffer;
myReader.close();
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
return data;
I have do all test on my Nexus S. If use internal storage I get a lag to read file (some seconds), if I use external storage I don't have it.
Any solution?
Solution is pretty obvious - just use BufferedReader. In your second sample use you use it, but in the first you don't. That's why you have difference in reading performance.
When you have just FileInputStream and make calls to read method it will actually read data each time from internal storage which is not so fast.
When you use BufferedReader or BufferedInputStream data will be read into memory buffer first and then when you call readLine data is read from this buffer. It dramatically decrease the number of IO operations on internal storage and performs a lot faster.

How to read a Text file (.txt) and set the words inside in String Variables on Android?

I am trying to read a Text file (.txt), Im done with that.
But I need to set in String variables every word inside the Text file.
For example this words are inside the Text file called words.txt :
whatsup; superman; heroe; batman;
And this is the code I am using to read words.txt :
File directory = Environment.getExternalStorageDirectory();
File file = new File(directory.getAbsolutePath()+"/HiMom", "words.txt");
try {
FileInputStream fIn = new FileInputStream(file);
InputStreamReader file = new InputStreamReader(fIn);
BufferedReader br=new BufferedReader(file);
String line = br.readLine();
String text = "";
while (line!=null)
{
text = text + line + "\n";
line = br.readLine();
}
br.close();
file.close();
etContentArchivo.setText(text);
} catch (IOException e) {
}
Now I need to read the Text file and set every word before a ";" in String variables, for Example:
String get1 = whatsup;
String get2 = superman;
String get3 = heroe;
String get4 = batman;
It can be also a String Array. But I dont know how to set the words in String variables.
Thanks.
You could do String[] words = text.split(";");

read file from the assets folder

I have a large text file that is in the assets folder, and I have a button that reads the file's next line successfully. However, I want to read the previous line in case the user clicks another button.
Reading the whole file to memory is not an option. The file lines are not numbered.
InputStream is = getResources().getAssets().open("abc.txt");
String result= convertStreamToString(is);
public static String convertStreamToString(InputStream is)
throws IOException {
Writer writer = new StringWriter();
char[] buffer = new char[2048];
try {
Reader reader = new BufferedReader(new InputStreamReader(is,
"UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
String text = writer.toString();
return text;
}
If you only need to keep track of one previous line, you can do something like the following, keeping track of the previous line through each iteration (I assumed you were using a reader; for this example, BufferedReader):
String previous = null, line; // null means no previous line
while (line = yourReader.readLine()) {
// Do whatever with line
// If you need the previous line, use:
if (yourCondition) {
if (previous != null) {
// Do whatever with previous
} else {
// No previous line
}
}
previous = line;
}
If you need to keep track of more than one previous line, you may have to expand that into an array, but you will be keeping a huge amount in memory if your file is large--as much as if you'd read the entire file, once you get to the last line.
There is no simple way in Java or Android to read the previous line, only the next (as it is easier in file I/O to more forward than backward).
One alternative I can think of is to keep a line marker (starting at 0), and as you advance through the lines, increase it. Then, to go backwards, you have to read the file line by line again, until you get to that line minus one. If you need to go backwards, go to that new line minus one, and so on. It would be a heavy operation, most likely, but would suit your needs.
Edit: If nothing above will work, there is also a method to read in a file backwards, in which you may be able to use to find the previous line by iterating forward. Just an alternative idea, but definitely not an easy one to implement.
public class LoadFromAltLoc extends Activity {
//a handle to the application's resources
private Resources resources;
//a string to output the contents of the files to LogCat
private String output;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//get the application's resources
resources = getResources();
try
{
//Load the file from the raw folder - don't forget to OMIT the extension
output = LoadFile("from_raw_folder", true);
//output to LogCat
Log.i("test", output);
}
catch (IOException e)
{
//display an error toast message
Toast toast = Toast.makeText(this, "File: not found!", Toast.LENGTH_LONG);
toast.show();
}
try
{
//Load the file from assets folder - don't forget to INCLUDE the extension
output = LoadFile("from_assets_folder.pdf", false);
//output to LogCat
Log.i("test", output);
}
catch (IOException e)
{
//display an error toast message
Toast toast = Toast.makeText(this, "File: not found!", Toast.LENGTH_LONG);
toast.show();
}
}
//load file from apps res/raw folder or Assets folder
public String LoadFile(String fileName, boolean loadFromRawFolder) throws IOException
{
//Create a InputStream to read the file into
InputStream iS;
if (loadFromRawFolder)
{
//get the resource id from the file name
int rID = resources.getIdentifier("fortyonepost.com.lfas:raw/"+fileName, null, null);
//get the file as a stream
iS = resources.openRawResource(rID);
}
else
{
//get the file as a stream
iS = resources.getAssets().open(fileName);
}
//create a buffer that has the same size as the InputStream
byte[] buffer = new byte[iS.available()];
//read the text file as a stream, into the buffer
iS.read(buffer);
//create a output stream to write the buffer into
ByteArrayOutputStream oS = new ByteArrayOutputStream();
//write this buffer to the output stream
oS.write(buffer);
//Close the Input and Output streams
oS.close();
iS.close();
//return the output stream as a String
return oS.toString();
}
}

Categories

Resources