简体   繁体   中英

Android java.lang.OutOfMemoryError only on Samsung devices

I have an app on Google Play that updated yesterday and I have started getting numerous reports from Crashlytics where my app crashes only on Samsung devices with Android 4.x (4.1.2 - 4.4.4).

The trace of the exception is as follows:

Fatal Exception: java.lang.RuntimeException: An error occured while executing doInBackground()
       at android.os.AsyncTask$3.done(AsyncTask.java:300)
       at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
       at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
       at java.util.concurrent.FutureTask.run(FutureTask.java:242)
       at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
       at java.lang.Thread.run(Thread.java:841)
Caused by java.lang.OutOfMemoryError
       at java.util.Arrays.copyOfRange(Arrays.java:2684)
       at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:467)
       at javax.crypto.Cipher.doFinal(Cipher.java:1204)
       at com.ijsoft.cpul.Util.DbMainFunctions.com.ijsoft.cpul.Util.AES256Cipher.decrypt(DbMainFunctions.java:2059)
       at com.ijsoft.cpul.Util.DbMainFunctions.initializeDb(DbMainFunctions.java:94)
       at com.ijsoft.cpul.SplashActivity$InitializeDb.doInBackground$9ecd34e(SplashActivity.java:2095)
       at android.os.AsyncTask$2.call(AsyncTask.java:288)
       at java.util.concurrent.FutureTask.run(FutureTask.java:237)
       at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
       at java.lang.Thread.run(Thread.java:841)

In other cases I received the following trace:

Fatal Exception: java.lang.RuntimeException: An error occured while executing doInBackground()
       at android.os.AsyncTask$3.done(AsyncTask.java:299)
       at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
       at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
       at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
       at java.util.concurrent.FutureTask.run(FutureTask.java:137)
       at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
       at java.lang.Thread.run(Thread.java:856)
Caused by java.lang.OutOfMemoryError
       at java.io.ByteArrayOutputStream.expand(ByteArrayOutputStream.java:91)
       at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:201)
       at com.ijsoft.cpul.Util.DbMainFunctions.initializeDb(DbMainFunctions.java:90)
       at com.ijsoft.cpul.SplashActivity$InitializeDb.doInBackground$9ecd34e(SplashActivity.java:2095)
       at android.os.AsyncTask$2.call(AsyncTask.java:287)
       at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
       at java.util.concurrent.FutureTask.run(FutureTask.java:137)
       at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
       at java.lang.Thread.run(Thread.java:856)

I leave part of the code where the error occurs:

AES256Cipher.java

public class AES256Cipher {
    public byte[] decrypt(byte[] textBytes, Context context)
            throws java.io.UnsupportedEncodingException,
            NoSuchAlgorithmException,
            NoSuchPaddingException,
            InvalidKeyException,
            InvalidAlgorithmParameterException,
            IllegalBlockSizeException,
            BadPaddingException {

        byte[] keyBytes = CommonFunctions.md5Package(context).getBytes();
        byte[] ivBytes = new byte[16];
        for (int j = 0; j < keyBytes.length; j+=2) {
            ivBytes[j/2]=keyBytes[31-j];
        }

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
        return cipher.doFinal(textBytes);
    }
}

DbMainFunctions.java

public synchronized static int initializeDb(int release, Context context) {
        int resultCode = 0;
        try {
            // Get the encrypted json file (database) from Assets
            AssetManager am = context.getAssets();
            InputStream is = am.open("assets");
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buff = new byte[4096];
            int i;
            while ((i = is.read(buff, 0, buff.length)) > 0) {
                baos.write(buff, 0, i);
            }
            is.close();
            baos.close();
            resultCode = updateDb(baos.toByteArray(), release, context);
            //baos.close();
        } catch (Exception e) {
            Log.e("DbMainFunctions", e.getMessage());
            //e.printStackTrace();
            resultCode = -1;
        }
        return resultCode;
    }

public synchronized static int updateDb(byte[] cipherJson, int release, Context context) {
        int resultCode = 0;
        AES256Cipher aes256;
        try {
            aes256 = new AES256Cipher();
            cipherJson = aes256.decrypt(cipherJson, context);
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
                InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            Log.e("DbMainFuctions", e.getMessage());
            return -2;
        }

     ...
}

My application does at that point is to get an encrypted file (filename: assets) with AES256 from the Assets directory included in the APK. The file is then decrypted and saved in a Byte Array. The file is approximately 4.4 MB in size. The initializeDb() method is called from an AsyncTask during doInBackground(), that is, all the previous code is executed in an AsyncTask

This is highly likely to give you an OutOfMemoryError , particularly prior to Android 5.0, as you do not necessarily have a 4.4MB contiguous block of free memory.

I would get rid of the encryption, since it offers no security (anyone can decrypt your file) and it wastes CPU, battery, and memory.

At minimum, stop trying to read the entire contents into memory, and decrypt the data in a streaming fashion (eg, 16KB at a time), streaming the decrypted data to an output file (since you do not have enough memory to hold that either, most likely).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM