简体   繁体   English

Xamarin使用Prism导致Autofac崩溃而形成DependencyService-未处理的异常

[英]Xamarin Forms DependencyService with Prism with Autofac crash - Unhandled Exception

So to continue on with my first foray into Xamarin, I'm trying to develop an Content page that will take a photo, and then save the photo on the device gallery for viewing. 因此,为了继续我的Xamarin尝试,我正在尝试开发一个将拍摄照片的“内容”页面,然后将照片保存在设备库中以供查看。 I'm using Prism with Autofac and I'm following the wiki documentation on DependencyService and the examples that was provided on GitHub , but the program is crashing without explaining why. 我正在将Prism与Autofac一起使用,并且正在遵循DependencyService上Wiki文档以及GitHub上提供的示例,但是该程序崩溃了而未说明原因。

I hate that! 我讨厌那个!

So, here's my interface: 所以,这是我的界面:

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

ViewModel: ViewModel:

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;
        }

    }

  }
}

and the Android code: 和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);
        }
    }
}

Notice that I DO have the assembly attribute for the DependencyService. 请注意, 确实具有DependencyService的Assembly属性。 I also wanted to note that I'm not using the emulator to test it out. 我还想指出,我没有使用仿真器对其进行测试。 Instead, I'm using my Galaxy Note 4 since I'm trying to test out the camera. 相反,我正在使用我的Galaxy Note 4,因为我正在尝试测试相机。 For that part, I'm using Xamarin.Plugins from James Montemagno and that's working fine. 对于这一部分,我使用的是James Montemagno的Xamarin.Plugins,效果很好。 I just can't save it, or see the pic if it is indeed saved to the device. 我只是无法保存它,或者查看图片是否确实保存到设备中。

So where am I going wrong with it? 那么,我在哪里出错呢?

UPDATE: I was asked by others on what permissions I'm putting into my Android app, so in the AndroidManifest.xml: 更新:有人问我要在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>

and in the file_paths.xml (in the Resources\\xml directory) 并在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>

I'm putting in the answer here just in case someone else has the same problem as I do in the future. 我在这里输入答案,以防其他人遇到与我将来相同的问题。 Also, since there's little documentation, and I had to cobble together this alternative solution from a Chinese website (of all places!) and discussion with some of the guys on Slate Prism-Forms channel, I thought it would be best to put it out there on the alternative solution so at least you get an rudimentary idea on to resolve the DependencyService issues, and workarounds using Autofac DI. 另外,由于文档很少,我不得不从一个中文网站 (到处都是)拼凑这个替代解决方案,并与Slate Prism-Forms频道的一些人讨论,我认为最好把它发布出来那里有替代解决方案,因此至少您可以初步了解解决DependencyService问题以及使用Autofac DI的解决方法。

I do want to note that there is discussion going on the Prism GitHub that Prism's implementation of DependencyService should be depreciated and go through this alternative that I'm describing here in this post. 我确实要注意,在Prism GitHub上正在进行讨论,即应该弃用Prism的DependencyService的实现,并通过本文将在此介绍的替代方法进行讨论。 So hopefully one of the guys on the development team will document it and give better code examples on what I'm showing here. 因此,希望开发团队中的一员能够对其进行记录,并针对我在此处显示的内容提供更好的代码示例。

That said, on with the show... 也就是说,在演出中...

Okay, so I found out the answer to the problem. 好的,所以我找到了问题的答案。 Long story short, the problem is the Autofac container. 长话短说,问题是Autofac容器。

And now the long winded version. 现在是长篇版。

From what I gathered Dan Siegel, of all the containers that Prism can implement for a IoC/DI container, I had to pick the one that doesn't play well with others! 从我收集的Dan Siegel的东西中,Prism可以为IoC / DI容器实现的所有容器中,我必须选择一个不能与其他容器配合的容器!

Why, do I say that it doesn't play well? 为什么,我说它播放不好? According to the Autofac documentation, the container is Immutable which means it cannot be modified once it's been built. 根据Autofac文档, 容器是不可变的 ,这意味着一旦构建容器便无法对其进行修改。 So my hypothesis is that when Prism goes through the project and tries to add in the registration types of DependencyService, Autofac is balking because the container is already built and therefore the "Unhandled Exception" is being thrown. 因此,我的假设是,当Prism遍历该项目并尝试添加DependencyService的注册类型时,Autofac陷入困境,因为该容器已被构建,因此将引发“未处理的异常”。

That explains the issue, but not the solution. 这说明了问题,但没有解决方案。

And what is the solution? 解决方案是什么? Well, it turns out that Brian (Lagunas) and Brian (Noyes) have implemented a new interface called IPlatformInitializer, and I therefore have no choice but to use ContainerBuilder.Update(container) to add in the new RegisterType, which implements the additional RegisterTypes for the DependencyService, and I had to implement it like this: 好吧,事实证明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);
    }
}

This class is already included in the Prism template project. 此类已包含在Prism模板项目中。 For Android, it's in MainActivity.cs file. 对于Android,它位于MainActivity.cs文件中。

Just in case you're wondering, it's the same for iOS and UWP. 万一您想知道,iOS和UWP也是一样。 So instead of being AndroidInitializer: 因此,与其成为AndroidInitializer:

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

On last thing: You can dump the [Assembly Dependency(typeof(X))] attribute since it is no longer needed. 最后一件事:您可以转储[Assembly Dependency(typeof(X))]属性,因为不再需要它。 But you still need to do constructor dependency injection as they stated in their documentation for DependencyService. 但是,您仍然需要按照其在DependencyService文档中所述的方式进行构造函数依赖项注入

As I said, the Prism gang is kicking around the idea of getting rid of their implementation of DependencyService on the next build of Prism and go down this route that I've explained to you. 正如我说的,Prism团伙正在摆脱在Prism的下一个版本上摆脱其对DependencyService的实现的想法,并按照我向您解释的方法走下去。

It's also interesting to note is that the guys over Autofac are ALSO discussing on getting rid of the ContainerBuilder.Update() on the next version release of Autofac 4. 还值得注意的是,Autofac上的家伙还在讨论在下一版本的Autofac 4 上摆脱ContainerBuilder.Update()的问题。

Just some fair warning, because of what I've put here may go out the window in the future. 只是一些合理的警告,因为我在这里放的内容可能会在将来消失。

I hope it helps someone out! 我希望它可以帮助某人!

Various members of the Prism community are committed to making the Autofac implementation of Prism for Xamarin.Forms (Prism.Autofac.Forms) excellent; Prism社区的各个成员致力于使Xamarin.Forms(Prism.Autofac.Forms)的Autofac实现出色。 so using this IoC container option is not a bad choice. 因此使用此IoC容器选项不是一个坏选择。 The issue has been that the first version of the Autofac implementation (for Prism.Forms) was written without the knowledge that the ContainerBuilder.Update() method was being deprecated; 问题在于,在不了解不推荐使用ContainerBuilder.Update()方法的情况下编写了Autofac实现的第一个版本(用于Prism.Forms)。 and that Autofac containers should be immutable (ie built once and not updated). 并且Autofac容器应该是不可变的(即,一次构建且不更新)。

Also, the Autofac implementation does not have the built-in ability to resolve dependencies via the Xamarin.Forms DependencyService, if they cannot be resolved from the Autofac IoC container. 另外,如果无法从Autofac IoC容器解析依赖关系,则Autofac实现不具有通过Xamarin.Forms DependencyService解析依赖关系的内置功能。 This is a known issue and is not likely to be fixed, since - as Steve points out - the integration between Prism for Xamarin.Forms and the DependencyService is being re-considered (and possibly deprecated). 这是一个已知问题,不可能解决,因为-正如Steve指出的那样-Xamarin.Forms的Prism与DependencyService之间的集成正在重新考虑(并且可能已弃用)。

So, with Autofac, you just need to perform a secondary registration of your Xamarin.Forms dependency. 因此,使用Autofac,您只需要对Xamarin.Forms依赖项执行二级注册。 You can do it as Steve showed - registering platform-specific implementations in the RegisterTypes() method of your IPlatformInitializer implementation; 您可以按照Steve的说明进行操作-在IPlatformInitializer实现的RegisterTypes()方法中RegisterTypes()特定于平台的实现; or you can use the Xamarin.Forms DependencyService to register it in the shared (PCL) project in the App.RegisterTypes() method (in your App.xaml.cs file). 或者您可以使用Xamarin.Forms DependencyService在注册它的共享(PCL)项目App.RegisterTypes()方法(在App.xaml.cs文件)。

Using the Xamarin.Forms DependencyService to register it in your shared project would look like this (again, in class that inherits from PrismApplication, which is generally the App.xaml.cs file): 使用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);
}

Final note: The information provided above is correct for version 6.3.0 of Prism.Autofac.Forms. 最后说明:上面提供的信息对于Prism.Autofac.Forms的6.3.0版本是正确的。 Since things are being re-worked (possibly for Prism 7.x) to remove the ability to update Autofac containers after they are built (ie make them immutable); 由于正在对某些东西进行重新处理(可能是针对Prism 7.x),因此在构建Autofac容器后便无法更新它们(即使其不可变); it seems likely that at some point in the future, the code above would change to: 似乎在将来的某个时候,以上代码将更改为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM