使用 Prism 在 Xamarin Forms 的后台服务中实现依赖注入

Posted

技术标签:

【中文标题】使用 Prism 在 Xamarin Forms 的后台服务中实现依赖注入【英文标题】:Implement dependency injection in background services in Xamarin Forms using Prism 【发布时间】:2017-12-19 05:20:52 【问题描述】:

我在我的 xamarin 表单项目中使用 Prism。我能够在我的视图模型中使用依赖注入(构造函数注入)而没有任何问题。我还利用后台服务在后台推送长时间运行的任务.如何在我的后台服务中注入依赖项?当我尝试将接口对象作为参数传递给构造函数(SyncingBackgroundingCode)时,对象(SqliteService)为空。我已经注册并解析了依赖项注入容器中的对象。 如何处理这种情况?谁能提供一个例子或链接来实现这个场景?

这是我试图实现依赖注入的一段代码。

This is in Droid :-

    public class androidSyncBackgroundService : Service
         
        CancellationTokenSource _cts;
        public override IBinder OnBind (Intent intent)
        
        return null;
        
        public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId)
         
        _cts = new CancellationTokenSource ();
        Task.Run (() => 
                try 
                //INVOKE THE SHARED CODE
                var oBackground = new SyncingBackgroundingCode();
                oBackground.RunBackgroundingCode(_cts.Token).Wait();
            
            catch (OperationCanceledException) 
        

        
        finally 
        if (_cts.IsCancellationRequested) 
        
                var message = new CancelledTask();
        Device.BeginInvokeOnMainThread (
                                    () => MessagingCenter.Send(message, "CancelledTask")
                );
            
            
            , _cts.Token);
        return StartCommandResult.Sticky;
            


        public override void OnDestroy ()
        
        if (_cts != null) 
            _cts.Token.ThrowIfCancellationRequested ();

        _cts.Cancel ();
            
        base.OnDestroy ();
            
        

    This is in PCL:-


         public class SyncingBackgroundingCode
                
                    public SQLiteConnection _sqlconnection;
                    SqliteCalls oSQLite = new SqliteCalls();
                ISqliteService _SqliteService;

                public SyncingBackgroundingCode(ISqliteService SqliteService)
                
                //object is null
                

                    public async Task RunBackgroundingCode(CancellationToken token)
                    
                            DependencyService.Get<ISQLite>().GetConnection();

                        await Task.Run (async () => 

                            token.ThrowIfCancellationRequested();

                            if (App.oSqliteCallsMainLH != null)
                                             
                                App.bRunningBackgroundTask = true;

                                oSQLite = App.oSqliteCallsMainLH;
                                await Task.Run(async () =>
                                
                                    await Task.Delay(1);
                                    oSQLite.ftnSaveOnlineModeXMLFormat("Offline", 0); 
                                    oSQLite.SyncEmployeeTableData();
                                    oSQLite.SaveOfflineAppCommentData();
                                    oSQLite.SaveOfflineAdditionToFlowData();
                                    await Task.Delay(500);

                                    var msgStopSyncBackgroundingTask = new StopSyncBackgroundingTask();
                                    MessagingCenter.Send(msgStopSyncBackgroundingTask, "StopSyncBackgroundingTask");  
                                );

                            

                        , token);
                    
                

【问题讨论】:

可能在容器有机会注册服务之前调用了 DependencyService.Get()。确保它们以正确的顺序被触发。否则,我看不出有任何理由导致服务没有得到解决。此外,在程序集上使用 [assembly: Dependency(typeof(SQLitService))] 注册服务可能是个好主意,这可确保您的服务始终在您的代码有机会解析之前注册。 【参考方案1】:

不幸的是,Xamarin 和 Xamarin Forms 没有在任何地方提供像 Prism 这样的框架来处理 IoC 场景。不过,有几种方法可以解决这个问题。

首先,Container 是后台服务中 PrismApplication 上的公共属性,您可以执行以下操作:

public class FooBackgroundService

    private App _app => (App)Xamarin.Forms.Application.Current;

    private void DoFoo()
    
        var sqlite = _app.Container.Resolve<ISQLite>();
    

另一种稍微复杂的方法是使用 ServiceLocator 模式。您可能会遇到以下情况:

public static class Locator

    private static Func<Type, object> _resolver;

    public static T ResolveService<T>() => 
        (T)_resolver?.Invoke(typeof(T));

    public static void SetResolver(Func<Type, object> resolver) => 
        _resolver = resolver;

然后,您只需在您的应用中设置解析器。 Prism 实际上对 ViewModel 定位器做了类似的事情,然后允许它注入正确的 NavigationService 实例。

public class App : PrismApplication

    protected override void OnInitialized()
    
        SetServiceLocator();
        NavigationService.NavigateAsync("MainPage");
    

    protected override void RegisterTypes()
    
        // RegisterTypes
    

    private void SetServiceLocator()
    
        Locator.SetResolver(type => Container.Resolve(type, true));
    

最后,您的服务将简单地引用服务定位器,例如:

public class BarBackgroundService

    public void DoBar()
    
        var sqlite = Locator.ResolveService<ISQLite>();
        // do foo
    

【讨论】:

以上是关于使用 Prism 在 Xamarin Forms 的后台服务中实现依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

为啥在使用 Prism 在 Xamarin.Forms 中导航到 root 时,我的 NavigationPage 顶部会出现默认标题?

使用 Prism 在 Xamarin Forms 的后台服务中实现依赖注入

Xamarin.Forms FlyoutPage 不适用于 Prism

走进 Prism for Xamarin.Forms

Xamarin.Forms+Prism—— 简单提示UI的使用

Prism Xamarin Forms ToolbarItem 出现两次