我不知道为什么,但似乎使用较新版本的 android 我从来没有得到有用的堆栈跟踪。他们似乎没有回到足够远的地方让我找到导致崩溃的代码行。例子:
08-17 09:33:38.449: E/AndroidRuntime(14389): FATAL EXCEPTION: main
08-17 09:33:38.449: E/AndroidRuntime(14389): java.lang.RuntimeException: Unable to resume activity {com.evidence/com.evidence.activity.EvidenceList}: android.database.StaleDataException: Attempted to access a cursor after it has been closed.
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2567)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2595)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1183)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.os.Handler.dispatchMessage(Handler.java:99)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.os.Looper.loop(Looper.java:137)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.app.ActivityThread.main(ActivityThread.java:4575)
08-17 09:33:38.449: E/AndroidRuntime(14389): at java.lang.reflect.Method.invokeNative(Native Method)
08-17 09:33:38.449: E/AndroidRuntime(14389): at java.lang.reflect.Method.invoke(Method.java:511)
08-17 09:33:38.449: E/AndroidRuntime(14389): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
08-17 09:33:38.449: E/AndroidRuntime(14389): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
08-17 09:33:38.449: E/AndroidRuntime(14389): at dalvik.system.NativeStart.main(Native Method)
08-17 09:33:38.449: E/AndroidRuntime(14389): Caused by: android.database.StaleDataException: Attempted to access a cursor after it has been closed.
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.database.BulkCursorToCursorAdaptor.throwIfCursorIsClosed(BulkCursorToCursorAdaptor.java:75)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.database.BulkCursorToCursorAdaptor.requery(BulkCursorToCursorAdaptor.java:144)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.database.CursorWrapper.requery(CursorWrapper.java:186)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.app.Activity.performRestart(Activity.java:4505)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.app.Activity.performResume(Activity.java:4531)
08-17 09:33:38.449: E/AndroidRuntime(14389): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2557)
08-17 09:33:38.449: E/AndroidRuntime(14389): ... 10 more
有没有办法获得未显示的“另外 10 个”?
public class EvidenceList extends StandardMenuActivity implements AdapterView.OnItemClickListener {
private ApplicationSettings settings;
private Context mContext;
private Cursor mCursor;
private Uri mData;
private ImageButton mActiveButton = null;
private static final String TAG = EvidenceList.class.getSimpleName();
protected static final int ACTION_LOGIN = 10;
protected static final int ACTION_ENTER_PIN = 11;
protected static final int ACTION_BULK_EDIT = 12;
private static final int DIALOG_CONFIRM_DELETE = 2;
private static final int DIALOG_LOADING = 3;
private static final int DIALOG_FILES_MISSING = 4;
private static final int MENU_ITEM_CANCEL_UPLOAD = 10001;
private static final int MENU_ITEM_DELETE_EVIDENCE = 10002;
private static final int MENU_ITEM_VIEW_EDIT_EVIDENCE = 10003;
private EvidenceDBHelper evidenceHelper;
protected UploadQueueHelper upHelper;
private ListView mListView;
EvidenceListCursorAdapter mAdapter;
private ImageButton mListAllButton, mListAudioButton, mListVideoButton, mListPhotoButton, mListUploadedButton;
private Button uploadButton;
private View actionButtonBar, tabButtonBar;
private TextView mTitleView;
private Button mSelectAllButton, mImportButton;
private EvidenceManager mEvidenceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
mContext = getApplicationContext();
settings = ApplicationSettings.getInstance(mContext);
mEvidenceManager = EvidenceManager.getInstance(mContext);
setContentView(R.layout.evidence_list);
actionButtonBar = findViewById(R.id.EvidenceSelectedItemsActionButtonBar);
tabButtonBar = findViewById(R.id.EvidenceListTabBar);
mData = getIntent().getData();
if (mData == null) {
mData = LocalEvidence.CONTENT_URI;//default data
}
upHelper = new UploadQueueHelper(mContext);
evidenceHelper = new EvidenceDBHelper(mContext);
mListAllButton = (ImageButton) findViewById(R.id.EvidenceListAllTabButton);
mListVideoButton = (ImageButton) findViewById(R.id.EvidenceListVideoTabButton);
mListAudioButton = (ImageButton) findViewById(R.id.EvidenceListAudioTabButton);
mListPhotoButton = (ImageButton) findViewById(R.id.EvidenceListPhotoTabButton);
mListUploadedButton = (ImageButton) findViewById(R.id.EvidenceListUploadTabButton);
mTitleView = (TextView) findViewById(R.id.EvidenceListTypeTitle);
mSelectAllButton = (Button) findViewById(R.id.CheckAll);
mImportButton = (Button) findViewById(R.id.ImportEvidence);
mImportButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showDialog(DIALOG_IMPORT_EVIDENCE);
}
});
mSelectAllButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EasyTracker.getTracker().trackEvent(TAG, "select_all", "on", 1);
Toast.makeText(mContext, getString(R.string.message_select_all), Toast.LENGTH_SHORT).show();
mAdapter.checkAll();
}
});
ListHasCheckedItemsListener checkedItemsListener = new ListHasCheckedItemsListener() {
@Override
public void onListCheckedStatusChange(boolean hasCheckedItems) {
showTabsOrButtons(hasCheckedItems);
}
};
mListView = (ListView) findViewById(android.R.id.list);
mListView.setEmptyView(findViewById(android.R.id.empty));
mCursor = getCursor();
mAdapter = new EvidenceListCursorAdapter(this, mCursor, getUriForCursor(), checkedItemsListener);
Log.d(TAG, "onCreate, cursor: " + mCursor + " with adapter@uri " + getUriForCursor());
uploadButton = (Button) findViewById(R.id.UploadEvidenceButton);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(this);
setActiveButton();
checkIfIntentHasTab(getIntent());
UploadManagerService.startUploader(this, UploaderAction.OP_START, 1, null);
}
@Override
public void onNewIntent(Intent intent) {
checkIfIntentHasTab(intent);
}
private void checkIfIntentHasTab(Intent intent) {
String tab = getIntent().getStringExtra("tab");
if (tab != null) {
if (tab.equals("uploads_completed")) {
changeView(R.id.EvidenceListUploadTabButton);
} else if (tab.equals("all_evidence")) {
changeView(R.id.EvidenceListAllTabButton);
} else if (tab.equals("videos")) {
changeView(R.id.EvidenceListVideoTabButton);
} else if (tab.equals("photos")) {
changeView(R.id.EvidenceListPhotoTabButton);
} else if (tab.equals("audio")) {
changeView(R.id.EvidenceListAllTabButton);
} else {
Log.e(TAG, "no match for tab " + tab);
}
}
}
public void titlebarLogoClicked(View button) {
Log.d(TAG, "Clicked titlebar");
launchMainActivity();
}
public void launchMainActivity() {
Intent i = new Intent(this, Home.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
//should be called after switching the cursor
private void setActiveButton() {
if (mData.equals(LocalEvidence.CONTENT_URI)){
mActiveButton = mListAllButton;
} else if (mData.equals(LocalEvidence.CONTENT_AUDIO_URI)){
mActiveButton = mListAudioButton;
} else if (mData.equals(LocalEvidence.CONTENT_VIDEO_URI)) {
mActiveButton = mListVideoButton;
} else if (mData.equals(LocalEvidence.CONTENT_PHOTO_URI)) {
mActiveButton = mListPhotoButton;
} else if (mData.equals(LocalEvidence.CONTENT_UPLOADED_URI)) {
mActiveButton = mListUploadedButton;
}
mActiveButton.setSelected(true);
setOthersInactive();
}
private void setOthersInactive() {
if (mActiveButton != mListAllButton) {
mListAllButton.setSelected(false);
}
if (mActiveButton != mListVideoButton) {
mListVideoButton.setSelected(false);
}
if (mActiveButton != mListAudioButton) {
mListAudioButton.setSelected(false);
}
if (mActiveButton != mListPhotoButton) {
mListPhotoButton.setSelected(false);
}
if (mActiveButton != mListUploadedButton) {
mListUploadedButton.setSelected(false);
}
}
private void changeCursor() {
mAdapter.changeCursor(getCursor());
}
private Cursor getCursor() {
return managedQuery(mData, null, null, null, null);
}
private Uri getUriForCursor() {
return LocalEvidence.CONTENT_URI;
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy");
mAdapter.close();
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
if (!FileUtils.isExternalStorageAvailableAndWritable(mContext)) {
Util.showAlertMsgNoExternalStorage(this);
return;
}
if (settings.requiresPin() && settings.getLastPinSuccessEntryTime() < System.currentTimeMillis() - (1000 * 60 * 15)) {
sendToEnterPin();
return;
}
//Set<Long> selections = mAdapter.getSelectedCheckboxMediaIds();
//setTabsOrButtons(selections);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
//super.onSaveInstanceState(outState);
//outState.
}
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
Log.d(TAG, "onListItemClick, item clicked with id " + id);
Intent openEvidenceIntent = getIntentForEvidenceId(id);
startActivity(openEvidenceIntent);
}
private void uncheckAll() {
mAdapter.uncheckAll();
}
@Override
public void onBackPressed() {
Log.d(TAG, "onBackPressed");
if (mAdapter.hasSelectedCheckboxes()) {
uncheckAll();
} else {
Intent i = new Intent(this, Home.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
//super.onBackPressed();
}
}
@Override
protected Dialog onCreateDialog(int id) {
Log.d(TAG, "onCreateDialog");
switch (id) {
case DIALOG_CONFIRM_DELETE:
String msg = getString(R.string.alert_delete_imported);
return new AlertDialog.Builder(this)
.setMessage(msg)
.setCancelable(false)
.setPositiveButton(getString(R.string.yes_btn),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
EvidenceList.this.doRemoveEvidence();
}
})
.setNegativeButton(R.string.no_btn, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}).create();
case DIALOG_LOADING:
ProgressDialog pdialog = new ProgressDialog(this);
pdialog.setMessage(getString(R.string.alert_checking_login));
return pdialog;
case DIALOG_FILES_MISSING:
return new AlertDialog.Builder(this)
.setTitle(getString(R.string.alert_files_missing_title))
.setMessage(getString(R.string.alert_files_missing))
.setNeutralButton(getString(R.string.ok_btn), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1id) {
dialog.cancel();
}
}).create();
default:
return super.onCreateDialog(id);
}
}
public void removeEvidence(View v) {
Log.d(TAG, "removeEvidence");
//Toast.makeText(mContext, "Going to delete.. ", Toast.LENGTH_LONG).show();
showDialog(DIALOG_CONFIRM_DELETE);
}
public void bulkEditEvidence(View v) {
Log.d(TAG, "bulkEditEvidence");
EasyTracker.getTracker().trackEvent(TAG, "bulk_edit", "", 1);
Set<Long> selections = mAdapter.getSelectedCheckboxMediaIds();
Iterator<Long> selectionIter = selections.iterator();
// Pass the array of id's checked to the intent service.
// We need to convert from Long[] to long[]
long[] ids = new long[selections.size()];
int i = 0;
while(selectionIter.hasNext()) {
long curr = selectionIter.next();
ids[i++] = curr;
}
Intent bulkEditIntent = new Intent(this, BulkEditEvidence.class);
bulkEditIntent.putExtra(BulkEditEvidence.EXTRA_EVIDENCE_IDS, ids);
startActivityForResult(bulkEditIntent, ACTION_BULK_EDIT);
}
public void doRemoveEvidence() {
Log.d(TAG, "doRemoveEvidence");
EasyTracker.getTracker().trackEvent(TAG, "delete", "", 1);
Set<Long> selectedIds = mAdapter.getSelectedCheckboxMediaIds();
if (selectedIds == null || selectedIds.size() < 1) {
Toast.makeText(mContext, getString(R.string.alert_no_evidence_removed), Toast.LENGTH_LONG).show();
return;
}
Iterator<Long> selectedIter = selectedIds.iterator();
List<Long> deleted = new ArrayList<Long>();
while(selectedIter.hasNext()) {
long selectedId = selectedIter.next();
Log.d(TAG, "going to delete evidence id: " + selectedId);
Evidence ev = evidenceHelper.getEvidencefromMediaRecord(selectedId);
if (ev != null) {
if (ev.getMediaId() > 0) {
//has a media id
int rowsAffected = evidenceHelper.deleteById(selectedId);
upHelper.deleteById(selectedId);
if (rowsAffected >0) {
deleted.add(selectedId);
}
} else {
if (ev != null) {
try {
ev.getFile().delete();
int rowsAffected = evidenceHelper.deleteById(selectedId);
upHelper.deleteById(selectedId);
if (rowsAffected > 0) {
deleted.add(selectedId);
}
} catch(Exception e) {
Log.e(TAG, "error trying to delete evidence " + selectedId + " msg: " + e.getMessage(), e);
}
}
}
} else {
String msg = String.format(getString(R.string.alert_ev_with_id_could_not_delete), "" + selectedId);
Toast.makeText(mContext, msg, Toast.LENGTH_LONG).show();
}
}
if (deleted != null && deleted.size() > 0) {
Long[] deletedArr = deleted.toArray(new Long[1]);
mAdapter.uncheckIds(deletedArr);
}
try {
dismissDialog(DIALOG_CONFIRM_DELETE);
} catch(Exception e) {
//who cares
}
}
public void uploadSelected(View v) throws URISyntaxException, IOException {
Log.d(TAG, "uploadSelected");
uploadButton.setEnabled(false);
showDialog(DIALOG_LOADING);
//if the last time the session was checked was more than 15 minutes (-5 seconds)
//then check the session again..
if (!settings.canUserAutoLogin()) {
dismissDialog(DIALOG_LOADING);
sendToLogin();
} else {
doUploadSelected();
uploadButton.setEnabled(true);
}
}
private void sendToLogin() {
Intent i = new Intent(Action.LOGIN);
startActivityForResult(i, ACTION_LOGIN);
}
private void sendToEnterPin() {
startActivityForResult(new Intent(this, EnterPin.class), ACTION_ENTER_PIN);
}
private void doUploadSelected() {
Log.d(TAG, "doUploadSelected");
Set<Long> selections = mAdapter.getSelectedCheckboxMediaIds();
Iterator<Long> selectionIter = selections.iterator();
// Pass the array of id's checked to the intent service.
// We need to convert from Long[] to long[]
Long[] origArr = selections.toArray(new Long[selections.size()]);
Log.d(TAG, "about to pass evidence IDs for upload | on thread " + Thread.currentThread().getId());
List<Long> toUpload = new ArrayList<Long>();
long[] ev_ids = new long[selections.size()];
int i = 0;
boolean alertMissing = false;
while(selectionIter.hasNext()) {
long curr = selectionIter.next();
Evidence ev = evidenceHelper.getEvidencefromMediaRecord(curr);
if (FileUtils.isFileAvailableAndReadable(ev.getFile())) {
toUpload.add(curr);
ev_ids[i] = curr;
i++;
} else {
alertMissing = true;
}
}
try {
dismissDialog(DIALOG_LOADING);
} catch(Exception e) {}
if (alertMissing) {
showDialog(DIALOG_FILES_MISSING);
}
if (ev_ids.length < 1) {
return;
}
EasyTracker.getTracker().trackEvent(TAG, "upload_selected", "" + ev_ids.length + " items" , 1);
UploadManagerService.startUploader(this, UploaderAction.OP_QUEUE_UPLOAD, 1, ev_ids);
uploadButton.setEnabled(true);
mAdapter.markIdsAsDisabled(origArr);
}
private void onLoginResult(int resultCode, Intent data) {
EasyTracker.getTracker().trackEvent(TAG, "login", "on", (resultCode == RESULT_OK) ? 1 : 0);
if (resultCode == RESULT_OK) {
doUploadSelected();
} else {
try {
dismissDialog(DIALOG_LOADING);
} catch (Exception e) {}
uploadButton.setEnabled(true);
Toast.makeText(mContext, getString(R.string.alert_cannot_upload_without_internet), Toast.LENGTH_LONG).show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult");
switch (requestCode) {
case ACTION_LOGIN:
onLoginResult(resultCode, data);
return;
case ACTION_ENTER_PIN:
if (resultCode != Activity.RESULT_OK) {
finish();//throw error message
}
return;
case ACTION_BULK_EDIT:
if (resultCode == RESULT_OK) {
uncheckAll();
}
return;
case ACTION_IMPORT_AUDIO:
case ACTION_IMPORT_VIDEO:
case ACTION_IMPORT_PHOTO:
onImportResult(requestCode, resultCode, data);
return;
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
private void onImportResult(int requestCode, int resultCode, Intent intent) {
ImportAction action = ImportAction.IMPORT_PHOTO;
if (requestCode == ACTION_IMPORT_AUDIO) {
action = ImportAction.IMPORT_AUDIO;
} else if (requestCode == ACTION_IMPORT_VIDEO) {
action = ImportAction.IMPORT_VIDEO;
}
EasyTracker.getTracker().trackEvent(TAG, "import", action.toString(), resultCode == RESULT_OK ? 1 : 0);
if (resultCode == RESULT_OK) {
Evidence ev = mEvidenceManager.importEvidence(this, intent, action);
if (ev != null) {
launchEvidenceListActivity(null);
BackupService.start(this, BackupService.OP_DATA_CHANGED);
}
}
}
public void showTabsOrButtons(boolean hasCheckedItems) {
if (hasCheckedItems) {
tabButtonBar.setVisibility(View.INVISIBLE);
actionButtonBar.setVisibility(View.VISIBLE);
if (mData.equals(LocalEvidence.CONTENT_UPLOADED_URI)) {
actionButtonBar.findViewById(R.id.UploadEvidenceButton).setVisibility(View.GONE);
actionButtonBar.findViewById(R.id.BulkEditEvidenceButton).setVisibility(View.GONE);
} else {
actionButtonBar.findViewById(R.id.UploadEvidenceButton).setVisibility(View.VISIBLE);
}
} else {
actionButtonBar.setVisibility(View.GONE);
tabButtonBar.setVisibility(View.VISIBLE);
}
}
public void changeView(int id) {
Uri newUri = null;
String newTitle = "";
if (id == R.id.EvidenceListAllTabButton) {
newUri = LocalEvidence.CONTENT_URI;
newTitle = getString(R.string.tab_all_pending_upload);
EasyTracker.getTracker().trackEvent(TAG, "switched_tab", "all", 1);
} else if (id == R.id.EvidenceListAudioTabButton) {
newUri = LocalEvidence.CONTENT_AUDIO_URI;
newTitle = getString(R.string.tab_audio_pending_upload);
EasyTracker.getTracker().trackEvent(TAG, "switched_tab", "audio", 1);
} else if (id == R.id.EvidenceListPhotoTabButton) {
newUri = LocalEvidence.CONTENT_PHOTO_URI;
newTitle = getString(R.string.tab_photos_pending_upload);
EasyTracker.getTracker().trackEvent(TAG, "switched_tab", "photos", 1);
} else if (id == R.id.EvidenceListVideoTabButton) {
newUri = LocalEvidence.CONTENT_VIDEO_URI;
newTitle = getString(R.string.tab_video_pending_upload);
EasyTracker.getTracker().trackEvent(TAG, "switched_tab", "videos", 1);
} else if (id == R.id.EvidenceListUploadTabButton) {
newUri = LocalEvidence.CONTENT_UPLOADED_URI;
newTitle = getString(R.string.tab_all_uploaded);
EasyTracker.getTracker().trackEvent(TAG, "switched_tab", "uploaded", 1);
}
//only change something if its a different view
if (!mData.equals(newUri) && newUri != null) {
mData = newUri;
changeCursor();
setActiveButton();
mTitleView.setText(newTitle);
}
}
public void changeView(View button) {
changeView(button.getId());
}
}