I'm currently downloading and save some pictures in my Android app.
Here is the class which do the work :
public class BitmapPersist extends AsyncTask<String, Void, Boolean> {
private ArrayList<String> photosNotDownloaded;
private File pointDir;
private KickstartrGeolocPoint point;
private OnBitmapPersistedListener onBitmapPersistedListener;
public BitmapPersist(ArrayList<String> photosNotDownloaded, File pointDir, KickstartrGeolocPoint point, OnBitmapPersistedListener onBitmapPersistedListener) {
this.photosNotDownloaded=photosNotDownloaded;
this.pointDir=pointDir;
this.point=point;
this.onBitmapPersistedListener=onBitmapPersistedListener;
}
#Override
protected Boolean doInBackground(String... params) {
Bitmap bmp;
FileOutputStream out = null;
for(String url : photosNotDownloaded) {
//download the picture synchronously
bmp = ImageLoader.getInstance().loadImageSync(url);
try {
out = new FileOutputStream(pointDir.getPath() + File.separator + FileUtils.getPointPhotoPrefix(point) + FileUtils.getFileNameFromUrl(url));
bmp.compress(Bitmap.CompressFormat.JPEG, 90, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (Throwable ignore) {
}
}
//send notification to the UI in order to scan again the directory and update the carousel
publishProgress();
}
return true;
}
#Override
protected void onPostExecute(Boolean success) {
if (success)
onBitmapPersistedListener.persistedSuccessfully(this.point, this.pointDir);
else
onBitmapPersistedListener.errorInPersistance();
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
onBitmapPersistedListener.onProgress();
}
}
This class take an ArrayList of urls in parameter, and for each photo, it download it and save it.
Each time a photo is persisted, a notification is sent to the view to update a carousel (thanks to publishProgress(); )
The problem is that publishProgress() does not work, and every photos are displayed at the same time when every photos are downloaded.
Here is the call of my asynctask :
private void persistImageforPoint(ArrayList<String> photosNotDownloaded, KickstartrGeolocPoint point, File pointDir) {
// Create a subfolder for each point with its id
if (!pointDir.exists()) {
if (!pointDir.mkdirs()) {
LogWrapper.debug(FileUtils.class, "Failed to create directory");
return;
}
}
//save the file. Asynchronous task --> do not block the UI
new BitmapPersist(photosNotDownloaded, pointDir, point, new OnBitmapPersistedListener() {
#Override
public void persistedSuccessfully(KickstartrGeolocPoint point, File pointDir) {
if(currentPoint!=null) {
File pointDirectory = FileUtils.getPointPhotoDir(getActivity(), currentPoint);
loadCarousel(currentPoint, pointDirectory);
}
}
#Override
public void errorInPersistance() {
LogWrapper.error(getClass(),"Error persisting image");
}
#Override
public void onProgress() {
if(currentPoint!=null) {
final File pointDir = FileUtils.getPointPhotoDir(getActivity(), currentPoint);
loadCarousel(currentPoint, pointDir);
}
}
}).execute();
}
I don't have any errors in my logcat.
Thanks for your help ;)
Related
I am creating an app where user can upload images, I am uploading images one after another using retrofit. Right now I am running a for loop for it, but it is not a good way to do it. I cannot use service because I need progress dialog as well on main screen to let user know upload is happening. Is there a way to maintain some kind of queue to handle this?
You can use the AsyncTask and Retrofit to do it it can be done with the retrofit alone but as I have already done it with AsyncTask have a look.
public class AsyncBulkUpload extends AsyncTask<Boolean, Integer, Boolean> {
private ApiInterface mApiInterface;
private Call<ResponseBody> mResponseBodyCall;
private Response<ResponseBody> mResponseBody;
private RequestBody mRequestBody;
private Service mService;
private Bitmap mItemBitmap;
public int mTotalCount = 0;
public AsyncBulkUpload(Service service) {
mService=service;
mApiInterface = ApiClient.getClient(mService.getApplicationContext()).create(ApiInterface.class);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mTotalCount = sClosetList.size();
//Show you dialog
}
#Override
protected Boolean doInBackground(Boolean... itsRetry) {
/**
*Check if its retry or not
* if yes don't get the placeholder ids.
* or else hit and get place holder ids
*/
//Run your loop here.
return upload();
}
private Boolean upload() {
if (isCancelled())
return false;
if (sClosetList.size() > 0) {
//Item image
try {
mItemBitmap = Glide.with(mService.getApplicationContext())
.load(sClosetList.get(0).mItemImage)
.asBitmap()
.into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get();
} catch (InterruptedException e) {
//Coudnt get the image so cant go further
LogPrint.printError("WHILE GETTING BITMPAP");
return false;
} catch (ExecutionException e) {
//Coudnt get the image so cant go further
LogPrint.printError("WHILE GETTING BITMPAP");
return false;
}
FormBody.Builder bodyBuilder = new FormBody.Builder();
bodyBuilder.add("image", mItemBitmap);
/**
*Label image can or can not be available
*/
mRequestBody = bodyBuilder.build();
mResponseBodyCall = mApiInterface.uploadProducts(mRequestBody);
try {
mResponseBody = mResponseBodyCall.execute();
} catch (IOException e) {
e.printStackTrace();
return false;
}
if (!mResponseBody.isSuccessful()) {
/**
*Some server error try again
*/
return false;
}
}
return true;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//Update your progressbar
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
//Dismiss the dialog
if (aBoolean) {
Toast.makeText(mService, R.string.uploaded_successful, Toast.LENGTH_LONG).show();
}
}
}
When I send a photo to the Microsoft Azure Face API (the api, tutorial), I am receiving
com.microsoft.projectoxford.face.rest.ClientException: Image size is too small.
But when I am debugging the application and when I inspect the following code
faceServiceClient.detect( params[0], false, false, expectedFaceAttributes )
actually it IS working and I CAN get the result, but only the first time. If I press "inspect" one more time I will again receiving the above mentioned error message.
P.S. I tried using different images and the behaviors is the same. I would appreciate any help.
The application:
public class MainActivity extends AppCompatActivity {
Bitmap bmpImage;
TextView txtResult;
ByteArrayInputStream bs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtResult = (TextView) findViewById(R.id.txtResult);
Button btnMicrosoft = (Button) findViewById(R.id.btnMicrosoft);
btnMicrosoft.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getFaces();
makeMicrosoftCall();
}
});
}
private void getFaces() {
bmpImage = BitmapFactory.decodeResource(this.getResources(), R.drawable.face_1);
}
private void makeMicrosoftCall() {
imgEncoding();
GetEmotionCall emotionCall = new GetEmotionCall();
emotionCall.execute(bs);
}
public void imgEncoding()
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bmpImage.compress(Bitmap.CompressFormat.JPEG, 100, baos);
bs = new ByteArrayInputStream(baos.toByteArray());
}
// asynchronous class which makes the api call in the background
private class GetEmotionCall extends AsyncTask<InputStream, String, Face[]> {
GetEmotionCall() {
}
#Override
protected void onPreExecute() {
super.onPreExecute();
txtResult.setText("Getting results...");
}
// this function is called when the api call is made
#Override
protected Face[] doInBackground(InputStream... params) {
FaceServiceClient faceServiceClient = new FaceServiceRestClient("https://westcentralus.api.cognitive.microsoft.com/face/v1.0", "*******************");
// the only attribute wanted it emotional state
FaceServiceClient.FaceAttributeType[] expectedFaceAttributes = new FaceServiceClient.FaceAttributeType[]{FaceServiceClient.FaceAttributeType.Emotion};
try {
//THE PROBLEMATIC AREA
return faceServiceClient.detect( params[0], false, false, expectedFaceAttributes );
} catch (ClientException e) {
Log.e("ClientException", e.toString());
return null;
} catch (IOException e) {
Log.e("IOException", e.toString());
e.printStackTrace();
return null;
}
}
}
}
I'm having a little trouble, im executing a method in my doinBackground task, so I'm getting a crash because im accesing to another class without finishing this method first, so i want to add a return or something to let the method know when it needs to launch the other activity. I have searched and I can't return a boolean, true or false into Firebase asynctask method. This is the method I use to download a file and replace it into internal memory, but when im doing this , the other activity I need to launch after this launches and i get a crash, so i need to first execute this download task and then if something is true launch my other activity
This is where I want to put a boolean or something that tells me that the download finished.
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
Log.e("TamañoArchivo",""+taskSnapshot.getTotalByteCount());
Log.e("NombreArchivo",""+xFile);
try {
FileOutputStream fos = context.openFileOutput("pictos.txt", Context.MODE_PRIVATE);
fos.write(getStringFromFile(xFile.getAbsolutePath()).getBytes());
Log.e("xFILEDESCARGARPAIS",""+getStringFromFile(xFile.getAbsolutePath()));
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
The method is not an asyncTask, is an async but from Firebase, this is the method:
public boolean DescargarArchivosPais(String locale){
File rootPath = new File(context.getCacheDir(),"MY_FILES");
if(!rootPath.exists()) {
rootPath.mkdirs();//si no existe el directorio lo creamos
}
StorageReference mStorageRef2 = FirebaseStorage.getInstance().getReference().child("Files/y/" + "y_" + locale + "." + "txt");
StorageReference mStorageRef1 = FirebaseStorage.getInstance().getReference().child("Files/x/" + "x_" + locale + "." + "txt");
Log.e("REFERENCIAx",""+ mStorageRef1);
Log.e("REFERENCIAy",""+ mStorageRef2);
final File xFile = new File(rootPath, "x.txt");
final File yFile = new File(rootPath, "y.txt");
mStorageRef1.getFile(xFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
Log.e("TamañoArchivo",""+taskSnapshot.getTotalByteCount());
Log.e("NombreArchivo",""+xFile);
try {
FileOutputStream fos = context.openFileOutput("x.txt", Context.MODE_PRIVATE);
fos.write(getStringFromFile(xFile.getAbsolutePath()).getBytes());
Log.e("LOG",""+getStringFromFile(xFile.getAbsolutePath()));
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
mStorageRef2.getFile(yFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
Log.e("TamañoArchivo",""+taskSnapshot.getTotalByteCount());
Log.e("NombreArchivo",""+yFile);
try {
FileOutputStream fos = context.openFileOutput("y.txt", Context.MODE_PRIVATE);
fos.write(getStringFromFile(gruposFile.getAbsolutePath()).getBytes());
Log.e("LOG2",""+getStringFromFile(gruposFile.getAbsolutePath()));
fos.close();
fSuccess = true;
} catch (Exception e) {
e.printStackTrace();
Log.e("printStackTrace",""+e.toString());
fSuccess = false;
}
fSuccess = true;
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
fSuccess=false;
Log.e("printStackTrace",""+e.toString());
}
});
return fSuccess;
}
Updated with following comment (replace Activity reference and introduce interface instead):
You can definitively do things like that with AsyncTask. Please have a look at the following minimalist code:
public class MyTask extends AsyncTask<Void, Void, Boolean> {
private IMyCallbackContext context;
public MyTask(IMyCallbackContext context) {
this.context = context;
}
#Override
protected void onPreExecute() {
// Here you are still on the MainThread
// Do Stuff
}
#Override
protected Boolean doInBackground(Void... params) {
// Here you are not on the MainThread
// Do Stuff
return isSuccess;
}
#Override
protected void onPostExecute(Boolean isSuccess) {
// Here you are again on the MainThread
if (isSuccess) {
context.onTaskSuccessDoStuff();
} else {
context.onTaskFailureDoStuff();
}
}
}
public interface IMyCallbackContext {
void onTaskSuccessDoStuff();
void onTaskFailureDoStuff();
}
public class MyActivity extends Activity implements IMyCallbackContext {
private void launchTask() {
MyTask myTask = new MyTask(this);
myTask.execute();
}
public void onTaskSuccessDoStuff() {
// Do stuff after the task has completed
}
public void onTaskFailureDoStuff() {
// Do stuff after the task has failed
}
}
Edit: sorry I thought you had an AsyncTask
onSuccess() method has an asynchronous behaviour. This means that in order to use the data that you are getting from Firebase Storage, you need to wait for it. So to do that, there is no need to use an AsyncTask, you can simply create your own custom callback.
To make this happen, please see the last part for my answer from this post. As Mohammed Atif mentioned in his comment, never use the Activity reference directly because it will cause memory leaks. So the way I mentioned above, is the simplest and safest way in which you can achieve this.
I have this code but the progress bar does not update the uploaded bytes/lenght of the file.
The progress dialog is displayed correctly but the progress stays in 0, then it simply disappears, the file was uploaded correctly but no progress is updated.
private class UploadFile extends AsyncTask<String, Integer, Boolean> {
#Override
protected Boolean doInBackground(String... params) {
FTPClient client = null;
String filePath = params[0];
try {
// Get the FTP Connection from the Utility class
client = FTPUtility.connect(ipAddress, userName, password);
//if directory is not there, create it.
try {
client.changeDirectory(params[1]);
} catch(Exception e) {
client.createDirectory(params[1]);
client.changeDirectory(params[1]);
}
if (client != null) {
try {
// Define the File with complete path to be uploaded
File fileUpload = new File(filePath);
fileSize= fileUpload.length();
Log.d("FTPSync", "File Size: "+fileSize);
Log.d("FTPSync", "Uploading the " + filePath
+ " to Remote Machine");
// Upload the file
client.upload(fileUpload, new FTPDataTransferListener() {
#Override
public void started() {
// Transfer started
Log.d("FTP","TRANSFER-STATUS: File transfer started...");
}
#Override
public void transferred(int length) {
int progress = (length*100)/((int)fileSize);
publishProgress(progress);
Log.d("FTP","Progress: "+progress);
}
#Override
public void completed() {
Log.d("FTP","TRANSFER-STATUS: File transfer completed...");
}
#Override
public void aborted() {
// Transfer aborted
Log.d("FTP","TRANSFER-STATUS: File transfer aborted...");
}
#Override
public void failed() {
// Transfer failed
Log.d("FTP","TRANSFER-STATUS: File transfer failed...");
}
});
Log.d("FTPSync", "Successfully Uploaded the "
+ filePath + " File to Remote Machine");
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
finally {
if (client != null) {
try {
client.disconnect(true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
#Override
protected void onPostExecute(Boolean result) {
pDialog.dismiss();
Toast.makeText(context, "Operation Completed", Toast.LENGTH_SHORT).show();
}
#Override
protected void onPreExecute() {
pDialog = new ProgressDialog(context);
pDialog.setMessage(message);
pDialog.setIndeterminate(true);
pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected void onProgressUpdate(Integer... values) {
pDialog.setProgress(values[0]);
}
}
private static class FTPUtility {
public static FTPClient connect(String ipAddress, String userName,
String password) {
FTPClient client = new FTPClient();
Log.d("FTP","Connecting to " + ipAddress);
try {
client.setType(FTPClient.TYPE_BINARY);
client.connect(ipAddress);
client.login(userName, password);
return client;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
The debug shows only one entry about Progress:100
How can I make the progress bar update.
NOTE: I've tried with small and large files, so it seems to be the same issue in both file sizes.
All you need is to sum length and put it in ProgressBar. Something like this:
public class MyTransferListener implements FTPDataTransferListener {
JProgressBar jp;
int transfBytes=0;
public MyTransferListener(JProgressBar jp){
this.jp=jp;
}
public void started() {
// Transfer started
jp.setValue(0);
}
public void transferred(int length) {
// Yet other length bytes has been transferred since the last time this
// method was called
transfBytes+=length;
jp.setValue(transfBytes);
}
public void completed() {
// Transfer completed
jp.setValue(jp.getMaximum());
}
public void aborted() {
// Transfer aborted
}
public void failed() {
// Transfer failed
}
}
client.upload(fileUpload, new FTPDataTransferListener() {
int progress = 0;
#Override
public void started() {
// Transfer started
Log.d("FTP","TRANSFER-STATUS: File transfer
started...");
}
#Override
public void transferred(int length) {
progress = progress + length;
publishProgress((int) (((float)progress *
(float)100) / (float)fileSize));
Log.d("FTP","Progress: "+progress);
}
#Override
public void completed() {
Log.d("FTP","TRANSFER-STATUS: File transfer
completed...");
}
#Override
public void aborted() {
// Transfer aborted
Log.d("FTP","TRANSFER-STATUS: File transfer
aborted...");
}
#Override
public void failed() {
// Transfer failed
Log.d("FTP","TRANSFER-STATUS: File transfer
failed...");
}
});
It worked for me:
#Override
public void transferred(int totalTranfered) {
// Sets the progress indicator to a max value, the
// current completion percentage, and "determinate"
// state
percent = percent + totalTranfered;
int percentage = (int) ((percent * 100)/ fileSize);
Log.e("Percentage Transfered", " " + percentage);
mBuilder.setProgress(100, percentage, false);
// Displays the progress bar for the first time.
mNotifyManager.notify(id, mBuilder.build());
// publishProgress(percent);
}
I know that the first you gonna this is... why the heck on the world you then use AsyncTask.
So here is my problem i am working on some Android app (API 7 for android 2.1 or higher) , and i am testing on emulator and everything was fine, so then i tested on HTC Sensation and it says NetworkOnMainThreadExeption!
I was downloading some pictures and then draw on the map.
So to solve this problem every (internet connection) in this case downloading the pictures i must put on AsyncTask to work.
So i need a method how to know when all pictures are done so i can start drawing..
I was trying so much and no result i have no idea. I got one solution with handler but if run on slower net i get nullpointer(because the pictures are not downloaded).
So please help me.
EDIT:
here is the idea:
Bitmap bubbleIcon ;
onCreate(){
...
// i am making call for Async
new ImgDown().execute(url);
//and then i calling functions and classes to draw with that picture bubbleIcon !
DrawOnMap(bubbleIcon);
}
//THIS IS ASYNC AND FOR EX. SUPPOSE I NEED TO DOWNLOAD THE PIC FIRST
class ImgDown extends AsyncTask<String, Void, Bitmap> {
private String url;
public ImgDown() {
}
#Override
protected Bitmap doInBackground(String... params) {
url = params[0];
try {
return getBitmapFromURL(url);
} catch (Exception err) {
}
return null;
}
#Override
protected void onPostExecute(Bitmap result) {
bubbleIcon = result;
bubbleIcon = Bitmap
.createScaledBitmap(bubbleIcon, 70, 70, true);
}
public Bitmap getBitmapFromURL(String src) {
try {
Log.e("src", src);
URL url = new URL(src);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
// /tuka decode na slika vo pomalecuk kvalitet!
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;
Bitmap myBitmap = BitmapFactory
.decodeStream(new FlushedInputStream(input));
Log.e("Bitmap", "returned");
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
Log.e("getBitmapFromURL", e.getMessage());
return null;
}
}
class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int byteValue = read();
if (byteValue < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
}
i hope now is more clear.
class OpenWorkTask extends AsyncTask {
#Override
protected Boolean doInBackground(String... params) {
// do something
return true;
}
#Override
protected void onPostExecute(Boolean result) {
// The results of the above method
// Processing the results here
myHandler.sendEmptyMessage(0);
}
}
Handler myHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
// calling to this function from other pleaces
// The notice call method of doing things
break;
default:
break;
}
}
};
You can write your own Delegate to delegate info about finishing the task, using OOP principles:
task_delegate.java
public interface TaskDelegate {
void TaskCompletionResult(String result);
}
main_activity.java
public class MainActivity extends Activity implements TaskDelegate {
//call this method when you need
private void startAsynctask() {
myAsyncTask = new MyAsyncTask(this);
myAsyncTask.execute();
}
//your code
#Override
public void TaskCompletionResult(String result) {
GetSomethingByResult(result);
}
}
my_asynctask.java
public class MyAsyncTask extends AsyncTask<Void, Integer, String> {
private TaskDelegate delegate;
protected MyAsyncTask(TaskDelegate delegate) {
this.delegate = delegate;
}
//your code
#Override
protected void onPostExecute(String result) {
delegate.TaskCompletionResult(result);
}
}
class openWorkTask extends AsyncTask<String, String, Boolean> {
#Override
protected Boolean doInBackground(String... params) {
//do something
return true;
}
#Override
protected void onPostExecute(Boolean result) {
// The results of the above method
// Processing the results here
}
}
I would use a Progress Dialog if I were you. This way users can see that something is happening while the ASyncTask downloads the picture. On PostExecute, call a method from your main code that checks if the pictures are null. Remember you cannot update the UI in the doInBackground method so do any UI work in either onPreExecute or onPostExecute
private class DownloadPictures extends AsyncTask<String, Void, String>
{
ProgressDialog progressDialog;
#Override
protected String doInBackground(String... params)
{
//Download your pictures
return null;
}
#Override
protected void onPostExecute(String result)
{
progressDialog.cancel();
//Call your method that checks if the pictures were downloaded
}
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(
YourActivity.this);
progressDialog.setMessage("Downloading...");
progressDialog.setCancelable(false);
progressDialog.show();
}
#Override
protected void onProgressUpdate(Void... values) {
// Do nothing
}
}