特定片段

时间:2016-04-27 01:50:26

标签: android sqlite android-fragments android-sqlite android-cursor

我有一个Android应用程序,它使用服务每隔5ms收集传感器数据并将其插入到sqlite表中。在典型的会话中,将有大约40分钟的录音。所有这些代码似乎都运行良好。

我有一个奇怪的问题,如果用户导航到特定片段,我会收到CursorWindow: Window is full: requested allocation XXX错误。我不确定导致该错误的特定片段有什么特别之处,并且只发生在这个片段

有问题的片段包含一个按钮,点击该按钮会做很多事情:

  • 在外部存储上创建一些目录
  • 从临时表中复制所有传感器数据并将其插入更永久的表
  • 将整个.db文件的副本创建到外部存储
  • 获取表格的内容并将其写入CSV文件
  • 查询传感器数据的另一个表,并将所有传感器数据写入另一个CSV文件
  • 使用媒体扫描程序扫描导出目录中的所有文件,以便可以通过MTP
  • 访问它们

片段代码看起来像这样(为简洁起见,许多catch块都被省略了 - 它们大多只是记录信息):

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;
        }
    }
}

在记录了大量传感器数据后,每次用户导航到片段时都会收到错误。但是当点击按钮时,我每隔2秒就会连续出现错误。

传感器录制服务代码可以在我的另一个问题中找到:Android sending messages between fragment and service

这个片段从我的DBHelper类(设置为单例)中调用了许多方法:

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和任何SQL连接在我的主要活动的onDestroy中关闭

我的媒体扫描仪课程也非常简单:

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
                }
            }
        );
    }
}

任何人都可以看到导致此光标窗口错误的片段代码有什么特别之处吗?

0 个答案:

没有答案