Android - Bitmap.CreateBitmap - 空指针异常

Jak*_*sky 5 c# android bitmap xamarin.android xamarin

有时当我试图创建模糊的位图时,我得到"空指针异常".

发生在这个代码块中(我最近开始捕获异常,所以至少它不会崩溃应用程序):

try
{
    using (Bitmap.Config config = Bitmap.Config.Rgb565) {
        return Bitmap.CreateBitmap (blurredBitmap, width, height, config);
    }
}
catch (Java.Lang.Exception exception)
{
    Util.Log(exception.ToString());
}
Run Code Online (Sandbox Code Playgroud)

有关我传递给"CreateBitmap"方法的参数的更多详细信息,请参阅这些图片:

在此输入图像描述

这是扩展的参数:

在此输入图像描述

完全例外:

exception {Java.Lang.NullPointerException:抛出了类型'Java.Lang.NullPointerException'的异常.在/Users/builder/data/lanes/2058/58099c53/source/mono/mcs/class/corlib/System.Runtime.ExceptionServices/ExceptionDispatchInfo.cs:61中的System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()[0x0000b]在Android.Runtime.JNIEnv.CallStaticObjectMethod(IntPtr jclass,IntPtr jmethod,Android.Runtime.JValue*parms)[0x00064] in /Users/builder/data/lanes/2058/58099c53/source/monodroid/src/Mono.Android/ src/Runtime/JNIEnv.g.cs:1301位于/ Users/builder/data中的Android.Graphics.Bitmap.CreateBitmap(System.Int32 []颜色,Int32宽度,Int32高度,Android.Graphics.Config配置)[0x00088] /lanes/2058/58099c53/source/monodroid/src/Mono.Android/platforms/android-22/src/generated/Android.Graphics.Bitmap.cs:

不确定这可能是Xamarin中的错误还是传递的参数错误.

Jak*_*sky 2

我收到了 Xamarin 团队成员之一 Jonathan Pryor 的回复:

NullPointerException 来自 Java 代码:

在 android.graphics.Bitmap.createBitmap(Bitmap.java:687) 在 android.graphics.Bitmap.createBitmap(Bitmap.java:707) 在 dalvik.system.NativeStart.run(本机方法)

快速浏览各种版本,Jelly Bean 可能适合:

https://github.com/android/platform_frameworks_base/blob/jb-release/graphics/java/android/graphics/Bitmap.java#L687

    return nativeCreate(colors, offset, stride, width, height,
                        config.nativeInt, false);
Run Code Online (Sandbox Code Playgroud)

快速浏览一下周围的方法体就会发现,它config 没有检查空性,因此如果null通过,将导致 NullPointerException。

但问题是你没有传递 null:

using (Bitmap.Config config = Bitmap.Config.Rgb565) {
    return Bitmap.CreateBitmap (blurredBitmap, width, height, config);
}
Run Code Online (Sandbox Code Playgroud)

...或者你是吗?

我建议您删除using块:

return Bitmap.CreateBitmap (blurredBitmap, width, height,
        Bitmap.Config.Rgb565);
Run Code Online (Sandbox Code Playgroud)

我认为可能会发生以下情况,但首先,题外话:

在 Xamarin.Android 的核心深处,存在 Java 对象与其对应的 C# 包装器对象之间的映射。构造函数调用、Java.Lang.Object.GetObject()等都会创建映射;Java.lang.Object.Dispose() 将删除映射。

其中的核心部分是对象标识:当 Java 实例暴露给 C# 代码并创建 C# 包装器时,相同的C# 包装器实例应继续为该 Java 实例重用。

这样做的隐含结果是任何实例实际上都是 全局的,因为如果多个代码路径/线程/等。获取同一个 Java 实例的 JNI 句柄,它们将获得相同的 C# 包装器。

这让我们回到了我的假设和您的代码块:Bitmap.Config 是一个 Java 枚举,这意味着每个成员都是一个 Java 对象。此外,它们是全局值,因此每个线程都可以访问这些成员,这意味着 C# Bitmap.Config.Rgb565 实例实际上是一个全局变量。

您正在 Dispose() 的全局变量。

这“很好”,因为下次访问 Bitmap.Config.Rgb565 时,将创建一个新的包装器。

但问题是,如果您有多个线程同时访问 Bitmap.Config.Rgb565,每个线程都尝试 Dispose() 一个实例。此时,两个线程可能引用相同的包装器实例是完全合理的,并且一个线程中的 Dispose() 将使另一个线程使用的实例无效。

这将导致null被传递给 Bitmap.createBitmap() 调用,这正是您所观察到的。

请尝试删除该using块,看看是否有帮助。

整个线程可以在这里访问。

然后我问:

乔纳森·普赖尔 - 为这个建议欢呼。我的问题是,如果删除 using 语句,是否会导致内存泄漏?意思是如果我停止处理新的配置实例?

他回答:

这就是 GC 的用途!

(此处插入咳嗽和笑声。)

我们可以争论很多。我认为这不是内存泄漏,因为内存是根深蒂固且众所周知的;不断访问Bitmap.Config.Rgb565将返回之前创建的实例,而不是不断创建新实例。本身不存在“泄漏”。

相反,我会认为实例和底层 GREF 是一种“税”;它被“烧毁”,是做生意成本的一部分。虽然将这些成本最小化是“很好”,但删除所有这些成本是不切实际的(例如,我们通过 .class_ref “丢失”每个类的 GREF,它用于查找方法 ID...),至少不适用于当前的架构。

(我也想不出会导致不同成本/“税收”的替代架构。虽然我确实有一些关于允许某些领域进行改进的想法,但它们并不大。)

我建议不要过多担心 Bitmap.Config.Rgb565 和类似的成员,除非/直到探查器或 GREF 计数显示其他情况。