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

Android应用报错:Could not copy bitmap to parcel blob 求助

问题:Android签名上传后崩溃,报错java.lang.RuntimeException: Could not copy bitmap to parcel blob

开发仓库管理Android(Java)应用,通过Volley的HTTP请求将员工签名图片上传至服务器,数据和图片已成功存储,但应用随后崩溃,报错java.lang.RuntimeException: Could not copy bitmap to parcel blob。未通过Intent传递图片,尝试过不传递idUsuario/nombreUsuario参数、上传后删除生成的图片,仍未解决。


相关代码

FirmaActivity代码

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.ctc.controlgarita.data.Empleado;
import com.github.gcacace.signaturepad.views.SignaturePad;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class FirmaActivity extends AppCompatActivity {

    String firmaEncoded;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_firma);

        Intent intent = getIntent();
        Button buttonBorrarFirma = findViewById(R.id.buttonBorrarFirma);
        Button buttonEnviarRegistro = findViewById(R.id.buttonEnviarRegistro);

        SignaturePad signaturePad = findViewById(R.id.signature_pad);
        signaturePad.setBackgroundColor(Color.WHITE);

        signaturePad.setOnSignedListener(new SignaturePad.OnSignedListener() {

            @Override
            public void onStartSigning() {
                // No se necesita
            }

            @Override
            public void onSigned() {
                buttonBorrarFirma.setEnabled(true);
                buttonEnviarRegistro.setEnabled(true);
            }

            @Override
            public void onClear() {
                buttonBorrarFirma.setEnabled(false);
                buttonEnviarRegistro.setEnabled(false);
            }
        });

        buttonBorrarFirma.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                signaturePad.clear();
            }
        });

        String nombreUsuario = intent.getStringExtra("nombreUsuario");
        String idUsuario = intent.getStringExtra("idUsuario");

        String tipoRegistro = intent.getStringExtra("tipoRegistro");
        String llave = intent.getStringExtra("llave");
        String corpo = intent.getStringExtra("corpo");
        String numeroMovil = intent.getStringExtra("numeroMovil");
        String numeroCorpo = intent.getStringExtra("numeroCorpo");
        String idEmpleado = intent.getStringExtra("idEmpleado");

        buttonEnviarRegistro.setOnClickListener(view -> {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            final Bitmap[] signatureBitmap = {signaturePad.getSignatureBitmap()};

            signatureBitmap[0].compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
            byte[] byteArray = byteArrayOutputStream.toByteArray();
            firmaEncoded = Base64.encodeToString(byteArray, Base64.DEFAULT);

            String url = "http://SOME_IP_ADDRESS/wsGarita/ingresarRegistro.php";
            StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
                    response -> {
                        try {
                            JSONObject jsonObject = new JSONObject(response);
                            boolean success = jsonObject.getBoolean("success");

                            if (success) {
                                Intent nextIntent = new Intent(FirmaActivity.this, FinRegistroActivity.class);

                                nextIntent.putExtra("nombreUsuario", nombreUsuario);
                                nextIntent.putExtra("idUsuario", idUsuario);

                                startActivity(nextIntent);

                            } else {
                                Toast.makeText(FirmaActivity.this, "ERROR al ingresar registro", Toast.LENGTH_LONG).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                            Toast.makeText(FirmaActivity.this, "ERROR al procesar JSON", Toast.LENGTH_LONG).show();
                        }
                    },
                    error -> {
                        Log.e("ErrorVolley", error.toString());
                    }) {
                @Override
                protected Map<String, String> getParams() {
                    // Parámetros para la solicitud POST
                    Map<String, String> params = new HashMap<>();
                    params.put("tipo_registro", tipoRegistro);
                    params.put("empleado_id", idEmpleado);
                    params.put("registro_llave", llave);
                    params.put("registro_corpo", corpo);
                    params.put("numero_movil", numeroMovil);
                    params.put("numero_corpo", numeroCorpo);
                    params.put("usuario_id", idUsuario);
                    params.put("firma", firmaEncoded);
                    return params;
                }
            };

            RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
            requestQueue.add(stringRequest);
        });
    }
}

FinRegistroActivity代码

public class FinRegistroActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fin_registro);

        Toolbar toolbar = findViewById(R.id.toolbar);
        TextView toolbarTitle = findViewById(R.id.toolbar_title);
        Intent intent = getIntent();

        String nombreUsuario = intent.getStringExtra("nombreUsuario");
        String idUsuario = intent.getStringExtra("idUsuario");

        if (toolbar != null) {
            setSupportActionBar(toolbar);
            if (getSupportActionBar() != null) {
                getSupportActionBar().setDisplayShowTitleEnabled(false); // Deshabilitar el título predeterminado

                if (nombreUsuario != null) {
                    toolbarTitle.setText(getString(R.string.toolbar_title, nombreUsuario));
                }
            }
        } // end if toolbar

        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Intent nextIntent = new Intent(FinRegistroActivity.this, RegistroActivity.class);

                nextIntent.putExtra("idUsuario", idUsuario);
                nextIntent.putExtra("nombreUsuario", nombreUsuario);

                startActivity(nextIntent);
            }
        }, 2500);
    }
}

堆栈跟踪

Reducing the number of considered missed Gc histogram windows from 407 to 100
2023-12-18 09:39:30.177  5179-5207  Parcel                  com.ctc.controlgarita                W  Expecting binder but got null!
2023-12-18 09:39:30.716  5179-5179  ActivityStopInfo        com.ctc.controlgarita                W  Bundle stats:
2023-12-18 09:39:30.719  5179-5179  ActivityStopInfo        com.ctc.controlgarita                W    android:viewHierarchyState [size=1428]
2023-12-18 09:39:30.722  5179-5179  ActivityStopInfo        com.ctc.controlgarita                W      android:views [size=1380]
2023-12-18 09:39:30.726  5179-5179  ActivityStopInfo        com.ctc.controlgarita                W    androidx.lifecycle.BundlableSavedStateRegistry.key [size=1228]
2023-12-18 09:39:30.726  5179-5179  ActivityStopInfo        com.ctc.controlgarita                W  PersistableBundle stats:
2023-12-18 09:39:30.726  5179-5179  ActivityStopInfo        com.ctc.controlgarita                W    [null]
2023-12-18 09:39:30.727  5179-5179  AndroidRuntime          com.ctc.controlgarita                D  Shutting down VM
2023-12-18 09:39:30.732  5179-5179  AndroidRuntime          com.ctc.controlgarita                E  FATAL EXCEPTION: main
                                                                                                    Process: com.ctc.controlgarita, PID: 5179
                                                                                                    java.lang.RuntimeException: Could not copy bitmap to parcel blob.
                                                                                                        at android.graphics.Bitmap.nativeWriteToParcel(Native Method)
                                                                                                        at android.graphics.Bitmap.writeToParcel(Bitmap.java:2140)
                                                                                                        at android.os.Parcel.writeParcelable(Parcel.java:2538)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2439)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2316)
                                                                                                        at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265)
                                                                                                        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802)
                                                                                                        at android.os.Bundle.writeToParcel(Bundle.java:1389)
                                                                                                        at android.os.Parcel.writeBundle(Parcel.java:1334)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2433)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2323)
                                                                                                        at android.os.Parcel.writeSparseArray(Parcel.java:1422)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2463)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2316)
                                                                                                        at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265)
                                                                                                        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802)
                                                                                                        at android.os.Bundle.writeToParcel(Bundle.java:1389)
                                                                                                        at android.os.Parcel.writeBundle(Parcel.java:1334)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2433)
                                                                                                        at android.os.Parcel.writeValue(Parcel.java:2323)
                                                                                                        at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265)
                                                                                                        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802)
                                                                                                        at android.os.Bundle.writeToParcel(Bundle.java:1389)
                                                                                                        at android.os.Parcel.writeTypedObject(Parcel.java:2157)
                                                                                                        at android.app.IActivityClientController$Stub$Proxy.activityStopped(IActivityClientController.java:1277)
                                                                                                        at android.app.ActivityClient.activityStopped(ActivityClient.java:86)
                                                                                                        at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:143)
                                                                                                        at android.os.Handler.handleCallback(Handler.java:942)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                        at android.os.Looper.loopOnce(Looper.java:201)
                                                                                                        at android.os.Looper.loop(Looper.java:288)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:7872)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
2023-12-18 09:39:30.750  5179-5179  Process                 com.ctc.controlgarita                I  Sending signal. PID: 5179 SIG: 9
---------------------------- PROCESS ENDED (5179) for package com.ctc.controlgarita ----------------------------
---------------------------- PROCESS STARTED (9849) for package com.ctc.controlgarita ----------------------------

解决方法

这个错误并非主动传递Bitmap导致的,而是系统在Activity销毁时自动保存视图状态(viewHierarchyState)时,尝试将SignaturePad中的Bitmap写入Parcel失败。从堆栈跟踪能看到,崩溃发生在activityStopped阶段,系统在保存Activity的状态Bundle,其中包含了视图层级的状态,而SignaturePad内部的Bitmap因为某种原因无法被序列化写入Parcel。

1. 禁止SignaturePad保存状态

在布局文件中给SignaturePad添加android:saveEnabled="false"属性,阻止系统自动保存它的状态:

<com.github.gcacace.signaturepad.views.SignaturePad
    android:id="@+id/signature_pad"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:saveEnabled="false" />

2. 在Activity停止时清空签名

在FirmaActivity中重写onStop()方法,在Activity停止前清空SignaturePad的内容,避免系统尝试保存Bitmap:

@Override
protected void onStop() {
    super.onStop();
    SignaturePad signaturePad = findViewById(R.id.signature_pad);
    if (signaturePad != null) {
        signaturePad.clear();
    }
}

3. 压缩签名Bitmap后再处理(可选优化)

虽然已经做了Base64编码,但获取签名Bitmap时可以先压缩尺寸,减少内存占用,也能降低系统保存状态时的压力:

// 替换原获取Bitmap的代码
Bitmap originalBitmap = signaturePad.getSignatureBitmap();
// 压缩到合适尺寸,比如宽高不超过500px
int maxSize = 500;
int width = originalBitmap.getWidth();
int height = originalBitmap.getHeight();
float scale = Math.min((float)maxSize/width, (float)maxSize/height);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, (int)(width*scale), (int)(height*scale), true);
// 后续用scaledBitmap进行压缩编码
scaledBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
// 用完后回收原Bitmap
originalBitmap.recycle();

为什么之前的尝试没用?

之前尝试不传递参数、删除生成的图片,但这些操作都没触及问题核心——系统自动保存Activity视图状态时处理SignaturePad的Bitmap失败,所以需要从阻止状态保存或清空Bitmap入手。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:14:50