How to prevent UI lag when updating Notification while downloading file? - java

I am currently using AsyncTask to download a large file in the background within my app, currently the download progress is shown as a ProgressDialog which is updated via onProgressUpdate as below:
protected String doInBackground(String... sUrl) {
try {
String destName = sUrl[1];
file_Delete(destName); // Just to make sure!
URL url = new URL(sUrl[0]);
URLConnection connection = url.openConnection();
connection.connect();
int fileLength = connection.getContentLength();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(destName);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
Log.e(TAG, NAME + ": Error downloading file! " + e.getMessage());
return e.getMessage();
}
return null;
}
#Override protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
DownloadImage.mProgressDialog.setProgress(progress[0]);
}
This works fine, however I now want to use a notification in the notification bar so keep track of the download instead (as the file can be rather large and users would like to keep track from outside the app).
I have tried the below code however the UI starts to lag badly, I can see its due to the publishProgress getting called alot, so how could I go about changing the background code to call publishProgress only every second
#Override protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
DownloadImage.mProgressDialog.setProgress(progress[0]);
DownloadImage.myNotification = new NotificationCompat.Builder(c)
.setContentTitle("Downloading SlapOS")
.setContentText("Download is " + progress[0] + "% done")
.setTicker("Downloading...")
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setProgress(100, progress[0], false)
.setSmallIcon(R.drawable.icon)
.build();
DownloadImage.notificationManager.notify(1, DownloadImage.myNotification);
}

so how could I go about changing the background code to call publishProgress only every second
I have done this before for an upload function that showed the % in a Notification, but same exact idea. Have your AsyncTask keep track of what percentDone the download is, and ONLY call publishProgress when percentDone changes. That way, you will only ever call publishProgress when the % downloaded changes, and the Notification therefore needs to update. This should resolve the UI lag.
I was writing this up as my suggested implementation, sounds like the OP already got it working. But maybe this will help someone else in the future:
byte data[] = new byte[1024];
long total = 0;
int count, latestPercentDone;
int percentDone = -1;
while ((count = input.read(data)) != -1) {
total += count;
latestPercentDone = (int) Math.round(total / fileLength * 100.0);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
publishProgress(percentDone);
}
output.write(data, 0, count);
}

I really liked your approach to it! I found that changing the code to the following made my progressbar update correctly. I had some issues while using math.round().
latestPercentDone = (int) ((dataBytesWritten / (float) totalSize) * 100);
if (percentDone != latestPercentDone) {
percentDone = latestPercentDone;
publishProgress(percentDone);
}

Related

Is it possible to somehow use .setMicrosecondPosition() with line or is it only possible to do with clip

I am currently making mp3 player in NetBeans 12.1 and I can't find a way to control current position of a song.
I have tried using .setMicrosecondPosition(), but it seems it only works with the clip not with the line.
Is it even possible for my player to change current position of the track or should I change my code?
This is the code of the player.
public void run() {
final File file = new File(filePath);
try (final AudioInputStream in = AudioSystem.getAudioInputStream(file)) {
final AudioFormat outFormat = getOutFormat(in.getFormat());
final Info info = new Info(SourceDataLine.class, outFormat);
try (final SourceDataLine line
= (SourceDataLine) AudioSystem.getLine(info)) {
getLine(line);
line.getMicrosecondPosition();
if (line != null) {
line.open(outFormat);
line.start();
long millis;
AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(file);
Map<?, ?> properties = ((TAudioFileFormat) fileFormat).properties();
String key = "duration";
String title = "title";
Long microseconds = (Long) properties.get(key);
maksimumSekunde = (int)TimeUnit.MICROSECONDS.toSeconds(microseconds);
title1 = (String) properties.get(title);
int mili = (int) (microseconds / 1000);
sec = (mili / 1000) % 60;
min = (mili / 1000) / 60;
setVolumeDown(sliderGlasnoca.getValue());
//STREAM
int n = 0;
final byte[] buffer = new byte[4096];
AudioInputStream inp = getAudioInputStream(outFormat, in);
while (n != -1) {
if (pauza == true) {
break;
}
if (stop == true) {
synchronized (LOCK) {
LOCK.wait();
}
}
n = inp.read(buffer, 0, buffer.length);
if (n != -1) {
line.write(buffer, 0, n);
}
millis = TimeUnit.MICROSECONDS.toMillis(line.getMicrosecondPosition());
trajanjeSekunde = (int)TimeUnit.MICROSECONDS.toSeconds(line.getMicrosecondPosition());
minutes = (millis / 1000) / 60;
seconds = ((millis / 1000) % 60);
//System.out.println(minutes + ":" + seconds + " " + "time = " + min + ":" + sec + " " + title1);
}
//STREAM
line.drain();
line.stop();
Finished();
}
} catch (InterruptedException ex) {
}
} catch (UnsupportedAudioFileException
| LineUnavailableException
| IOException e) {
throw new IllegalStateException(e);
}
}
Its my first time posting here.
I always just counted and discarded frames from bytes being read via the AudioInputStream, but looking anew at the API, I see that one can use the AudioInputStream.skip(...) method to jump forward a given number of bytes. Calculating the number of bytes corresponding to a given amount of time involves knowing the number of bytes per frame, e.g., 16-bit encoding, stereo is 4 bytes per frame, and the sample rate.
IDK if one can reliably skip backwards. This will depend on whether one can "mark" and "reset" the file being read by the AudioInputStream. If these capabilities are supported, it seems conceivable that one could mark(...) the start of the AudioInputStream. Then, to go backwards, first reset() back to the beginning and then jump forward via skip(...). I haven't tested this. A lot would depend on the number of bytes permitted in the mark(...) method.
If starting or stopping in the middle of playing audio, the data that is fed to the SourceDataLine would potentially exhibit "clicks" due to the discontinuity in the signal. To deal with that it might be necessary to convert the starts and stops to PCM and ramp the volume up if starting, or down if stopping. The number of frames required would probably need to be determined by experimenting. I'm guessing 64 frames for 44100fps might be a good first try.

Calculated download speed is wrong when using TrafficStats class

I want to build android download speed test. To do that I am using TrafficStats class. Problem is that I am getting wrong results. Results are almost the same when I run the test but I put heavy load on my Internet connection before I run test. I download file for 30 seconds and after that (or when file is downloaded) and then calculate bytes using TrafficStats
Does someone knows where is the problem?
This is code that I am using:
#Override
protected String doInBackground(String... urls) {
String downloaded ="";
// String uploaded = "";
try{
long BeforeTime = System.currentTimeMillis();
long TotalTxBeforeTest = TrafficStats.getTotalTxBytes();
long TotalRxBeforeTest = TrafficStats.getTotalRxBytes();
URL url = new URL(urls[0]);
URLConnection connection = new URL(urls[0]).openConnection();
connection.setUseCaches(false);
connection.connect();
InputStream input = connection.getInputStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(input);
byte[] buffer = new byte[1024];
int n = 0;
long endLoop = BeforeTime+30000;
while(System.currentTimeMillis() < endLoop) {
/* if (bufferedInputStream.read(buffer) != -1){
break;
}*/
}
long TotalTxAfterTest = TrafficStats.getTotalTxBytes();
long TotalRxAfterTest = TrafficStats.getTotalRxBytes();
long AfterTime = System.currentTimeMillis();
double TimeDifference = AfterTime - BeforeTime;
double rxDiff = TotalRxAfterTest - TotalRxBeforeTest;
double txDiff = TotalTxAfterTest - TotalTxBeforeTest;
Log.e(TAG, "Download skinuto. "+ rxDiff);
if((rxDiff != 0) && (txDiff != 0)) {
double rxBPS = (rxDiff / (TimeDifference/1000)); // total rx bytes per second.
double txBPS = (txDiff / (TimeDifference/1000)); // total tx bytes per second.
downloaded = String.valueOf(rxBPS) + "B/s. Total rx = " + rxDiff;
// uploaded = String.valueOf(txBPS) + "B/s. Total tx = " + txDiff;
}
else {
downloaded = "No downloaded bytes.";
}
}
catch(Exception e){
Log.e(TAG, "Error while downloading. "+ e.getMessage());
}
return downloaded;
}
I tried your code - it seems to work fine for me BUT i changed
while(System.currentTimeMillis() < endLoop) {
/* if (bufferedInputStream.read(buffer) != -1) {
break;
}*/
}
to
while(System.currentTimeMillis() < endLoop) {
if (bufferedInputStream.read(buffer) == -1){
break;
}
}
since read returns -1 if the end of the stream is reached.

Read all InputStream values at once into a byte[] array

Is there a way to read all InputStream values at once without a need of using some Apache IO lib?
I am reading IR signal and saving it from the InputStream into the byte[] array. While debugging, I have noticed that it works only if I put a delay there, so that I read all bytes at once and then process it.
Is there a smarter way to do it?
CODE:
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[100];
int numberOfBytes;
removeSharedPrefs("mSharedPrefs");
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
numberOfBytes = mmInStream.read(buffer);
Thread.sleep(700); //If I stop it here for a while, all works fine, because array is fully populated
if (numberOfBytes > 90){
// GET AXIS VALUES FROM THE SHARED PREFS
String[] refValues = loadArray("gestureBuffer", context);
if (refValues!=null && refValues.length>90) {
int incorrectPoints;
if ((incorrectPoints = checkIfGesureIsSameAsPrevious(buffer, refValues, numberOfBytes)) < 5) {
//Correct
} else {
//Incorrect
}
}
saveArray(buffer, numberOfBytes);
}else{
System.out.println("Transmission of the data was corrupted.");
}
buffer = new byte[100];
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, numberOfBytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Edit:
My old answer is wrong, see EJPs comment! Please don't use it. The behaviour of ByteChannels depend on wether InputStreams are blocking or not.
So this is why I would suggest, you just copy IOUtils.read from Apache Commons:
public static int read(final InputStream input, final byte[] buffer) throws IOException {
int remaining = buffer.length;
while (remaining > 0) {
final int location = buffer.length - remaining;
final int count = input.read(buffer, location, remaining);
if (count == -1) { // EOF
break;
}
remaining -= count;
}
return buffer.length - remaining;
}
Old answer:
You can use ByteChannels and read into a ByteBuffer:
ReadableByteChannel c = Channels.newChannel(inputstream);
ByteBuffer buf = ByteBuffer.allocate(numBytesExpected);
int numBytesActuallyRead = c.read(buf);
This read method is attempting to read as many bytes as there is remaining space in the buffer. If the stream ends before the buffer is fully filled, the number of bytes actually read is returned. See JavaDoc.

Android - how to calculate the time needed to download a file?

In my Android application, I am using WIFI link speed to get the speed of the WIFI
and get the length content of a file before downloading the file
and then I am trying to get the esitmated time of download before downloading the file
but the time I get is incorrect I don't know why !
that is my code to estimate the time before downloading
URL u = new URL(url);
HttpURLConnection c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.connect();
contentLength = Long.parseLong(c.getHeaderField("Content-Length"));
System.out.println("content"+contentLength);
float contentLength_float=contentLength/(float)(1000*1000);//migabyte
float speed=((float)(mActivity.speed_wifi()))/(float)8;//convert mbps(migabit) to migabyte ps
float sec=contentLength_float/speed;//get the sec from m/m/s
and function wifi speed ()
public int speed_wifi()
{
WifiManager mainWifi;
mainWifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = mainWifi.getConnectionInfo();
int speed=0;
if(wifiInfo.getBSSID()!=null)
{
speed=wifiInfo.getLinkSpeed();
}
return speed;
}
The wifi link speed you get by using that function is the maximum speed that can be achieved by the wifi in the phone, it is not the actual speed.
There is no way of determining the wifi speed before the download starts.
What you can do is that, start showing the estimated time as the download is started based on the current download speed. For this -
find out how much data is downloaded in a small chunk of time like 2 sec which will be current_speed = data_downloaded/time (time can be 2 sec or anything you want)
Now the estimated time will be file_size/current_speed.
So in this way you can start showing the estimated time just 1 or 2 seconds after the download is started.
use AysnTask
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/file_name.extension");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}

Progress bar freezing while downloading file in java [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JProgressBar wont update
So I am trying to show the download progress of a file being downloaded in Java. I can output the current percentage as a String to the console, but when I try to update the UI, it freezes until the download is complete.
public void downloadFile(String fileName) {
try {
long startTime = System.currentTimeMillis();
System.out.println("Connecting to site...\n");
URL url = new URL("http://assets.minecraft.net/"+fileName+"/minecraft.jar");
URLConnection connection = url.openConnection();
try (InputStream reader = url.openStream(); FileOutputStream writer = new FileOutputStream("minecraft.jar")) {
byte[] buffer = new byte[153600];
int totalBytesRead = 0;
int bytesRead = 0;
int totalSize = connection.getContentLength();
jProgressBar1.setMaximum(totalSize);
System.out.println("Downloading\n");
while ((bytesRead = reader.read(buffer)) > 0) {
writer.write(buffer, 0, bytesRead);
buffer = new byte[153600];
totalBytesRead += bytesRead;
jProgressBar1.setValue(totalBytesRead);
System.out.println(totalBytesRead*100/totalSize+"% complete");
}
long endTime = System.currentTimeMillis();
System.out.println("Done. " + (new Integer(totalBytesRead).toString()) + " bytes read (" + (new Long(endTime - startTime).toString()) + " millseconds).\n");
}
}
catch (MalformedURLException e) {
}
catch (IOException e) {
}
}
I read somewhere that you need to use another thread but I was unable to successfully do that.
One option would be using SwingWorker. Here are some useful answers you may consider:
Need to have JProgress bar to measure progress when copying directories and files
How SwingWorker works

Categories

Resources