Xamarin Forms DependencyService with Prism with Autofac crash - 未处理的异常
Xamarin Forms DependencyService with Prism with Autofac crash - Unhandled Exception
因此,为了继续我对 Xamarin 的第一次尝试,我正在尝试开发一个内容页面,该页面将拍摄照片,然后将照片保存在设备图库中以供查看。我将 Prism 与 Autofac 一起使用,并且正在关注 wiki documentation on DependencyService and the examples that was provided on GitHub,但程序崩溃了,但没有解释原因。
我讨厌那个!
所以,这是我的界面:
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>
我把答案放在这里是为了防止以后有人遇到和我一样的问题。此外,由于文档很少,我不得不从 Chinese website(所有地方!)中拼凑这个替代解决方案,并与 Slate Prism-Forms 频道上的一些人讨论,我认为最好是将其放在替代解决方案中,这样至少您对解决 DependencyService 问题和使用 Autofac DI 的解决方法有了初步的了解。
我确实想指出,关于 Prism GitHub 的讨论正在进行,即 Prism 的 DependencyService 实现应该贬值,并通过我在此 post 中描述的替代方案。因此,希望开发团队中的一位成员将其记录下来,并就我在这里展示的内容提供更好的代码示例。
也就是说,继续表演...
好的,所以我找到了问题的答案。长话短说,问题出在 Autofac 容器上。
现在是冗长的版本。
根据我收集到的 Dan Siegel 的信息,在 Prism 可以为 IoC/DI 容器实施的所有容器中,我不得不选择与其他容器不兼容的那个!
为什么,我说它不好玩?根据 Autofac 文档,the container is Immutable 这意味着它一旦构建就无法修改。所以我的假设是,当 Prism 通过项目并尝试添加 DependencyService 的注册类型时,Autofac 犹豫不决,因为容器已经构建,因此 "Unhandled Exception" 被抛出。
这说明了问题,但没有解决方案。
解决方案是什么?好吧,事实证明 Brian (Lagunas) 和 Brian (Noyes) 已经实现了一个名为 IPlatformInitializer 的新接口,因此我别无选择,只能使用 ContainerBuilder.Update(container) 添加新的 RegisterType,它实现了DependencyService 的附加 RegisterTypes,我必须像这样实现它:
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);
}
}
此 class 已包含在 Prism 模板项目中。对于 Android,它在 MainActivity.cs 文件中。
以防万一你想知道,iOS 和 UWP 是一样的。所以不是 AndroidInitializer:
- iOS初始化程序:IPlatformInitializer (iOS)(在 AppDelegate.cs 内)
- UWPInitializer:IPlatformInitializer (UWP)(在 MainPage.xaml.cs 内)
最后一件事:您可以转储 [Assembly Dependency(typeof(X))] 属性,因为它不再需要了。 但是您仍然需要执行构造函数依赖注入,正如他们在 DependencyService 的文档中所述。
正如我所说,Prism 团队正在考虑 getting rid of their implementation of DependencyService 下一个 Prism 版本的想法,并沿着我已经向您解释过的这条路线前进。
另外值得注意的是,Autofac 的开发人员也在 discussing on getting rid of the ContainerBuilder.Update() Autofac 4 的下一个版本发布中。
只是一些公平的警告,因为我放在这里的内容将来可能会在 window 中消失。
希望对大家有所帮助!
Prism 社区的各个成员都致力于使 Xamarin.Forms (Prism.Autofac.Forms) 的 Prism Autofac 实现非常出色;所以使用这个 IoC 容器选项是个不错的选择。问题是 Autofac 实现的第一个版本(针对 Prism.Forms)是在不知道 ContainerBuilder.Update() 方法被弃用的情况下编写的;并且 Autofac 容器应该是不可变的(即构建一次并且不更新)。
此外,如果无法从 Autofac IoC 容器解析依赖项,Autofac 实现不具备通过 Xamarin.Forms DependencyService 解析依赖项的内置功能。这是一个已知问题,不太可能得到解决,因为 - 正如史蒂夫指出的那样 - 正在重新考虑 Xamarin.Forms 的 Prism 和 DependencyService 之间的集成(并且可能已弃用)。
因此,使用 Autofac,您只需对 Xamarin.Forms 依赖项执行二次注册。你可以像史蒂夫展示的那样做——在你的IPlatformInitializer
实现的RegisterTypes()
方法中注册特定于平台的实现; 或 您可以使用 Xamarin.Forms DependencyService 将其注册到 App.RegisterTypes()
方法中的共享 (PCL) 项目中(在您的 App.xaml.cs 文件).
使用 Xamarin.Forms DependencyService 在您的共享项目中注册它看起来像这样(同样,在继承自 PrismApplication 的 class 中,通常是 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>();
}
因此,为了继续我对 Xamarin 的第一次尝试,我正在尝试开发一个内容页面,该页面将拍摄照片,然后将照片保存在设备图库中以供查看。我将 Prism 与 Autofac 一起使用,并且正在关注 wiki documentation on DependencyService and the examples that was provided on GitHub,但程序崩溃了,但没有解释原因。
我讨厌那个!
所以,这是我的界面:
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>
我把答案放在这里是为了防止以后有人遇到和我一样的问题。此外,由于文档很少,我不得不从 Chinese website(所有地方!)中拼凑这个替代解决方案,并与 Slate Prism-Forms 频道上的一些人讨论,我认为最好是将其放在替代解决方案中,这样至少您对解决 DependencyService 问题和使用 Autofac DI 的解决方法有了初步的了解。
我确实想指出,关于 Prism GitHub 的讨论正在进行,即 Prism 的 DependencyService 实现应该贬值,并通过我在此 post 中描述的替代方案。因此,希望开发团队中的一位成员将其记录下来,并就我在这里展示的内容提供更好的代码示例。
也就是说,继续表演...
好的,所以我找到了问题的答案。长话短说,问题出在 Autofac 容器上。
现在是冗长的版本。
根据我收集到的 Dan Siegel 的信息,在 Prism 可以为 IoC/DI 容器实施的所有容器中,我不得不选择与其他容器不兼容的那个!
为什么,我说它不好玩?根据 Autofac 文档,the container is Immutable 这意味着它一旦构建就无法修改。所以我的假设是,当 Prism 通过项目并尝试添加 DependencyService 的注册类型时,Autofac 犹豫不决,因为容器已经构建,因此 "Unhandled Exception" 被抛出。
这说明了问题,但没有解决方案。
解决方案是什么?好吧,事实证明 Brian (Lagunas) 和 Brian (Noyes) 已经实现了一个名为 IPlatformInitializer 的新接口,因此我别无选择,只能使用 ContainerBuilder.Update(container) 添加新的 RegisterType,它实现了DependencyService 的附加 RegisterTypes,我必须像这样实现它:
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);
}
}
此 class 已包含在 Prism 模板项目中。对于 Android,它在 MainActivity.cs 文件中。
以防万一你想知道,iOS 和 UWP 是一样的。所以不是 AndroidInitializer:
- iOS初始化程序:IPlatformInitializer (iOS)(在 AppDelegate.cs 内)
- UWPInitializer:IPlatformInitializer (UWP)(在 MainPage.xaml.cs 内)
最后一件事:您可以转储 [Assembly Dependency(typeof(X))] 属性,因为它不再需要了。 但是您仍然需要执行构造函数依赖注入,正如他们在 DependencyService 的文档中所述。
正如我所说,Prism 团队正在考虑 getting rid of their implementation of DependencyService 下一个 Prism 版本的想法,并沿着我已经向您解释过的这条路线前进。
另外值得注意的是,Autofac 的开发人员也在 discussing on getting rid of the ContainerBuilder.Update() Autofac 4 的下一个版本发布中。
只是一些公平的警告,因为我放在这里的内容将来可能会在 window 中消失。
希望对大家有所帮助!
Prism 社区的各个成员都致力于使 Xamarin.Forms (Prism.Autofac.Forms) 的 Prism Autofac 实现非常出色;所以使用这个 IoC 容器选项是个不错的选择。问题是 Autofac 实现的第一个版本(针对 Prism.Forms)是在不知道 ContainerBuilder.Update() 方法被弃用的情况下编写的;并且 Autofac 容器应该是不可变的(即构建一次并且不更新)。
此外,如果无法从 Autofac IoC 容器解析依赖项,Autofac 实现不具备通过 Xamarin.Forms DependencyService 解析依赖项的内置功能。这是一个已知问题,不太可能得到解决,因为 - 正如史蒂夫指出的那样 - 正在重新考虑 Xamarin.Forms 的 Prism 和 DependencyService 之间的集成(并且可能已弃用)。
因此,使用 Autofac,您只需对 Xamarin.Forms 依赖项执行二次注册。你可以像史蒂夫展示的那样做——在你的IPlatformInitializer
实现的RegisterTypes()
方法中注册特定于平台的实现; 或 您可以使用 Xamarin.Forms DependencyService 将其注册到 App.RegisterTypes()
方法中的共享 (PCL) 项目中(在您的 App.xaml.cs 文件).
使用 Xamarin.Forms DependencyService 在您的共享项目中注册它看起来像这样(同样,在继承自 PrismApplication 的 class 中,通常是 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>();
}