AsyncTask app still running slowly
I've an app that places an effect on a bitmap. When the user slides the slidebar the level of distortion of the efect increases/decreases and the bitmap is then redrawn. My custom view class is called TouchView and this class gets the bitmap and calls a method from the Filters class that processes the image. I've had help implementing asyncTask to speed the processing up but to no avail. Is AsyncTask implemented properly. Thanks Matt.
public class TouchView extends View{
private File tempFile;
private byte[] imageArray;
private Bitmap bgr;
private Bitmap bm;
private Bitmap bgr2 = null;;
private Paint pTouch;
private int centreX = 1;
private int centreY = 1;
private int radius = 50;
private int Progress = 1;
private static final String TAG = "*********TouchView";
private Filters f = null;
private boolean AsyncRunning = false;
public TouchView(Context context) {
super(context);
// TouchView(context, null);
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
//.....code that gets the bitmap from camera and sdcard
bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length, bfo);
bgr = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
bgr = bm.copy(bm.getConfig(), true);
bgr2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
// instantiate the image processing class
f = new Filters();
}// end of touchView constructor
public void findCirclePixels(){
//set level of distortion
float prog = (float)Progress/150000;
// call processing method on bitmap
bgr2 = f.barrel(bgr,prog);
}// end of changePixel()
}//end of onTouchEvent
public void initSlider(final HorizontalSlider slider)
{
Log.e(TAG, "******setting up slider*********** ");
slider.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
@Override
public void onProgressChanged(View v, int progress) {
// TODO Auto-generated method stub
setProgress(progress);
Log.e(TAG, "***********progress = "+Progress);
}
};
private class MyTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
}
@Override
protected Void doInBackground(Void... params) {
TouchView.this.findCirclePixels();
return null;
}
protected void onPostExecute(Void result) {
TouchView.this.invalidate();
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawBitmap(bgr2, 0, 0, null);
}//end of onDraw
protected void setProgress(int progress2) {
this.Progress = progress2;
new MyTask().execute();
}
}
.
[update] This is what i've got now. The app is running slower now unfortunately. Have you any further ideas that i could try?
public class TouchView extends View{
private File tempFile;
private byte[] imageArray;
private Bitmap bgr;
private Bitmap bm;
private Bitmap bgr2 = null;;
private Paint pTouch;
private int centreX = 1;
private int centreY = 1;
private int radius = 50;
private int Progress = 1;
private static final String TAG = "*********TouchView";
private Filters f = null;
private boolean AsyncRunning = false;
private MyTask mt = null;
public TouchView(Context context) {
super(context);
// TouchView(context, null);
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
tempFile = new File(Environment.getExternalStorageDirectory().
getAbsolutePath() + "/"+"image.jpg");
imageArray = new byte[(int)tempFile.length()];
try{
InputStream is = new FileInputStream(tempFile);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
int i = 0;
while (dis.available() > 0) {
imageArray[i] = dis.readByte();
i++;
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 1;
bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length, bfo);
bgr = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
bgr = bm.copy(bm.getConfig(), true);
bgr2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
f = new Filters();
}// end of touchView constructor
public void findCirclePixels(){
float prog = (float)Progress/150000;
bgr2 = f.barrel(bgr,prog);
}// end of changePixel()
public void initSlider(final HorizontalSlider slider)
{
Log.e(TAG, "******setting up slider*********** ");
slider.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
@Override
public void onProgressChanged(View v, int progress) {
// TODO Auto-generated method stub
setProgress(progress);
Log.e(TAG, "***********progress = "+Progress);
}
};
private class MyTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
}
@Override
protected Void doInBackground(Void... params) {
TouchView.this.findCirclePixels();
return null;
}
protected void onPostExecute(Void result) {
while(!isCancelled()){
TouchView.this.invalidate();
}
mt.cancel(true);
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawBitmap(bgr2, 0, 0, null);
canvas.drawCircle(centreX, centreY, radius,pTouch);
}//end of onDraw
protected void setProgress(int progress2) {
this.Progress = progress2;
mt = new MyTask();
mt.execute();
AsyncRunning = true;
}
}
.
[update 2]
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ProgressBar;
public class HorizontalSlider extends ProgressBar {
private OnProgressChangeListener listener;
private static int padding = 2;
public interface OnProgressChangeListener {
void onProgressChanged(View v, int progress);
}
/*
public HorizontalSlider(Context context, AttributeSet attrs,
Map inflateParams, int defStyle) {
super(context, attrs, inflateParams, defStyle);
}
public HorizontalSlider(Context context, AttributeSet attrs,
Map inflateParams) {
super(context, attrs, inflateParams, android.R.attr.progressBarStyleHorizontal);
}*/
public HorizontalSlider(Context context) {
super(context);
}
public HorizontalSlider(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public void setOnProgressChangeListener(OnProgressChangeListener l) {
listener = l;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_MOVE) {
float x_mouse = event.getX() - padding;
float width = getWidth() - 2*padding;
int progress = Math.round((float) getMax() * (x_mouse / width));
if (progress < 0)
progress = 0;
this.setProgress(progress);
if (listener != null)
listener.onProgressChanged(this, progress);
}
return true;
}
}
.
[update 3]
Hi, ok i've changed my code to your amendements and changed i couple of lines so the view is updated by adding TouchView.this.invalidate() etc. Regarding the the horizontalSlider i've set it to check for only ACTION_UP when setting the progress. This way the user can move the slidebar but the view is only invalidated when the user releases the slidebar. i kinda hoped that the bitmap would be updated in real-time as the bar is moved, but the image processing class 'Filters' takes about 20 seconds to process a bitmap which is no good. i think i need to work on the latter as i'm sure you should be able to process a bitmap faster than that!:). If this is as fast as AsyncTask can go then i might have to think about drawing the camera bitmap first with no distortion, then create another bitmap overlay where only the circle effect is present. this way the filter cod only has maybe 1/3 of the pixels to loop through? i'll post the code to make sure that it's how you've suggested.
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import com.tecmark.HorizontalSlider.OnProgressChangeListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff.Mode;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class TouchView extends View{
private File tempFile;
private byte[] imageArray;
private Bitmap bgr;
private Bitmap bm;
private Bitmap bgr2 = null;;
private Paint pTouch;
private int centreX = 1;
private int centreY = 1;
private int radius = 50;
private int Progress = 1;
private static final String TAG = "*********TouchView";
private Filters f = null;
private boolean AsyncRunning = false;
private MyTask mt = null;
public TouchView(Context context) {
super(context);
// TouchView(context, null);
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
tempFile = new File(Environment.getExternalStorageDirectory().
getAbsolutePath() + "/"+"image.jpg");
imageArray = new byte[(int)tempFile.length()];
try{
InputStream is = new FileInputStream(tempFile);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
int i = 0;
while (dis.available() > 0) {
imageArray[i] = dis.readByte();
i++;
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 1;
bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length, bfo);
bgr = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
bgr = bm.copy(bm.getConfig(), true);
bgr2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
f = new Filters();
pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);
pTouch.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
pTouch.setColor(Color.TRANSPARENT);
pTouch.setStyle(Paint.Style.STROKE);
}// end of touchView constructor
public void findCirclePixels(){
float prog = (float)Progress/150000;
bgr2 = f.barrel(bgr,prog);
}// end of changePixel()
public void initSlider(final HorizontalSlider slider)
{
// Log.e(TAG, "******setting up slider*********** ");
slider.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() 开发者_Python百科{
@Override
public void onProgressChanged(View v, int progress) {
// TODO Auto-generated method stub
setProgress(progress);
//TouchView.this.Progress = progress;
if (mt != null) {
mt.cancel(true);
}
mt = new MyTask();
mt.execute();
}
};
private class MyTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
// Log.e(TAG, "***********in PREEXECUTE");
}
@Override
protected Void doInBackground(Void... params) {
// Log.e(TAG, "***********in DOINBACKGROUND");
if(!mt.isCancelled()){
// Log.e(TAG, "***********in doInBackgroud about to call fcp()");
// Log.e(TAG, "***********in doinbackground asyncStatus = "+mt.getStatus());
TouchView.this.findCirclePixels();
Log.e(TAG, "***********in doinbackground fcp() called!!!!");
}
return null;
}
protected void onPostExecute(Void result) {
// Log.e(TAG, "***********in POSTEXECUTE");
if(!isCancelled()){
// Log.e(TAG, "***********in postExecute task not canclled and about to invalidate");
// Log.e(TAG, "***********in postexecute asyncStatus = "+mt.getStatus());
TouchView.this.invalidate();
// Log.e(TAG, "***********in postexecute invalidate() called!!!!");
// Log.e(TAG, "***********in postexecute asyncStatus = "+mt.getStatus());
}
}
}// end of mytask
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawBitmap(bgr2, 0, 0, null);
canvas.drawCircle(centreX, centreY, radius,pTouch);
}//end of onDraw
protected void setProgress(int progress2) {
//Log.e(TAG, "***********in SETPROGRESS");
this.Progress = progress2;
//Log.e(TAG, "***********in setprogress progress = "+Progress);
//Log.e(TAG, "***********in setProgress about to create mytask ");
mt = new MyTask();
//Log.e(TAG, "***********in setprogress about to execute mytask");
//Log.e(TAG, "***********in setprogress asyncStatus = "+mt.getStatus());
mt.execute();
// Log.e(TAG, "***********in setprogress asyncStatus = "+mt.getStatus());
// Log.e(TAG, "***********in setprogress mytask executed!!!!! ");
}
}
.
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP
/*|| action == MotionEvent.ACTION_MOVE*/) {
float x_mouse = event.getX() - padding;
float width = getWidth() - 2*padding;
int progress = Math.round((float) getMax() * (x_mouse / width));
if (progress < 0)
progress = 0;
this.setProgress(progress);
if (listener != null)
listener.onProgressChanged(this, progress);
}
return true;
}
}
It appears that for every value change in your slider, you are creating your MyTask
and running it. Instead, one thing that might perform better is only updating your listener when the slider has been released (stopped moving). It looks like HorizontalSlider
is a custom class, so I can't speak for what is happening in there.
If a slider is being moved from left to right, it may fire off update events 100 times during that move. That would be 100 background tasks!
UPDATE
One possibility would be to cancel()
your active MyTask
before you produce another one. If you take that route you need to call isCancelled()
in onPostExecute
and only invalidate your view if the task is not cancelled. However, if the code in findCirclePixels()
is the bottleneck, this will not work. My guess is that cancelling the active task before creating a new one will NOT solve your problem.
Coding this up properly (only creating your MyTask
when necessary) is still going to be the best route. Consider that for every AsyncTask
the OS has to spawn a new background thread, which comes with its own overhead. Don't be fooled by the small amount of code you have put in doInBackground()
.
UPDATE AGAIN
I have cleaned up your code and added comments for suggestion:
public class TouchView extends View{
private File tempFile;
private byte[] imageArray;
private Bitmap bgr;
private Bitmap bm;
private Bitmap bgr2 = null;;
private Paint pTouch;
private int centreX = 1;
private int centreY = 1;
private int radius = 50;
private int Progress = 1;
private static final String TAG = "*********TouchView";
private Filters f = null;
private MyTask mt = null;
public TouchView(Context context) {
super(context);
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
tempFile = new File(Environment.getExternalStorageDirectory().
getAbsolutePath() + "/"+"image.jpg");
imageArray = new byte[(int) tempFile.length()];
try{
InputStream is = new FileInputStream(tempFile);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
int i = 0;
while (dis.available() > 0) {
imageArray[i] = dis.readByte();
i++;
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 1;
bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length, bfo);
bgr = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
bgr = bm.copy(bm.getConfig(), true);
bgr2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
f = new Filters();
}
public void findCirclePixels(){
float prog = (float) Progress / 150000;
bgr2 = f.barrel(bgr, prog);
}
public void initSlider(final HorizontalSlider slider) {
slider.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
@Override
public void onProgressChanged(View v, int progress) {
/*
TODO: If there is a way to see if the slider is still being changed (control
has not been released), then you should return and not continue with the
creation of another task
*/
this.Progress = progress2;
if (mt != null) {
mt.cancel();
}
mt = new MyTask();
mt.execute();
}
};
private class MyTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
}
@Override
protected Void doInBackground(Void... params) {
// This check is not necessary if coded properly
if (!isCancelled()) {
TouchView.this.findCirclePixels();
}
return null;
}
protected void onPostExecute(Void result) {
// This check is not necessary if coded properly
if (!isCancelled()) {
TouchView.this.invalidate();
}
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawBitmap(bgr2, 0, 0, null);
canvas.drawCircle(centreX, centreY, radius, pTouch);
}
}
精彩评论