Suppose an app gets a content stream by being called with the android.intent.action.SEND filter. Also suppose the Uri and the stream is received like this:
Uri contentUri = (Uri) extras.get(Intent.EXTRA_STREAM);
InputStream in = getContentResolver().openInputStream(contentUri);
Now, how can I determine the total length of the content without reading the whole stream? Is it even possible? in.available() isn't necessarily accurate, so I guess I do have to read the stream until the end, first?
The content might be very large, like about 50MB or more. So, I'd rather only read it once.
You can't determine the amount of data in a stream without reading it; you can, however pass the length via some other extra if you are the source of input.
I would read into a ByteArrayOutputStream and then call toByteArray() to get the resultant byte array. You don't need to define the size in advance (although it's possibly an optimisation if you know it. In many cases you won't)
Related
I have an app similar to an explorer and allow the user to share folders, multiple folders and multiple files. The share intent will hold a list of all shared files in the end.
If the user shares a folder with 1000 files, this is just to much. But how can I determine if it's possible to share the amount of files the user selected?
I want to check if it's possible to share the amount of files the user selected and if not, I want to tell him that he selected to much files and reached an android limit. In the best case I would like to tell him that his share intent is let's say 2.5MB and that the limit is 1MB, so he can estimate by himself how many files he should select...
Question
Is it possible to determine if a share intent is < 1MB and therefore is possible to be shared?
Is it possible to determine the size of a share intent?
Edit
To eliminat confusion:
I don't share files directly in the intent, but only the URI or relative path to the files. So the length of the URI or the length of the relative path is important for the size of the intent, but not if it points to big or small files.
The 1MB limit is valid for the intent (parceled intend I think) and if the intent holds to much data, it will fail SILENTLY and that's the problem
I'm looking for a reusable solution, that works with ANY intent! I am sometimes adding share targets and sometimes not.
For Intent:
You can write that intent object to a Parcel and use dataSize() method to get serialized data size contained in that intent. Make sure to recycle() the parcel afterwards.
final Intent testIntent = getIntent();
final Parcel testParcel = Parcel.obtain();
try {
testParcel.writeParcelable(testIntent,0);
Log.i("TEST", String.format("Intent size: %s", testParcel.dataSize()));
}finally {
testParcel.recycle();
}
For files:
If you can retrieve list of file names from intent, then you can query each file size and sum it up:
sizeBytes += new File(filePath).length();
For a directory though you'll need to walk and add up the size of all files under it. You can use DirectoryWalker class for this.
Why don't call a shell command to get a size of files?
It won't determine intent size exactly but gives you a nice prediction.
try {
Process process = Runtime.getRuntime().exec("ls -l");
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
} catch (InterruptedException e) {
e.printStackTrace();
}
Ensure you don't use awk and other which can be unavailable on some devices.
For directories sizes you will need to use something different than ls.
what you could do is that you keep reference to the file names length that are being selected to avoid looping through them later.
1 character = 1 byte. so, about 1000 characters equal 1 kilobyte.
so base on this you could limit the user selection and be able to actually tell them while they are selecting the files, that they reached the maximum number of files to select(a general message). example code:
private int maxLength = 0;
onSelectFile(File file){
maxLength += file.getName().length()
if(maxLength < 1mb ){//display massage and dont add the new selected file}
}
I believe there's no direct way of checking it, but you can do a manual work on it to get the size of your intent.
Considering that Intent implements Parcelable you should be able to get a Parcel, write to it, and check it's size before sending/launching the intent. Something like that:
Intent intent = ....
... do your intent and fill with the URI data.
Parcel parcel = Parcel.obtain();
intent.writeToParcel(parcel, 0);
int size = parcel.dataSize(); // HERE IS THE NUMBER YOU WANT TO CHECK
parcel.recycle(); // don't forget to return the parcel to the system
parcel = null;
I'm not sure if that size is in bytes, but it should be. If you want the official docs are here https://developer.android.com/reference/android/os/Parcel.html
On a user experience note, you might want to keep checking the size as the user selects stuff, so that once the limit is reached, you can block the user from selecting any extra itens and thou don't waste user time.
It might make more sense to put a sensible worse case number on it. "You have shared too many files (1005/1000 files)" would be a useful message. Telling a user they've shared too many bytes will be confusing when you're referring to the length of file names, not size of the files.
The worst case file name is 127 characters or 255 bytes.
1MB/256 = 4096
So, I would think 4000 would be risky, but 2000-3000 should be fine, depending on the size of the rest of your payload.
If you think that's not enough for your users, then you need a different solution, not a warning.
In my Android project, I have a 2M-bytes raw data file. Since my application is a long-life app, I don't want it to always seize 2M memory. The data file has been formatted, once I need to some data from the data file, I just need to seek to some position and read several bytes.
The Resource class can only return an InputStream on raw file, but InputStream cannot do random read.
Is there a way on Android to random read some bytes from the raw data file? Or I have to read the entire file into memory when I only need a few bytes.
InputStream can skip bytes with skip() can also mark an offset with mark(), on reset() it can go back to marked position. All that can be used to do random IO.
You can store byte offsets in a separate lookup file as well.
Android is built upon Java so take a look at this tutorial:
http://docs.oracle.com/javase/tutorial/essential/io/rafs.html
I want to make a custom sipdroid client by using reverse byte order. I think that makes other Voip clients cannot decode these data.
So I read the code of the SipDroid. I found RTP data goes this way:
1. AudioRecord.read(originalPCM)
2. encode(originalPCM, encodedData)
3. rtp_socket.send(rtp_packet) //the encodeData is rtp_packet's data part
And the other side is:
1. rtp_receive(rtp_packet)
2. decode(encodeData, PCMData) //the encodeData is rtp_packet's data part
3. AudioTrack.write(PCMData)
So I modified the SipdroidSocket class. In send method, I add the following code at the beginning.
byte[] b = pack.getData();
reverse(b);
pack.setData(b);
And add the following code at the end of the receive method.
byte[] b = pack.getData();
reverse(b);
pack.setData(b);
I think in this way, the two client can work as usual. But it failed. And I don't know the reason. Please help me to find out why. Thanks.
You should not reverse the hole buffer unless you receive 2,4,8 bytes at a time.
You should treat the data as elements of 2,4,8 bytes depending on how the data
were stored. The code i see here will not work.
suppose you have a buffer of data bytes 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08
stored as 4 byte elements of 0x04030201-0x08070605.
Reversing the hole buffer will produce 0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01
which is wrong because you will end up with 0x05060708-0x04030201
If you reverse one element(4 bytes) at a time.
Keep in mind that the size of an element depends on how the values were stored
This question is related to my previous question but you dont need to read that in order to understand it.
Now I was trying to convert bitmap into smaller parts and then save those smaller parts.
Issue I get is, Only the first part gets saved in the file whose size is way bigger than the full image. Below is the code I am using:
for (int i = 0; i < Image.getHeight(); i++)
{
fout = new FileOutputStream(file, true);
Bitmap temp = Bitmap.createBitmap(Image, 0, i,Image.getWidth(), 1);
temp.compress(Bitmap.CompressFormat.PNG, 100, fout);
fout.flush();
fout.close();
}
The Code is pretty simple but i dont understand that why the only first row gets written in the file.
UPDATE::
Merlin and Deepak are right. I tried now with giving different names and all the parts were successfully written to different files. Now you know the problem, should i go for the removing of header from second chunk and removing of eof from first chunk or what?
I'm going to resist the urge to ask why on earth you are doing this as it is highly inefficient, so let's have a look.
So you are writing one line of pixels at a time but you are writing them to the same file repeatedly with the append flag set to true which is correct.
What you have missed is the fact that when you write a bitmap you are writing is self contained. So a program reading th first line will expect that to be the entire bitmap.
This is the equivalent of having an EOF marker in a text file. All the lines are being written but when reading it the reader gives up after the first EOF
You would need to research the the structure of a PNG file to understand more fully what is happening
Since you are appending compressed files (.png) one after the other, opening the resultant file will just show the first bit encoded data, which is your first row. This is logical too since the encoded image header has number of bytes comprising the encoded content and the decoders will not bother about the rest of the data in the file after end marker.
I just tried copying an .png file at the end of another, when i open the file i seen the unchanged first image!
Your logic is wrong because you cannot append each row as png to a file. Probably it has some header stuff, so they would be appended after each append.
I recently created a program that gets medi-large amounts of xml data and converts it into arrays of Strings, then displays the data.
The program works great, but it freezes when it is making the arrays (for around 16 seconds depending on the size).
Is there any way I can optimize my program (Alternatives to string arrays etc.)
3 optimizations that should help:
Threading
If the program freezes it most likely means that you're not using a separate thread to process the large XML file. This means that your app has to wait until this task finishes to respond again.
Instead, create a new thread to process the XML and notify the main thread via a Handler when it's done, or use AsyncTask. This is explained in more detail here.
Data storage
Additionally, a local SQLite database might be more appropriate to store large amounts of data, specially if you don't have to show it all at once. This can be achieved with cursors that are provided by the platform.
Configuration changes
Finally, make sure that your data doesn't have to be reconstructed when a configuration change occurs (such as an orientation change). A persistent SQLite database can help with that, and also these methods.
You can use SAX to process the stream of XML, rather than trying to parse the whole file and generating a DOM in memory.
If you find that you really are using too much memory, and you have a reason to keep the string in memory rather than caching them on disk, there are certainly ways you can reduce the memory requirements. It's a sad fact that Java strings use a lot of space. They require two objects (the string itself and an underlying char array) and use two bytes per char. If your data is mostly 7-bit ASCII, you may be better of leaving it as a UTF-8 encoded byte stream, using 1 byte per character in the typical case.
A very effective scheme is to maintain an array of 32k byte buffers, and append the UTF-8 representation of each new string onto the first empty space in one of those arrays. Your reference to the string becomes a simple integer: PTR = (buffer index * 32k) + (buffer offset). "PTR/32k" yields the index of the desired byte buffer, and "PTR % 32k" yields the location within the buffer. Use either an initial length byte or a null terminator to keep track of how long the string is. When you need to access one of the strings, don't allocate a new String object: unpack it into a mutable StringBuilder or work directly with the UTF-8 byte representation.
The above approach is obviously a lot more work, but can save you between a factor of 2 and 6 in memory usage (depending on the length of your strings). However, you should beware of premature optimization. If your problem is with the processing time to parse your input, or is somewhere else in your program, you could find that you've done a lot of work to fix something that isn't your bottleneck and thus get no improvement at all.