【问题标题】:Xamarin Forms DependencyService with Prism with Autofac crash - Unhandled ExceptionXamarin Forms DependencyService with Prism with Autofac 崩溃 - 未处理的异常
【发布时间】:2017-04-01 03:14:42
【问题描述】:

因此,为了继续我对 Xamarin 的首次尝试,我正在尝试开发一个可以拍摄照片的内容页面,然后将照片保存在设备库中以供查看。我正在将 Prism 与 A​​utofac 一起使用,我正在关注 wiki documentation on DependencyServiceGitHub 上提供的示例,但程序在没有解释原因的情况下崩溃。

我讨厌那个!

所以,这是我的界面:

   public interface ISavePicture
{
    void SavePictureToGallery(string path);
}

视图模型:

public class PluginPageViewModel : BindableBase
{
    private ISavePicture _savePicture;

    public PluginPageViewModel(ISavePicture savePicture)
    {
        try
        {
            TakePicCommand = new DelegateCommand(TakePicture);
            _savePicture = savePicture;
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);    
        }

    }

    public  ICommand TakePicCommand { get; private set; }

    private async void TakePicture()
    {
        try
            // Code here for getting the camera to take a picture ...

            _savePicture.SavePictureToGallery(filePath);
        }

        catch (Exception e)
        {
            Debug.WriteLine(e);
            throw;
        }

    }

  }
}

和 Android 代码:

using Android.App;
using Android.Content;
using Java.IO;
using RolodexDEMO_XF.Droid.Service;
using RolodexDEMO_XF.Services;
using Xamarin.Forms;
using Uri = Android.Net.Uri;

[assembly: Dependency(typeof(SavePicture_Android))]
namespace RolodexDEMO_XF.Droid.Service
{
    public class SavePicture_Android : Activity, ISavePicture
    {
        public void SavePictureToGallery(string path)
        {
            Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
            var file = new File(path);
            Uri contentUri = Uri.FromFile(file);
            mediaScanIntent.SetData(contentUri);
            SendBroadcast(mediaScanIntent);
        }
    }
}

请注意,我愿意 具有 DependencyService 的程序集属性。我还想指出,我没有使用模拟器来测试它。相反,我正在使用我的 Galaxy Note 4,因为我正在尝试测试相机。对于这部分,我使用的是 James Montemagno 的 Xamarin.Plugins,效果很好。我只是无法保存它,或者如果它确实保存到设备中,请查看图片。

那么我哪里错了?

更新:其他人问我要在我的 Android 应用中添加什么权限,所以在 AndroidManifest.xml 中:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" package="RolodexDEMOXF.RolodexDEMOXF">
    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application android:theme="@style/MyTheme" android:label="Rolodex DEMO">
        <provider android:name="android.support.v4.content.FileProvider" android:authorities="RolodexDEMOXF.fileprovider" android:exported="false" android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
        </provider>
    </application>
</manifest>

在 file_paths.xml 中(在 Resources\xml 目录中)

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/RolodexDEMOXF/files/Pictures" />
    <external-path name="my_movies" path="Android/data/RolodexDEMOXF/files/Movies" />
</paths>

【问题讨论】:

  • 您是否设置了所有必要的权限?它到底在哪里崩溃?
  • 嗨 Jason,我刚刚添加了清单文件以显示我所做的事情。到目前为止,在我为 DependencyService 添加代码之前,这些权限一直在为我工作。当我单击一个按钮时,它会启动相机应用程序,拍摄照片,如果您接受,它将显示在 XAML 中的 Image 控件中,该控件可作为 ImageSource 绑定到 VM。但是在我实现代码之后,它会启动,但是在我看到按钮之前就抛出了 Unhandled Execption!
  • 您需要使用调试器单步执行代码来识别崩溃的根源。我猜您的 Prism 或 AutoFac 设置存在问题,但由于您没有显示该代码,因此很难说。

标签: c# xamarin.android xamarin.forms autofac prism-6


【解决方案1】:

Prism 社区的各个成员都致力于使 Prism for Xamarin.Forms (Prism.Autofac.Forms) 的 Autofac 实现出色;所以使用这个 IoC 容器选项是一个不错的选择。问题是 Autofac 实现的第一个版本(用于 Prism.Forms)是在不知道 ContainerBuilder.Update() 方法已被弃用的情况下编写的;并且 Autofac 容器应该是不可变的(即构建一次而不更新)。

此外,Autofac 实现不具有通过 Xamarin.Forms DependencyService 解决依赖关系的内置功能,如果它们无法从 Autofac IoC 容器中解析。这是一个已知问题,不太可能得到解决,因为正如 Steve 指出的那样,正在重新考虑 Prism for Xamarin.Forms 和 DependencyService 之间的集成(并且可能已弃用)。

因此,使用 Autofac,您只需对 Xamarin.Forms 依赖项进行二次注册。你可以像史蒂夫展示的那样做——在你的IPlatformInitializer实现的RegisterTypes()方法中注册特定于平台的实现; 您可以使用 Xamarin.Forms DependencyService 在 App.RegisterTypes() 方法(在您的 App.xaml.cs 文件中)的共享 (PCL) 项目中注册它。

使用 Xamarin.Forms DependencyService 在您的共享项目中注册它看起来像这样(同样,在从 PrismApplication 继承的类中,通常是 App.xaml.cs 文件):

protected override void RegisterTypes()
{
   var builder = new ContainerBuilder();

   builder.Register(ctx => Xamarin.Forms.DependencyService.Get<ISavePicture>())
      .As<ISavePicture>();

   builder.Update(Container);
}

最后说明:上面提供的信息对于 Prism.Autofac.Forms 版本 6.3.0 是正确的。由于正在重新设计(可能针对 Prism 7.x)以消除在构建 Autofac 容器后更新它们的能力(即使它们不可变);似乎在未来的某个时候,上面的代码会变成:

protected override void RegisterTypes()
{
   Container.Register(ctx => Xamarin.Forms.DependencyService.Get<ISavePicture>())
      .As<ISavePicture>();
}

【讨论】:

  • 感谢杰里米的更新!什么时候会有这个问题的最终解决方案,是否会有进一步的文档来实现 Prism 中的 DependencySerivce?
【解决方案2】:

我在这里输入答案,以防其他人将来遇到与我相同的问题。此外,由于文档很少,我不得不从Chinese website(所有地方!)拼凑这个替代解决方案,并与 Slate Prism-Forms 频道上的一些人讨论,我认为最好把它那里有替代解决方案,所以至少你对解决 DependencyService 问题有一个基本的想法,以及使用 Autofac DI 的解决方法。

我确实想指出,Prism GitHub 上正在进行讨论,认为 Prism 的 DependencyService 实现应该被贬低,并通过我在这篇文章中描述的这个替代方案。因此,希望开发团队中的一个人将其记录下来,并就我在这里展示的内容提供更好的代码示例。

也就是说,节目继续……

好的,所以我找到了问题的答案。长话短说,问题出在 Autofac 容器上。

现在是冗长的版本。

根据我收集到的 Dan Siegel 的信息,在 Prism 可以为 IoC/DI 容器实现的所有容器中,我必须选择一个不能很好地与其他容器配合使用的容器!

为什么,我说它玩得不好?根据 Autofac 文档,the container is Immutable 这意味着它一旦构建就无法修改。所以我的假设是,当 Prism 通过项目并尝试添加 DependencyService 的注册类型时,Autofac 正在犹豫,因为容器已经构建,因此正在抛出“未处理的异常”。

这说明了问题,但不是解决方案。

解决方案是什么?好吧,事实证明,Brian (Lagunas) 和 Brian (Noyes) 已经实现了一个名为 IPlatformInitializer 的新接口,因此我别无选择,只能使用 ContainerBuilder.Update(container) 添加新的 RegisterType,它实现了额外的 RegisterTypes对于 DependencyService,我必须像这样实现它:

public class AndroidInitializer : IPlatformInitializer
{
    public void RegisterTypes(IContainer container)
    {
        var temp = new ContainerBuilder();

        temp.RegisterType<SavePicture_Android>().As<ISavePicture>();
        temp.RegisterType<MessageService_Android>().As<IMessageService>();
        // ... add more RegisterTypes as needed ... 

        temp.Update(container);
    }
}

此类已包含在 Prism 模板项目中。对于 Android,它位于 MainActivity.cs 文件中。

以防万一,iOS 和 UWP 也是如此。所以不要成为AndroidInitializer:

  • iOSInitializer : IPlatformInitializer (iOS)(在 AppDelegate.cs 内)
  • UWPInitializer:IPlatformInitializer (UWP)(在 MainPage.xaml.cs 中)

最后一件事:您可以转储 [Assembly Dependency(typeof(X))] 属性,因为它不再需要。 但您仍然需要进行构造函数依赖注入,正如他们在 DependencyService 文档中所述。

正如我所说,Prism 帮会在 Prism 的下一个版本中围绕 getting rid of their implementation of DependencyService 的想法,并沿着我向您解释过的这条路线前进。

值得注意的是,Autofac 的开发人员在 Autofac 4 的下一个版本发布时也是 discussing on getting rid of the ContainerBuilder.Update()

只是一些公平的警告,因为我在这里放的东西将来可能会消失。

希望对大家有所帮助!

【讨论】:

    猜你喜欢
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-07
    相关资源
    最近更新 更多