问题
I have an Activity in which I have a ProgressBar
,an ImageView
and a TextView
,I update all three from an AsyncTask
.All three get updated when the screen is completely in one orientation when the task is running,but the ImageView
and TextView
are not displayed and the ProgressBar
freezes when the screen orientation changes from one orientation to another.
Adding the attach
and detach
methods to the task and using retainNonConfigurationInstance
to return the task when the Activity
and using getLastNonConfigurationInstance
is destroyed has had no effect.I have also implement three methods for getting the various progress values from the AsyncTask
to no effect.
MyActivity looks like this:
static final String TAG="ImageUpdateActivity";
TextView txt_currentOp;
ImageView img_currentOp;
ImageUpdatingTask task;
CustomProgressBar updatebar;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_imageupdate);
txt_currentOp=(TextView)findViewById(R.id.txt_currentOp);
img_currentOp=(ImageView)findViewById(R.id.img_updateOp);
updatebar=(CustomProgressBar)findViewById(R.id.progressbar_update);
String filename=getIntent().getStringExtra("pathName");
task=(ImageUpdatingTask)getLastNonConfigurationInstance();
if(task!=null)
{
task.attach(this);
if(task.getStatus()==AsyncTask.Status.RUNNING)
{
Log.d(TAG, "The progress description is: "+task.getProgressDesc());
txt_currentOp.setText(task.getProgressDesc());
img_currentOp.setImageBitmap(task.getProgressBitmap());
updatebar.setProgress(task.getProgress());
}
}
else
{
task=new ImageUpdatingTask(this);
task.execute(filename);
}
}
public Object retainNonConfigurationInstance()
{
task.detach();
return task;
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
if(task.getStatus()!=AsyncTask.Status.FINISHED)
{
task.cancel(true);
task=null;
}
Intent i=new Intent(this,ImagePreviewActivity.class);
startActivity(i);
}
return super.onKeyDown(keyCode, event);
}
This is how I update the progress from my doInBackground
method where
int progress=0;
Bitmap progressBitmap=null;
String progressDesc=null;
are global variables.
mOperation=BITMAP_TO_PIX;
progressDesc=getValueFromOperation(mOperation);
Pix pix=convertBitmapToPix(bitmap);
mOperation=CONVERT_TO_8;
progressDesc=getValueFromOperation(mOperation);
Pix pix2=convertOperation(pix);
temp=pix2.copy();
tempImg=convertPixToBitmap(temp);
progressBitmap=tempImg;
temp=null;
progress+=10;//60
publishProgress(tempImg);
And in my publishProgress
I use:
@Override
protected void onProgressUpdate(Bitmap... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
int oldOperation=0,oldProgress=0;
if(mOperation!=oldOperation)
{
String progressText=getValueFromOperation(mOperation);
Log.d(TAG, progressText);
activity.txt_currentOp.setText(progressText);
oldOperation=mOperation;
}
if(oldProgress!=progress)
{
Log.d(TAG,"Update the progress: "+progress);
activity.updatebar.setProgress(progress);
oldProgress=progress;
}
activity.img_currentOp.setImageBitmap(values[0]);
}
And the Activity,is passed to the task using the constructor:
public ImageUpdatingTask(ImageUpdateActivity activity)
{
this.activity=activity;
}
These are the methods that take care of interaction between the AsyncTask
and the Activity
:
public void attach(ImageUpdateActivity activity)
{
this.activity=activity;
}
public void detach()
{
activity=null;
}
public int getProgress()
{
return progress;
}
public Bitmap getProgressBitmap()
{
return progressBitmap;
}
public String getProgressDesc()
{
return progressDesc;
}
回答1:
When orientation changes your activity gets is destroyed and recreated. Fragments are hosted by an activity.
By default, Fragments are destroyed and recreated along with their parent Activitys when a configuration change occurs. Calling Fragments setRetainInstance(true)
allows us to bypass this destroy-and-recreate cycle, signaling the system to retain the current instance of the fragment when the activity is recreated.
public void setRetainInstance (boolean retain)
Added in API level 11
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
You can check this blog for a workaround suggested . Uses interface as callback to the activity.
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
and the source code for the same is available at
https://github.com/alexjlockwood/worker-fragments
Quoting from the blog
Flow of Events
When the MainActivity starts up for the first time, it instantiates and adds the TaskFragment to the Activity's state. The TaskFragment creates and executes an AsyncTask and proxies progress updates and results back to the MainActivity via the TaskCallbacks interface. When a configuration change occurs, the MainActivity goes through its normal lifecycle events, and once created the new Activity instance is passed to the onAttach(Activity) method, thus ensuring that the TaskFragment will always hold a reference to the currently displayed Activity instance even after the configuration change. The resulting design is both simple and reliable; the application framework will handle re-assigning Activity instances as they are torn down and recreated, and the TaskFragment and its AsyncTask never need to worry about the unpredictable occurrence of a configuration change.
来源:https://stackoverflow.com/questions/19949013/the-asynctask-fails-when-i-rotate-the-device-to-landscape