Android Reading from an Input Stream Efficiently

Android Reading from an Input stream efficiently

The problem in your code is that it's creating lots of heavy String objects, copying their contents and performing operations on them. Instead, you should use StringBuilder to avoid creating new String objects on each append and to avoid copying the char arrays. The implementation for your case would be something like this:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}

You can now use total without converting it to String, but if you need the result as a String, simply add:

String result = total.toString();

I'll try to explain it better...

  • a += b (or a = a + b), where a and b are Strings, copies the contents of both a and b to a new object (note that you are also copying a, which contains the accumulated String), and you are doing those copies on each iteration.
  • a.append(b), where a is a StringBuilder, directly appends b contents to a, so you don't copy the accumulated string at each iteration.

How do an InputStream, InputStreamReader and BufferedReader work together in Java?

This Streams in Java concepts and usage link, give a very nice explanations.

This

Streams, Readers, Writers, BufferedReader, BufferedWriter – these are the terminologies you will deal with in Java. There are the classes provided in Java to operate with input and output. It is really worth to know how these are related and how it is used. This post will explore the Streams in Java and other related classes in detail. So let us start:

Let us define each of these in high level then dig deeper.

Streams
Used to deal with byte level data

Reader/Writer
Used to deal with character level. It supports various character encoding also.

BufferedReader/BufferedWriter
To increase performance. Data to be read will be buffered in to memory for quick access.

While these are for taking input, just the corresponding classes exists for output as well. For example, if there is an InputStream that is meant to read stream of byte, and OutputStream will help in writing stream of bytes.

InputStreams
There are many types of InputStreams java provides. Each connect to distinct data sources such as byte array, File etc.

For example FileInputStream connects to a file data source and could be used to read bytes from a File. While ByteArrayInputStream could be used to treat byte array as input stream.

OutputStream
This helps in writing bytes to a data source. For almost every InputStream there is a corresponding OutputStream, wherever it makes sense.


UPDATE

What is Buffered Stream?

Here I'm quoting from Buffered Streams, Java documentation (With a technical explanation):

Buffered Streams

Most of the examples we've seen so far use unbuffered I/O. This means
each read or write request is handled directly by the underlying OS.
This can make a program much less efficient, since each such request
often triggers disk access, network activity, or some other operation
that is relatively expensive.

To reduce this kind of overhead, the Java platform implements buffered
I/O streams. Buffered input streams read data from a memory area known
as a buffer; the native input API is called only when the buffer is
empty. Similarly, buffered output streams write data to a buffer, and
the native output API is called only when the buffer is full.

Sometimes I'm losing my hair reading a technical documentation. So, here I quote the more humane explanation from https://yfain.github.io/Java4Kids/:

In general, disk access is much slower than the processing performed
in memory; that’s why it’s not a good idea to access the disk a
thousand times to read a file of 1,000 bytes. To minimize the number
of times the disk is accessed, Java provides buffers, which serve as
reservoirs of data.

Sample Image

In reading File with FileInputStream then BufferedInputStream, the
class BufferedInputStream works as a middleman between FileInputStream
and the file itself. It reads a big chunk of bytes from a file into
memory (a buffer) in one shot, and the FileInputStream object then
reads single bytes from there, which are fast memory-to-memory
operations. BufferedOutputStream works similarly with the class
FileOutputStream.

The main idea here is to minimize disk access. Buffered streams are
not changing the type of the original streams — they just make reading
more efficient. A program performs stream chaining (or stream piping)
to connect streams, just as pipes are connected in plumbing.

Proper way to make an input stream keep reading until program closes?

If I understand right, you issue is to stop reading from the InputStream. You can use a volatile boolean variable to stop reading:

class PollingRunnable implements Runnable{

private static final String TAG = PollingRunnable.class.getSimpleName();

private InputStream inputStream;
private volatile boolean shouldKeepPolling = true;

public PollingRunnable(InputStream inputStream) {
this.inputStream = inputStream;
}

public void stopPolling() {
shouldKeepPolling = false;
}

@Override
public void run() {
while (shouldKeepPolling) {
final byte[] buffer = new byte[256];
int bytesRead = 0;
try {
bytesRead = inputStream.read(buffer, 0, buffer.length);
String fullResponse = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
//Process response
} catch (IOException e) {
Log.e(TAG, "Exception while polling input stream! ", e);
} finally {
if(inputStream != null) {
try {
inputStream.close();
} catch (IOException e1) {
Log.e(TAG, "Exception while closing input stream! ", e1);
}
}
}
}
}
}

To stop polling just use:

// Supply you input stream here
PollingRunnable pollingRunnable = new PollingRunnable(inputStream);
new Thread(pollingRunnable).start();

//To stop polling
pollingRunnable.stopPolling();

Android - Whats the most efficient way to read an inputstream and save as file?

int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int remaining = length;
int read = 0;
while (remaining > 0
&& (read = in.read(buffer, 0, Math.min(remaining, bufferSize))) >= 0) {
out.write(buffer, 0, read);
remaining -= read;
}

Note that the above makes sure you don't write more that length bytes. But it doesn't make sure you write exactly length bytes. I don't see how you could do this without reading length bytes in memory, or reading length bytes and writing to a temp file, then writing the temp file to the final destination.

Seeking on an InputStream in Android - Java

Here's one approach worth trying that worked for me. The idea is to cast InputStream to FileInputStream by getting AssetFileDescriptor, and then getting an instance of FileChannel that lets you seek back and forth.

long position = 0, bytesRead = 0, currentRead = 0;

Context context = getApplicationContext();
ContentResolver cr = context.getContentResolver();
InputStream is = cr.openInputStream(fileUri);
AssetFileDescriptor assetFileDescriptor = null;
FileInputStream fis = null;
FileChannel fc = null;

try {
assetFileDescriptor = getContentResolver().openAssetFileDescriptor(fileUri[0], "r");
is = cr.openInputStream(fileUri[0]);
Utility.sendFileInfo(is, out, fileName);

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

fis = new FileInputStream(assetFileDescriptor.getFileDescriptor());;
fc = fis.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(Utility.BUFFER_SIZE);

while(position < filesize){
try {
fc.position(position);
currentRead = fc.read(buffer);

//Use the populated buffer to send data out
SendDatagramPacket(buffer, 0, currentRead)
bytesRead += currentRead;
position = bytesRead;
buffer = ByteBuffer.allocate(Utility.BUFFER_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}

How do I read / convert an InputStream into a String in Java?

A nice way to do this is using Apache commons IOUtils to copy the InputStream into a StringWriter... something like

StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, encoding);
String theString = writer.toString();

or even

// NB: does not close inputStream, you'll have to use try-with-resources for that
String theString = IOUtils.toString(inputStream, encoding);

Alternatively, you could use ByteArrayOutputStream if you don't want to mix your Streams and Writers

Java InputStream . Is there is Correct And Efficient way?

Using only standard API, the DataInputStream class has a method called readFully that fills a byte array from the stream:

byte[] data = new byte[lengthtoread];
DataInputStream in = new DataInputStream(mmInStream);
in.readFully(data);

Don't forget to close the streams when you are done with them!



Related Topics



Leave a reply



Submit