SQL Cursor throws out of memory while calling getString

余生颓废 提交于 2019-12-23 12:56:26

问题


Attachment Reference to my earlier question :- Out of memory

I would try to be as precise as possible. I am getting a response from my web-service call a long base64 string. I decode the string and get a huge string which contains my data. I de-serialize the string and create the object of my class using the string as below.

String decryptedXml = XmlObject.toDecryptedXmlString(gameDetail.getGameData(), app.getSessionEncryptionKey());
Game noviceGame = deserialiseGame(decryptedXml, NoviceGamer.class);

desrialiseGame() is just a method which desrialise the data and create and return my game instance. To maintain this object to multiple session(login/logout) I store my entire gameData(my string whose De-serialization gave me my Game instance) in database.

Next time when user login, to create the Game instance I fetch the string from my DB and again try to De-serialize so that I get back my Game instance. But when I try to get the string from my DB, I get 'OUT OF MEMORY' exception while fetching the string.

Method called to De-serialize the game is below.

private HashMap<String, Game> games = new HashMap<String, Game>();

public void load(LocalDatabaseHelper localDbHelper) throws Exception
{
    synchronized(gameLockObject) {
        GameDetailDAO dao = new GameDetailDAO(localDbHelper);

        //this will fetch me the all the entities from databse
        ArrayList<GameDetailEntity> dbGameDetails = dao.getEntities(null, null);

        for (GameDetailEntity gameDetail : dbGameDetails) {
            String gameLevel = gameDetail.getDetailLevel();             

            String gameXml = gameDetail.getGameData();

            Game game = null;
            if(gameLevel.equalsIgnoreCase("Novice")) {
                game = Job.deserialiseJob(gameXml, NoviceLevel.class);
            }
            else if (gameLevel.equalsIgnoreCase("Expert")) { 
                game = Job.deserialiseJob(gameXml, ExpertLevel.class);
            }

            //set the job version
            game.setGameversion(gameDetail.getGameVersion());
            game.setMagicNumber(gameDetail.getMagicNumber());
            game.setInactiveUser(gameDetail.getInactiveUser());
            game.setStartTime(gameDetail.getStartTime());
            game.setFinishTime(gameDetail.getFinishTime());
            game.setGameCompletionTime(gameDetail.getGameCompletionTime());
            if (!StringUtils.isNullOrEmpty(gameDetail.getGameStatus())) {
                game.setGameStatus(GameStatus.valueOf(gameDetail.getGameStatus()));
            }

            //add the job to the store
            games.put(gameDetail.getGameRef().toLowerCase(Locale.getDefault()), game);
        }
    }
}

My Database transaction is as below:

@Override
    protected GameEntity getEntityFromCursor(Cursor cursor) 
    {
        String gameRef = cursor.getString(cursor.getColumnIndex(GAME_REF));
        String detailLevel = cursor.getString(cursor.getColumnIndex(DETAIL_LEVEL));
        int gameVersion = cursor.getInt(cursor.getColumnIndex(GAME_VERSION));
        String gameData = cursor.getString(cursor.getColumnIndex(GAME_DATA));
        String status = cursor.getString(cursor.getColumnIndex(GAME_STATUS));

        long longStart = cursor.getLong(cursor.getColumnIndex(VISIT_START_TIME));
        Date startTime = longStart == -1 ? null : new Date(longStart);

        long longFinish = cursor.getLong(cursor.getColumnIndex(VISIT_END_TIME));
        Date finishTime = longFinish == -1 ? null : new Date(longFinish);

        long longComplete = cursor.getLong(cursor.getColumnIndex(GAME_COMPLETION_TIME));
        Date completionTime = longComplete == -1 ? null : new Date(longComplete);

        GameEntity entity = new GameEntity(gameRef, detailLevel, gameVersion, gameData, );
        entity.setGameStatus(status);
        entity.setStartTime(startTime);
        entity.setFinishTime(finishTime);
        entity.setGameCompletionTime(completionTime);
        return entity;
    }

but when I try to fetch the data from database @Line String gameData = cursor.getString(cursor.getColumnIndex(GAME_DATA)); I get out of memory error. As per my finding, When I add the flag largeHeap=true in manifest in application tag, my application gets damn slow. And also developer.android states

Never request a large heap simply because you've run out of memory and you need a quick fix—you should use it only when you know exactly where all your memory is being allocated and why it must be retained. Yet, even when you're confident your app can justify the large heap, you should avoid requesting it to whatever extent possible.

Can anybody suggest me how to avoid this. There is no bitmap use which most of the SO question contains. Any help will be appreciated.


回答1:


SQLCipher for Android allocates a memory buffer for each cursor window with a maximum size of 1 MB. Since you commented that the game data file is quite large, the size is likely exceeding the maximum size of the cursor window, causing the memory allocation error. In this case, we would probably recommend one of two options:

  1. Normalize the game data into a more typical database structures (i.e. multiple tables, columns, rows).
  2. Split the game data file into fragments, with each of them smaller than 1 MB, then reassemble them in the application.



回答2:


I ran into problem because of the simple reason, the size of the string was huge. HUGE. So I decided to reduce the size of the string. I have segregated the image data from the XML and stored in different table. This reduce the amount of data that needed to de-serialize. I reload the extra data separately. Thanks for answers and for your precious time.



来源:https://stackoverflow.com/questions/25979248/sql-cursor-throws-out-of-memory-while-calling-getstring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!