I currently have an asynctask
which downloads a mp3 from a server. When the user starts to download it, a status bar notification is created. This displays the progress of the download in real time. My only concern is that the phone slows down almost too much. Is there any way to delay the progress displayed or a way to make my code faster? Thanks.
Code below:
public class DownloadFile extends AsyncTask<String, String, String> {
CharSequence contentText;
Context context;
CharSequence contentTitle;
PendingIntent contentIntent;
int HELLO_ID = 1;
long time;
int icon;
CharSequence tickerText;
File file;
public void downloadNotification() {
String ns = Context.NOTIFICATION_SERVICE;
notificationManager = (NotificationManager) getSystemService(ns);
icon = R.drawable.sdricontest;
//the text that appears first on the status bar
tickerText = "Downloading...";
time = System.currentTimeMillis();
notification = new Notification(icon, tickerText, time);
context = getApplicationContext();
//the bold font
contentTitle = "Your download is in progress";
//the text that needs to change
contentText = "0% complete";
Intent notificationIntent = new Intent(Intent.ACTION_VIEW);
notificationIntent.setType("audio/*");
contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notificationManager.notify(HELLO_ID, notification);
}
@Override
protected void onPreExecute() {
//execute the status bar notification
downloadNotification();
super.onPreExecute();
}
@Override
protected String doInBackground(String... url) {
int count;
try {
URL url2 = new URL(sdrUrl);
HttpURLConnection connection = (HttpURLConnection) url2.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.connect();
int lengthOfFile = connection.getContentLength();
//make the stop drop rave folder
File sdrFolder = new File(Environment.getExternalStorageDirectory() + "/StopDropRave");
boolean success = false;
if (!sdrFolder.exists()) {
success = sdrFolder.mkdir();
}
if (!success) {
String PATH = Environment.getExternalStorageDirectory()
+ "/StopDropRave/";
file = new File(PATH);
file.mkdirs();
} else {
String PATH = Environment.getExternalStorageDirectory()
+ "/StopDropRave/";
file = new File(PATH);
file.mkdirs();
}
String[] path = url2.getPath().split("/");
String mp3 = path[path.length - 1];
String mp31 = mp3.replace("%20", " ");
String sdrMp3 = mp31.replace("%28", "(");
String sdrMp31 = sdrMp3.replace("%29", ")");
String sdrMp32 = sdrMp31.replace("%27", "'");
File outputFile = new File(file, sdrMp32);
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream input = connection.getInputStream();
byte[] data = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress("" + (int) (total * 100 / lengthOfFile));
fos.write(data, 0, count);
}
fos.close();
input.close();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public void onProgressUpdate(String... progress) {
contentText = Integer.parseInt(progress[0]) + "% complete";
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notificationManager.notify(HELLO_ID, notification);
super.onProgressUpdate(progress);
}
}
I saw similar results, you need to not push the update the notification so often, i changed mine to update only update a few times a second. (e.g. in onProgressUpdate keep track of the last time you called notify, and only call notify if you're past 100ms of the previous call, or if you're at the max value.
I had similar issue once, I solved it using CountDownTimer.
Similar to how @superfell suggested, you can call progress update of AsyncTask regularly while downloading file. And call the Notification Manager only at specific interval.
After calling start()
of CountDownTimer, it will call onTick()
function after every fixed interval of time, and will call onFinish()
either when timer is timed out or when called explicitly. cancel()
function will only cancel the timer and will not call onFinish()
method.
class DownloadMaterial extends AsyncTask<String, String, String> {
CountDownTimer cdt;
int id = i;
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
@Override
protected void onPreExecute() {
/**
* Create custom Count Down Timer
*/
cdt = new CountDownTimer(100 * 60 * 1000, 500) {
public void onTick(long millisUntilFinished) {
mNotifyManager.notify(id, mBuilder.build());
}
public void onFinish() {
mNotifyManager.notify(id, mBuilder.build());
}
};
}
@Override
protected String doInBackground(String... strings) {
/**
* Start timer to update Notification
* Set Progress to 20 after connection
* Build Notification
* Increment Progress
* Download and Save file
*/
try {
mNotifyManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(context);
mBuilder.setContentTitle("Downloading File")
.setContentText(file_name)
.setProgress(0, 100, false)
.setOngoing(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(Notification.PRIORITY_LOW);
// Initialize Objects here
publishProgress("5");
mNotifyManager.notify(id, mBuilder.build());
cdt.start();
// Create connection here
publishProgress("20");
// Download file here
while ((count = input.read(data)) != -1) {
total += count;
publishProgress("" + (int) (20 + (total * 80 / fileLength)));
output.write(data, 0, count);
}
} catch (Exception e) {
return "Failed";
}
return "Success";
}
@Override
protected void onProgressUpdate(String... values) {
/**
* Update Download Progress
*/
mBuilder.setContentInfo(values[0] + "%")
.setProgress(100, Integer.parseInt(values[0]), false);
}
@Override
protected void onPostExecute(String s) {
String title;
if (s.equals("Success")) {
title = "Downloaded";
} else {
title = "Error Occurred";
}
mBuilder.setContentTitle(title)
.setContentInfo("")
.setOngoing(false)
.setProgress(0, 0, false);
cdt.onFinish();
cdt.cancel();
}
}
It is a good practice to call onFinish()
first and then call cancel()
.
I too experienced this problem. I was updating the progress bar WAY too often (even when the progress didn't change), here's how I fixed that:
// While loop from generic download method.
int previousProgress = 0;
while ((count = inputStream.read(buff)) != -1) {
outputStream.write(buff, 0, count);
totalBytesDownloaded += count;
int prog = (int) (totalBytesDownloaded * 100 / contentLength);
if (prog > previousProgress) {
// Only post progress event if we've made progress.
previousProgress = prog;
myPostProgressMethod(prog);
}
}
Now the app runs great and the user still receives a progress notification.
I had the same issue that I was not able to update the progress bar notification even with an interval of 3 seconds so after hours of digging I came to realize the fact that whenever we update the notification the RemoteView object must be re-instantiated and re-initialized to the Notification object's contentView. After doing this I was able to update the Notification progress bar with an interval of 100ms-500ms for a very long period without facing any UI blocking.
Note: If you don't agree you can verify this answer by running this snippet after commenting out the marked line and see the difference. It may take about 5mins to start the severe UI blockage which will heat up your device and may stop functioning. I tried with an S3 mini with Android 4.2.2 and the updateNotification(....) method was called from a worker thread inside a service. and Moreover I already double checked it and don't know what happens when Notification.Builder is used for the same purpose.
Note: The reason to write this answer after 3 years of the question is because I wonder that I did not find even a single stackoverflow answer or other blog post handling this serious issue with this very simple solution.
I hope this answer will be helpful for other newbies like me. Enjoy.
Here is my copy pasted code that you can use directly.... I use the same code for updating a notification layout which contains two ProgressBars and four TextViews with a frequency of 500ms-100ms.
//long mMaxtTimeoutNanos = 1000000000 // 1000ms.
long mMinTimeNanos = 100000000;//100ms minimum update limit. For fast downloads.
long mMaxtTimeoutNanos = 500000000;//500ms maximum update limit. For Slow downloads
long mLastTimeNanos = 0;
private void updateNotification(.....){
// Max Limit
if (mUpdateNotification || ((System.nanoTime()-mLastTimeNanos) > mMaxtTimeoutNanos)) {
// Min Limit
if (((System.nanoTime() - mLastTimeNanos) > mMinTimeNanos)) {
mLastTimeNanos = System.nanoTime();
// instantiate new RemoteViews object.
// (comment out this line and instantiate somewhere
// to verify that the above told answer is true)
mRemoteView = new RemoteViews(getPackageName(),
R.layout.downloader_notification_layout);
// Upate mRemoteView with changed data
...
...
// Initialize the already existing Notification contentView
// object with newly instatiated mRemoteView.
mNotification.contentView = mRemoteView;
mNotificationManager.notify(mNotificatoinId, mNotification);
mUpdateNotification = false;
}
}
}
来源:https://stackoverflow.com/questions/6929323/android-download-file-status-bar-notification-slowing-down-phone