【问题标题】:How to unload an image displayed on an Image object to free up memory?如何卸载 Image 对象上显示的图像以释放内存?
【发布时间】:2021-07-20 14:28:52
【问题描述】:

我正在开发一个 Xamarin.Forms Android 项目,该项目在按下按钮时一个接一个地显示图像。我当前的测试设备是 HTC Nexus 9,显然,由于其规格有限,在尝试加载我的第 17 张图片时出现以下错误:

java.lang.OutOfMemoryError: Failed to allocate a 11059212 byte allocation with 8286168 free bytes and 7MB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:620)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:455)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:1155)
    at android.content.res.ResourcesImpl.loadDrawableForCookie(ResourcesImpl.java:720)
    at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:571)
    at android.content.res.Resources.getDrawable(Resources.java:771)
    at android.content.Context.getDrawable(Context.java:525)
    at androidx.core.content.ContextCompat.getDrawable(ContextCompat.java:455)
    at androidx.appcompat.widget.ResourceManagerInternal.getDrawable(ResourceManagerInternal.java:144)
    at androidx.appcompat.widget.ResourceManagerInternal.getDrawable(ResourceManagerInternal.java:132)
    at androidx.appcompat.content.res.AppCompatResources.getDrawable(AppCompatResources.java:104)
    at crc64ee486da937c010f4.ButtonRenderer.n_onClick(Native Method)
    at crc64ee486da937c010f4.ButtonRenderer.onClick(ButtonRenderer.java:104)
    at android.view.View.performClick(View.java:5637)
    at android.view.View$PerformClick.run(View.java:22433)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6229)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781)

我尝试在.Android > Properties > Android Options > Advanced 下将Java Max Heap Size 设置为1G,但没有任何改变。

我找到了potential solution here,但显然,它用于基于 Java 的 Android 应用程序,我不知道在 Xamarin.Forms 的 Android 项目上哪里可以找到类似的清单文件。

下面是我使用最简单的 MVVM 实现的代码的 sn-p:

.xaml

<Image Source="{Binding ImgSource}"
       WidthRequest="960"
       HeightRequest="720" />
       

<!--#region Page1 Controls-->
<Button Command="{Binding Page1DhcpIconCommand}"
        IsVisible="{Binding Page1ControlVisible}"
        BackgroundColor="{Binding Page1ControlColor}"
        Opacity="0.25"
        RelativeLayout.XConstraint="57"
        RelativeLayout.YConstraint="679"
        RelativeLayout.WidthConstraint="46"
        RelativeLayout.HeightConstraint="37" />
<!--#endregion-->

<!--#region Page2 Controls-->
<Button Command="{Binding Page2RolesCommand}"
        IsVisible="{Binding Page2ControlVisible}"
        BackgroundColor="{Binding Page2ControlColor}"
        Opacity="0.25"
        RelativeLayout.XConstraint="24"
        RelativeLayout.YConstraint="85"
        RelativeLayout.WidthConstraint="46"
        RelativeLayout.HeightConstraint="15" />
<!--#endregion-->

<!--#region Page3 Controls-->
<Button Command="{Binding Page3AddRolesCommand}"
        IsVisible="{Binding Page3ControlVisible}"
        BackgroundColor="{Binding Page3ControlColor}"
        Opacity="0.25"
        RelativeLayout.XConstraint="720"
        RelativeLayout.YConstraint="199"
        RelativeLayout.WidthConstraint="71"
        RelativeLayout.HeightConstraint="21" />
<!--#endregion-->

ViewModel.cs

#region Backing Fields
string imgSource;

bool page1ControlVisible;
string page1ControlColor;

bool page2ControlVisible;
string page2ControlColor;

bool page3ControlVisible;
string page3ControlColor;
#endregion
#region Private Variables
private readonly bool _transparentOverride;
#endregion
#region Public Properties
public string ImgSource
{
    get => imgSource;
    set => SetProperty(ref imgSource, value);
}

#region Page1 Properties & Commands
public bool Page1ControlVisible
{
    get => page1ControlVisible;
    set => SetProperty(ref page1ControlVisible, value);
}
public string Page1ControlColor
{
    get => page1ControlColor;
    set => SetProperty(ref page1ControlColor, value);
}

public Command Page1DhcpIconCommand { get; }
#endregion

#region Page2 Properties & Commands
public bool Page2ControlVisible
{
    get => page2ControlVisible;
    set => SetProperty(ref page2ControlVisible, value);
}
public string Page2ControlColor
{
    get => page2ControlColor;
    set => SetProperty(ref page2ControlColor, value);
}

public Command Page2RolesCommand { get; }
#endregion

#region Page3 Properties & Commands
public bool Page3ControlVisible
{
    get => page3ControlVisible;
    set => SetProperty(ref page3ControlVisible, value);
}
public string Page3ControlColor
{
    get => page3ControlColor;
    set => SetProperty(ref page3ControlColor, value);
}

public Command Page3AddRolesCommand { get; }
#endregion
#endregion


public DHCPConfigurationViewModel()
{
    _transparentOverride = true;

    ImgSource = "img_dhcp_config_01.jpg";

    #region Page1 Properties & Commands
    Page1ControlVisible = true;
    Page1ControlColor = _transparentOverride ? "Transparent" : "Red";

    Page1DhcpIconCommand = new Command(Page1DhcpIcon);
    #endregion

    #region Page2 Properties & Commands
    Page2ControlVisible = false;
    Page2ControlColor = _transparentOverride ? "Transparent" : "Orange";

    Page2RolesCommand = new Command(Page2Roles);
    #endregion

    #region Page3 Properties & Commands
    Page3ControlVisible = false;
    Page3ControlColor = _transparentOverride ? "Transparent" : "Yellow";

    Page3AddRolesCommand = new Command(Page3AddRoles);
    #endregion
}

private void Page1DhcpIcon()
{
    ImgSource = "img_dhcp_config_02.jpg";

    Page1ControlVisible = false;
    Page2ControlVisible = true;
}

private void Page2Roles()
{
    ImgSource = "img_dhcp_config_03.jpg";

    Page2ControlVisible = false;
    Page3ControlVisible = true;
}

private void Page3AddRoles()
{
    ImgSource = "img_dhcp_config_04.jpg";

    Page3ControlVisible = false;
    Page4ControlVisible = true;
}

【问题讨论】:

  • 清单文件在Android项目的Properties文件夹中

标签: android image xamarin.forms


【解决方案1】:

对此,您可以查看文档Improve Xamarin.Forms App Performance

特别部分:Optimize image resources:

显示图像资源可以大大增加应用程序的 内存占用。因此,它们只应在需要时创建 并且应该在应用程序不再需要时立即发布 他们。例如,如果应用程序通过读取来显示图像 它来自流的数据,确保仅在创建流时才创建流 必需,并确保在不再释放流时释放流 必需的。这可以通过在页面打开时创建流来实现 创建,或者当 Page.Appearing 事件触发时,然后处理 Page.Disappearing 事件触发时的流。

使用ImageSource.FromUri 方法下载要显示的图像时,通过确保将UriImageSource.CachingEnabled 属性设置为true 来缓存下载的图像。

您还可以尝试 nuget Xamarin.FFImageLoading.Forms,这是一个 xamarin 库,可在 Xamarin.Forms 上快速轻松地加载图像。 更多详情请查看:https://github.com/luberda-molinet/FFImageLoading

【讨论】:

  • 嗨,据我了解。 Xamarin.FFImageLoading.Forms 和文档迎合来自Uri 的图像。但是,我的图像是存储在 .Android &gt; Resources &gt; Drawable 文件夹中的本地资源。我想尝试一下来自流的图像,但是如何通过 MVVM 模式实现(并在不再需要时处理)?
  • Nuget Xamarin.FFImageLoading.Forms 也可以用于本地资源。您也可以在这里查看示例代码:github.com/luberda-molinet/FFImageLoading/tree/master/samples
猜你喜欢
  • 1970-01-01
  • 2016-07-09
  • 2017-12-31
  • 1970-01-01
  • 1970-01-01
  • 2014-12-30
  • 1970-01-01
  • 2010-12-06
  • 2015-05-27
相关资源
最近更新 更多