#xamarin.forms #push-notification #xamarin.android #firebase-cloud-messaging #prism
#xamarin #xamarin.forms #xamarin.android #unity-контейнер #prism
Вопрос:
Я новичок в Xamarin и Prism, поэтому простите меня, если я упускаю что-то очевидное. Я следую примеру этого проекта в GitHub. Однако при запуске моего проекта Android я получаю следующую ошибку.
Система.Исключение InvalidOperationException: текущий тип, MyApp.Abstractions.IFacebookManager, является интерфейсом и не может быть сконструирован. Вам не хватает сопоставления типов?
Ошибка возникает в EntryPage.xaml.g.cs
в LoadFromXaml()
методе.
[global::Xamarin.Forms.Xaml.XamlFilePathAttribute("Views\EntryPage.xaml")]
public partial class EntryPage : global::Xamarin.Forms.ContentPage {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "2.0.0.0")]
private void InitializeComponent() {
// Exception thrown here!
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(EntryPage));
}
}
Это наводит меня на мысль, что я неправильно использую контейнер IoC, но я не вижу, что я делаю иначе, чем в примере. EntryPageViewModel
Конструктор принимает IFacebookManager
в качестве параметра, и я понимаю, что Unity должен позаботиться об этом. У меня есть точка останова в конструкторе, но она не попадает. Это решение Xamarin .NET Standard. Я был бы признателен за любую помощь и указания. Спасибо!
Вот мой App.xaml.cs
using MyApp.Abstractions;
using MyApp.Helpers;
using MyApp.Services;
using MyApp.ViewModels;
using MyApp.Views;
using Prism;
using Prism.Ioc;
using Prism.Unity;
using Xamarin.Forms;
namespace MyApp
{
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
ServiceResolver.Instance.Add<ICloudService, AzureCloudService>();
NavigationService.NavigateAsync("NavigationPage/EntryPage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<EntryPage, EntryPageViewModel>();
}
}
}
Вот мой MainActivity.cs
using Android.App;
using Android.Content.PM;
using Android.OS;
using MyApp.Droid.Services;
using MyApp.Abstractions;
using Android.Content;
using Xamarin.Facebook;
using Xamarin.Forms;
using Prism;
using Prism.Ioc;
namespace MyApp.Droid
{
[Activity(Label = "MyApp", Icon = "@drawable/icon", Theme = "@style/MainTheme", Name = "com.mydomain.myapp.MainActivity", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
ICallbackManager fbCallbackManager;
AndroidLoginProvider loginProvider;
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();
global::Xamarin.Forms.Forms.Init(this, bundle);
DependencyService.Register<IFacebookManager, FacebookManager>();
LoadApplication(new App(new AndroidInitializer()));
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
var manager = DependencyService.Get<IFacebookManager>();
if (manager != null)
{
(manager as FacebookManager)._callbackManager.OnActivityResult(requestCode, (int)resultCode, data);
}
}
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
Ответ №1:
Вы смешиваете службу зависимостей Xamarin Forms со службой, предоставляемой с Prism.Unity
Вместо вызова DependencyService.Register<IFacebookManager, FacebookManager>();
Вам нужно зарегистрировать его в AndroidInitializer
.
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry container)
{
container.RegisterSingleton<IFacebookManager, FacebookManager>();
}
}
Затем вы всегда можете разрешить зависимость вручную, вызвав App.Container.Resolve
inside OnActivityResult
.
Комментарии:
1. Я думал об этом, но пытался следовать примеру. В любом случае, этот подход кажется хорошим, но я не могу вызвать
App.Container.Resolve<IFacebookManager>()
, потому что я получаю ошибку:An object reference is required for the non-static field, method, or property 'PrismApplicationBase.Container.'
Я предполагаю, чтоApp
здесь должна быть ссылка на экземплярApp
класса, а не ссылка на сам класс. Есть ли простой способ получить ссылку на текущий экземпляр приложения из моего проекта Android?2. @NSouth Да, вы можете вызвать ((приложение) Xamarin. Формы. Приложение. Текущий). Container.Resolve()
3. Я должен указать здесь, что Prism 7.1, который имеет ContainerProvider<T> для разрешения типов в XAML, также переопределяет статическое текущее свойство, чтобы вы могли сделать
App.Current.Container.Resolve<T>()
. Также вы не должны использовать ServiceLocator или службу зависимостей XF, поскольку это создает анти-шаблон с несколькими контейнерами.
Ответ №2:
Чтобы завершить предыдущий ответ, который мне очень помог решить аналогичную проблему: с prism 7 вам необходимо разрешить зависимость с помощью IContainerProvider
следующим образом:
var container = (App.Current as Prism.Unity.PrismApplication).Container;
var manager = container.Resolve<IFacebookManager>();
В этом случае Unity используется в качестве «контейнера» IoC
Комментарии:
1. Есть ли что-то вроде «универсального» контейнера?
2. Извините, не могли бы вы объяснить свой вопрос, я не понимаю, что вы имеете в виду
3. Вместо указания, какое
IoC
пространство имен я буду использовать, есть что-то вроде: (App.Current как Prism. Универсальное приложение PRISM). Контейнер?4. Я бы сказал, что нет, вы не можете. Но обратите внимание, что теперь, вероятно, из-за новой версии Prism (7.1), вы можете получить свой контейнер таким образом:
container=PrismApplicationBase.Current.Container;
Это позволяет вам получить его, не указывая, какой IoC вы будете использовать.5. Отлично! Спасибо за эту информацию 🙂 Я бы хотел, чтобы они включили это в свои примечания к выпуску.