I have done my research very thoroughly with no actual result so here I am.
I have a horizontal ProgressBar in my app which I use to indicate a progress in file upload. When I upload a whole directory though, I am trying to make it show progress of single files uploaded at the time. I use asynctasks with a FixedThreadPool(1). The thing is, that only the first task triggers the progressbar.
The method in main activity:
uploadExecutor = Executors.newFixedThreadPool(1);
private void upload() throws DropboxException, IOException {
if(isDir){
DataDir dir = new DataDir(file);
List<File> files = dir.getFileList();
for(File item : files){
Upload upload = new Upload(getActivity(), dropbox, "/Apps/Sink/", item, bar);
upload.executeOnExecutor(uploadExecutor);
}
}
else {
Upload upload = new Upload(getActivity(), dropbox, "/Apps/Sink/", file, bar);
upload.executeOnExecutor(uploadExecutor);
}
}
Upload is a class that implements AsyncTask and handles the upload of the file.
When created, the instance of the class sets the bar to be visible and in OnPostExecute() sets it to be invisible
EDIT
Adding the Upload class.
public Upload(Context context, DropboxAPI<?> api, String dropboxPath,
File file, ProgressBar mBar) {
this.mBar = mBar;
mBar.setProgress(0);
mBar.setVisibility(View.VISIBLE)
#Override protected Boolean doInBackground(Void... params){
try {
// By creating a request, we get a handle to the putFile operation,
// so we can cancel it later if we want to
FileInputStream fis = new FileInputStream(mFile);
String path = mPath + mFile.getName();
mRequest = mApi.putFileOverwriteRequest(path, fis, mFile.length(),
new ProgressListener() {
#Override
public long progressInterval() {
// Update the progress bar every half-second or so
return 500;
}
#Override
public void onProgress(long bytes, long total) {
publishProgress(bytes);
}
});
if (mRequest != null) {
mRequest.upload();
return true;
}
#Override
protected void onProgressUpdate(Long... progress) {
int percent = (int)(100.0*(double)progress[0]/mFileLen + 0.5);
//mDialog.setProgress(percent);
mBar.setProgress(percent);
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
showToast("File successfully uploaded");
} else {
showToast(mErrorMsg);
}
mBar.setVisibility(View.INVISIBLE);
}
EDIT
I resolved the issue by moving the for cycle into the upload class.
Very similar approach HERE
In your Upload class you can implement the method for AsynkTak that checks for progress status changes
protected void onProgressUpdate(Integer... progress) { /Your code/ }
http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate(Progress...)
The problem you have is that you initialize one AsyncTask per file and give each of them a reference to the same ProgressBar.
What happens is, you loop through the files and init the AsyncTasks. Now if the first task finishes the bar will be set to invisible. The following tasks aren't able to set it visible again with your current setup.
You can try to set the bar visible in onPostExecute() of the AsyncTask, because this one will be invoked before doInBackground() kicks in. Or you can trigger the onProgreesUpdate() at the beginning of doInBackground() to make it visible there.
Related
I am working on an Android App for handheld Scan Devices and want to download around 4.500 items from an MySQL database via Retrofit2 into a SQlite Database on the device; hence, when I tried to download all items at once, it slowed down the UI and froze the app up to 5 minutes; I googled a lot on how to solve the problem and couldn´t come up with a fitting solution for now; hence I tried to download the Database with 7 columns for each item - hence, around 31.500 entries in the database - in "Chunks" by iterating in a For-each loop and using .stream() and .limit() in a Background threat, like this:
public static void writeItemsToDatabase(Context mContext, String basic) {
//creating the itemApi interface
ItemApi itemApi = retrofit.create(ItemApi.class);
//making the call object
Call<List<Item>> call = itemApi.checkItems(basic);
call.enqueue(new Callback<List<Item>>() {
#Override
public void onResponse(#NonNull Call<List<Item>> call,
#NonNull Response<List<Item>> response) {
if (response.isSuccess()) {
List<Item> itemList;
itemList = response.body();
int dataSize = response.body().size();
Log.d(TAGGG, String.valueOf(dataSize));
itemList.forEach(List -> Log.d(TAGGG, String.valueOf(List.getEan())));
itemList.forEach(List -> Log.d(TAGGG, String.valueOf(List.getNo())));
class DownloadTask extends AsyncTask<String, Integer, String> {
// Runs in UI before background thread is called
#Override
protected void onPreExecute() {
super.onPreExecute();
// Do something like display a progress bar
}
// This is run in a background thread
#Override
protected String doInBackground(String... params) {
// Do something that takes a long time, for example:
for (int i = 0; i <= 3 ; i++) {
try (DatabaseHandler itemsManager = new DatabaseHandler((XXXXApp)
mContext.getApplicationContext())) {
itemList.stream().limit(1500).forEach(item -> {
itemsManager.addItem(item);
itemsManager.close();
});
}
// Call this to update your progress
publishProgress(i);
}
return "this string is passed to onPostExecute";
}
// This is called from background thread but runs in UI
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// Do things like update the progress bar
}
// This runs in UI when background thread finishes
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// Do things like hide the progress bar or change a TextView
}
}
new DownloadTask().execute();
}
}
#Override
public void onFailure(Call<List<Item>> call, Throwable t) {}
});
return;
}
however, the result is not satisfying as the Database doesn´t get´s downloaded properly; I changed the values for i to 9 and .limit() to 500 (to achieve the same result, the Download of +4.500 Items) with the same result.
The problem certainly is in this code snippet:
for (int i = 0; i <= 3 ; i++) {
try (DatabaseHandler itemsManager = new DatabaseHandler((XXXApp)
mContext.getApplicationContext()))
{
itemList.stream().limit(1500).forEach(item -> {
itemsManager.addItem(item);
itemsManager.close();
});
}
// Call this to update your progress
publishProgress(i);
}
It is the nearest approach that I´ve found to what I want to achieve after googling a lot; the problem certainly is that it´s a For-Loop that closes the Database each time and reopens it; I am also not sure if the amount of Data is too big for an SQlite database; hence any help or hints on how to solve this properly would be very much appreciated, thanks!
Create once instance of DatabaseHandler(what is it? you can use room with more comfortable API) and reuse it.
Insert many(100-500) items in one transaction.
Or you can create sqlite db file on server side then download it and open as DB in android app.
I am building an OCR Android application that performs many image processing tasks in the background which takes some time to complete.
The steps it performs are as follows:
Capture image
Display image to user, provide option to recapture the image or proceed
Display processed image, provide option to recapture the image or proceed.
Extract text
These tasks are time consuming and I would like to reduce some time by starting the next task as soon as the the previous finishes, while displaying the progress dialog "Please wait" only if the user clicks the proceed button and the task has not completed.
I am wondering is this possible and if so how do I achieve this?
Below is my code for the OCR task:
private class OCRTask extends AsyncTask<Void, Void, String> {
ProgressDialog mProgressDialog;
public OCRTask(PreviewActivity activity) {
mProgressDialog = new ProgressDialog(activity);
}
#Override
protected String doInBackground(Void... params) {
String path = previewFilePath;
String ocrText;
OCR ocr = new OCR();
ocrText = ocr.OCRImage(path, PreviewActivity.this);
// Write the result to a txt file and store it in the same dir as the temp img
// find the last occurence of '/'
int p=previewFilePath.lastIndexOf("/");
// e is the string value after the last occurence of '/'
String e=previewFilePath.substring(p+1);
// split the string at the value of e to remove the it from the string and get the dir path
String[] a = previewFilePath.split(e);
String dirPath = a[0];
String fileString = dirPath + "ocrtext.txt";
File file = new File(fileString);
try {
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(ocrText);
bw.close();
System.out.println("done!!");
} catch (IOException i) {
i.printStackTrace();
}
new WordCorrect(fileString);
return ocrText;
}
#Override
protected void onPreExecute() {
// Set the progress dialog attributes
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setMessage("Extracting text...");
mProgressDialog.show();
}
#Override
protected void onPostExecute(String result) {
// dismiss the progress dialog
mProgressDialog.dismiss();
Intent i;
i = new Intent(PreviewActivity.this, ReceiptEditActivity.class);
// Pass the file path and text result to the receipt edit activity
i.putExtra(FILE_PATH, previewFilePath);
Log.e("OCR TEXT: ", result);
i.putExtra(OCR_TEXT, result);
// Start receipt edit activity
PreviewActivity.this.startActivityForResult(i, 111);
finish();
}
#Override
protected void onProgressUpdate(Void... values) {}
}
Any help or guidance is greatly appreciated!
Just take one boolean variable inside your Activity or Fragment, like
public static boolean isTaskRunning = false;
and inside onPreExecute() of AsyncTask, change its value to true.
YourActivity.isTaskRunning = true;
I am considering that you have taken this variable in activity class. And inside onPostExecute(String result), revert its value to false
YourActivity.isTaskRunning = false;
Now, on button click, check for this variable value, if its true then show your progress dialog otherwise not.
I am trying to download multiple files in listview with progressbar. What I achieved is, I can start a particular download, pause/resume it using AsyncTask and progress bar is updated(for single file), this part works well
My problem is I am not able to download multiple files simultaneously and when I leave the listview to another screen although my download is going on in the background but progress is not updated, progress bar shows 0 progress as if it is not downloading but its been downloading in the background.
Finally I found the answer which was much simpler than I thought, here it is as follows
Create a service having Asynctask for downloading and hashtable of values(url, Asynctask)
Pass the value(url, Asynctask) when a list item is clicked and check whether that hashtable contain the value already if yes cancel that Asynctask task if no add it to hashtable and start Asynctask
now for updating progress in my adapter I ran a thread which iterate over hashtable and passes the value using BroadcastListener.
And in activity intercept the broadcast and depending on the ListItem visible update the progress
PS: If anybody needs some code I can provide basic code of the description explained above
public class DownloadingService extends Service {
public static String PROGRESS_UPDATE_ACTION = DownloadingService.class.getName() + ".progress";
private static final long INTERVAL_BROADCAST = 800;
private long mLastUpdate = 0;
private Hashtable<String, DownloadFile> downloadTable;
private LocalBroadcastManager broadcastManager;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MessageEntity entityRecieved = (MessageEntity) intent.getSerializableExtra("ENTITY");
queueDownload(entityRecieved);
return super.onStartCommand(intent, flags, startId);
}
private void queueDownload(MessageEntity entityRecieved){
if (downloadTable.containsKey(entityRecieved.getPacketID())) {
DownloadFile downloadFile = downloadTable.get(entityRecieved.getPacketID());
if (downloadFile.isCancelled()) {
downloadFile = new DownloadFile(entityRecieved);
downloadTable.put(entityRecieved.getPacketID(), downloadFile);
startDownloadFileTask(downloadFile);
} else {
downloadFile.cancel(true);
downloadTable.remove(entityRecieved.getPacketID());
}
} else {
DownloadFile downloadFile = new DownloadFile(entityRecieved);
downloadTable.put(entityRecieved.getPacketID(), downloadFile);
startDownloadFileTask(downloadFile);
}
}
#Override
public void onCreate() {
super.onCreate();
downloadTable = new Hashtable<String, DownloadFile>();
broadcastManager = LocalBroadcastManager.getInstance(this);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
void startDownloadFileTask(DownloadFile asyncTask) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
asyncTask.execute();
}
private void publishCurrentProgressOneShot(boolean forced) {
if (forced || System.currentTimeMillis() - mLastUpdate > INTERVAL_BROADCAST) {
mLastUpdate = System.currentTimeMillis();
int[] progresses = new int[downloadTable.size()];
String[] packetIds = new String[downloadTable.size()];
int index = 0;
Enumeration<String> enumKey = downloadTable.keys();
while (enumKey.hasMoreElements()) {
String key = enumKey.nextElement();
int val = downloadTable.get(key).progress;
progresses[index] = val;
packetIds[index++] = key;
}
Intent i = new Intent();
i.setAction(PROGRESS_UPDATE_ACTION);
i.putExtra("packetIds", packetIds);
i.putExtra("progress", progresses);
mBroadcastManager.sendBroadcast(i);
}
class DownloadFile extends AsyncTask<Void, Integer, Void> {
private MessageEntity entity;
private File file;
private int progress;
public DownloadFile(MessageEntity entity) {
this.entity = entity;
}
#Override
protected Void doInBackground(Void... arg0) {
String filename = entity.getMediaURL().substring(entity.getMediaURL().lastIndexOf('/') + 1);
file = new File(FileUtil.getAppStorageDir().getPath(), filename);
downloadFile(entity.getMediaURL(), file);
return null;
}
public String downloadFile(String download_file_path, File file) {
int downloadedSize = 0;
int totalSize = 0;
try {
// download the file here
while ((bufferLength = inputStream.read(buffer)) > 0 && !isCancelled()) {
progress = percentage;
publishCurrentProgressOneShot(true);
}
} catch (final Exception e) {
return null;
}
return file.getPath();
}
}
On big problem with AsynchTask is when you finish its activity, AsynchTask looses it's track with your UI. After that when you return back to that activity the progressBar is not updating even if the download progress still running in background. In fact that AsynchTask is not belong to the new Activity you lunched so the new instance of progress bar in new Activity will not updating.
To fix this problem I suggest you:
1- Run a thread with a timerTask in onResume() which updates ur progressbar with values updating from the AsyncTask running background. Something like this:
private void updateProgressBar(){
Runnable runnable = new updateProgress();
background = new Thread(runnable);
background.start();
}
public class updateProgress implements Runnable {
public void run() {
while(Thread.currentThread()==background)
try {
Thread.sleep(1000);
Message msg = new Message();
progress = getProgressPercentage();
handler.sendMessage(msg);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
}
}
}
private Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
progress.setProgress(msg.what);
}
};
and when your activity is not visible you must destroy the thread:
private void destroyRunningThreads()
{
if(background!=null)
{
background.interrupt();
background=null;
}
}
2- Define a global static boolean variable. Set it true in onPreExecute and in onPostExecute set it to false. It shows that you are downloading or not, so you can check if the variable is equal to true, show the previous progressbar dialog.(you can do something like this with an integer value-or array of integers- in order to show the update percentage for each download progress).
3- The last way I personally used is to show the download progress in Notification Bar and in my list view I just show that it is downloading right now or not.(using 2nd method with a boolean values). In this way even if you finish the activity the notification bar is still updated with download progress.
when you leave your activity, the activity that asynctask shows the progressbar is killed and thus the progressBar dose not show anymore when you come back on new activity because the asynctask dose not aware of your new activity.
General solution that will work in any cases for example when your user closes your app and again opens it and wants to know the progressBar is separating your presentation completely. that means you can create sharedPreferences or database table and put your state of your file in to it while your asynctask is downloading. for example every 500 milisecond update the sharedPreferences or database table with how much downloaded from total file size. then when user come back to your new activity you read from DB or sharedPreferences to show progressBar and update it every for example 1000 milisecond. In this way your user will know the progressBar even if he closes the app and opens it again. I know it takes a bit more work but it surely makes your users be happy.
in order to read and update at fixed rate you can use scheduleAtFixedRate
Iv'e got an Android app that is using a list activity to display a list of items pulled from the internet. I First use an AsyncTask to load the list and that Async task finishes it calls a different async task to start loading the thumbnail pictures that go along with the list items. The problem I am having is that the user has access to a refresh button that they can press at any time and when it is pressed, the whole list of items is delete and the loading starts over. The Async task that loads the thumbnails could potentially still be running if this happens and may try to add a thumbnail to a non existing list item then. Iv'e tried synchronizing on the list, using a Boolean which after researching I realized would not work. I have also tried using a static atomic boolean to check if refresh has been hit to cancel the thumbnail loader. Any ideas?
public class LoadItems extends AsyncTask<Void, Void, Boolean> {
private Activity activity;
private static boolean loading = false;
public static final AtomicBoolean refreshing = new AtomicBoolean(false);
private static final String TAG = "LoadItems";
private int start;
private List<ListItem> items;
public LoadItems(Activity activity) {
this.activity = activity;
}
#Override
protected void onPreExecute() {
loading = true;
start = ItemViewer.itemList.size();
}
#Override
protected Boolean doInBackground(Void... arg0) {
items = WebFunctions.getMoreItems(activity);
return (items != null);
}
protected void onPostExecute(Boolean success) {
if (success) {
for (ListItem item: items) {
ItemViewer.itemList.add(item);
Log.d(TAG, "added item " + item.getTitle());
}
LoadThumbnails thumbnailLoader = new LoadThumbnails();
thumbnailLoader.execute(start, ItemViewer.itemList.size());
}
loading = false;
}
public void protectedExecute() {
if (!loading)
execute();
}
public void refresh() {
if (!refreshing.getAndSet(true)) {
WebFunctions.reset();
ItemViewer.itemList.removeAllItems();
execute();
}
}
}
public class LoadThumbnails extends AsyncTask<Integer, Void, Drawable> {
private int position;
private int end;
#Override
protected Drawable doInBackground(Integer... params) {
position = params[0];
end = params[1];
Drawable thumbnail = null;
synchronized(ItemViewer.itemList) {
if (LoadItems.refreshing.get())
cancel(true);
String url = ItemViewer.itemList.get(position).getThumbnailUrl();
if (!url.isEmpty())
thumbnail = WebFunctions.loadDrawableFromUrl(ItemViewer.activity, url);
}
return thumbnail;
}
protected void onPostExecute(Drawable d) {
synchronized (ItemViewer.itemList) {
if (LoadItems.refreshing.get())
cancel(true);
if (d != null)
ItemViewer.itemList.setThumbnail(position, d);
position++;
if (position < end) {
LoadThumbnails lt = new LoadThumbnails();
lt.execute(position, end);
}
}
}
}
This is pretty simple to solve. Whenever the user hits the refresh button, make sure you call cancel() on the last async tasks you have created before you create new tasks. For example,
private void onRefreshClick(View v) {
if(mLastLoadItemTask != null) mLastLoadItemTask.cancel(true);
if(mLastLoadThumbnailTask != null) mLastLoadThumbnailTask.cancel(true);
mLastLoadItemTask = new LoadItems(...);
mLastLoadItemTask.execute();
}
Then, in the onPostExecute of each of your async tasks, first check to see if they were cancelled by calling isCancelled(). If they were cancelled, make sure the onPostExecute method does no work by just returning. For example,
protected void onPostExecute(...) {
if(isCancelled()) return;
//Adding items to list
//Or start load thumbnail task
}
As you can see that should prevent any unintentional or stale updates because the onPostExecute methods and your cancel calls will all happen on the main therad. The last thing I would suggest is to alter your loadThumbs task to be able to stop doing work as soon as possibly by checking isCancelled() whenever it makes sense to do so.
The following steps might help:
cache the results, whatever you have previously pulled from the net should be saved and quickly restored back when your application is launched. this way you avoid long delays and empty screens on application startup, which, in turn, stops the user from pressing 'reload'
make a boolean variable reload_in_progress, set it to true when you start pulling data from the net, and set it to false when all thumbnails are ready. 'reload' click handler should ignore clicks when reload_in_progress is true.
show some king of progress bar to the user, so (s)he knows it's already reloading and does not push reload again.
almost forgot, never update data shown to the user "live", this leads to wonderful situations, when the user clicks on item while it's changing and doing something completely different from what (s)he expected. long updates should keep its data to themselves and quickly swap old data for the new one only when everything is ready.
I've developed an application that takes content from the internet and shows it accordingly on the device's screen . The program works just fine , a little bit slow . It takes about 3-4 seconds to load and display the content . I would like to put all the code that fetches the content and displays it in a background thread and while the program is doing those functions , I would like to display a progress dialog. Could you help me do this ? I would like especially to learn how to put the code in a background thread.
MY CODE
public class Activity1 extends Activity
{
private ProgressDialog progressDialog;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new AsyncTask<Integer, Integer, Boolean>()
{
ProgressDialog progressDialog;
#Override
protected void onPreExecute()
{
/*
* This is executed on UI thread before doInBackground(). It is
* the perfect place to show the progress dialog.
*/
progressDialog = ProgressDialog.show(Activity1.this, "",
"Loading...");
}
#Override
protected Boolean doInBackground(Integer... params)
{
if (params == null)
{
return false;
}
try
{
/*
* This is run on a background thread, so we can sleep here
* or do whatever we want without blocking UI thread. A more
* advanced use would download chunks of fixed size and call
* publishProgress();
*/
Thread.sleep(params[0]);
// HERE I'VE PUT ALL THE FUNCTIONS THAT WORK FOR ME
}
catch (Exception e)
{
Log.e("tag", e.getMessage());
/*
* The task failed
*/
return false;
}
/*
* The task succeeded
*/
return true;
}
#Override
protected void onPostExecute(Boolean result)
{
progressDialog.dismiss();
/*
* Update here your view objects with content from download. It
* is save to dismiss dialogs, update views, etc., since we are
* working on UI thread.
*/
AlertDialog.Builder b = new AlertDialog.Builder(Activity1.this);
b.setTitle(android.R.string.dialog_alert_title);
if (result)
{
b.setMessage("Download succeeded");
}
else
{
b.setMessage("Download failed");
}
b.setPositiveButton(getString(android.R.string.ok),
new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dlg, int arg1)
{
dlg.dismiss();
}
});
b.create().show();
}
}.execute(2000);
new Thread()
{
#Override
public void run()
{
// dismiss the progressdialog
progressDialog.dismiss();
}
}.start();
}
}
The app crashes , NullPointerException among other stuff . Could you help me ? thanks.
You code is fine, except for the last Thread, which, beside being useless, is the reason your app crashes : when the thread is started, the progressDialog is not initialized yet.
Otherwise, this should work like a charm.
Edit
One more thing: giving null as a onClickListener for the positive or negative button simply dismiss the dialog (which is what you do), so
b.setPositiveButton(android.R.string.ok, null);
is equivalent, only shorter.
You do the downloading in the doInBackground() method. Now you need to override onProgressUpdate() method where you'll do .setProgress() to your progressbar. onProgressUpdate() runs on ui-thread. Use .publishProgress() method from where (from background thread i.e doInBackground() method) you'll make a call to onProgressUpdate().
I hope this idea will help you.
http://developer.android.com/reference/android/os/AsyncTask.html#publishProgress(Progress...)