How to use AsyncTask to update a global variable - java

What I want to do: I am putting rows into a ListView using a custom "Location" adapter. I am trying to add a Bitmap into a row of the ListView. This Bitmap is coming from a URL. So, I have a global variable public static Bitmap bitmap and want to update this variable using an AsyncTask. Here is my code:
try {
String s = "";
JSONArray jArray = new JSONArray(result);
for (int i = 0; i < jArray.length(); i++) {
final JSONObject json = jArray.getJSONObject(i);
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
//here I am calling my new task and giving it the ID to find the image
BitmapWorkerTask myTask = new BitmapWorkerTask(json.getInt("ID"));
myTask.execute();
adapter.add(new Location(bitmap, json
.getString("PlaceTitle"), json
.getString("PlaceDetails"), json
.getString("PlaceDistance"), json
.getString("PlaceUpdatedTime")));
bitmap = null;
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error Parsing Data " + e.toString());
}
and here is my AsyncTask
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private int photoID = 0;
public BitmapWorkerTask(int photoID) {
// Use a WeakReference to ensure the ImageView can be garbage collected
this.photoID = photoID;
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
String initialURL = "http://afs.spotcontent.com/img/Places/Icons/";
final String updatedURL = initialURL + photoID + ".jpg";
Bitmap bitmap2 = null;
try {
bitmap2 = BitmapFactory.decodeStream((InputStream) new URL(
updatedURL).getContent());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap2;
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap2) {
bitmap = bitmap2;
}
}
So, for each iteration, I am feeding the AsyncTask an ID which it uses to find the image, and then (I was hoping) should update the global Bitmap which is passed into the adapter. When I run my app, the every listing's picture is empty. Any ideas on what I'm doing wrong?

Consider the following possible order your commands get executed (remembering that tasks run in the background, so the order is non-deterministic):
myTask.execute()
BitmapWorkerTask.doInBackground()
adapter.Add(new Location(bitmap, .......
BitmapWorkerTask.onPostExecute()
When you create the Location() object in step 3, the 'bitmap' object being passed is the global object pointer. Its value is NOT valid yet, because onPostExecute() wasn't called yet. So the Location object is created with a non-bitmap object. On step 4, when the bitmap is finally retrieved, the value of the global object pointer is changed (correctly), but that doesn't affect the (empty) bitmap object already passed to Location on step 2... Which is why you don't see the bitmap on your view.
What you could do is pass an additional parameter to the BitmapWorkerTask constructor: You could pass the Location object (or the underlying bitmap). From onPostExecute() you could then update that Location/bitmap object with the retrieved bitmap. You don't need a global variable here.

Related

Issues with Microsoft API

So Im having trouble using Microsoft's Emotion API for Android. I have no issues with regards to running the Face API; Im able to get the face rectangles but I am not able to get it working on the emotion api. I am taking images using the builtin Android camera itself. Here is the code I am using:
private void detectAndFrame(final Bitmap imageBitmap)
{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
ByteArrayInputStream inputStream =
new ByteArrayInputStream(outputStream.toByteArray());
AsyncTask<InputStream, String, List<RecognizeResult>> detectTask =
new AsyncTask<InputStream, String, List<RecognizeResult>>() {
#Override
protected List<RecognizeResult> doInBackground(InputStream... params) {
try {
Log.e("i","Detecting...");
faces = faceServiceClient.detect(
params[0],
true, // returnFaceId
false, // returnFaceLandmarks
null // returnFaceAttributes: a string like "age, gender"
);
if (faces == null)
{
Log.e("i","Detection Finished. Nothing detected");
return null;
}
Log.e("i",
String.format("Detection Finished. %d face(s) detected",
faces.length));
ImageView imageView = (ImageView)findViewById(R.id.imageView);
InputStream stream = params[0];
com.microsoft.projectoxford.emotion.contract.FaceRectangle[] rects = new com.microsoft.projectoxford.emotion.contract.FaceRectangle[faces.length];
for (int i = 0; i < faces.length; i++) {
com.microsoft.projectoxford.face.contract.FaceRectangle rect = faces[i].faceRectangle;
rects[i] = new com.microsoft.projectoxford.emotion.contract.FaceRectangle(rect.left, rect.top, rect.width, rect.height);
}
List<RecognizeResult> result;
result = client.recognizeImage(stream, rects);
return result;
} catch (Exception e) {
Log.e("e", e.getMessage());
Log.e("e", "Detection failed");
return null;
}
}
#Override
protected void onPreExecute() {
//TODO: show progress dialog
}
#Override
protected void onProgressUpdate(String... progress) {
//TODO: update progress
}
#Override
protected void onPostExecute(List<RecognizeResult> result) {
ImageView imageView = (ImageView)findViewById(R.id.imageView);
imageView.setImageBitmap(drawFaceRectanglesOnBitmap(imageBitmap, faces));
MediaStore.Images.Media.insertImage(getContentResolver(), imageBitmap, "AnImage" ,"Another image");
if (result == null) return;
for (RecognizeResult res: result) {
Scores scores = res.scores;
Log.e("Anger: ", ((Double)scores.anger).toString());
Log.e("Neutral: ", ((Double)scores.neutral).toString());
Log.e("Happy: ", ((Double)scores.happiness).toString());
}
}
};
detectTask.execute(inputStream);
}
I keep getting the error Post Request 400, indicating some sort of issue with the JSON or the face rectangles. But I'm not sure where to start debugging this issue.
You're using the stream twice, so the second time around you're already at the end of the stream. So either you can reset the stream, or, simply call the emotion API without rectangles (ie skip the call to the face API.) The emotion API will determine the face rectangles for you.

Cannot Retrieve Query from Current User - Parse (Android)

I'm trying to retrieve an image from the current user to display as a profile picture.
I can quite easily get it to work by inserting a specific object ID after query.getInBackground() What I want to do is to pull the "profilePicture" of the user currently logged in, hence current User, and not just one specific user. How can I fix this?
progressDialog = ProgressDialog.show(GalleryActivity.this, "",
"Downloading Image...", true);
// Locate the class table named "ImageUpload" in Parse.com
ParseQuery<ParseUser> query = ParseUser.getQuery();
// Locate the objectId from the class
ParseUser currentUser = ParseUser.getCurrentUser();
query.getInBackground(String.valueOf(ParseUser.getCurrentUser()),
new GetCallback<ParseUser>() {
public void done(ParseUser object,
ParseException e) {
// TODO Auto-generated method stub
// Locate the column named "ImageName" and set
// the string
ParseFile fileObject = (ParseFile) object
.get("profilePicture");
fileObject
.getDataInBackground(new GetDataCallback() {
public void done(byte[] data,
ParseException e) {
if (e == null) {
Log.d("test",
"We've got data in data.");
// Decode the Byte[] into
// Bitmap
Bitmap bmp = BitmapFactory
.decodeByteArray(
data, 0,
data.length);
// Get the ImageView from
// main.xml
ImageView image = (ImageView) findViewById(R.id.galleryProfilePic);
// Set the Bitmap into the
// ImageView
Bitmap resized = Bitmap.createScaledBitmap(bmp, 300, 300, true);
Bitmap conv_bm = getRoundedRectBitmap(resized, 300);
image.setImageBitmap(conv_bm);
// Close progress dialog
progressDialog.dismiss();
} else {
Log.d("test",
"There was a problem downloading the data.");
ImageView image = (ImageView) findViewById(R.id.galleryProfilePic);
image.setImageResource(R.drawable.ic_default_profile_picture);
}
}
});
}
});
I actually got this working by passing the object ID of the current user as a string into the query to replace the static object ID:
String currentUser = ParseUser.getCurrentUser().getObjectId();
query.getInBackground(currentUser,
new GetCallback<ParseUser>() {

InputStream freezes UI on Android

I am downloading images in background for a list. I have noticed that even with AsycTask the scrolling is not smooth. After some hours of debugging, I have came to conclusion that the InputStream is the guilty one.
doInBackground should be working on a non-ui thread, then why does it impact the UI?
If I comment out the InputStream code, the scrolling is smooth.
I have 3 images in a row, so usually 3 images are loaded at the same time.
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(String... params) {
InputStream is = null;
try {
is = (InputStream) new URL(params[0]).getContent();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
// REMOVED ON PURPOSE TO CONFIRM THAT INPUTSTREAM IS GUILTY
}
}

ImageView unable to display images one by one

What I'm trying to do:
I have several JPG files saved in a subfolder under the "assets" folder. My app should go into this subfolder, load the first file, display it for 30 seconds, then load the second file, display it for 30 seconds, and so on, ..., until the last file. This is done in a loop.
What problem I'm having:
The app goes through all the iterations of the loop. It loads each file correctly, reports the width and height of each image correctly. However, the screen is black with no images being displayed, until after the whole loop finishes. Then the last image is displayed.
Here is my layout.xml file (I'd like to handle images of different sizes, but will worry about it later):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mylayout_id"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/image_display"
android:layout_width="400dp"
android:layout_height="400dp" >
</ImageView>
<Button
android:id="#+id/button_start"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="pressedStart"
android:text="#string/btn_start" />
</LinearLayout>
Here is my code:
public class MyImageActivity extends Activity {
private final String TAG = "MyImageActivity";
private ImageView mIV;
private Bitmap mFaceBitmap;
private AssetManager mAssetMan = null;
private String[] mImgFileNames = null;
private int mBitmapWidth;
private int mBitmapHeight;
#Override
public void onCreate(Bundle savedInstanceState) {
Log.e(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
mIV = (ImageView) findViewById(R.id.image_display);
}
#Override
protected void onResume() {
Log.e(TAG, "onResume");
super.onResume();
mAssetMan = getAssets();
}
public void pressedStart(View view) {
Log.e(TAG, "pressedStart");
try {
mImgFileNames = mAssetMan.list("my_subfolder");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Log.d(TAG, "mImgFileNames = " + mImgFileNames);
Log.d(TAG, "number of image files = " + mImgFileNames.length);
for (int i = 0; i < mImgFileNames.length; i++) {
Log.d(TAG, "image file name = " + mImgFileNames[i]);
mIV.setImageDrawable(null);
mIV.invalidate();
InputStream istr = null;
try {
istr = mAssetMan.open("my_subfolder" + mImgFileNames[i]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// load the photo
Bitmap b = BitmapFactory.decodeStream(istr);
mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
b.recycle();
mBitmapWidth = mFaceBitmap.getWidth();
mBitmapHeight = mFaceBitmap.getHeight();
Log.d(TAG, "mBitmapWidth = " + mBitmapWidth);
Log.d(TAG, "mBitmapHeight = " + mBitmapHeight);
Drawable drawable = new BitmapDrawable(getResources(), mFaceBitmap);
mIV.setImageDrawable(drawable);
mIV.invalidate();
SystemClock.sleep(5000);
}
}
}
I found some other discussions about similar problems here and here. I tried a few suggested solutions, e.g. adding setImageDrawable(null) to force ImageView to update; that did not work for me.
Anything wrong with my approach? I really appreciate your helps and suggestions.
Thanks in advance & Have a nice weekend.
This is happening because you are placing the new image within the holder and calling invalidate to make it display but then you turn around and put the UI thread to sleep for 5 seconds so I cannot update it. By the time the system goes to update it, it is already moving on to the next image. I would use a AsyncTask to load the next Image in the background and then put a timer of 5 seconds in the the background thread before it updates the UI with the new data.
EDIT
Here is the reworked version of your code that is operational if this is your exact code. If not you should be fine. (if the formatting is bad, it is because my work uses IE8 still and it doesn't let me see the formatting. I will fix when I get home.)
public class MyImageActivity extends Activity {
private final String TAG = "MyImageActivity";
private ImageView mIV;
#Override
public void onCreate(Bundle savedInstanceState) {
Log.e(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
mIV = (ImageView) findViewById(R.id.image_display);
}
#Override
protected void onResume() {
Log.e(TAG, "onResume");
super.onResume();
}
public void pressedStart(View view) {
new SlideShowTask().execute();
}
private class SlideShowTask extends AsyncTask<Void, Drawable, Void>{
private Bitmap mFaceBitmap;
private AssetManager mAssetMan = null;
private String[] mImgFileNames = null;
private int mBitmapWidth;
private int mBitmapHeight;
#Override
Protected Void doInBackground(Void... params){
mAssetMan = getAssets();
Log.e(TAG, "pressedStart");
try {
mImgFileNames = mAssetMan.list("my_subfolder");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Log.d(TAG, "mImgFileNames = " + mImgFileNames);
Log.d(TAG, "number of image files = " + mImgFileNames.length);
for (int i = 0; i < mImgFileNames.length; i++) {
Log.d(TAG, "image file name = " + mImgFileNames[i]);
InputStream istr = null;
try {
istr = mAssetMan.open("my_subfolder/" + mImgFileNames[i]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// load the photo
Bitmap b = BitmapFactory.decodeStream(istr);
mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
b.recycle();
mBitmapWidth = mFaceBitmap.getWidth();
mBitmapHeight = mFaceBitmap.getHeight();
Log.d(TAG, "mBitmapWidth = " + mBitmapWidth);
Log.d(TAG, "mBitmapHeight = " + mBitmapHeight);
Drawable drawable = new BitmapDrawable(getResources(), mFaceBitmap);
publishProgress(drawable);
SystemClock.sleep(5000);
}
}
#Override
Protected Void onProgressUpdate(Drawable... values){
super.onProgressUpdate(values);
mIV.setImageDrawable(values[0]);
mIV.invalidate();
}
}
}
This happens because you put the main Ui Thread to sleep. And you should never do that. As it means that the whole application will hang for 5seconds which is wrong.
You should create a new thread and update the UI from there like this code
which is the easiest.
runOnUiThread(new Runnable(){
#Override
public void run(){
<--Your Code Here -->
<--sleep for 5000ms-->
}
});
or use a Handler or an ASynkTask.
There are many good tutorials for each of them here.
Try using a new thread or Runnable and add your code to the run function and than make that thread sleep.
Since you are updating your UI you would also need a handler. check the link below
www.vogella.com/articles/AndroidBackgroundProcessing/article.html

Android : Updating UI components from async class

I have UI component where I have table which consist of ImageViews, Now I want to set the image from URL.
Now when I try to call asynchronously the other class where the image is set to UI it gives me error that UI can be updated only by UI thread. I want to load the UI and load images when they are available.
Here is my code :
Calling function...
new AsyncImageLoader(context,imgviewArray).execute();
// Called class..
#Override
protected Void doInBackground(Void... urls) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
int i = 0;
try{
db=new MyDB(mainact_instance);
System.out.println("Inside doInBackground");
if(isNetworkAvailable())
{
System.out.println("Network connected");
System.out.println("Fetching from network-Images");
Movie[] movies = db.selectRecords();
for(Movie mv : movies)
{
try {
URL url = new URL(IMAGE_URL+mv.getId()+".jpg");
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
images[i].setImageBitmap(bmp);
i++;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
else{
System.out.println("Network not connected..fetching max id row");
}
}catch(Exception ex){ex.printStackTrace();
}
return null;
//db.close();
}
Please tell me how I can I achieve this?
move your UI thread related code to onPostExecute || onProgressUpdate
for example:
protected void onPostExecute(Long result) {
// after finishing fetching your image from URL, update the ImageView here
images[i].setImageBitmap(bmp);
}
I can see you are downloading multiple images inside the AsyncTask then you can use PublishProgress you need to update your AsyncTask definition to allow passing Bitmaps to publishProgress method as follow:
AsyncTask<whatever..., Bitmaps, whatever...>()
Then
publishProgress(yourBitmapObject);
protected void onProgressUpdate(Bitmap[] values) {
images[i].setImageBitmap(values[0]);
};

Categories