检查应用是否已安装 - Android

147

我正在尝试从Google Play安装应用程序。我可以理解,在打开Google Play商店的URL时,它会打开Google Play,当我按下返回按钮时,该活动将恢复。

Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(appURL));
marketIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
startActivity(marketIntent);

当我回到这个活动时,我尝试调用onResume()来检查应用程序是否已安装,但是我收到了一个错误:

@Override
protected void onResume() {
    super.onResume();
    boolean installed = false;
    while (!installed) {
        installed  =   appInstalledOrNot(APPPACKAGE);
        if (installed) {
             Toast.makeText(this, "App installed", Toast.LENGTH_SHORT).show();
        }
    }
}

private boolean appInstalledOrNot(String uri) {
  PackageManager pm = getPackageManager();
  boolean app_installed = false;
  try {
      pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
      app_installed = true;
  }
  catch (PackageManager.NameNotFoundException e) {
      app_installed = false;
  }
  return app_installed ;
}

错误信息如下:

E/AndroidRuntime(796): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.appinstaller/com.example.appinstaller.MainActivity}: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=market://details?id=com.package.name flg=0x40080000 }

我猜测是因为onPause()。有没有更好的方法来实现呢?我正在尝试检查应用程序是否安装完成。

可能是检测应用程序是否已安装?的重复。 - Philipp Jahoda
如果我理解正确的话,你想要实现的是:启动你的应用程序 -> 前往 Play 商店 -> 点击安装 -> 返回到你自己的活动 -> 启动新安装的应用程序.. 是这样吗? - Varun
不,回到我最初打开的应用程序。我需要使用PackageManager获取有关已安装应用程序的一些信息。此信息检索在我从最初使用的Web URL打开的应用程序安装后开始。 - Siddharthan Asokan
请检查此答案,它适用于Android 11 https://dev59.com/-WMl5IYBdhLWcg3wBzDy#72014352 - Rasoul Miri
13个回答

385

试一试:

private boolean isPackageInstalled(String packageName, PackageManager packageManager) {
    try {
        packageManager.getPackageInfo(packageName, 0);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        return false;
    }
}

注意!

从 Android 11(API 30)开始,根据您正在寻找的包,您可能需要在清单文件中声明 <queries>。有关更多信息,请参阅文档

工作原理:

它尝试获取您传递名称的包的信息。如果失败并抛出了 NameNotFoundException,则表示没有安装该名称的任何包,因此我们返回 false

请注意,我们传递了一个 PackageManager 而不是 Context,这样该方法就稍微更灵活可用,不违反迪米特法则。 只要有一个 PackageManager 实例,您就可以在没有访问 Context 实例的情况下使用该方法。

如何使用:

public void someMethod() {
    // ...
    
    PackageManager pm = context.getPackageManager();
    boolean isInstalled = isPackageInstalled("com.somepackage.name", pm);
    
    // ...
}

1
条件是:我需要检查安装过程是否完成。我点击安装按钮,同时尝试在循环中检查安装情况。代码没问题,但我正在寻找检查安装是否完成的方法。 - Siddharthan Asokan
3
您可以使用广播接收器来监听“package_added”事件。 - Varun
@Varun,我刚刚编辑了代码。我不再使用应用程序包名称,而是使用Google Play商店中应用程序的Web URL。 - Siddharthan Asokan
4
强调在您的清单文件中添加 <queries> 行! - ConcernedHobbit
1
@ConcernedHobbit 好了吗? :) - Robin Kanters
显示剩余6条评论

98
自 Android 11 (API 级别 30) 开始,默认情况下,大多数用户安装的应用程序是不可见的。您需要在清单文件中静态声明要获取有关哪些应用程序的信息,如下所示:

自 Android 11 (API 级别 30) 开始,默认情况下,大多数用户安装的应用程序是不可见的。您需要在清单文件中静态声明要获取有关哪些应用程序的信息,如下所示:

<manifest>
    <queries>
        <!-- Explicit apps you know in advance about: -->
        <package android:name="com.example.this.app"/>
        <package android:name="com.example.this.other.app"/>
    </queries>
    
    ...
</manifest>
然后,@RobinKanters的答案(链接)可行:
private boolean isPackageInstalled(String packageName, PackageManager packageManager) {
    try {
        packageManager.getPackageInfo(packageName, 0);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        return false;
    }
}

// ...
// This will return true on Android 11 if the app is installed,
// since we declared it above in the manifest.
isPackageInstalled("com.example.this.app", pm); 
// This will return false on Android 11 even if the app is installed:
isPackageInstalled("another.random.app", pm); 

了解更多请点击以下链接:


2
注意:根据专用文档页面,您可以默认获取应用程序本身的applicationInfo。因此,在其自己的清单中不需要包含该应用程序。 - BenjyTec
<queries> <!-- If you don't know apps in advance : --> <package android:name="QUERY_ALL_PACKAGES"/> </queries> - Arpit
@ArpitRastogi 这带有一定的限制。您不能将其用于所有类型的应用程序。您的应用程序必须符合政策。被授予此权限的应用程序必须遵守用户数据政策,包括突出显示的披露和同意要求,并且不得将其用于未公开或无效的目的。更多信息 - Bhavin Patel
@bhavin 这仅适用于发布在 Google Play 商店上的应用程序。如果您没有在 Google Play 上,则无需遵守此规定。 - Adam Burley

49

Robin Kanters的回答是正确的,但它会检查已安装应用程序,而不考虑它们的启用或禁用状态。

我们都知道应用程序可以被安装但被用户禁用,因此无法使用。

这将检查已安装并且已启用的应用程序:

public static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
    try {
        return packageManager.getApplicationInfo(packageName, 0).enabled;
    }
    catch (PackageManager.NameNotFoundException e) {
        return false;
    }
}

您可以将此方法放入类中,例如Utils,然后使用以下方式在任何地方调用:

您可以将此方法放入类中,例如Utils,然后使用以下方式在任何地方调用:

boolean isInstalled = Utils.isPackageInstalled("com.package.name", context.getPackageManager())

11

更快的解决方案:

private boolean isPackageInstalled(String packagename, PackageManager packageManager) {
    try {
        packageManager.getPackageGids(packagename);
        return true;
    } catch (NameNotFoundException e) {
        return false;
    }
}

getPackageGidsgetPackageInfo更加经济实惠,因此它可以更快地工作。

Run 10000 on API 15
Exists pkg:
getPackageInfo: nanoTime = 930000000
getPackageGids: nanoTime = 350000000
Not exists pkg:
getPackageInfo: nanoTime = 420000000
getPackageGids: nanoTime = 380000000

Run 10000 on API 17
Exists pkg:
getPackageInfo: nanoTime = 2942745517
getPackageGids: nanoTime = 2443716170
Not exists pkg:
getPackageInfo: nanoTime = 2467565849
getPackageGids: nanoTime = 2479833890

Run 10000 on API 22
Exists pkg:
getPackageInfo: nanoTime = 4596551615
getPackageGids: nanoTime = 1864970154
Not exists pkg:
getPackageInfo: nanoTime = 3830033616
getPackageGids: nanoTime = 3789230769

Run 10000 on API 25
Exists pkg:
getPackageInfo: nanoTime = 3436647394
getPackageGids: nanoTime = 2876970397
Not exists pkg:
getPackageInfo: nanoTime = 3252946114
getPackageGids: nanoTime = 3117544269

注意:在某些虚拟空间中,此方法可能无法正常工作。它们可以违反Android API并始终返回一个数组,即使没有与该包名称对应的应用程序。
在这种情况下,请使用getPackageInfo


5

试试这个:

public static boolean isAvailable(Context ctx, Intent intent) {
    final PackageManager mgr = ctx.getPackageManager();
    List<ResolveInfo> list =
        mgr.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    return list.size() > 0;
}

4

你可以在Kotlin中使用这个extentions.kt

fun Context.isPackageInstalled(packageName: String): Boolean {
    return try {
        packageManager.getPackageInfo(packageName, 0)
        true
    } catch (e: PackageManager.NameNotFoundException) {
        false
    }
}

用法

context.isPackageInstalled("com.somepackage.name")

3
在安卓11版本之后,您需要在应用程序的清单文件中的<queries>中添加包名。如果您没有在清单文件中添加<queries>,则context.getPackageManager().getApplicationInfo将会抛出一个异常(System.err: android.content.pm.PackageManager$NameNotFoundException)。请参考以下链接:link。例如:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <queries>
        <package android:name="com.xxx.yyy" />
    </queries>

    <application></application>

</manifest>

Java

 public boolean applicationIsInstall(Context context , String packageName){
        try {
            context.getPackageManager().getApplicationInfo(packageName, 0);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }

Koltin

fun applicationIsInstall(context: Context, packageName: String): Boolean {
    return try {
        context.packageManager.getApplicationInfo(packageName, 0)
        true
    } catch (e: PackageManager.NameNotFoundException) {
        e.printStackTrace()
        false
    }
}

2
如果你想在不使用try catch块的情况下尝试它,可以使用以下方法, 创建一个意图并设置要验证的应用程序的包。
val intent = Intent(Intent.ACTION_VIEW)
intent.data = uri
intent.setPackage("com.example.packageofapp")

然后调用以下方法来检查应用程序是否已安装

fun isInstalled(intent:Intent) :Boolean{
    val list = context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
    return list.isNotEmpty()
}

0

虽然答案已经发布,并且可以在Android 29或Android 10上使用,但是当应用程序目标为Android 11(API级别30)或更高版本并查询有关安装在设备上的其他应用程序的信息时,系统默认会过滤此信息。此过滤行为意味着您的应用程序无法检测到设备上安装的所有应用程序,这有助于最小化您的应用程序可以访问但不需要完成其用例的潜在敏感信息。

因此,要实现这一点,我们只需要添加 <queries> 或需要在 AndroidManifest.xml 文件中添加以下权限-

<uses-permission
    android:name="android.permission.QUERY_ALL_PACKAGES" />

这是因为Android 11中有一些行为变化- https://developer.android.com/training/package-visibility


0

如果您正在寻找Kotlin解决方案,可以使用此方法,

在这里,我分享了完整的代码,并处理了启用状态。检查Android Kotlin中是否安装了应用程序

fun isAppInstalled(packageName: String, context: Context): Boolean {
        return try {
            val packageManager = context.packageManager
            packageManager.getPackageInfo(packageName, 0)
            true
        } catch (e: PackageManager.NameNotFoundException) {
            false
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接