private static Set<String> generateNewAssetPaths(Application application, String path) {
    Set<String> assetPaths = null;

    if ((path != null) && (assetPathsHistory != null && assetPathsHistory.contains(path))) {
      // already added, just return null
      return null;
    }

    assetPaths = new LinkedHashSet<String>();
    assetPaths.add(application.getApplicationInfo().sourceDir);
    if (ignoreOpt && Build.VERSION.SDK_INT > 20) {
      assetPaths.add(WebViewGoogleAssetPath);
    }

    // get original asset paths on android above 5.0 in case webview not
    // added
    try {
      if (mOriginAssetsPath == null && Build.VERSION.SDK_INT > 20 && !ignoreOpt) {
        mOriginAssetsPath = getOriginAssetsPath(application.getResources().getAssets());
        assetPaths.addAll(mOriginAssetsPath);
      }
    } catch (Throwable e) {
    }

    if (assetPathsHistory != null) assetPaths.addAll(assetPathsHistory);

    if (!TextUtils.isEmpty(path)) assetPaths.add(path);

    return assetPaths;
  }
  /**
   * 安装一个插件
   *
   * @param srcPluginFile
   * @return
   */
  public static synchronized int installPlugin(String srcPluginFile) {
    LogUtil.e("开始安装插件", srcPluginFile);
    if (TextUtils.isEmpty(srcPluginFile) || !new File(srcPluginFile).exists()) {
      return SRC_FILE_NOT_FOUND;
    }

    // 第0步,先将apk复制到宿主程序私有目录,防止在安装过程中文件被篡改
    if (!srcPluginFile.startsWith(sApplication.getCacheDir().getAbsolutePath())) {
      String tempFilePath =
          sApplication.getCacheDir().getAbsolutePath()
              + File.separator
              + System.currentTimeMillis()
              + ".apk";
      if (FileUtil.copyFile(srcPluginFile, tempFilePath)) {
        srcPluginFile = tempFilePath;
      } else {
        LogUtil.e("复制插件文件失败", srcPluginFile, tempFilePath);
        return COPY_FILE_FAIL;
      }
    }

    // 第1步,验证插件APK签名,如果被篡改过,将获取不到证书
    // sApplication.getPackageManager().getPackageArchiveInfo(srcPluginFile,
    // PackageManager.GET_SIGNATURES);
    Signature[] pluginSignatures = PackageVerifyer.collectCertificates(srcPluginFile, false);
    boolean isDebugable =
        (0 != (sApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
    if (pluginSignatures == null) {
      LogUtil.e("插件签名验证失败", srcPluginFile);
      new File(srcPluginFile).delete();
      return SIGNATURES_INVALIDATE;
    } else if (NEED_VERIFY_CERT && !isDebugable) {
      // 可选步骤,验证插件APK证书是否和宿主程序证书相同。
      // 证书中存放的是公钥和算法信息,而公钥和私钥是1对1的
      // 公钥相同意味着是同一个作者发布的程序
      Signature[] mainSignatures = null;
      try {
        PackageInfo pkgInfo =
            sApplication
                .getPackageManager()
                .getPackageInfo(sApplication.getPackageName(), PackageManager.GET_SIGNATURES);
        mainSignatures = pkgInfo.signatures;
      } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
      }
      if (!PackageVerifyer.isSignaturesSame(mainSignatures, pluginSignatures)) {
        LogUtil.e("插件证书和宿主证书不一致", srcPluginFile);
        new File(srcPluginFile).delete();
        return VERIFY_SIGNATURES_FAIL;
      }
    }

    // 第2步,解析Manifest,获得插件详情
    PluginDescriptor pluginDescriptor = PluginManifestParser.parseManifest(srcPluginFile);
    if (pluginDescriptor == null || TextUtils.isEmpty(pluginDescriptor.getPackageName())) {
      LogUtil.e("解析插件Manifest文件失败", srcPluginFile);
      new File(srcPluginFile).delete();
      return PARSE_MANIFEST_FAIL;
    }

    PackageInfo packageInfo =
        sApplication
            .getPackageManager()
            .getPackageArchiveInfo(srcPluginFile, PackageManager.GET_GIDS);
    if (packageInfo != null) {
      pluginDescriptor.setApplicationTheme(packageInfo.applicationInfo.theme);
      pluginDescriptor.setApplicationIcon(packageInfo.applicationInfo.icon);
      pluginDescriptor.setApplicationLogo(packageInfo.applicationInfo.logo);
    }

    boolean isNeedPending = false;
    // 第3步,检查插件是否已经存在,若存在删除旧的
    PluginDescriptor oldPluginDescriptor =
        getPluginDescriptorByPluginId(pluginDescriptor.getPackageName());
    if (oldPluginDescriptor != null) {
      LogUtil.e(
          "已安装过,安装路径为",
          oldPluginDescriptor.getInstalledPath(),
          oldPluginDescriptor.getVersion(),
          pluginDescriptor.getVersion());

      // 检查插件是否已经加载
      if (oldPluginDescriptor.getPluginContext() != null) {
        if (!oldPluginDescriptor.getVersion().equals(pluginDescriptor.getVersion())) {
          LogUtil.e("旧版插件已经加载, 且新版插件和旧版插件版本不同,进入pending状态,新版插件将在安装后进程重启再生效");
          isNeedPending = true;
        } else {
          LogUtil.e("旧版插件已经加载, 且新版插件和旧版插件版本相同,拒绝安装");
          new File(srcPluginFile).delete();
          return FAIL_BECAUSE_HAS_LOADED;
        }
      } else {
        LogUtil.e("旧版插件还未加载,忽略版本,直接删除旧版,尝试安装新版");
        remove(oldPluginDescriptor.getPackageName());
      }
    }

    // 第4步骤,复制插件到插件目录
    String destApkPath =
        pluginManager.genInstallPath(
            pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
    boolean isCopySuccess = FileUtil.copyFile(srcPluginFile, destApkPath);

    if (!isCopySuccess) {

      LogUtil.e("复制插件到安装目录失败", srcPluginFile);
      // 删掉临时文件
      new File(srcPluginFile).delete();
      return COPY_FILE_FAIL;
    } else {

      // 第5步,先解压so到临时目录,再从临时目录复制到插件so目录。 在构造插件Dexclassloader的时候,会使用这个so目录作为参数
      File apkParent = new File(destApkPath).getParentFile();
      File tempSoDir = new File(apkParent, "temp");
      Set<String> soList = FileUtil.unZipSo(srcPluginFile, tempSoDir);
      if (soList != null) {
        for (String soName : soList) {
          FileUtil.copySo(tempSoDir, soName, apkParent.getAbsolutePath());
        }
        // 删掉临时文件
        FileUtil.deleteAll(tempSoDir);
      }

      // 第6步 添加到已安装插件列表
      pluginDescriptor.setInstalledPath(destApkPath);
      boolean isInstallSuccess = false;
      if (!isNeedPending) {
        isInstallSuccess = pluginManager.addOrReplace(pluginDescriptor);
      } else {
        isInstallSuccess = pluginManager.pending(pluginDescriptor);
      }
      // 删掉临时文件
      new File(srcPluginFile).delete();

      if (!isInstallSuccess) {
        LogUtil.e("安装插件失败", srcPluginFile);

        new File(destApkPath).delete();

        return INSTALL_FAIL;
      } else {
        // 通过创建classloader来触发dexopt,但不加载
        LogUtil.d("正在进行DEXOPT...", pluginDescriptor.getInstalledPath());
        FileUtil.deleteAll(new File(apkParent, "dalvik-cache"));
        PluginCreator.createPluginClassLoader(
            pluginDescriptor.getInstalledPath(), pluginDescriptor.isStandalone(), null);
        LogUtil.d("DEXOPT完毕");

        if (!isNeedPending) {
          LocalServiceManager.registerService(pluginDescriptor);
        }

        changeListener.onPluginInstalled(
            pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
        LogUtil.e("安装插件成功," + (isNeedPending ? " 重启进程生效" : " 立即生效"), destApkPath);

        return SUCCESS;
      }
    }
  }
  /**
   * 安装一个插件
   *
   * @param srcPluginFile
   * @return
   */
  public static synchronized int installPlugin(String srcPluginFile) {
    LogUtil.d("开始安装插件", srcPluginFile);
    if (TextUtils.isEmpty(srcPluginFile) || !new File(srcPluginFile).exists()) {
      return SRC_FILE_NOT_FOUND;
    }

    // 第0步,先将apk复制到宿主程序私有目录,防止在安装过程中文件被篡改
    if (!srcPluginFile.startsWith(sApplication.getCacheDir().getAbsolutePath())) {
      String tempFilePath =
          sApplication.getCacheDir().getAbsolutePath()
              + File.separator
              + System.currentTimeMillis()
              + ".apk";
      if (FileUtil.copyFile(srcPluginFile, tempFilePath)) {
        srcPluginFile = tempFilePath;
      } else {
        LogUtil.e("复制插件文件失败失败", srcPluginFile, tempFilePath);
        return COPY_FILE_FAIL;
      }
    }

    // 第1步,验证插件APK签名,如果被篡改过,将获取不到证书
    // sApplication.getPackageManager().getPackageArchiveInfo(srcPluginFile,
    // PackageManager.GET_SIGNATURES);
    Signature[] pluginSignatures = PackageVerifyer.collectCertificates(srcPluginFile, false);
    boolean isDebugable =
        (0 != (sApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
    if (pluginSignatures == null) {
      LogUtil.e("插件签名验证失败", srcPluginFile);
      new File(srcPluginFile).delete();
      return SIGNATURES_INVALIDATE;
    } else if (NEED_VERIFY_CERT && !isDebugable) {
      // 可选步骤,验证插件APK证书是否和宿主程序证书相同。
      // 证书中存放的是公钥和算法信息,而公钥和私钥是1对1的
      // 公钥相同意味着是同一个作者发布的程序
      Signature[] mainSignatures = null;
      try {
        PackageInfo pkgInfo =
            sApplication
                .getPackageManager()
                .getPackageInfo(sApplication.getPackageName(), PackageManager.GET_SIGNATURES);
        mainSignatures = pkgInfo.signatures;
      } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
      }
      if (!PackageVerifyer.isSignaturesSame(mainSignatures, pluginSignatures)) {
        LogUtil.e("插件证书和宿主证书不一致", srcPluginFile);
        new File(srcPluginFile).delete();
        return VERIFY_SIGNATURES_FAIL;
      }
    }

    // 第2步,解析Manifest,获得插件详情
    PluginDescriptor pluginDescriptor = ManifestParser.parseManifest(srcPluginFile);
    if (pluginDescriptor == null || TextUtils.isEmpty(pluginDescriptor.getPackageName())) {
      LogUtil.e("解析插件Manifest文件失败", srcPluginFile);
      new File(srcPluginFile).delete();
      return PARSE_MANIFEST_FAIL;
    }

    PackageInfo packageInfo =
        sApplication
            .getPackageManager()
            .getPackageArchiveInfo(srcPluginFile, PackageManager.GET_GIDS);
    pluginDescriptor.setApplicationTheme(packageInfo.applicationInfo.theme);
    pluginDescriptor.setApplicationIcon(packageInfo.applicationInfo.icon);
    pluginDescriptor.setApplicationLogo(packageInfo.applicationInfo.logo);

    // 第3步,检查插件是否已经存在,若存在删除旧的
    PluginDescriptor oldPluginDescriptor =
        getPluginDescriptorByPluginId(pluginDescriptor.getPackageName());
    if (oldPluginDescriptor != null) {
      LogUtil.e("已安装过,先删除旧版本", pluginDescriptor.getInstalledPath());
      remove(pluginDescriptor.getPackageName());
    }

    // 第4步骤,复制插件到插件目录
    String destPluginFile =
        pluginManager.genInstallPath(
            pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
    boolean isCopySuccess = FileUtil.copyFile(srcPluginFile, destPluginFile);

    if (!isCopySuccess) {

      LogUtil.d("复制插件到安装目录失败", srcPluginFile);
      new File(srcPluginFile).delete();
      return COPY_FILE_FAIL;
    } else {

      // 第5步,复制插件so到插件so目录, 在构造插件Dexclassloader的时候,会使用这个so目录作为参数
      File tempDir = new File(new File(destPluginFile).getParentFile(), "temp");
      Set<String> soList = FileUtil.unZipSo(srcPluginFile, tempDir);
      if (soList != null) {
        for (String soName : soList) {
          FileUtil.copySo(
              tempDir, soName, new File(destPluginFile).getParent() + File.separator + "lib");
        }
        FileUtil.deleteAll(tempDir);
      }

      // 第6步 添加到已安装插件列表
      pluginDescriptor.setInstalledPath(destPluginFile);
      boolean isInstallSuccess = pluginManager.addOrReplace(pluginDescriptor);

      new File(srcPluginFile).delete();

      if (!isInstallSuccess) {
        LogUtil.d("安装插件失败", srcPluginFile);
        return INSTALL_FAIL;
      } else {
        changeListener.onPluginInstalled(
            pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
        LogUtil.d("安装插件成功", destPluginFile);
        return SUCCESS;
      }
    }
  }