/// <summary>
        /// 异步加载bundle列表
        /// </summary>
        /// <param name="packInfo">bundle打包信息</param>
        /// <param name="complete">加载完成回调</param>
        /// <param name="failure">加载失败回调</param>
        /// <param name="loading">加载进度回调</param>
        /// <returns></returns>
        public IEnumerator LoadAsync(JWObjList <BundlePackInfo> packInfos,
                                     BundleLoadedDelegate complete,
                                     BundleLoadFailedDelegate failure,
                                     BundleLoadingDelegate loading = null)
        {
            if (packInfos == null)
            {
                JW.Common.Log.LogE("Async loading bundle with null pack info.");
                yield break;
            }

            //JWObjList<BundleRef> bundleRefs = new JWObjList<BundleRef>();

            for (int i = 0; i < packInfos.Count; ++i)
            {
                BundlePackInfo packInfo = packInfos[i];
                if (packInfo != null)
                {
                    // ab has been loaded
                    BundleRef br = null;
                    if (_bundleDict.TryGetValue(packInfo.Path, out br))
                    {
                        br.RefCnt++;

                        if (_unloadingBundle.ContainsKey(packInfo.Path))
                        {
                            _unloadingBundle.Remove(packInfo.Path);
                        }

                        if (loading != null)
                        {
                            loading(packInfo, 1f);
                        }

                        if (complete != null)
                        {
                            complete(br);
                        }
                        continue;
                    }

                    // loading
                    if (_loadingBundle.Contains(packInfo.Path))
                    {
                        yield return(null);

                        while (_loadingBundle.Contains(packInfo.Path))
                        {
                            yield return(null);
                        }

                        if (_bundleDict.TryGetValue(packInfo.Path, out br))
                        {
                            br.RefCnt++;

                            if (complete != null)
                            {
                                complete(br);
                            }
                        }

                        continue;
                    }

                    if (packInfo.HasFlag(EBundleFlag.UnCompress) || packInfo.HasFlag(EBundleFlag.LZ4))
                    {
                        LoadSync(packInfo);
                        yield break;
                    }
                    //LZMA
                    // 放在加载依赖前,因为加载依赖表示已经开始加载了
                    _loadingBundle.Add(packInfo.Path);

                    // dependency
                    for (int j = 0; packInfo.Dependencies != null && j < packInfo.Dependencies.Count; j++)
                    {
                        yield return(StartCoroutine(LoadAsync(packInfo.Dependencies[j], null, null)));
                    }

                    // no bundle
                    if (packInfo.IsNoBundle() && !packInfo.Outside)
                    {
                        _loadingBundle.Remove(packInfo.Path);
                        yield break;
                    }

                    // load
                    string path = GetBundleFullPath(packInfo, true);
                    JW.Common.Log.LogD("Async load bundle liat path:{0}", path);
                    UnityWebRequest www = null;
                    www = UnityWebRequest.GetAssetBundle(path);
                    yield return(www.SendWebRequest());

                    // loading progress
                    if (loading != null)
                    {
                        float progress = 0.0f;
                        while (string.IsNullOrEmpty(www.error) && !www.isDone)
                        {
                            if (www.downloadProgress < 0.00001 && www.downloadProgress > -0.00001)
                            {
                                progress += 0.01f;
                                loading(packInfo, progress);
                            }
                            else
                            {
                                loading(packInfo, www.downloadProgress);
                            }

                            yield return(null);
                        }

                        if (string.IsNullOrEmpty(www.error))
                        {
                            loading(packInfo, 1f);
                        }
                    }

                    _loadingBundle.Remove(packInfo.Path);

                    // failed
                    if (!string.IsNullOrEmpty(www.error))
                    {
                        JW.Common.Log.LogE("Async loading bundle {0} failed, error:{1}", packInfo.Path, www.error);

                        if (failure != null)
                        {
                            failure(packInfo, www.error);
                        }

                        www.Dispose();

                        continue;
                    }

                    AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www);
                    // succeed
                    if (ab != null)
                    {
                        br        = new BundleRef(packInfo);
                        br.Bundle = ab;
                        _bundleDict.Add(packInfo.Path, br);

                        if (complete != null)
                        {
                            complete(br);
                        }
                    }
                    www.Dispose();
                }
            }
        }
        /// <summary>
        /// 批量并发加载bundle
        /// </summary>
        /// <param name="bundleList">bundle打包信息列表</param>
        /// <param name="complete">全部加载完成回调</param>
        /// <param name="progress">加载进度,0-1</param>
        /// <returns></returns>
        public IEnumerator BatchLoadAsync(JWObjList <BundlePackInfo> bundleList, Action complete, BundleBatchLoadingDelegate progress = null, BundleLoadedDelegate loaded = null, BundleLoadFailedDelegate failure = null)
        {
            if (bundleList == null || bundleList.Count == 0)
            {
                if (progress != null)
                {
                    progress(1f);
                }

                if (complete != null)
                {
                    complete();
                }

                yield break;
            }

            // 并发加载多个bundle
            JWObjList <string>         failedBundles = null;
            Dictionary <string, float> progressDict  = new Dictionary <string, float>(bundleList.Count); // 因为是并发,所以计算进度稍微复杂一些,需要每个bundle的进度收集计算
            float step = 1f / bundleList.Count;                                                          // 大致平分每个bundle的加载时间

            for (int i = 0; i < bundleList.Count; i++)
            {
                BundlePackInfo bundleInfo = bundleList[i];
                StartCoroutine(LoadAsync(bundleInfo, delegate(BundleRef bundleRef)
                {
                    // complete one
                    if (bundleRef != null)
                    {
                        // 已加载完成某一个bundle
                        if (loaded != null)
                        {
                            loaded(bundleRef);
                        }

                        if (progressDict.ContainsKey(bundleRef.Path))
                        {
                            progressDict[bundleRef.Path] = 1f;
                        }
                        else
                        {
                            progressDict.Add(bundleRef.Path, 1f);
                        }
                    }
                }, delegate(BundlePackInfo packInfo, string error)
                {
                    if (failedBundles == null)
                    {
                        failedBundles = new JWObjList <string>();
                    }

                    if (!failedBundles.Contains(packInfo.Path))
                    {
                        failedBundles.Add(packInfo.Path);
                    }

                    if (failure != null)
                    {
                        failure(packInfo, error);
                    }
                }, delegate(BundlePackInfo packInfo, float prog)
                {
                    // 加载进度回调,会有多次
                    if (progress != null)
                    {
                        // 记录每次每个bundle的进度
                        if (progressDict.ContainsKey(packInfo.Path))
                        {
                            progressDict[packInfo.Path] = prog;
                        }
                        else
                        {
                            progressDict.Add(packInfo.Path, prog);
                        }

                        // 计算总进度
                        float totalProgress = 0;
                        for (int j = 0; j < bundleList.Count; j++)
                        {
                            float eachProg = 0;
                            if (progressDict.TryGetValue(bundleList[j].Path, out eachProg))
                            {
                                totalProgress += eachProg * step;
                            }
                        }

                        progress(totalProgress);
                    }
                }));
            }

            // 检查是否全部完成
            while (true)
            {
                bool completed = true;
                for (int i = 0; i < bundleList.Count; i++)
                {
                    BundlePackInfo bundleInfo = bundleList[i];
                    if (_bundleDict.ContainsKey(bundleInfo.Path))
                    {
                        continue;
                    }

                    if (failedBundles != null && failedBundles.Contains(bundleInfo.Path))
                    {
                        continue;
                    }

                    completed = false;
                    yield return(null);

                    break;
                }

                if (completed)
                {
                    if (complete != null)
                    {
                        complete();
                    }

                    yield break;
                }
            }
        }
        //异步
        #region Load Asynchronize

        /// <summary>
        /// 异步加载bundle
        /// </summary>
        /// <param name="packInfo">bundle打包信息</param>
        /// <param name="complete">加载完成回调</param>
        /// <param name="failure">加载失败回调</param>
        /// <param name="loading">加载进度回调</param>
        /// <returns></returns>
        public IEnumerator LoadAsync(BundlePackInfo packInfo, BundleLoadedDelegate complete, BundleLoadFailedDelegate failure, BundleLoadingDelegate loading = null)
        {
            if (packInfo == null)
            {
                JW.Common.Log.LogE("Async loading bundle with null pack info.");
                yield break;
            }

            //判断是否已经加载
            BundleRef br = null;

            if (_bundleDict.TryGetValue(packInfo.Path, out br))
            {
                br.RefCnt++;

                if (_unloadingBundle.ContainsKey(packInfo.Path))
                {
                    _unloadingBundle.Remove(packInfo.Path);
                }

                if (loading != null)
                {
                    loading(packInfo, 1f);
                }

                if (complete != null)
                {
                    complete(br);
                }

                yield break;
            }

            // 正在加载
            if (_loadingBundle.Contains(packInfo.Path))
            {
                yield return(null);

                while (_loadingBundle.Contains(packInfo.Path))
                {
                    yield return(null);
                }

                if (_bundleDict.TryGetValue(packInfo.Path, out br))
                {
                    br.RefCnt++;

                    if (complete != null)
                    {
                        complete(br);
                    }
                }

                yield break;
            }
            //LoadFormFile 同步加载
            if (packInfo.HasFlag(EBundleFlag.UnCompress) || packInfo.HasFlag(EBundleFlag.LZ4))
            {
                LoadSync(packInfo);
                yield break;
            }

            //LZMA
            _loadingBundle.Add(packInfo.Path);
            // 依赖
            for (int i = 0; packInfo.Dependencies != null && i < packInfo.Dependencies.Count; i++)
            {
                yield return(StartCoroutine(LoadAsync(packInfo.Dependencies[i], null, null)));
            }
            // 内部含有
            if (packInfo.IsNoBundle() && !packInfo.Outside)
            {
                _loadingBundle.Remove(packInfo.Path);
                if (failure != null)
                {
                    failure(packInfo, "");
                }
                yield break;
            }

            {
                //对于LZMA AssetBundle.LoadFromFileAsync 会占用解压内存
                //手册说用UnityWebRequest 可以首次解压 后缓存,但对于本地的包又说用AssetBundle.LoadFromFileAsync
                string path = GetBundleFullPath(packInfo, false);
                JW.Common.Log.LogD("Async load bundle path:{0}", path);
                AssetBundleCreateRequest www = AssetBundle.LoadFromFileAsync(path);
                float progress = 0.0f;
                while (!www.isDone)
                {
                    if (loading != null)
                    {
                        if (www.progress < 0.00001 && www.progress > -0.00001)
                        {
                            progress += 0.01f;
                            loading(packInfo, progress);
                        }
                        else
                        {
                            loading(packInfo, www.progress);
                        }
                    }
                    yield return(null);
                }

                _loadingBundle.Remove(packInfo.Path);
                // failed
                if (www.assetBundle == null)
                {
                    JW.Common.Log.LogE("Async loading bundle {0} failed, error:{1}", path, "");
                    if (failure != null)
                    {
                        failure(packInfo, "");
                    }
                    www = null;
                    yield break;
                }

                if (loading != null)
                {
                    loading(packInfo, 1f);
                }

                // succeed
                if (www.assetBundle != null)
                {
                    br        = new BundleRef(packInfo);
                    br.Bundle = www.assetBundle;
                    _bundleDict.Add(packInfo.Path, br);

                    if (complete != null)
                    {
                        complete(br);
                    }
                }
                www = null;
            }

            /*Test2
             * {
             *  string path = GetBundleFullPath(packInfo, true);
             *  JW.Common.Log.LogD("Async load bundle use UnityWebRequest path:{0}", path);
             *  UnityWebRequest www = UnityWebRequest.GetAssetBundle(path);
             *  yield return www.SendWebRequest();
             *  float progress = 0.0f;
             *  while (!www.isDone)
             *  {
             *      if (loading != null)
             *      {
             *          if (www.downloadProgress < 0.00001 && www.downloadProgress > -0.00001)
             *          {
             *              progress += 0.01f;
             *              loading(packInfo, progress);
             *          }
             *          else
             *          {
             *              loading(packInfo, www.downloadProgress);
             *          }
             *      }
             *      yield return null;
             *  }
             *
             *  _loadingBundle.Remove(packInfo.Path);
             *  // failed
             *  AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www);
             *  if (ab == null)
             *  {
             *      JW.Common.Log.LogE("Async loading bundle use UnityWebRequest {0} failed, error:{1}", path, "");
             *      if (failure != null)
             *      {
             *          failure(packInfo, "");
             *      }
             *      www.Dispose();
             *      www = null;
             *      yield break;
             *  }
             *
             *  if (loading != null)
             *  {
             *      loading(packInfo, 1f);
             *  }
             *
             *  // succeed
             *  if (ab != null)
             *  {
             *      br = new BundleRef(packInfo);
             *      br.Bundle = ab;
             *      _bundleDict.Add(packInfo.Path, br);
             *
             *      if (complete != null)
             *      {
             *          complete(br);
             *      }
             *  }
             *  www.Dispose();
             *  www = null;
             * }*/
        }