Updating notificationbar from AsynTask causes the notificationbar to crash - android

I am uploading a video file using an Async Task. To track the progress I have a notification running in the statusbar. The notification works and updates correctly, but it causes severe performance issues to the extent where the statusbar crashes and the phone needs to be restarted. My code as follows:
private class UploadMedia extends AsyncTask<Void, Integer, String> {
private int NOTIFICATION_ID = 1;
private CharSequence _contentTitle;
private final NotificationManager _notificationManager = (NotificationManager) getActivity()
.getApplicationContext()
.getSystemService(
getActivity().getApplicationContext().NOTIFICATION_SERVICE);
Notification _notification;
PendingIntent _pendingIntent;
private long totalSize;
private int _progress = 0;
private InputStreamBody isb;
private File uploadFile;
protected void onPreExecute() {
Intent intent = new Intent();
_pendingIntent = PendingIntent.getActivity(getActivity(), 0,
intent, 0);
_contentTitle = "Uploader " + mediaTitle + " til Skoletube";
CharSequence contentText = _progress + "% complete";
_notification = new Notification(R.drawable.icon, _contentTitle,
System.currentTimeMillis());
_notification.flags = _notification.flags
| Notification.FLAG_ONGOING_EVENT;
_notification.contentIntent = _pendingIntent;
_notification.setLatestEventInfo(getActivity(), _contentTitle,
contentText, _pendingIntent);
_notificationManager.notify(NOTIFICATION_ID, _notification);
Toast.makeText(getActivity(), "Starter upload", Toast.LENGTH_SHORT)
.show();
try {
uploadFile = new File(_mediaFile.getPath());
// FileInputStream is = new FileInputStream(uploadFile);
//
// ByteArrayOutputStream bos = new ByteArrayOutputStream();
// byte[] b = new byte[1024];
// int bytesRead;
// while ((bytesRead = is.read(b)) != -1) {
// bos.write(b, 0, bytesRead);
// }
// byte[] data = bos.toByteArray();
//
// isb = new InputStreamBody(new ByteArrayInputStream(data),
// uploadFile.getName());
} catch (Exception ex) {
Log.i(TAG,
"Pre execute - oh noes... D: "
+ ex.getLocalizedMessage());
}
}
#Override
protected String doInBackground(Void... params) {
String result = "";
try {
// Inititate connectionparts
HttpClient client = new DefaultHttpClient();
HttpPost postRequest = new HttpPost(
"http://www.skoletube.dk/beta/api_userupload.php");
CustomMultipartEntity multipartE = new CustomMultipartEntity(
HttpMultipartMode.BROWSER_COMPATIBLE,
new ProgressListener() {
#Override
public void transferred(long num) {
publishProgress((int) ((num / (float) totalSize) * 100));
}
});
// Add the post elements
String timestamp = String
.valueOf(System.currentTimeMillis() / 1000);
String mode = "xml";
String hashSum = Utils.md5(ActiveUser.getPartner() + timestamp
+ ActiveUser.getInstance().getToken()
+ ActiveUser.getInstance().getSecret()
+ ActiveUser.getInstance().getUserID()
+ spnChannel.getSelectedItem().toString()
+ mediaDescribtion + "KEYWORDLOL"
+ spnPublic.getSelectedItem().toString() + mediaTitle
+ ActiveUser.getSharedkey());
multipartE.addPart("uid", new StringBody(ActiveUser
.getInstance().getUserID()));
multipartE.addPart("token", new StringBody(ActiveUser
.getInstance().getToken()));
multipartE.addPart("token_secret", new StringBody(ActiveUser
.getInstance().getSecret()));
multipartE.addPart("partner",
new StringBody(ActiveUser.getPartner()));
multipartE.addPart("timestamp",
new StringBody(timestamp.toString()));
multipartE.addPart("key", new StringBody(hashSum));
multipartE.addPart("video_title", new StringBody(mediaTitle));
multipartE.addPart("video_desc", new StringBody(
mediaDescribtion));
multipartE.addPart("video_keyword",
new StringBody("KEYWORDLOL"));
multipartE.addPart("video_privacy", new StringBody(spnPublic
.getSelectedItem().toString()));
multipartE.addPart("video_channel", new StringBody(spnChannel
.getSelectedItem().toString()));
multipartE.addPart("videoupload", new FileBody(uploadFile));
postRequest.setEntity(multipartE);
totalSize = multipartE.getContentLength();
HttpResponse loginResponse = client.execute(postRequest);
HttpEntity theEnt = loginResponse.getEntity();
result = EntityUtils.toString(theEnt);
Log.i(TAG, "Result: " + result);
} catch (Exception ex) {
Log.i(TAG,
"Do in background - oh noes... D: "
+ ex.getLocalizedMessage());
}
return result;
}
#Override
protected void onProgressUpdate(Integer... progress) {
if (_notification == null)
return;
_progress = progress[0];
_contentTitle = "Uploader " + mediaTitle + " til Skoletube";
CharSequence contentText = _progress + "% complete";
_notification.setLatestEventInfo(getActivity(), _contentTitle,
contentText, _pendingIntent);
_notificationManager.notify(NOTIFICATION_ID, _notification);
}
#Override
protected void onPostExecute(String result) {
_notificationManager.cancel(NOTIFICATION_ID);
}
}
I am testing this on a HTC Sensation. The problems occur the instant I press the notification bar, causing it to expand. The phone freezes and its touch and go whether I will actually get to the notification bar or the notification bar will crash. If i do get to the notification bar the performance issues persist, and closing the notification bar again is just as tricky as opening it.
What I'm thinking is maybe the sheer amount of notification updates sent could be causing the problem, but I am not sure.
Appreciate any ideas and suggestions.

Your suspicions are right.
The following instruction
publishProgress((int) ((num / (float) totalSize) * 100));
will be called very frequently, at short intervals.
What I would do in such situation is to store the pourcent avancement I want to display and send it only if it changed since the latest call.
In the doInBackground method, you can declare a variable such as:
int lastPourcent = 0;
Then, in the transferred method:
int currentPoucent = (int) ((num / (float) totalSize) * 100);
if (currentPourcent > lastPourcent) {
publishProgress(currentPourcent);
lastPourcent = currentPourcent;
}
It will significantly reduce the number of call to the refresh method of the Notification.

The problem is that your code is overflowing the Notification service with updates every time you execute publishProgress(). What I've done, and what you should do, is to implement a solution that doesn't overflow the service, but instead make your code update the Notification about every five or ten percent.

Related

Transfer Image throug BLE in android

I'm transfering an image of 1 mb using the following code.
The image gets transferred successfully if a thread delay is implemented between each packets.
If the thread delay is not set all the packets are sent from BluetoothGattServer but the BluetoothGattCallback does not receive all the packets.
Can anyone guide in sending the packets without the thread delay
Implement thread between each packets
private void sendingContinuePacket(BluetoothGattCharacteristic characteristic,
byte[] CHARACTERS) {
boolean isComplete = false;
runOnUiThread(() -> {
tv_status.setText("Sending Data...!!");
startTime = SystemClock.uptimeMillis();
customHandler.postDelayed(updateTimerThread, 0);
});
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] packetNoByte;
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
final int remainingTime = time;
if (!hasDisconnected) {
this.runOnUiThread(new Runnable() {
#Override
public void run() {
mRelativeLayout.setVisibility(View.VISIBLE);
if (totalPackets != 0) {
showProgress(totalPackets, remainingTime);
}
}
});
} else {
runOnUiThread(() -> {
mProgressBar.setProgress(0);
tv_progress.setText(0 + "%");
tv_timer.setText("00:00:00");
tv_imageSize.setText("");
tv_status.setText("");
Toast.makeText(PeripheralRoleActivity.this, "Something went wrong, Please Try again", Toast.LENGTH_SHORT).show();
customHandler.removeCallbacks(updateTimerThread);
});
return;
}
int a;
int b;
/**
* #param THREAD_SLEEP_TIME_FOR_NOTIFICATION
* this delay is placed to give a small pause while sending the data packe
* */
try {
Thread.sleep(Constants.THREAD_SLEEP_TIME_FOR_NOTIFICATION);
} catch (InterruptedException e) {
e.printStackTrace();
}
sentPacket = sentPacket + 1;
byte[] packetArray = Utils.getUtilsClass().toByteArray(sentPacket);
packetNoByte = Arrays.copyOf(packetArray, packetArray.length);
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
if(b-a ==0){
return;
}
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] last_packet =
new byte[packetNoByte.length + character_length];
System.arraycopy(packetNoByte, 0, last_packet,
0, packetNoByte.length);
System.arraycopy(sending_last_hex, 0, last_packet,
packetNoByte.length, sending_last_hex.length);
Log.d("Sending packets", Arrays.toString(last_packet));
// Set value for characteristic
characteristic.setValue(last_packet);
notifyCharacteristicChanged();
isComplete = true;
customHandler.removeCallbacks(updateTimerThread);
currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
Log.d("Collection", "End Time: " + currentDateTimeString);
Utils.getUtilsClass().sendNotification(getApplicationContext(), "Data Transfer", "Transfer Complete");
} else {
Log.i("", "CONTINUE PACKET ");
a = ((sending_continue_hex.length) * time);
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] sending_continue_packet =
new byte[packetNoByte.length + sending_continue_hex.length];
System.arraycopy(packetNoByte, 0, sending_continue_packet,
0, packetNoByte.length);
System.arraycopy(sending_continue_hex, 0, sending_continue_packet,
packetNoByte.length, sending_continue_hex.length);
Log.d("data transfer a", String.valueOf(a));
Log.d("data transfer b", String.valueOf(b));
Log.d("data trans bytes", String.valueOf(sending_continue_hex.length));
if(output == null){
output = new ByteArrayOutputStream();
}
try {
if {
characteristic.setValue(sending_continue_packet);
Log.d("Sending packets", Arrays.toString(sending_continue_packet));
notifyCharacteristicChanged();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d("Data byte", "times " + time);
if (isComplete) {
characteristic.setValue("Completed");
notifyCharacteristicChanged();
}
runOnUiThread(() -> tv_status.setText("Data sent!!"));
}
}
Updated Code
//the following function is used break the image byte [] into packets and store it in an arraylist
private void breakPackets(byte[] CHARACTERS) {
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
packetList = new ArrayList<>();
sendingPacket = 0;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
int a;
int b;
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
//packetList is an ArrayList<byte[]>
packetList.add(sending_last_hex);
startSendingPackets(sendingPacket);
} else {
a = (sending_continue_hex.length) * time;
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
packetList.add(sending_continue_hex);
}
Log.d("Data byte", "times " + time);
}
}
//the following function is used to set the byte[] from the arraylist to the characteristics and then notify the characteristics
private void startSendingPackets(int packet) {
isCommand = false;
mSampleCharacteristic.setValue(packetList.get(packet));
notifyCharacteristicChanged();
Log.i("packeting", "Sending ------------> " + packet);
}
/*************************************************/
#Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
//check if status is success
if (status == BluetoothGatt.GATT_SUCCESS) {
//if status is not successful isExecutable is false and the else loop is executed to resend the same packet that has failed
if (isExecutable) {
// Log.i("packeting", "Sent ------------> " + sendingPacket);
sendingPacket = sendingPacket + 1;
int size = packetList.size();
if (sendingPacket <= size-1) {
startSendingPackets(sendingPacket);
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
} else {
sendCommand("Completed");
}
} else {
startSendingPackets(sendingPacket);
isExecutable = true;
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
}
}else{
//if status is not successful
isExecutable = false;
Log.d(MainActivity.TAG, "Notification sent. fail Status: " + status );
}
}
As can be read in the documentation at https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback.html#onNotificationSent(android.bluetooth.BluetoothDevice,%20int):
When multiple notifications are to be sent, an application must wait
for this callback to be received before sending additional
notifications.
This means after you have called notifyCharacteristicChanged, you cannot call notifyCharacteristicChanged again until the callback onNotificationSent has been received. So you need to remove your for-loop and refactor your code to follow the API rules.
The reason for this is to get flow control. If you just push new packets faster than the BLE link's throughput, the internal buffers get full and packet loss will occur. That's why a delay might seem to work, but it's not a robust solution so that's why you should wait for the onNotificationSent callback since that means the BLE stack is ready to accept new packets.

How to update UI with download progress when download is a runnable being executed in a ThreadpoolExecutor?

I am implementing a download manager in native android where a thread pool executor is used to implement parallel downloads. A runnable is where the actual download happens, which is being executed on the pool threads. How can I send the download progress from the runnable to the UI? In order to send broadcasts, I need to pass context into the runnable. Is that the appropriate way?
How can I handle pause/resume/cancel of download gracefully?
Right now the moment user taps the pause/cancel button the value is updated in the DB and while the Thread.CurrentThread().IsInterrupted condition in the runnable becomes valid I check the status in database and decide whether I need to delete the partially downloaded file (if its cancel).
Also, will it be possible to know when the download completes so that I can remove the future object from the list?
public class Downloadable : Java.Lang.Object, IRunnable
{
private readonly string _destination;
private readonly int _productId;
public Downloadable(int productId)
{
_productId = productId;
_destination = Utils.StoragePath() + productId + ".zip";
}
public void Run()
{
int count;
try
{
Response response = CloudService.GetCloud().GetDownLoadURL(_productId.ToString(), true).Result;
if (string.Equals(response.status, "error", StringComparison.OrdinalIgnoreCase) || string.Equals(response.status, "internalError", StringComparison.OrdinalIgnoreCase))
{
//send error
}
else
{
DownloadPath downloadPath = JsonConvert.DeserializeObject<DownloadPath>(response.data);
string offlineUrl = downloadPath.contentUrl.Offline;
if (string.IsNullOrWhiteSpace(offlineUrl))
{
//send error
}
else
{
File directory = new File(Utils.StoragePath());
if (!directory.Exists())
directory.Mkdirs();
URL url = new URL(offlineUrl);
HttpURLConnection connection = (HttpURLConnection)url.OpenConnection();
long total = 0;
File file = new File(_destination);
file.CreateNewFile();
if (file.Exists() && file.Length() > 0)
{
total = file.Length();
connection.SetRequestProperty("Range", "Bytes=" + total + "-");
}
connection.Connect();
int lenghtOfFile = connection.ContentLength;
BufferedInputStream bufferedInputStream = new BufferedInputStream(url.OpenStream());
FileOutputStream fileOutputStream = new FileOutputStream(_destination, true);
byte[] buffer = new byte[1024];
count = 0;
while ((count = bufferedInputStream.Read(buffer, 0, 1024)) != -1)
{
if (Thread.CurrentThread().IsInterrupted)
{
if (DBService.GetDB().GetStatus(_productId) == (int)IpcCommon.Enumerations.Status.DOWNLOAD)
file.Delete();
break;
}
total += count;
System.Console.WriteLine("__PROGRESS__ " + (int)((total * 100) / lenghtOfFile));
System.Console.WriteLine("__PROGRESS__ ID " + _productId);
//publishProgress("" + (int)((total * 100) / lenghtOfFile));
fileOutputStream.Write(buffer, 0, count);
}
fileOutputStream.Close();
bufferedInputStream.Close();
}
}
}
catch (System.Exception exception)
{
IpcCommon.App.Logger.Log("Downloadable - File Download", new System.Collections.Generic.Dictionary<string, string> { { "Error", exception.Message } });
}
}
}
Dictionary<int, IFuture> _runningTaskList = new Dictionary<int, IFuture>();
int noOfCores = Runtime.GetRuntime().AvailableProcessors();
LinkedBlockingQueue _taskQueue = new LinkedBlockingQueue();
_threadPoolExecutor = new ThreadPoolExecutor(noOfCores, noOfCores * 2, 1, TimeUnit.Minutes, _taskQueue);
IFuture future = _threadPoolExecutor.Submit(new Downloadable(productId));
_runningTaskList.Add(productId, future);

Handling Multiple Downloads With Notifications Using Retrofit Library

I'm following a tutorial to download files using the Retrofit library.
My app's UI has several different buttons which download different files using the above method. The problem is when someone presses another button after pressing the first, it's queued and starts after the first finishes. I want it to start right away simultaneously.
Here's the code for the DownloadService:
public class DownloadService extends IntentService {
public DownloadService() {
super("Download Service");
}
private int totalFileSize;
private NotificationCompat.Builder notificationBuilder;
private NotificationManager notificationManager;
#Override
protected void onHandleIntent(Intent intent) {
MyApp x = (MyApp)getApplicationContext();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_file_download_deep_orange_a400_18dp)
.setContentTitle("Downloading")
.setContentText("Please wait...")
.setAutoCancel(true);
notificationManager.notify(x.ID, notificationBuilder.build());
Log.i("Paras", "onHandleIntent: " + x.filename + x.url);
initDownload(x.filename,x.url,x.ID);
}
private void initDownload(String filename, String url, int id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://dl.dropboxusercontent.com/")
.build();
RequestInterface.RetrofitInterface retrofitInterface = retrofit.create(RequestInterface.RetrofitInterface.class);
Call<ResponseBody> request = retrofitInterface.downloadFile(url);
try {
downloadFile(request.execute().body(),filename,id);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void downloadFile(ResponseBody body, String filename,int id) throws IOException {
int count;
byte data[] = new byte[1024 * 4];
long fileSize = body.contentLength();
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);
OutputStream output = new FileOutputStream(outputFile);
long total = 0;
long startTime = System.currentTimeMillis();
int timeCount = 1;
while ((count = bis.read(data)) != -1) {
total += count;
totalFileSize = (int) (fileSize / (Math.pow(1, 2))) / 1000;
double current = Math.round(total / (Math.pow(1, 2))) / 1000;
int progress = (int) ((total * 100) / fileSize);
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download,id);
timeCount++;
}
output.write(data, 0, count);
}
onDownloadComplete(filename,id);
output.flush();
output.close();
bis.close();
}
private void sendNotification(Download download, int id) {
sendIntent(download);
notificationBuilder.setProgress(100, download.getProgress(), false);
notificationBuilder.setContentText("Downloading file " + download.getCurrentFileSize() + "/" + totalFileSize + " KB");
notificationManager.notify(id, notificationBuilder.build());
}
private void sendIntent(Download download) {
Intent intent = new Intent(subject.MESSAGE_PROGRESS);
intent.putExtra("download", download);
LocalBroadcastManager.getInstance(DownloadService.this).sendBroadcast(intent);
}
private void onDownloadComplete(String filename,int id) {
try {
Download download = new Download();
download.setProgress(100);
sendIntent(download);
notificationManager.cancel(id);
notificationBuilder.setProgress(0, 0, false);
notificationBuilder.setContentText("Tap to open");
notificationManager.notify(id, notificationBuilder.build());
String path1 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename;
File file = new File(path1);
Uri uri_path = Uri.fromFile(file);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension
(MimeTypeMap.getFileExtensionFromUrl(path1));
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setType(mimeType);
intent.setDataAndType(uri_path, mimeType);
PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);
String string = filename;
notificationBuilder
.setContentIntent(pIntent)
.setAutoCancel(true)
.setContentTitle(string + " Downloaded");
Log.i("Paras", "onDownloadComplete: " + string);
notificationManager.notify(id, notificationBuilder.build());
} catch (Exception ex) {
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
}
}
Then I read about IntentService and Service classes. Do Service classes allow simultaneous downloads? I tried something like this:
public class DownloadService extends Service {
public DownloadService() {
super();
}
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
MyApp x = (MyApp)getApplicationContext();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(getBaseContext())
.setSmallIcon(R.drawable.ic_file_download_deep_orange_a400_18dp)
.setContentTitle("Downloading")
.setContentText("Please wait...")
.setAutoCancel(true);
notificationManager.notify(x.ID, notificationBuilder.build());
Log.i("Paras", "onHandleIntent: " + x.filename + x.url);
initDownload(x.filename,x.url,x.ID);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
MyApp x = (MyApp)getApplicationContext();
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj= intent.putExtra("ID",x.ID);
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private int totalFileSize;
private NotificationCompat.Builder notificationBuilder;
private NotificationManager notificationManager;
private void initDownload(String filename, String url, int id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://dl.dropboxusercontent.com/")
.build();
RequestInterface.RetrofitInterface retrofitInterface = retrofit.create(RequestInterface.RetrofitInterface.class);
Call<ResponseBody> request = retrofitInterface.downloadFile(url);
try {
downloadFile(request.execute().body(),filename,id);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void downloadFile(ResponseBody body, String filename,int id) throws IOException {
int count;
byte data[] = new byte[1024 * 4];
long fileSize = body.contentLength();
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);
OutputStream output = new FileOutputStream(outputFile);
long total = 0;
long startTime = System.currentTimeMillis();
int timeCount = 1;
while ((count = bis.read(data)) != -1) {
total += count;
totalFileSize = (int) (fileSize / (Math.pow(1, 2))) / 1000;
double current = Math.round(total / (Math.pow(1, 2))) / 1000;
int progress = (int) ((total * 100) / fileSize);
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download,id);
timeCount++;
}
output.write(data, 0, count);
}
onDownloadComplete(filename,id);
output.flush();
output.close();
bis.close();
}
private void sendNotification(Download download, int id) {
sendIntent(download);
notificationBuilder.setProgress(100, download.getProgress(), false);
notificationBuilder.setContentText("Downloading file " + download.getCurrentFileSize() + "/" + totalFileSize + " KB");
notificationManager.notify(id, notificationBuilder.build());
}
private void sendIntent(Download download) {
Intent intent = new Intent(subject.MESSAGE_PROGRESS);
intent.putExtra("download", download);
LocalBroadcastManager.getInstance(DownloadService.this).sendBroadcast(intent);
}
private void onDownloadComplete(String filename,int id) {
try {
Download download = new Download();
download.setProgress(100);
sendIntent(download);
notificationManager.cancel(id);
notificationBuilder.setProgress(0, 0, false);
notificationBuilder.setContentText("Tap to open");
notificationManager.notify(id, notificationBuilder.build());
String path1 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename;
File file = new File(path1);
Uri uri_path = Uri.fromFile(file);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension
(MimeTypeMap.getFileExtensionFromUrl(path1));
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setType(mimeType);
intent.setDataAndType(uri_path, mimeType);
PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);
String string = filename;
notificationBuilder
.setContentIntent(pIntent)
.setAutoCancel(true)
.setContentTitle(string + " Downloaded");
Log.i("Paras", "onDownloadComplete: " + string);
notificationManager.notify(id, notificationBuilder.build());
} catch (Exception ex) {
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
}
}
But it didn't work. Any clues what I should do? I'm ready to give more details if required.
Edit 1: DownloadService runs on a function "startDownload" which is executed by various buttons. As you can see in 2nd code, class Extends Service. There is one thread which handles all those button clicks. If you look in comments, it's suggested that I should use Service and different threads for all those clicks. Now how can I make so many threads programmatically. There are almost 40 buttons which make use of DownloadService.
Thanks to #Lxu, I have got it working. So IntentServiceis meant to do one task at a time and can't do multiple tasks simultaneously. We should use Serviceinstead. It allows multiple tasks to be performed simultaneously. We can create multiple threads inside the Service which will be executed simultaneously. My problem got solved by putting all the code of onCreate() to onStartCommand(). When a service is called for the first time, onCreate() is called and after that, it's not called anymore no matter how many times service is called. On every service call, onStartCommand() is executed which creates new thread every time. That's it.
Here's the complete code:
public class DownloadService extends Service {
public DownloadService() {
super();
}
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
int id1;
int id2;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
MyApp x = (MyApp)getApplicationContext();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.ic_file_download_deep_orange_a400_18dp)
.setContentTitle("Downloading")
.setContentText("Please wait...")
.setAutoCancel(true);
notificationManager.notify(x.ID, notificationBuilder.build());
Log.i("Paras", "onHandleIntent: " + x.filename + x.url + " " + x.ID);
initDownload(x.filename,x.url,x.ID);
}
}
#Override
public void onCreate() {
// Get the HandlerThread's Looper and use it for our Handler
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
MyApp x = (MyApp)getApplicationContext();
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private int totalFileSize;
private NotificationCompat.Builder notificationBuilder;
private NotificationManager notificationManager;
private void initDownload(String filename, String url, int id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://dl.dropboxusercontent.com/")
.build();
RequestInterface.RetrofitInterface retrofitInterface = retrofit.create(RequestInterface.RetrofitInterface.class);
Call<ResponseBody> request = retrofitInterface.downloadFile(url);
try {
downloadFile(request.execute().body(),filename,id);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void downloadFile(ResponseBody body, String filename,int id) throws IOException {
int count;
byte data[] = new byte[1024 * 4];
long fileSize = body.contentLength();
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);
OutputStream output = new FileOutputStream(outputFile);
long total = 0;
long startTime = System.currentTimeMillis();
int timeCount = 1;
while ((count = bis.read(data)) != -1) {
total += count;
totalFileSize = (int) (fileSize / (Math.pow(1, 2))) / 1000;
double current = Math.round(total / (Math.pow(1, 2))) / 1000;
int progress = (int) ((total * 100) / fileSize);
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download,id);
timeCount++;
}
output.write(data, 0, count);
}
onDownloadComplete(filename,id);
output.flush();
output.close();
bis.close();
}
private void sendNotification(Download download, int id) {
sendIntent(download,id);
notificationBuilder.setProgress(100, download.getProgress(), false)
.setContentTitle("Downloading");
notificationBuilder.setContentText("Downloading file " + download.getCurrentFileSize() + "/" + totalFileSize + " KB");
notificationManager.notify(id, notificationBuilder.build());
}
private void sendIntent(Download download, int id) {
Intent intent = new Intent(subject.MESSAGE_PROGRESS);
intent.putExtra("download", download);
LocalBroadcastManager.getInstance(DownloadService.this).sendBroadcast(intent);
}
private void onDownloadComplete(String filename,int id) {
try {
Download download = new Download();
download.setProgress(100);
sendIntent(download,id);
notificationManager.cancel(id);
notificationBuilder.setProgress(0, 0, false);
notificationBuilder.setContentText("Tap to open");
notificationManager.notify(id, notificationBuilder.build());
String path1 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename;
File file = new File(path1);
Uri uri_path = Uri.fromFile(file);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension
(MimeTypeMap.getFileExtensionFromUrl(path1));
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setType(mimeType);
intent.setDataAndType(uri_path, mimeType);
PendingIntent pIntent = PendingIntent.getActivity(this,(int) System.currentTimeMillis(), intent, 0);
String string = filename;
notificationBuilder
.setContentIntent(pIntent)
.setAutoCancel(true)
.setContentTitle(string + " Downloaded");
Log.i("Paras", "onDownloadComplete: " + string);
notificationManager.notify(id, notificationBuilder.build());
}catch (Exception ex){
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
}
}

How to Show download progress in progress bar in Android?

I am using .net web services and I am able to download the file in sd card, But I want to show progress bar when particular file start download and complete and then I want to show options like view and cancel.
Click event Class:
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
LazyAdapter ca = (LazyAdapter)parent.getAdapter();
FolderList item_name = (FolderList)ca.getItem(position);
FolderList DocumentID = (FolderList)ca.getItem(position);
FolderList type = (FolderList)ca.getItem(position);
Intent mIntent = new Intent();
mIntent.putExtra("item_name", item_name.folder_name);
mIntent.putExtra("item_id", DocumentID.ID);
mIntent.putExtra("item_type", type.type);
mIntent.getStringExtra("item_name");
String Type = mIntent.getStringExtra("item_type");
Log.i("Type", Type);
if {
// Some code here...
} else {
Intent i = new Intent(getApplicationContext(), Display_image.class);
i.putExtra("item_name", item_name.folder_name);
i.putExtra("ID", DocumentID.ID);
i.putExtra("item_type", type.type);
i.putExtra("User_ID",User_ID);
i.getStringExtra("item_name");
Id = i.getStringExtra("ID");
i.getStringExtra("item_type");
startActivity(i);
}
}
My Code: I want to use download manager
SoapPrimitive DocumentResponse = (SoapPrimitive)Envelope.getResponse();
Log.i("DocumentResponse", DocumentResponse.toString());
String DocAsString = DocumentResponse.toString();
byte[] decodedString = Base64.decode(DocAsString, Base64.DEFAULT);
File direct = new File(Environment.getExternalStorageDirectory() + "/MyFolder");
if(!direct.exists())
{
direct.mkdir();
}
File photo = new File(Environment.getExternalStorageDirectory() + "/MyFolder", Name);
if (photo.exists())
{
photo.delete();
}
try {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(Name));
request.setDescription("Have Fun ;)");
request.setTitle("Downloading...");
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
FileOutputStream fos=new FileOutputStream(photo.getPath());
fos.write(decodedString);
fos.close();
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
ContentValues values= new ContentValues();
System.out.println(values);
}
catch (java.io.IOException e)
{
Log.e("PictureDemo", "Exception in photoCallback", e);
}
}
Please suggest how to use download manager into it??? thanks
You could use a ProgressBar to accomplish this.
First, add a progressbar to your interface like this:
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#android:style/Widget.ProgressBar.Small"/>
Then in your code update your method like this:
protected ProgressBar mProgressBar;
protected long downloadId;
protected DownloadManager manager;
public void startdownload() {
//<SET UP DOWNLOAD MANAGER HERE>
downloadId = manager.enqueue(request);
mProgressBar = findViewById(R.id.progress_bar);
Timer myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(downloadId);
Cursor cursor = manager.query(q);
cursor.moveToFirst();
int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
cursor.close();
int dl_progress = (bytesDownloaded * 1f / bytesTotal) * 100;
runOnUiThread(new Runnable(){
#Override
public void run(){
mProgressbar.setProgress((int) dl_progress);
}
});
}
}, 0, 10);
}
Use Follow Method
private NotificationManager mNotifyManager;
case R.id.btnSubmit:
mNotifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(context);
mBuilder.setContentTitle("BuyMixTapes")
.setContentText("Download in progress")
.setSmallIcon(R.drawable.appiconmain);
new DownloadFile().execute(vv);
public class DownloadFile extends AsyncTask<String, String, String> {
/**
* Before starting background thread
* Show Progress Bar Dialog
*/
#Override
protected void onPreExecute() {
super.onPreExecute();
//context.showDialog(progress_bar_type);
mBuilder.setProgress(100, 0, false);
mNotifyManager.notify(id, mBuilder.build());
}
protected String doInBackground(String... f_url) {
int count;
try {
for (int i = 0; i < f_url.length; i++) {
Log.e("0url",""+f_url[0]);
Log.e("1url",""+f_url[1]);
// Log.e("1url",""+f_url[1]);
URL url = new URL(f_url[i]);
URLConnection conection = url.openConnection();
conection.connect();
// getting file length
int lenghtOfFile = conection.getContentLength();
// input stream to read file - with 8k buffer
InputStream input = new BufferedInputStream(url.openStream(), 8192);
// Output stream to write file
// OutputStream output = new FileOutputStream("/sdcard/"+f_url[i]);
OutputStream output = new FileOutputStream(
"/sdcard/" +i + "buymix.mp3");
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
publishProgress("" + (int) ((total * 100) / lenghtOfFile));
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close();
}
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
/**
* Updating progress bar
*/
/**
* After completing background task
* Dismiss the progress dialog
**/
#Override
protected void onProgressUpdate(String... values) {
// Update progress
mBuilder.setProgress(100, Integer.parseInt(values[0]), false);
mNotifyManager.notify(id, mBuilder.build());
super.onProgressUpdate(values);
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
mBuilder.setContentText("Download complete");
// Removes the progress bar
String imagePath = Environment.getExternalStorageDirectory()
.toString() + "/downloaded.mp3";
mBuilder.setProgress(0, 0, false);
mNotifyManager.notify(id, mBuilder.build());
}
}
Update 2021:
Add a ProgressBar in your layout (TextView is just for showing more information):
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/progressBar"
android:text="Hello World!" />
Then before onCreate:
DownloadManager downloadManager;
long downloadReference;
TextView text1;
ProgressBar progressBar;
Timer progressTimer;
Afterwards, in onCreate:
text1 = (TextView) findViewById(R.id.textView1);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
Then after setting up download manager:
// <Some codes which setup download manager>
downloadReference = downloadManager.enqueue(request); // enqueue a new download
// update progressbar
progressTimer = new Timer();
progressTimer.schedule(new TimerTask() {
#Override
public void run() {
DownloadManager.Query downloadQuery = new DownloadManager.Query();
downloadQuery.setFilterById(downloadReference);
Cursor cursor = downloadManager.query(downloadQuery);
if (cursor.moveToFirst()) { // this "if" is crucial to prevent a kind of error
final int downloadedBytes = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
final int totalBytes = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); // integer is enough for files under 2GB
cursor.close();
final float downloadProgress = downloadedBytes * 100f / totalBytes;
if(downloadProgress > 99.9) // stop repeating timer (it's also useful for error prevention)
progressTimer.cancel();
runOnUiThread(new Runnable() {
#Override
public void run() {
text1.setText(downloadedBytes + "\n" + totalBytes + "\n" + downloadProgress + "%");
progressBar.setProgress((int) downloadProgress);
}
});
}
}
}, 0, 1000);
Some tips:
If your file is larger than 2GB, you should use another type instead of int such as double etc.
You can stop timer somewhere else for example, in BroadcastReceiver if you have.
Task executions is every 1000 milliseconds (every 1 second) which is obviously changable.

force close, download file with asynctask

i have a running download function. but when i run it, like 80% of the time it make my phone laggy, force close, not responding for a very long time like 1~2 minutes. this case happened very randomly, i cant really trace what is the problem. the device will turn back to normally after the download is complete. i have tried on various devices such as galaxy S2, galaxy note, SE xperia Arc S, and few tables. problem remains the same. can anyone advice me how to improve my code? below is my existing code:
public void onClickDownload(View view){
String url = "http://www.mydomain.com./" + fileURL;
url = url.replaceAll(" ","%20");
String sourceUrl = url;
new DownloadFileAsync().execute(sourceUrl);
}
public class DownloadFileAsync extends AsyncTask<String, Integer, Void> {
private boolean run_do_in_background = true;
#Override
protected void onPreExecute(){
super.onPreExecute();
}
#Override
protected Void doInBackground(String... aurl) {
int count;
try {
URL url = new URL(aurl[0]);
URLConnection conexion = url.openConnection();
conexion.connect();
int lengthOfFile = conexion.getContentLength();
Log.d("ANDRO_ASYNC", "Lenght of file: " + lengthOfFile);
File folder = new File(Environment.getExternalStorageDirectory() + "/MaxApps");
boolean success = false;
if (!folder.exists()) {
success = folder.mkdirs();
}
if (!success) {
} else {
}
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream("/sdcard/MaxApps/" + apkURL);
byte data[] = new byte[100*1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
int progressPercent = (int) ((total*100)/lengthOfFile);
if(progressPercent % 5 == 0){
publishProgress(progressPercent);
}
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
notificationManager.cancel(Integer.parseInt(ID.toString()));
Notification MyN = new Notification(); MyN.icon = R.drawable.logo1;
MyN.tickerText = "Download Failed";
MyN.number = 1;
MyN.setLatestEventInfo (getApplicationContext(), apkURL + " Download Failed.", "Please try again", MyPI);
MyN.flags |= Notification.FLAG_AUTO_CANCEL;
MyNM.notify(1, MyN);
run_do_in_background = false;
}
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
notification.contentView.setProgressBar(R.id.pbStatus, 100, progress[0], false);
notificationManager.notify(Integer.parseInt(ID.toString()), notification);
}
#Override
protected void onPostExecute(Void unused) {
if(run_do_in_background) {
notificationManager.cancel(Integer.parseInt(ID.toString()));
Notification MyN = new Notification(); MyN.icon = R.drawable.logo1;
MyN.tickerText = "Download Complete";
MyN.number = 1;
MyN.setLatestEventInfo (getApplicationContext(), "Download Complete, Click to install.", apkURL, MyPI);
MyN.flags |= Notification.FLAG_AUTO_CANCEL;
MyNM.notify(Integer.parseInt(ID.toString()) , MyN);
}
}
}
It might be that updating UI is taking long? Maybe you can try to test like this and see if it makes a difference:
#Override
protected void onProgressUpdate(Integer... progress) {
Log.d("ANDRO_ASYNC", "Progress: " + progress[0]);
}
By the way, this is not related to what you are asking, but I think you have problem in your code that handles the progress:
int progressPercent = (int) ((total*100)/lengthOfFile);
if(progressPercent % 5 == 0){
publishProgress(progressPercent);
}
That will update the progress only when it is exactly 5,10,15,20% etc... I guess you actually want to update progress when it is at least %5 further than before.
int previousProgress = 0;
while ((count = input.read(data)) != -1) {
total += count;
int progressPercent = (int) ((total*100)/lengthOfFile);
if(progressPercent - previousProgress >= 5) {
previousProgress = progressPercent;
publishProgress(progressPercent);
}
output.write(data, 0, count);
}

Categories

Resources