You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AsyncTask图片压缩报错:FATAL EXCEPTION: AsyncTask #3(待传Firebase Storage)

解决AsyncTask图片压缩时的FATAL EXCEPTION问题

嘿,我帮你排查下这个AsyncTask崩溃的问题,看了你的代码片段,有几个明显的坑需要填上,还有一些潜在的风险会导致崩溃,我来一步步帮你解决:

先揪出代码里的致命问题

  1. 参数类型完全不匹配:你的AsyncTask泛型声明是<Uri, Integer, byte[]>,但构造函数却传入了Bitmap,doInBackground接收的是Uri参数,但你实际要处理的是构造里存的Bitmap——这种逻辑混乱很容易导致空指针或者类型转换异常,直接触发崩溃。
  2. Activity上下文泄漏:在onPreExecute里直接用NextActivity.this,AsyncTask会持有Activity的强引用,如果Activity在压缩任务完成前被销毁(比如用户按了返回键),就会导致内存泄漏,甚至因为上下文失效触发崩溃。
  3. 潜在的OOM风险:处理大图时如果没做Bitmap回收,很容易触发内存溢出,这也是AsyncTask崩溃的高频原因。

修正后的完整AsyncTask代码

我重新写了一个更健壮的版本,解决了上面的问题,还加入了回调接口解耦逻辑:

import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.lang.ref.WeakReference;

public class BackgroundImageResize extends AsyncTask<Void, Integer, byte[]> {
    private final WeakReference<Context> mContextRef;
    private final Bitmap mBitmap;
    private OnCompressListener mListener;

    // 定义回调接口,把压缩结果传递给上层
    public interface OnCompressListener {
        void onCompressSuccess(byte[] compressedData);
        void onCompressFailed(Exception e);
    }

    public BackgroundImageResize(Context context, Bitmap bitmap, OnCompressListener listener) {
        mContextRef = new WeakReference<>(context);
        mBitmap = bitmap;
        mListener = listener;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Context context = mContextRef.get();
        if (context != null) {
            Toast.makeText(context, "Compressing image...", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected byte[] doInBackground(Void... params) {
        if (mBitmap == null) {
            return null;
        }
        try {
            // 双重压缩策略:先质量压缩,太大再尺寸压缩
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            // 质量压缩(JPEG格式,70%质量,可调整)
            mBitmap.compress(Bitmap.CompressFormat.JPEG, 70, baos);
            
            int fileSize = baos.size();
            // 如果超过100KB,循环缩小尺寸(可根据需求调整阈值)
            while (fileSize > 1024 * 100) {
                baos.reset();
                // 把图片尺寸缩小一半
                Bitmap resizedBitmap = Bitmap.createScaledBitmap(mBitmap,
                        mBitmap.getWidth() / 2, mBitmap.getHeight() / 2, true);
                resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, baos);
                // 回收临时Bitmap,避免OOM
                if (!resizedBitmap.isRecycled() && resizedBitmap != mBitmap) {
                    resizedBitmap.recycle();
                }
                fileSize = baos.size();
            }
            // 回收原始Bitmap(如果后续不再使用)
            if (!mBitmap.isRecycled()) {
                mBitmap.recycle();
            }
            return baos.toByteArray();
        } catch (Exception e) {
            Log.e("BackgroundResize", "Image compression failed", e);
            return null;
        }
    }

    @Override
    protected void onPostExecute(byte[] result) {
        super.onPostExecute(result);
        Context context = mContextRef.get();
        
        if (context != null && result != null) {
            Toast.makeText(context, "Compression completed", Toast.LENGTH_SHORT).show();
            // 回调给上层处理上传逻辑
            if (mListener != null) {
                mListener.onCompressSuccess(result);
            }
        } else {
            if (mListener != null) {
                mListener.onCompressFailed(new Exception("Compression failed or context is invalid"));
            }
        }
    }
}

如何在Activity中使用

// 假设你已经获取到了要压缩的Bitmap对象
Bitmap originalBitmap = ...;

new BackgroundImageResize(this, originalBitmap, new BackgroundImageResize.OnCompressListener() {
    @Override
    public void onCompressSuccess(byte[] compressedData) {
        // 压缩成功,调用上传到Firebase的方法
        uploadImageToFirebase(compressedData);
    }

    @Override
    public void onCompressFailed(Exception e) {
        Toast.makeText(NextActivity.this, "Compression failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();
    }
}).execute();

上传到Firebase Storage的示例代码

import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;

import java.io.ByteArrayInputStream;

private void uploadImageToFirebase(byte[] compressedData) {
    // 获取Firebase Storage引用
    StorageReference storageRef = FirebaseStorage.getInstance().getReference();
    // 生成唯一的文件名(用时间戳避免重复)
    StorageReference imageRef = storageRef.child("compressed_images/" + System.currentTimeMillis() + ".jpg");

    ByteArrayInputStream inputStream = new ByteArrayInputStream(compressedData);
    UploadTask uploadTask = imageRef.putStream(inputStream);

    // 监听上传结果
    uploadTask.addOnSuccessListener(taskSnapshot -> {
        // 上传成功,获取下载URL
        imageRef.getDownloadUrl().addOnSuccessListener(downloadUri -> {
            String imageUrl = downloadUri.toString();
            Toast.makeText(this, "Upload successful! URL: " + imageUrl, Toast.LENGTH_LONG).show();
            // 这里可以把URL保存到Firestore或者SharedPreferences
        });
    }).addOnFailureListener(e -> {
        Toast.makeText(this, "Upload failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        Log.e("FirebaseUpload", "Upload failed", e);
    });
}

额外推荐:用Coroutines替代AsyncTask(更现代的方案)

Android 11之后AsyncTask已经被废弃,推荐用Kotlin Coroutines或者Java的Executor来处理后台任务,代码更简洁且更安全。这里给你一个Kotlin Coroutines的示例:

import android.graphics.Bitmap
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

// 在Activity中调用
lifecycleScope.launch {
    Toast.makeText(this@NextActivity, "Compressing image...", Toast.LENGTH_SHORT).show()
    val compressedData = withContext(Dispatchers.IO) {
        compressImage(originalBitmap)
    }
    if (compressedData != null) {
        Toast.makeText(this@NextActivity, "Compression done", Toast.LENGTH_SHORT).show()
        uploadImageToFirebase(compressedData)
    } else {
        Toast.makeText(this@NextActivity, "Compression failed", Toast.LENGTH_SHORT).show()
    }
}

// 压缩函数
private fun compressImage(bitmap: Bitmap): ByteArray? {
    return try {
        val baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 70, baos)
        var fileSize = baos.size()
        while (fileSize > 1024 * 100) {
            baos.reset()
            val resizedBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.width / 2, bitmap.height / 2, true)
            resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, baos)
            if (!resizedBitmap.isRecycled && resizedBitmap != bitmap) {
                resizedBitmap.recycle()
            }
            fileSize = baos.size()
        }
        bitmap.recycle()
        baos.toByteArray()
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

内容的提问来源于stack exchange,提问作者Aditya Sissodiya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:03:25