Android cursor memory error in specific fragment

I have an Android application that uses a service to collect sensor data every 5 ms and inserts it into the sqlite table. A typical session will have about 40 minutes of recording. All this code is working fine.

I have a strange problem: if the user navigates to a specific fragment, I get an error CursorWindow: Window is full: requested allocation XXX . I am not sure what is special about this particular fragment that causes this error and only happens with this single fragment

This snippet contains a button that, when pressed, will do several things:

  • Creates some directories on external storage
  • Copy all sensor data from a temporary table and paste it into a more permanent table
  • Creates a copy of the entire .db file for external storage
  • Takes the contents of a table and writes it to a CSV file
  • It will request another table for the sensor data and write all the data of this sensor to another CSV file
  • Uses a media scanner to scan all files in the export directory so that they can be obtained through MTP

The code for the snippet is as follows (many catch blocks were left for brevity - they basically just write information):

 public class SaveFragment extends Fragment implements View.OnClickListener { Button saveButton; MainActivity mainActivity; DBHelper dbHelper; Boolean subjectDataExists; MediaScanner mediaScanner; static ProgressDialog dialog; public SaveFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_save, container, false); //Get save button view saveButton = (Button) view.findViewById(R.id.saveButton); saveButton.setOnClickListener(this); //Get DBHelper dbHelper = DBHelper.getInstance(getActivity(), new DatabaseHandler()); //Check if sensor data has been recorded subjectDataExists = dbHelper.checkSubjectDataExists(Short.parseShort(dbHelper.getTempSubInfo("subNum"))); // Inflate the layout for this fragment return view; } @Override public void onClick(View v) { //Alert dialog for saving/quitting AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mainActivity); if (subjectDataExists) { alertDialogBuilder.setTitle("Save and quit?"); alertDialogBuilder.setMessage("Are you sure you want to save the data and quit the current session?"); } else { alertDialogBuilder.setTitle("Quit?"); alertDialogBuilder.setMessage("Are you sure you want to quit the current session? \n\n No data will be saved."); } alertDialogBuilder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { //Save if sensor data exists, otherwise quit if (subjectDataExists) { new ExportDatabaseCSVTask().execute(); } else { quitSession(); } } }); alertDialogBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog quitAlertDialog = alertDialogBuilder.create(); quitAlertDialog.show(); } //Quit the current session and go back to login screen private void quitSession(){ Intent intent = new Intent(getActivity(), LoginActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); getActivity().finishAffinity(); } //Message handler class for database progress updates private static class DatabaseHandler extends Handler { @Override public void handleMessage (Message msg){ Double progressPercent = (Double) msg.obj; Integer progressValue = 40 + (int) Math.ceil(progressPercent/2); dialog.setProgress(progressValue); } } //Async class for CSV export task public class ExportDatabaseCSVTask extends AsyncTask<String, Integer, Boolean> { @Override protected void onPreExecute() { //show a progress dialog } protected Boolean doInBackground(final String... args) { //Create directories for the output csv files String pathToExternalStorage = Environment.getExternalStorageDirectory().toString(); File exportDir = new File(pathToExternalStorage, "/Data"); File subjectDataDir = new File(exportDir, "/subjects"); publishProgress(5); //The sleep is here just so the progress updates in the dialog are visually slower SystemClock.sleep(100); if (!exportDir.exists()) { Boolean created = exportDir.mkdirs(); } publishProgress(10); SystemClock.sleep(100); if (!subjectDataDir.exists()) { Boolean created = subjectDataDir.mkdirs(); } publishProgress(15); SystemClock.sleep(100); //If all directories have been created successfully if (exportDir.exists() && subjectDataDir.exists()) { try { //Copy temp subject and sensor data to persistent db tables dbHelper.copyTempData(); publishProgress(20); SystemClock.sleep(200); //Backup the SQL DB file File data = Environment.getDataDirectory(); String currentDBPath = "//data//com.example.app//databases//" + DBHelper.DATABASE_NAME; File currentDB = new File(data, currentDBPath); File destDB = new File(exportDir, DBHelper.DATABASE_NAME); publishProgress(25); SystemClock.sleep(100); if (exportDir.canWrite()) { if (currentDB.exists()) { FileChannel src = new FileInputStream(currentDB).getChannel(); FileChannel dst = new FileOutputStream(destDB).getChannel(); dst.transferFrom(src, 0, src.size()); src.close(); dst.close(); } } publishProgress(35); SystemClock.sleep(300); //Export subjects table/tracking sheet File trackingSheet = new File(exportDir, "trackingSheet.csv"); try{ dbHelper.exportTrackingSheet(trackingSheet); } catch (SQLException | IOException e){ } publishProgress(40); SystemClock.sleep(300); //Export individual subject data String subNum = dbHelper.getTempSubInfo("subNum"); File subjectFile = new File(subjectDataDir, subNum + ".csv"); try{ dbHelper.exportSubjectData(subjectFile, subNum); } catch (SQLException | IOException e){ } publishProgress(90); SystemClock.sleep(300); //Scan all files for MTP List<String> fileList = getListFiles(exportDir); String[] allFiles = new String[fileList.size()]; allFiles = fileList.toArray(allFiles); mediaScanner = new MediaScanner(); try{ mediaScanner.scanFile(getContext(), allFiles, null, mainActivity.logger); } catch (Exception e) { } publishProgress(100); SystemClock.sleep(400); return true; } catch (SQLException | IOException e) { } else { //Directories don't exist if (!exportDir.exists()) { } else if (!subjectDataDir.exists()) { return false; } } public void onProgressUpdate(Integer ... progress){ dialog.setProgress(progress[0]); if (progress[0] == 100){ dialog.setMessage("Quitting..."); } } protected void onPostExecute(final Boolean success) { if (dialog.isShowing()) { dialog.dismiss(); } if (success) { //Restart app and go back to login screen quitSession(); } } //Recursive file lister for MTP private List<String> getListFiles(File parentDir) { ArrayList<String> inFiles = new ArrayList<>(); File[] files = parentDir.listFiles(); //Loop through everything in base directory, including folders for (File file : files) { if (file.isDirectory()) { //Recursively add files from subdirectories inFiles.addAll(getListFiles(file)); } else { inFiles.add(file.getAbsolutePath()); } } return inFiles; } } } 

After a lot of sensor data has been recorded, I get an error every time the user navigates to a fragment. But when the button is pressed, I get an error continuously every 2 seconds.

The sensor registration service code can be found in my other question: Android sends messages between the fragment and the service

This snippet calls several methods from my DBHelper class (which is configured as single):

 public class DBHelper extends SQLiteOpenHelper { SQLiteDatabase db; CSVWriter csvWrite; Cursor curCSV; static Handler messageHandler; private static DBHelper sInstance; public static synchronized DBHelper getInstance(Context context) { if (sInstance == null) { sInstance = new DBHelper(context.getApplicationContext()); Log.d(TAG, "New DBHelper created"); } return sInstance; } public static synchronized DBHelper getInstance(Context context, Handler handler) { if (sInstance == null) { sInstance = new DBHelper(context.getApplicationContext()); Log.d(TAG, "New DBHelper created"); } messageHandler = handler; return sInstance; } private DBHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); db = this.getWritableDatabase(); } public boolean checkSubjectDataExists(Short subNum) throws SQLException { //Check if sensor data, for this subject, exists in the temp data table String query = "SELECT * FROM " + DATA_TABLE_NAME_TEMP + " WHERE " + DATA_SUBJECT + "=" + subNum; Cursor c = db.rawQuery(query, null); boolean exists = (c.getCount() > 0); c.close(); return exists; } public void copyTempData() throws SQLException{ String copySubjectSQL = "INSERT INTO " + SUBJECTS_TABLE_NAME + " SELECT * FROM " + SUBJECTS_TABLE_NAME_TEMP; db.execSQL(copySubjectSQL); String copyDataSQL = "INSERT INTO " + DATA_TABLE_NAME + " SELECT * FROM " + DATA_TABLE_NAME_TEMP; db.execSQL(copyDataSQL); } public void exportTrackingSheet(File outputFile) throws SQLException, IOException { csvWrite = new CSVWriter(new FileWriter(outputFile)); curCSV = db.rawQuery("SELECT * FROM " + SUBJECTS_TABLE_NAME, null); csvWrite.writeNext(curCSV.getColumnNames()); while (curCSV.moveToNext()) { String arrStr[] = {curCSV.getString(0), curCSV.getString(1), curCSV.getString(2), curCSV.getString(3), curCSV.getString(4), curCSV.getString(5), curCSV.getString(6)}; csvWrite.writeNext(arrStr); } csvWrite.close(); curCSV.close(); } public void exportSubjectData(File outputFile, String subNum) throws IOException, SQLException { csvWrite = new CSVWriter(new FileWriter(outputFile)); curCSV = db.rawQuery("SELECT * FROM " + DATA_TABLE_NAME + " WHERE id = " + subNum, null); csvWrite.writeNext(curCSV.getColumnNames()); Integer writeCounter = 0; Integer numRows = curCSV.getCount(); while (curCSV.moveToNext()) { writeCounter++; String arrStr[] = {curCSV.getString(0), curCSV.getString(1), curCSV.getString(2), curCSV.getString(3), curCSV.getString(4), curCSV.getString(5), curCSV.getString(6), curCSV.getString(7), curCSV.getString(8), curCSV.getString(9), curCSV.getString(10), curCSV.getString(11), curCSV.getString(12), curCSV.getString(13), curCSV.getString(14), curCSV.getString(15), curCSV.getString(16), curCSV.getString(17), curCSV.getString(18), curCSV.getString(19), curCSV.getString(20), curCSV.getString(21), curCSV.getString(22), curCSV.getString(23), curCSV.getString(24), curCSV.getString(25)}; csvWrite.writeNext(arrStr); if ((writeCounter % 1000) == 0){ csvWrite.flush(); } Double progressPercent = Math.ceil(((float) writeCounter / (float) numRows)*100); Message msg = Message.obtain(); msg.obj = progressPercent; msg.setTarget(messageHandler); msg.sendToTarget(); } csvWrite.close(); curCSV.close(); } } 

DBHelper and any SQL connections are closed in onDestroy my main action

My media scanner class is also quite simple:

 public class MediaScanner { protected void scanFile(final Context context, String[] files, String[] mimeTypes, final Logger logger) { MediaScannerConnection.scanFile(context, files, mimeTypes, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(String path, Uri uri) { //Log some info } } ); } } 

Can anyone see anything special in my code snippet causing this cursor window error?

+7
android sqlite android-fragments android-sqlite android-cursor
source share

No one has answered this question yet.

See similar questions:

3
Android sends messages between fragment and service

or similar:

3606
Close / hide Android soft keyboard
3295
Why is the Android emulator so slow? How can we speed up Android emulator development?
3288
Correct use cases for Android UserManager.isUserAGoat ()?
2609
Is there a unique identifier for an Android device?
2510
How to keep Android activity state by saving instance state?
2097
Is there a way to run Python on Android?
1858
"Debug certificate expired" error in Android Eclipse plugins
1844
What is "Context" on Android?
937
findViewById in fragment
603
Best practice for instantiating a new Android snippet

All Articles