/// <summary>
        /// 取bundle
        /// </summary>
        /// <param name="bundlePath">路径</param>
        /// <returns></returns>
        public AssetBundle GetBundle(string bundlePath)
        {
            BundleRef bundleRef = null;

            if (_bundleDict.TryGetValue(bundlePath, out bundleRef))
            {
                if (null != bundleRef && null != bundleRef.Bundle)
                {
                    return(bundleRef.Bundle);
                }
            }

            return(null);
        }
        /// <summary>
        /// 立即卸载
        /// </summary>
        /// <param name="path"></param>
        void UnloadImmediately(string path)
        {
            BundleRef br = null;

            if (_bundleDict.TryGetValue(path, out br))
            {
                if (br.Bundle != null)
                {
                    br.Bundle.Unload(false);
                }

                _bundleDict.Remove(br.Path);
            }
        }
        /// <summary>
        /// 卸载bundle,只减引用计数
        /// </summary>
        /// <param name="pi"></param>
        public void Unload(BundlePackInfo pi, bool immediately = false)
        {
            if (pi == null)
            {
                return;
            }

            if (pi.Life == EBundleLife.Immediate)
            {
                return;
            }

            BundleRef br = null;

            if (_bundleDict.TryGetValue(pi.Path, out br))
            {
                if (pi.Life == EBundleLife.Cache)
                {
                    if (br.RefCnt > 0)
                    {
                        br.RefCnt--;
                    }
                    else
                    {
                        Log.LogW("Unloading bundle [{0}] while its reference count is zero.", pi.Path);
                    }

                    if (br.RefCnt == 0 && immediately)
                    {
                        UnloadImmediately(br.Path);
                    }
                }
                else if (pi.Life == EBundleLife.Resident)
                {
                    if (br.RefCnt > 1)
                    {
                        br.RefCnt--;
                    }
                    else
                    {
                        JW.Common.Log.LogE("Unloading RESIDENT bundle {0}", br.Path);
                    }
                }
            }
            else
            {
                Log.LogW("Unloading bundle which is not exist. path:{0}", pi.Path);
            }
        }
        /// <summary>
        /// 真正卸载无用bundle
        /// </summary>
        public void UnloadUnusedBundles(JWObjList <string> willUseAssets = null)
        {
            JWObjList <string> unloaded = null;
            var itor = _bundleDict.GetEnumerator();

            while (itor.MoveNext())
            {
                BundleRef br = itor.Current.Value;
                if (br.RefCnt <= 0 && !_loadingBundle.Contains(br.Path))
                {
                    // bundle对应的资源可能还会被用到,暂时不卸载
                    if (willUseAssets != null && willUseAssets.Count > 0 && br.PackInfo != null)
                    {
                        for (int i = 0; i < willUseAssets.Count; i++)
                        {
                            string asset = willUseAssets[i];
                            if (br.PackInfo.Contains(asset))
                            {
                                continue;
                            }
                        }
                    }

                    if (unloaded == null)
                    {
                        unloaded = new JWObjList <string>();
                    }

                    unloaded.Add(br.Path);

                    if (br.Bundle != null)
                    {
                        br.Bundle.Unload(false);
                    }

                    // depencency
                    for (int i = 0; br.PackInfo.Dependencies != null && i < br.PackInfo.Dependencies.Count; i++)
                    {
                        Unload(br.PackInfo.Dependencies[i]);
                    }
                }
            }

            for (int i = 0; unloaded != null && i < unloaded.Count; i++)
            {
                _bundleDict.Remove(unloaded[i]);
            }
        }
        /// <summary>
        /// 卸载所有bundle,包括常驻的
        /// </summary>
        public void UnloadAll()
        {
            var       enumerator = _bundleDict.GetEnumerator();
            BundleRef bundleRef  = null;

            while (enumerator.MoveNext())
            {
                bundleRef = enumerator.Current.Value;
                if (null != bundleRef && null != bundleRef.Bundle)
                {
                    bundleRef.Bundle.Unload(true);
                }
            }

            _bundleDict.Clear();
        }
        /// <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();
                }
            }
        }
        //异步
        #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;
             * }*/
        }
        /// <summary>
        /// 同步加载bundle
        /// </summary>
        /// <param name="packInfo">bundle打包信息</param>
        public void LoadSync(BundlePackInfo packInfo)
        {
            if (packInfo == null)
            {
                JW.Common.Log.LogE("Sync loading bundle with null pack info.");
                return;
            }

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

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

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

                return;
            }

            // 是否正在加载
            if (_loadingBundle.Contains(packInfo.Path))
            {
                JW.Common.Log.LogE("Bundle is loading when load sync. bundle:{0}", packInfo.Path);
                return;
            }

            // 依赖
            for (int i = 0; packInfo.Dependencies != null && i < packInfo.Dependencies.Count; i++)
            {
                LoadSync(packInfo.Dependencies[i]);
            }

            //干活
            AssetBundle ab   = null;
            string      path = GetBundleFullPath(packInfo, false);

            JW.Common.Log.LogD("Sync load bundle path:{0}", path);
            //
            if (packInfo.HasFlag(EBundleFlag.UnCompress) || packInfo.HasFlag(EBundleFlag.LZ4))
            {
                ab = AssetBundle.LoadFromFile(path);
            }
            else
            {
                //LZMA 压缩的AssetBundle
                //LZMA压缩的 u2017可以直接通过 LoadFromFile 创建支持StreamingAssetPath 内存占用大
                ab = AssetBundle.LoadFromFile(path);

                /*
                 * byte[] bytes = null;
                 * //优先外围目录
                 * if (FileUtil.IsExistInIFSExtraFolder(packInfo.Path))
                 * {
                 *  bytes = File.ReadAllBytes(path);
                 * }
                 * else
                 * {
                 *
                 *  //StreamingAsset目录的
                 #if UNITY_ANDROID && !UNITY_EDITOR
                 *  bytes = NativeHelper.Android_ReadFileInAssets(packInfo.Path);
                 #else
                 *  bytes = File.ReadAllBytes(path);
                 #endif
                 * }
                 *
                 * if (null != bytes)
                 * {
                 *  //注意
                 *  //https://unity3d.com/cn/learn/tutorials/topics/best-practices/assetbundle-fundamentals?playlist=30089
                 *  //The peak amount of memory consumed by this API will be at least twice the size of the AssetBundle:
                 *  ab = AssetBundle.LoadFromMemory(bytes);
                 * }*/
            }

            // 完成
            if (ab != null)
            {
                br        = new BundleRef(packInfo);
                br.Bundle = ab;
                _bundleDict.Add(packInfo.Path, br);
            }
        }