在Android中通过Intent传递活动之间的Bitmap数据

82

我在Activity1中有一个名为bmp的位图变量,我想将该位图发送到Activity2。

以下是我使用的代码来通过意图传递它。

Intent in1 = new Intent(this, Activity2.class);
in1.putExtra("image",bmp);
startActivity(in1);

在Activity2中,我尝试使用以下代码访问位图

Bundle ex = getIntent().getExtras();
Bitmap bmp2 = ex.getParceable("image");
ImageView result = (ImageView)findViewById(R.Id.imageView1);
result.setImageBitmap(bmp);

应用程序没有出现异常,但它没有给出预期的结果


3
这不是你的代码副本,因为我至少看到两个拼写错误。 - Christine
@Christine:这真的是我的代码,呵呵,但我从很多教程中学到了它... XP - adi.zean
2
那么你为什么要创建一个Bitmap bmp2,并使用setImageBitmap(bmp)来设置它呢?而且,R.Id.imageView1肯定不起作用。应该是R.id.imageView1。 - Christine
3
当然,您可以将位图写入文件,并在第二个活动中读取此文件。您可以使用同一文件来确保图像在设备旋转时保持不变。 - Christine
1
在发布问题之前,请确保您理解您要发布的代码,从StackOverflow复制并粘贴以修复错误是无用的。@Christine - 我正要评论关于拼写错误的同样事情。 - milosmns
可能是重复的问题:如何将位图对象从一个活动传递到另一个活动 - AdamHurwitz
8个回答

230

在将其添加到 intent、发送出去并解码之前,将它转换为字节数组。

//Convert to byte array
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();

Intent in1 = new Intent(this, Activity2.class);
in1.putExtra("image",byteArray);

然后在第二个Activity中:

byte[] byteArray = getIntent().getByteArrayExtra("image");
Bitmap bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);

编辑

我觉得应该更新这篇文章,分享最佳实践:

在你的第一个活动中,你应该将位图保存到磁盘上,然后在下一个活动中重新加载它。确保在第一个活动中回收位图以便进行垃圾回收:

活动1:

try {
    //Write file
    String filename = "bitmap.png";
    FileOutputStream stream = this.openFileOutput(filename, Context.MODE_PRIVATE);
    bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
    
    //Cleanup
    stream.close();
    bmp.recycle();

    //Pop intent
    Intent in1 = new Intent(this, Activity2.class);
    in1.putExtra("image", filename);
    startActivity(in1);
} catch (Exception e) {
    e.printStackTrace();
}

在第二个Activity中,加载位图:

Bitmap bmp = null;
String filename = getIntent().getStringExtra("image");
try {
    FileInputStream is = this.openFileInput(filename);
    bmp = BitmapFactory.decodeStream(is);
    is.close();
} catch (Exception e) {
    e.printStackTrace();
}

8
这解决了我的一个问题。因为发送了一个完整的位图,导致抛出了“transactionTooLargeException”的异常。 - Reinherd
1
我在想如何在 Fragment 中使用 getIntent()?我已经看到这是不可能的,但肯定有办法。 - StillLearningToCode
谢谢你的回答。我不明白为什么将位图写入文件并稍后读取比发送字节数组更好? - ahmed_khan_89
1
字节数组 = 内存中的3个图像(首先是bmp,然后是字节数组,最后是bmp) 文件 = 内存中的2个图像(首先是bmp,然后是文件,最后是bmp) - Zaid Daghestani
1
我强烈建议避免使用字节数组,因为你可能会达到2MB的限制并且导致内存泄漏。我建议创建一个临时文件,并通过操作处理其路径。 - Agna JirKon Rx
显示剩余3条评论

13

有时,位图可能太大而无法进行编码和解码,或者以字节数组的形式在意图中传递。这可能会导致OOM或糟糕的用户界面体验。

我建议考虑将位图放入新活动的静态变量中(使用该位图的活动),并在不再需要它时仔细将其设置为null(即在onDestroy中,但仅当“isChangingConfigurations”返回false时)。


4

我们可以仅传递位图的Uri而不是传递整个位图对象。如果位图对象过大,会导致内存问题。

FirstActivity。

intent.putExtra("uri", Uri);

从SecondActivity中获取位图。

Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),Uri.parse(uri));

当我在URI中传递图像路径时,出现了IO异常。可能的原因是什么? - Dilip Poudel

2

使用 Kotlin 代码通过 Intent 将 Bitmap 发送到另一个 Activity:

1- 在第一个 Activity 中:

          val i = Intent(this@Act1, Act2::class.java)
           var bStream  =  ByteArrayOutputStream()
            bitmap.compress(Bitmap.CompressFormat.PNG, 50, bStream)
            val byteArray = bStream.toByteArray()
            i.putExtra("image", byteArray )
            startActivity(i)

2- 在第二个活动中(读取位图图像):

 var bitmap : Bitmap? =null
    if (intent.hasExtra("image")){
      //convert to bitmap          
        val byteArray = intent.getByteArrayExtra("image")
        bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
    }

3- 如果你需要将它设置为一个视图的背景,比如 ConstraintLayout 或者其他 ... :

 if (bitmap != null) {
     //Convert bitmap to BitmapDrawable
     var bitmapDrawable = BitmapDrawable( resources , bitmap)
      root_constraintLayout.backgroundDrawable = bitmapDrawable
   }

谷歌刚刚宣布由于用户采用率不高,他们将停止Kotlin的开发。 - CaptainCrunch

1

创建一个单例类来存储位图数据

public final class BitmapData {
    private static final BitmapData bitmapData = new BitmapData();
    private Bitmap bitmap;

    private BitmapData() {
    }

    public static BitmapData getInstance() {
        return bitmapData;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public void setBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
    }

在第一个活动中,您可以这样设置。

BitmapData.getInstance().setBitmap(bitmap);

在第二个活动中

Bitmap bitmap = BitmapData.getInstance().getBitmap();

注意:检查空值。

class BitmapData private constructor() { var bitmap: Bitmap? = nullcompanion object { val instance = BitmapData() }}以上是上述代码的 Kotlin 版本。谢谢。 - tbfp

0
我想为那些想要这样做的人(但可能问错了问题)也发布一个最佳实践答案。
不要传递位图(我假设您从网络上下载了它,否则,您已经有了文件引用),我建议使用像Universal Image Loader这样的图像加载器将图像下载到ImageView中。您可以配置它将图像缓存到磁盘中:
 DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
                .cacheInMemory(true)
                .cacheOnDisk(true)
                .considerExifParams(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .build();
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
                .defaultDisplayImageOptions(defaultOptions)
                .build();

        ImageLoader.getInstance().init(config);

现在,只需在您的意图中传递图像URL并使用UIL加载图像即可。例如,在您新创建的活动中,图像将立即加载,因为它是从缓存中加载的 - 即使您的图像URL自下载以来已过期。

0
我们也可以不通过Intent传递数据来解决这个问题。只需将图像存储在内存中,在下一个Activity中从该位置加载图像,这也可以避免因传递大型位图数据而导致应用程序崩溃。 注意:您甚至不需要将位置路径传递给Intent,记住路径并使用它即可。

-7
Bundle b = new Bundle();
b.putSerializable("Image", Bitmap);
mIntent.putExtras(b);
startActivity(mIntent);

8
位图不可序列化! - Carnal
请查看此链接:https://dev59.com/BW025IYBdhLWcg3wqn4N - Prashant Mishra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接