如果我选择一个大图像,OnActivityResult会崩溃

时间:2013-07-26 15:59:09

标签: android android-activity android-image

我必须在图库中获取图像的图像路径,以便将其保存为列表,不幸的是,如果所选图像太大,以下代码会使应用程序崩溃(并且我的try catch块中的警报未被查看) 。

private void openImage() {

         try{
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.setType("image/*");
            startActivityForResult(i, FILE_REQ_CODE);
            } catch (RuntimeException e) {
                           e.printStackTrace();
               Alerts.TooLargeImage(LoadImage.this);
            }

    }

    protected void onActivityResult(int requestCode, int resultCode,
            Intent intentData) {
        try{
            Uri tmp = intentData.getData();
            String path = tmp.toString();
            imagePathList.add(path);
            preview.setImageURI(tmp);
            FileArchiveManager.saveImagePath(imagePathList);
            super.onActivityResult(requestCode, resultCode, intentData);
            } catch (RuntimeException e) {
                           e.printStackTrace();
               Alerts.TooLargeImage(LoadImage.this);
            }
    }

错误日志

java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=8135KB, Allocated=3718KB, Bitmap Size=11707KB)
    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:694)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:494)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
    at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)
    at android.widget.ImageView.resolveUri(ImageView.java:592)
    at android.widget.ImageView.setImageURI(ImageView.java:313)
    at com.myapp.LoadImage.onActivityResult(LoadImage.java:131)
    at android.app.Activity.dispatchActivityResult(Activity.java:4108)
    at android.app.ActivityThread.deliverResults(ActivityThread.java:3016)
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:3072)
    at android.app.ActivityThread.access$2000(ActivityThread.java:135)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1084)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:150)
    at android.app.ActivityThread.main(ActivityThread.java:4385)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
    at dalvik.system.NativeStart.main(Native Method)

如何解决此问题?

3 个答案:

答案 0 :(得分:1)

来自http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

使用BitmapFactory.decodeResource()BitmapFactory.Options.inSampleSize将允许您将位图的缩减采样版本加载到RAM中(因为您实际上不需要显示如此大的图像)而不会导致java.lang.OutofMemoryError

答案 1 :(得分:0)

如果您使用的是android 3.0及更高版本,请在清单文件中的应用程序标记内使用波纹管代码

应用         android:largeHeap =“ true”

答案 2 :(得分:0)

我也遇到了同样的问题,我只是使用了 Glide 。我需要从任何1:1比例的图像代码中输出640x640图像:

class AlarmAudioService : Service() {
    private var mediaPlayer: MediaPlayer? = null
    private val completedTimers = mutableListOf<String>()
    private val runningTimers = mutableListOf<String>()
    private var notificationController: NotificationController? = null

    enum class SERVICE_ACTION {
        START,
        ADD_RUNNING_TIMER,
        REMOVE_RUNNING_TIMER,
        TIMER_COMPLETE,
        TIMERS_IN_FOCUS,
        STOP
    }

    companion object {
        private var isServiceRunning: Boolean = false

        fun startService(context: Context) {
            if (isServiceRunning) return

            val intent = createAlarmAudioServiceIntent(context, SERVICE_ACTION.START)
            ContextCompat.startForegroundService(context, intent)
        }

        fun addRunningTimer(context: Context, timerTitle: String) {
            if (!isServiceRunning) return

            val intent = createAlarmAudioServiceIntent(context, SERVICE_ACTION.ADD_RUNNING_TIMER)
            val paramTitleKey = context.getString(R.string.notifications_parameter_title_key)
            intent.putExtra(paramTitleKey, timerTitle)
            ContextCompat.startForegroundService(context, intent)
        }

        fun removeRunningTimer(context: Context, timerTitle: String) {
            if (!isServiceRunning) return

            val intent = createAlarmAudioServiceIntent(context, SERVICE_ACTION.REMOVE_RUNNING_TIMER)
            val paramTitleKey = context.getString(R.string.notifications_parameter_title_key)
            intent.putExtra(paramTitleKey, timerTitle)
            ContextCompat.startForegroundService(context, intent)
        }

        fun timerComplete(context: Context, timerTitle: String) {
            if (!isServiceRunning) return

            val intent = createAlarmAudioServiceIntent(context, SERVICE_ACTION.TIMER_COMPLETE)
            val paramTitleKey = context.getString(R.string.notifications_parameter_title_key)
            intent.putExtra(paramTitleKey, timerTitle)
            ContextCompat.startForegroundService(context, intent)
        }

        fun timersInFocus(context: Context) {
            if (!isServiceRunning) return

            // This is called when we got the user attention, we can assume they know of completed timers and we can stop the alarm sound
            val intent = createAlarmAudioServiceIntent(context, SERVICE_ACTION.TIMERS_IN_FOCUS)
            ContextCompat.startForegroundService(context, intent)
        }

        fun stopService(context: Context) {
            if (!isServiceRunning) return

            val intent = createAlarmAudioServiceIntent(context, SERVICE_ACTION.STOP)
            ContextCompat.startForegroundService(context, intent)
        }

        private fun createAlarmAudioServiceIntent(context: Context, action: SERVICE_ACTION): Intent {
            val intent = Intent(context, AlarmAudioService::class.java)
            intent.putExtra("action", action)
            return intent
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val action = intent?.getSerializableExtra("action") as SERVICE_ACTION

        when (action) {
            SERVICE_ACTION.START -> start()
            SERVICE_ACTION.ADD_RUNNING_TIMER -> addRunningTimer(intent)
            SERVICE_ACTION.REMOVE_RUNNING_TIMER -> removeRunningTimer(intent)
            SERVICE_ACTION.TIMER_COMPLETE -> timerComplete(intent)
            SERVICE_ACTION.TIMERS_IN_FOCUS -> timersInFocus()
            SERVICE_ACTION.STOP -> stop()
        }

        return START_STICKY
    }

    //#region Service Actions
    private fun start() {
        if (isServiceRunning) {
            return
        }

        isServiceRunning = true
        InitNotificationController()
        updateNotificationDescription()
        InitMediaPlayer()
    }

    private fun addRunningTimer(intent: Intent) {
        val paramTitleKey = getString(R.string.notifications_parameter_title_key)
        val timerTitle = intent.getStringExtra(paramTitleKey) ?: ""
        if (timerTitle.isNotEmpty()) {
            runningTimers.add(timerTitle)
        }
    }

    private fun removeRunningTimer(intent: Intent) {
        val paramTitleKey = getString(R.string.notifications_parameter_title_key)
        val timerTitle = intent.getStringExtra(paramTitleKey) ?: ""
        if (timerTitle.isNotEmpty()) {
            runningTimers.remove(timerTitle)
        }
    }

    private fun timerComplete(intent: Intent) {
        val paramTitleKey = getString(R.string.notifications_parameter_title_key)
        val timerTitle = intent.getStringExtra(paramTitleKey) ?: ""
        if (timerTitle.isNotEmpty()) {
            completedTimers.add(timerTitle)
            runningTimers.removeAll{ it == timerTitle }
        }
        StartSound()
        updateNotificationDescription()
    }

    private fun timersInFocus() {
        StopSound()
        completedTimers.clear()
        updateNotificationDescription()
    }

    private fun stop() {
        if (runningTimers.count() > 0) {
            return
        }

        isServiceRunning = false
        runningTimers.clear()
        completedTimers.clear()
        mediaPlayer?.stop()
        mediaPlayer?.release()
        mediaPlayer = null
        notificationController?.CancelNotification(1)
        notificationController = null
        stopSelf()
    }
    //#endregion


    private fun InitMediaPlayer() {
        if (mediaPlayer == null) {
            val attr = AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_ALARM)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
                    .build()
            mediaPlayer = MediaPlayer.create(this, R.raw.alarm_sound, attr, 0)
            mediaPlayer?.isLooping = true
        }
    }

    private fun InitNotificationController() {
        if (notificationController == null) {
            notificationController = NotificationController(this)
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    private fun StartSound() {
        // Only start media player if not playing
        if (mediaPlayer?.isPlaying == false) {
            mediaPlayer?.start()
        }
    }

    private fun StopSound() {
        if (mediaPlayer?.isPlaying == true) {
            mediaPlayer?.pause()
        }
    }

    private fun createNotificationDescription(): String {
        return when {
            completedTimers.count() > 1 -> {
                "Multiple timers are completed."
            }
            completedTimers.count() == 1 -> {
                "${completedTimers.first()} is completed."
            }
            else -> {
                "You will be notified when a timer is complete."
            }
        }
    }

    private fun updateNotificationDescription() {
        if (!isServiceRunning) {
            return
        }

        val description = createNotificationDescription()
        // Oreo API 26+ requires a foreground notification for a service
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notification = notificationController?.CreateForegroundNotification(description)
            startForeground(1, notification)
        } else {
            val notification = notificationController?.CreateForegroundNotification(description)
            startForeground(1, notification)
        }
    }
}

使用最大40mb的图像文件进行测试