I am running some performance tests and see some spikes in my tests results.
This is the code I use to measure the time of the writing to file.
public class Test extends AndroidTestCase {
private DecimalFormat decimalFormat = new DecimalFormat("#.##");
private final Boolean isInternal = true;
public void testWrite() {
Thread thread = new Thread() {
public void run() {
File dummy_file = createFile("dummy2", true);
File file = createFile("normal_write", isInternal);
for (int i = 0; i < 12500; i++) {
long start = System.nanoTime();
write(dummy_file, String.valueOf(i));
long stop = System.nanoTime();
double mSec = ((double) (stop - start) / 1000000.0);
write(file, decimalFormat.format(mSec));
}
}
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public File createFile(String fileName, Boolean isInternal) {
deleteFile(fileName, isInternal);
try {
String file_path = "";
if(isInternal == true) {
file_path = getContext().getFilesDir().getAbsolutePath() + "/" + fileName + ".dat";
} else {
file_path = Environment.getExternalStorageDirectory() + "/" + fileName + ".dat";
}
File file = new File(file_path);
file.createNewFile();
FileWriter fw = new FileWriter(file.getAbsoluteFile(), true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("##;##\n" +
"#LiveGraph demo file.\n" +
"Time");
bw.newLine();
bw.close();
return file;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void deleteFile(String fileName, Boolean isInternal) {
String file_path = "";
if ( isInternal == true) {
file_path = getContext().getFilesDir().getAbsolutePath() + "/" + fileName + ".dat";
} else {
file_path = Environment.getExternalStorageDirectory() + "/" + fileName + ".dat";
}
File file = new File(file_path);
file.delete();
}
public void write(File file, String content) {
try {
FileWriter fw = new FileWriter(file.getAbsoluteFile(), true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.newLine();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Mostly the time it takes to write is ~0.1-0.2ms but there are spikes which takes 10+ ms and can not figure out what the cause of this is. I can not link the graph here right now as I am a new user here.
I am really stuck with this any ideas?.
This is part of the time measurements.
0.16
0.16
0.17
0.15
0.15
2.5
0.17
0.16
0.16
0.16
0.15
0.17
0.17
0.17
0.19
0.19
0.17
0.2
0
0
4.79
0.24
0.23
0.28
0.23
0.28
0.03
0
11.23
0.16
0.15
0.18
0.16
0.16
0.17
0.16
5.84
It may be related to Android's storage access specifics:
Background on disks on phones Wait, what’s wrong with hitting the
disk? Android devices are all running flash memory, right? That’s like
a super-fast SSD with no moving parts? I shouldn’t have to care?
Unfortunately, you do.
You can’t depend on the flash components or filesystems used in most
Android devices to be consistently fast. The YAFFS filesystem used on
many Android devices, for instance, has a global lock around all its
operations. Only one disk operation can be in-flight across the entire
device. Even a simple “stat” operation can take quite a while if you
are unlucky. Other devices with more traditional block device-based
filesystems still occasionally suffer when the block rotation layer
decides to garbage collect and do some slow internal flash erase
operations. (For some good geeky background reading, see
lwn.net/Articles/353411)
The take-away is that the “disk” (or filesystem) on mobile devices is
usually fast, but the 90th percentile latencies are often quite poor.
Also, most filesystems slow down quite a bit as they get more full.
(See slides from Google I/O Zippy Android apps talk, linked off
code.google.com/p/zippy-android)
http://android-developers.blogspot.com/2010/12/new-gingerbread-api-strictmode.html
Related
I am developing an application for remote field use that will collect and store video data in MJPEG format. I am currently developing for the IP67 rated Samsung i9295 S4 phone. The location where this will be deployed has no cellular network coverage. The application runs as expected except that every 3-6 days, the application crashes. I "suspect" that the problem may be in the below code block, however I am not getting the expected appendLog() error messages from CATCH.
Two questions: Is there anything obviously missing from the code below that I should implement that would keep resources freed up and prevent memory leaks? Is there a better way to catch this issue that will prevent the application crashes that I am currently having?
Thanks in advance...
#Override
public void onPreviewFrame(byte[] b, Camera c) {
if (bRecordingFlag ) {
if (parameters.getPreviewFormat() == ImageFormat.NV21) {
Log.v(LOGTAG,"Started Writing Frame");
try {
byte[] jpegByteArray;
makeNewTimestamps();
YuvImage im = new YuvImage(b, ImageFormat.NV21, parameters.getPreviewSize().width, parameters.getPreviewSize().height, null);
Rect r = new Rect(0,0,parameters.getPreviewSize().width,parameters.getPreviewSize().height);
ByteArrayOutputStream jpegByteArrayOutputStream = new ByteArrayOutputStream();
im.compressToJpeg(r, iJpegQuality, jpegByteArrayOutputStream);
jpegByteArray = jpegByteArrayOutputStream.toByteArray();
byte[] boundaryBytes = (szBoundaryStart + jpegByteArray.length + " bytes" + szAbsoluteTime + szDateTime + szJpegQuality + iJpegQuality + "%" + szFramesPerSecond + iFps +szCamcorderFrame + camcorderProfile.videoFrameHeight +" x "+ camcorderProfile.videoFrameWidth + szCamcorderBitrate + camcorderProfile.videoBitRate + szLightLevel + fLightLevel + szBoundaryEnd).getBytes();
bos.write(boundaryBytes);
jpegByteArray = jpegByteArrayOutputStream.toByteArray();
bos.write(jpegByteArray);
bos.flush();
fos.flush();
} catch (IOException e) {
e.printStackTrace();
String szStackTrace = Log.getStackTraceString(e);
appendLog(" - Exception at onPreviewFrame()." + szStackTrace);
}
Log.v(LOGTAG,"Finished Writing Frame");
} else {
Log.v(LOGTAG,"NOT THE RIGHT FORMAT");
}
}
}
I am using JSch to perform an SFTP download in Android. This is on a LAN with 802.11n and an SFTP server on a wired gigabit connection. I am getting about 8 mbytes/sec on a laptop (also 802.11n) with the same code but I am only getting 40kbytes/sec on Android. Are there some flags or something I need to turn on to get this to transfer faster? I have tried it on a Nexus 5 and a Nexus 6, both with 5.1. I tried a couple of apps and one downloaded the file at 230kbytes/sec and the other right around 40kbytes/sec so I'm guessing one of them has the same issue I do.
Here is my code:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread thread = new Thread() {
#Override
public void run() {
try {
JSch jsch = new JSch();
jsch.setConfig("StrictHostKeyChecking", "no");
Session session = jsch.getSession("ftptest", "192.168.1.205");
session.setPort(22);
session.setPassword("password");
session.connect();
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
SftpProgressMonitor monitor = new SftpProgressMonitor() {
long finalCount = 0;
long start = -1;
#Override
public void init(int op, String src, String dest, long max) {
start = System.currentTimeMillis();
}
#Override
public boolean count(long count) {
finalCount += count;
/* long took = (System.currentTimeMillis() - start) / 1000;
if (took > 0) {
Log.w("SFTP", "Transferred so far " + finalCount + " at speed bytes/sec " + (finalCount / took));
}*/
return true;
}
#Override
public void end() {
long took = (System.currentTimeMillis() - start) / 1000;
Log.w("SFTP", "Transferred " + finalCount + " in " + took + " speed bytes/sec " + (finalCount / took ));
}
};
InputStream stream = channel.get("file", monitor);
int read = -1;
byte[] bs = new byte[8192];
while((read = stream.read(bs)) >= 0){
//do nothing
}
} catch (JSchException e) {
e.printStackTrace();
} catch (SftpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
thread.start();
}
}
EDIT: It seems to be much faster (about 160kbytes/sec) if I tell it to write the stream into a file without giving me an InputStream. The code looks pretty different between the two get methods but even if I went that route, 160kbytes/sec is still much slower than I had hoped.
What version of Android are you testing with? Can you adb shell in and see what these settings are (when you're on wifi)?
cat /proc/sys/net/ipv4/tcp_rmem
cat /proc/sys/net/ipv4/tcp_wmem
And then if they are low (something like "4092 8760 11680"), then try setting them to larger values:
sudo echo "524288,1048576,2097152" > /proc/sys/net/ipv4/tcp_rmem
sudo echo "262144,524288,1048576" > /proc/sys/net/ipv4/tcp_wmem
Then try your test again.
This could be from a bug where the wifi buffers were being set too small in some cases (https://code.google.com/p/android/issues/detail?id=64706).
Have you tried wrapping your stream in a buffering stream?
BufferedInputStream bis = new BufferedInputStream(channel.get("file", monitor));
8192 bytes seems like a small buffer...
Also make sure you close your connections/streams, maybe you have a lot of outgoing connections you don't know about that's hogging the hardware?
I am currently writing an android app that logs the accelerometer. (its a test app at the moment so i can prototype an algorithm.
To write out a list of SensorEventStore's (which is just a way of storing the data from a SensorEvent) to the SD card from a 30 minute recording, locks up the GUI for about 20 - 30 seconds while writing the file.
I am using the following code to write out the file to the SD card.
#Override
public void onMessage(Messages message, Object param[]) {
if(message == IDigest.Messages.SaveData) {
File folder = (File) param[0];
File accFileAll = new File(folder, startTime + "_all.acc");
FileWriter accFileWriterAll;
try {
accFileWriterAll = new FileWriter(accFileAll);
} catch (IOException e) {
accFileWriterAll = null;
}
for(Iterator<SensorEventStore> i=eventList.iterator(); i.hasNext();) {
SensorEventStore e = i.next();
if(accFileWriterAll != null) {
try {
accFileWriterAll.write(
String.format(
"%d,%d,%f,%f,%f\r\n",
e.timestamp,
e.accuracy,
e.values[0],
e.values[1],
e.values[2]
)
);
accFileWriterAll.flush();
} catch (IOException ex) {
}
}
}
new SingleMediaScanner(RunBuddyApplication.Context, accFileAll);
}
}
Can anyone give me any pointers to make this not lock up the UI, or not have to take the amount of time it currently takes to write out the file.
Firstly you should try to do this in the background. The AsyncTask is fairly well suited for the task.
Other than that, you should remove the flush() statement, and probperly close() your file writer. The flush causes the data to be written to disk in rather small portions, which is really slow. If you leave the filewriter to its own flushing, it will determine a buffer size on its own. When you properly close the FileWriter, the remaining data should be written to disk as well.
Also, you could take a look at "Try with resources" for your filewriter, but that is optional.
According to changes for kitakt 4.4 there were some problems with playing shoutcast streams (those returning "ICY" instead of "HTTP/1.x" response).
So solution for kitkat was to reregister "icy" protocol prefix in JVM once before we opened a stream by this:
try {
java.net.URL.setURLStreamHandlerFactory( new java.net.URLStreamHandlerFactory(){
public java.net.URLStreamHandler createURLStreamHandler( String protocol ) {
Log.d( LOG, "Asking for stream handler for protocol: '" + protocol + "'" );
if ("icy".equals( protocol )) return new com.spoledge.aacdecoder.IcyURLStreamHandler();
return null;
}
});
}
catch (Throwable t) {
Log.w( LOG, "Cannot set the ICY URLStreamHandler - maybe already set ? - " + t );
}
I have problem with open audio stream to make it register. After I call url.opnestream(stream) I got exception:
java.net.ProtocolException: Unexpected status line: ICY 200 OK
How could I fix it?
Here is sample of registering audio, so far what I did..
try {
URL url = null;
url = new URL(u);
inputStream = url.openStream();
startTime = System.currentTimeMillis();
Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
String fileName = File.separator + "radio_" + "recording_" + channelMetadata.replaceAll("\\W", "") + System.currentTimeMillis();
if(isSDPresent)
{
outputSource = Environment.getExternalStorageDirectory() + fileName;
}
else
{
outputSource = Environment.getDataDirectory() + fileName;
}
if(contentType.equals("audio/aacp"))
fileOutputStream = new FileOutputStream(outputSource + ".acc");
else if(contentType.equals("audio/mpeg"))
fileOutputStream = new FileOutputStream(outputSource + ".mp3");
else
fileOutputStream = new FileOutputStream(outputSource + ".nieznany_format");
int bytesRead = 0;
int bytes;
while (((bytes = inputStream.read()) != -1) && isRecording) {
fileOutputStream.write(bytes);
bytesRead++;
stopTime = System.currentTimeMillis();
long seconds = (Math.abs(startTime-stopTime));
int minutes = 1000 * 60 * 60;
if(minutes<=seconds)
{
Log.d("xxx", "recording task exceed stopped");
break;
}
}
inputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
isRecording = false;
}
isRecording = false;
return null;
Various changes were made for Android 4.4, and it seems that the non-standard ShoutCast ICY header is not supported by Android.
It seems though that the good people of OkHttp fixed the issue (issue 1 and issue 2) already a few days ago.
What you can do, is simply use OkHttp lib directly in your application and by that you'll use the newer OkHttp version (the one with the fix) and not the one shipped with Android (where you'll have to wait for an OS update).
This will also fix it for your application running on other devices that might suffer from that issue.
The answer of Assaf Gamliel worked for me. For those not familiar with this, you have to download the last .jar from okHttp and the .jar from the dependency okio . Add them to your libs directory and connect like this :
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(mediaUrl)
.build();
Response response = client.newCall(request).execute();
InputStream stream = response.body().byteStream();
The easiest fix ever.
which is 100% working for me,
for shoutcast , change your URL ex. "http://192.168.1.1:9292" to "icy://192.168.1.1:9292"
and you'll be fine.
I'm downloading sets of images in separated threads and saving them to the SD card. The problem is that when I run two or more downloading threads the saved images are corrupted. If just one thread is running the images are ok. I'm downloading it from the same domain but different url, e.g. www.test.com/set1/img1.jpg, www.test.com/set2/img1.jpg etc.
I'm saving them to different folders by the name of the set. I noticed that mostly larger images are corrupted (over 500 KB), smaller are usually ok, but not always.
Do you have any clue why the files get corrupted when multiple threads are running?
Here's a part of the code I'm using:
protected class DownloadTask extends DownloadRunnable {
#Override
public void run() {
InputStream is = null;
OutputStream os = null;
File bitmapFile = null;
/** some more declarations and preparations are here */
for (int pg=getDownloadedPages(); pg < numPages; ++pg) {
for (char ch='a'; ch <= 'e'; ++ch) {
/* check for pause */
synchronized (pauseLock) {
while (paused && !aborted) {
try {
pauseLock.wait();
} catch (InterruptedException e) {
}
}
}
fileName = "page-" + df.format(pg) + "-" + ch;
url = MainApp.getRestrictedUrl(MainApp.tstcode, urlFile + fileName+ ".jpg");
is = new BufferedInputStream(new URL(url).openStream());
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
bitmapFile = new File(pathToSave, fileName + MagazinePage.FILE_EXT);
MainApp.encryptToFile(bitmapFile, is);
dwnlSize += bitmapFile.length();
}
is.close();
}
}
}
public static void encryptToFile(File file, InputStream is) throws IOException {
BufferedOutputStream os = null;
try {
if (file.exists()) {
file.delete();
} else {
file.getParentFile().mkdirs();
}
file.createNewFile();
os = new BufferedOutputStream(new FileOutputStream(file));
IkioskContentProvider.getInstance().encrypt(is, os);
} finally {
os.close();
}
}
}
DownloadRunnable is custom abstract class implementing Runnable. And I'm using it in thread the regular way:
protected void downloadIssuePages() {
dwnlTask = new DownloadTask();
new Thread(dwnlTask).start();
}
I'm calling downloadIssuePages() on two different objects to download two sets for example.
Using SDK version 11 (Android 3.0), device Acer Iconia Tab A500 with Android 3.1
I've tried to disable writing for the second thread, only the first thread was saving files to find out if there's a problem in reading from the stream or writing. Apparently writing was the problem as data was correct in this case.
So I decided to use lock around writing to the file and looks like it's working fine:
synchronized (MainApp.fileWritingLockObj) {
while (MainApp.fileWritingLocked) {
try {
MainApp.fileWritingLockObj.wait();
} catch (InterruptedException e) {
}
}
MainApp.fileWritingLocked = true;
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
bitmapFile = new File(pathToSave, fileName + MagazinePage.FILE_EXT);
MainApp.encryptToFile(bitmapFile, is);
dwnlSize += bitmapFile.length();
}
is.close();
MainApp.fileWritingLocked = false;
MainApp.fileWritingLockObj.notifyAll();
}