问题
I originally had two ListActivity classes.
I'm experimenting with having 1 Activity and, using a ViewFlipper, flipping between the two lists.
It seems to be working great except for one problem. If I rotate the emulator, I'll randomly get a crash. It doesn't happen every time and I can't figure out a specific workflow to trigger it. This is the error:
01-12 16:28:58.292: WARN/dalvikvm(877): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
01-12 16:28:58.292: ERROR/AndroidRuntime(877): Uncaught handler: thread main exiting due to uncaught exception
01-12 16:28:58.312: ERROR/AndroidRuntime(877): java.lang.IllegalArgumentException: Receiver not registered: android.widget.ViewFlipper$1@43d44458
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.app.ActivityThread$PackageInfo.forgetReceiverDispatcher(ActivityThread.java:667)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.app.ApplicationContext.unregisterReceiver(ApplicationContext.java:747)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:321)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.widget.ViewFlipper.onDetachedFromWindow(ViewFlipper.java:104)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.View.dispatchDetachedFromWindow(View.java:5835)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1076)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1074)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1074)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.ViewRoot.dispatchDetachedFromWindow(ViewRoot.java:1570)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.ViewRoot.doDie(ViewRoot.java:2556)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.ViewRoot.die(ViewRoot.java:2526)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:218)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.view.Window$LocalWindowManager.removeViewImmediate(Window.java:436)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3498)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3599)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.app.ActivityThread.access$2300(ActivityThread.java:119)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1867)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.os.Handler.dispatchMessage(Handler.java:99)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.os.Looper.loop(Looper.java:123)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at android.app.ActivityThread.main(ActivityThread.java:4363)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at java.lang.reflect.Method.invokeNative(Native Method)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at java.lang.reflect.Method.invoke(Method.java:521)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
01-12 16:28:58.312: ERROR/AndroidRuntime(877): at dalvik.system.NativeStart.main(Native Method)
Does anyone have any ideas as to what might be the problem?
Here is a basic skeleton of my Activity:
public class ActivityFlipper extends Activity implements OnItemClickListener, OnClickListener {
ViewFlipper flipper;
// query tokens
final int LIST1_MY_QUERY_TOKEN = 42;
final int LIST2_MY_QUERY_TOKEN = 43;
// vars for list1 screen
private List1Adapter mList1Adapter = null;
private Cursor mList1Cursor = null;
List1QueryHandler mList1QueryHandler;
// vars for list2 screen
private List2Adapter mList2Adapter = null;
private Cursor mList2Cursor = null;
List2QueryHandler mList2QueryHandler;
// ui controls for list1 screen
private ListView mList1;
private Button mAdd;
private TextView mList1Header;
// ui controls for list2 screen
private ListView mList2;
private TextView mList2Header;
private Button mSelector;
// ui controls common
private ProgressDialog mScannerProgress;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.layout_flipper);
// set up flipper screen
flipper = (ViewFlipper) findViewById(R.id.flipper);
// set up list1 screen
mList1 = (ListView)findViewById(R.id.list1_list);
mList1.setOnItemClickListener(this);
mAdd = (Button)findViewById(R.id.list1_add);
mAdd.setOnClickListener(this);
mList1Header = (TextView)findViewById(R.id.list1_header);
mList1Header.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
flipper.setInAnimation(inFromRightAnimation());
flipper.setOutAnimation(outToLeftAnimation());
flipper.showNext();
}
});
setList1Adapter();
mList1QueryHandler = new List1QueryHandler(this);
registerForContextMenu(mList1);
registerForContextMenu(mAdd);
// set up list2 screen
mList2 = (ListView)findViewById(R.id.list2_list);
mList2Header = (TextView)findViewById(R.id.list2_header);
mList2Header.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
flipper.setInAnimation(inFromLeftAnimation());
flipper.setOutAnimation(outToRightAnimation());
flipper.showPrevious();
}
});
mSelector = (Button)findViewById(R.id.list2_changestore);
mSelector.setOnClickListener(this);
setList2Adapter();
mList2QueryHandler = new List2pingQueryHandler(this);
registerForContextMenu(mList2);
}
private void setList1Adapter() {
mList1Adapter = new List1Adapter(ActivityList1.this, null);
mList1.setAdapter(mList1Adapter);
}
private void setList2Adapter() {
mList2Adapter = new List2Adapter(ActivityList1.this, null);
mList2.setAdapter(mList2Adapter);
}
@Override
protected void onPause() {
// remove broadcast listener
unregisterReceiver(onBroadcast);
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
// add broadcast listener
registerReceiver(onBroadcast, new IntentFilter("listquery"));
// get rid of scanner progress if showing
if (mScannerProgress != null && mScannerProgress.isShowing()) {
mScannerProgress.dismiss();
}
// get data to populate lists
doList1Query(false);
doList2Query(false);
}
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
if (v.getId() == mList1.getId()) {
// do stuff
}
else if (v.getId() == mList2.getId()) {
// do stuff
}
}
public void onClick(View v) {
if (v.getId() == R.id.list1_add) {
// do stuff
}
else if (v.getId() == R.id.list2_changestore) {
// do stuff
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// do stuff
}
Cursor doList1Query(boolean sync) {
// Cancel any pending queries
mList1QueryHandler.cancelOperation(LIST1_MY_QUERY_TOKEN);
if (sync) {
try {
return getContentResolver().query(uri,
columns,
where,
null,
order);
}
catch (UnsupportedOperationException ex) {
}
}
else {
setProgressBarIndeterminateVisibility(true);
mList1QueryHandler.startQuery(LIST1_MY_QUERY_TOKEN,
null,
uri,
columns,
where,
null,
order);
}
return null;
}
Cursor doList2Query(boolean sync) {
mList2QueryHandler.cancelOperation(LIST2_MY_QUERY_TOKEN);
if (sync) {
try {
return getContentResolver().query(uri,
getDisplayColumns(),
where,
null,
order);
}
catch (UnsupportedOperationException ex) {
}
}
else {
setProgressBarIndeterminateVisibility(true);
mList2QueryHandler.startQuery(LIST2_MY_QUERY_TOKEN,
null,
uri,
columns,
where,
null,
order);
}
return null;
}
private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context ctxt, Intent i) {
doList1Query(false);
doList2Query(false);
}
};
private Animation inFromRightAnimation() {
Animation inFromRight = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, +1.0f, Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f
);
inFromRight.setDuration(500);
inFromRight.setInterpolator(new AccelerateInterpolator());
return inFromRight;
}
private Animation outToLeftAnimation() {
Animation outtoLeft = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, -1.0f,
Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f
);
outtoLeft.setDuration(500);
outtoLeft.setInterpolator(new AccelerateInterpolator());
return outtoLeft;
}
private Animation inFromLeftAnimation() {
Animation inFromLeft = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, -1.0f, Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f
);
inFromLeft.setDuration(500);
inFromLeft.setInterpolator(new AccelerateInterpolator());
return inFromLeft;
}
private Animation outToRightAnimation() {
Animation outtoRight = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, +1.0f,
Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f
);
outtoRight.setDuration(500);
outtoRight.setInterpolator(new AccelerateInterpolator());
return outtoRight;
}
}
回答1:
Indeed this is a bug in the bowels of Android. You can work around it by using a "SafeViewFlipper" instead:
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ViewFlipper;
public class SafeViewFlipper extends ViewFlipper {
public SafeViewFlipper(Context context) {
super(context);
}
public SafeViewFlipper(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onDetachedFromWindow() {
try {
super.onDetachedFromWindow();
}
catch (IllegalArgumentException e) {
// This happens when you're rotating and opening the keyboard that the same time
// Possibly other rotation related scenarios as well
stopFlipping();
}
}
}
来源:https://stackoverflow.com/questions/4674796/crash-when-rotating-activity-using-viewflipper