/**
   * 初始化loader, 只可调用一次
   *
   * @param app
   */
  public static synchronized void initLoader(Application app, PluginManager manager) {

    if (!isLoaderInited) {
      LogUtil.d("插件框架初始化中...");

      isLoaderInited = true;
      sApplication = app;
      pluginManager = manager;
      changeListener = new PluginCallbackImpl();

      AndroidAppIActivityManager.installProxy();
      AndroidAppINotificationManager.installProxy();
      AndroidAppIPackageManager.installProxy(sApplication.getPackageManager());
      AndroidWidgetToast.installProxy();
      // 不可在主进程中同步安装,因为此时ActivityThread还没有准备好, 会导致空指针。
      new Handler()
          .postDelayed(
              new Runnable() {
                @Override
                public void run() {
                  AndroidWebkitWebViewFactoryProvider.installProxy();
                }
              },
              50);

      PluginInjector.injectInstrumentation();
      PluginInjector.injectHandlerCallback();
      PluginInjector.injectBaseContext(sApplication);

      pluginManager.loadInstalledPlugins();
      Iterator<PluginDescriptor> itr = getPlugins().iterator();
      while (itr.hasNext()) {
        PluginDescriptor plugin = itr.next();
        LocalServiceManager.registerService(plugin);
      }
      changeListener.onPluginLoaderInited();

      if (Build.VERSION.SDK_INT >= 14) {
        sApplication.registerActivityLifecycleCallbacks(
            new Application.ActivityLifecycleCallbacks() {
              @Override
              public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}

              @Override
              public void onActivityStarted(Activity activity) {}

              @Override
              public void onActivityResumed(Activity activity) {}

              @Override
              public void onActivityPaused(Activity activity) {}

              @Override
              public void onActivityStopped(Activity activity) {}

              @Override
              public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}

              @Override
              public void onActivityDestroyed(Activity activity) {
                PluginStubBinding.unBindLaunchModeStubActivity(
                    activity.getClass().getName(), activity.getIntent());
              }
            });
      }
      LogUtil.d("插件框架初始化完成");
    }
  }
  /**
   * 安装一个插件
   *
   * @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;
      }
    }
  }