我看prism的一点感受(二) 下载模块

上一篇讲了silverlight应用程序启动的过程,以及第一个视图(MagePage)是如何显示出来的。但是MagePage一般作为应用程序的主窗口,是用来作容器的,真正要显示给用户看的视图是在其他模块里,而MagePage通常会设置一个Region,然后把其他模块的视图嵌在这个Region里呈现出来。

按理应该在这里讲Region的相关内容。但是视图在Region里呈现出来之前,其实还有一个环节,就是先下载模块(将其打包的xap文件),然后指定这个模块里的哪个视图(一个模块其实就是一个silverlight应用程序,我们一般把项目按照不同的功能,分割成多个Silverlight应用程序,里面有多个xaml文件,当主窗口要调用哪个功能模块时,就下载哪个xap)要首先显示出来。

所以,我按照程序执行的先后顺序来讲解Prism,ModuleRegion.


上面是一块Moudlarity类库的源码截图。有很多类文件列在其中,那我们从哪里看起呢???

好的,关于Module这一块,我们首先应该去看看,它是怎么被下载的,下载后又是如何加载到宿主应用程序里的。这是我们用PrismSilverlight项目的一个核心问题。每个功能模块都独立打包,程序运行时,再按需下载,所以不了解Prism下载xap的运行机制,那么也不算是深入了解Prism.

那我们就在里面找找有没有关于下载的类。还真的有,大家看到FileDownloader.cs文件名没有???见名思意,都知道这是个关于文件下载的类了。

FileDownloader类其实是对WebClient类的一个包装,如果以前有做过按需下载的同学们,一定会知道,我们通常用webclient去服务器下载xap文件,然后加载到宿主程序。

FileDownloader提供了一个方法

public void DownloadAsync(Uri uri, object userToken)

执行异步下载。同时也提供了两个事件:

DownloadProgressChanged

DownloadCompleted

来帮助我们跟踪下载的状态。

 

但是使用Prism的同学都知道,下载Module时,代码里并没有用这个类,那么我们怎么能够下载呢。接着看...在Modularity还包含了另外一个类,XapModuleTypeloader.这个类是对FileDownloader的再次封装。

FileDownloader只是提供了一个简单的从服务器下载流文件方法。而XapModuleTypeloader 则提供了一个LoadModuleType(ModuleInfo moduleInfo) 方法, ModuleInfo类型的参数指出了要下载的xap文件地址,这个xap文件里包含的模块命名空间,以及模块是否初时下载还是延时下载,这个模块是否依赖其他模块,最重要的一点是,这个模块被下载后,宿主程序应该首先去执行哪个类。

LoadModuleType方法依据这些信息调用FileDownloader类的DownloadAsync方法,异步下载xap文件,并在DownloadCompleted事件里,将下载的流加载到宿主程序里。加载流到宿主程序的操作由

LoadAssemblyFromStream(Stream sourceStream, AssemblyPart assemblyPart)方法执行。

看到这里我们了解了Prism框架把过去程序员自已编写的按需下载的代码,给封装在这两个类里了。

但是我们在UntiyBootstrapper.Run() 方法里,没有看到XapModuleTypeloader类的调用。所以可以判断XapMoudleTypeloader类仍然被封装了一道。再看….

上一篇文章里,我们在UntiyBootstrapper.Run()里看到与Moudle相关的类有两个

1.       ModuleCatalog

2.       ModuleManager

      分别看这两个类,我在ModuleManager类里看到了XapModuleTypeloader的身影。

      ModuleManager类有一个私有的方法

private void BeginRetrievingModule(ModuleInfo moduleInfo)

        {

            ModuleInfo moduleInfoToLoadType = moduleInfo;

            IModuleTypeLoader moduleTypeLoader = this.GetTypeLoaderForModule(moduleInfoToLoadType);

            moduleInfoToLoadType.State = ModuleState.LoadingTypes;

 

            // Delegate += works differently betweem SL and WPF.

            // We only want to subscribe to each instance once.

            if (!this.subscribedToModuleTypeLoaders.Contains(moduleTypeLoader))

            {

                moduleTypeLoader.ModuleDownloadProgressChanged += this.IModuleTypeLoader_ModuleDownloadProgressChanged;

                moduleTypeLoader.LoadModuleCompleted += this.IModuleTypeLoader_LoadModuleCompleted;

                this.subscribedToModuleTypeLoaders.Add(moduleTypeLoader);

            }

 

            moduleTypeLoader.LoadModuleType(moduleInfo);[l1] 

        }

 

      BeginRetrievingModule只是个私有的方法,如果想让应用程序下载模块的话,那一定要有一个公共的接口才行。

ModuleManager类里,有两个公共的方法与BeginRetrievingModule方法相关,怎么叫相关??因为BeginRetrievingModule方法又被另一个私有方法LoadModuleTypes(IEnumerable<ModuleInfo> moduleInfos) 方法调用,而这两个公共方法又调用了LoadModuleTypes(IEnumerable<ModuleInfo> moduleInfos)

 

        我们一个一个来看

1.              public void LoadModule(string moduleName)

这个方法简单的调用了LoadMoudles方法,完成了一次下载的操作

2      public void Run()

          这个Run方法绕了一个弯子去调用LoadMoudles,它首先去调用LoadModulesWhenAvailable方法,而LoadModulesWhenAvailable 方法是找出模块InitializationMode WhenAvailable的进行下载。

         先解释下InitializationMode是什么,这是个枚举类型,有WhenAvailable, OnDemand 两个值,如果哪个模块信息里标明InitializationModeWhenAvailable的话,那么这个模块会在程序初始化时马上下载,如果是OnDemand的话,那么这个模块就放着,等程序运行时,想下载的时候再下载。

          我们的程序是如何做到立马下载和延时下载的呢。其实就是分别调用上面的两个方法就可以了. LoadModule方法,就是让你可以在任意地方调用,下载模块。

         Run呢,则不需要我们去调用它!!!!!!

回到上一篇关于UnityBootstrapper类的代码里去看看,.它里面有个Run方法,这是程序一开始运行,就在这个方法里的最后几行有这样一段代码

if (this.Container.IsRegistered<IModuleManager>())

            {

                this.Logger.Log(Resources.InitializingModules, Category.Debug, Priority.Low);

                this.InitializeModules();[l2] 

            }

 

         最后一行有个this.InitalizeModules() 方法

        protected override void InitializeModules()

        {

            IModuleManager manager;

 

            try

            {

                manager = this.Container.Resolve<IModuleManager>();[l3] 

            }

            catch (ResolutionFailedException ex)

            {

                if (ex.Message.Contains("IModuleCatalog"))

                {

                    throw new InvalidOperationException(Resources.NullModuleCatalogException);

                }

 

                throw;

            }

 

            manager.Run();[l4] 

        }

好了,现在我们看清楚整个下载模块的脉络了吧。

1.在我们写的Silverlight程序里,调用UnityBootstraper类的Run方法

2. Run方法在最后几行里去调用ModuleManager.Run方法

3. MoudleManager.Run方法去调用MoudleManager .LoadModulesWhenAvailable方法,找出需要即时下载的模块

4. MoudleManager .LoadModulesWhenAvailable调用MoudleManager. LoadModuleTypes方法

5. ModuleManager. LoadModuleTypes调用MoudleManager. BeginRetrievingModule方法

6. MoudleManager. BeginRetrievingModule调用XapModuleTypeloader. LoadModuleType方法

7. XapModuleTypeloader一边去调用FileDownloader. DownloadAsync下载流,一边等下载完了后,用XapModuleTypeloader. LoadAssemblyFromStream方法加载流到宿主应用程序。

 

呵呵,绕吧。 上面讲的是程序初始化时下载模块的流程。

如果我们在程序运行中,想调用另一个模块下载呢???那就直接调用MoudleManger. LoadModule方法下载吧。

 

现在有个疑问,我们从始至终都没有看到哪行代码里去指定要下载的xap文件路径,下载后要显示的xaml文件也没有指定。

这个问题下一篇再讲…..

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 [l1]moduleTypeLoader.LoadMoudleType就是下载模块的方法

moduleTypeLoaderIModuleTypeLoader接口的实例,

XapModuleTypeloaderIModuleTypeLoader接口的子类,所以moduleTypeLoader.LoadModuleType就是XapModuleTypeloader.LoadModuleType(ModuleInfo moduleinfo)

 

 [l2]仔细看这行

 [l3]实例化MoudleManager

 [l4]就是这个ModuleManager.Run()方法

posted @ 2010-11-29 12:26  jassonzhang  阅读(778)  评论(0编辑  收藏  举报