admin管理员组文章数量:1632827
这里写目录标题
- 前言
- 获取已安装应用列表
- 判断应用是否已安装
- 判断是否是系统应用
- 判断是否是persist应用
- 获取应用信息
- 获取应用申请的权限
- 获取签名信息
- 获取 MD5、 SHA1、 SHA256
- 判断两个应用签名是否一致
- 获取四大组件信息
- 清除应用缓存
前言
android.content.pm.PackageManager
用于检索与设备上当前安装的应用程序包相关的各种信息的类。
要获取应用相关的信息,都用到它。
通过 Context#getPackageManager 实例化。
获取已安装应用列表
获取所有已安装的应用,
private void getAllInstallApp() {
List<PackageInfo> list = getPackageManager().getInstalledPackages(0);
if (null != list && list.size() > 0) {
Log.d(TAG, " getAllInstallApp -- list.size() : " + list.size());
for (PackageInfo p : list) {
Log.d(TAG, "getAllInstallApp -- p.packageName : " + p.packageName);
}
}
}
这样写,AS 报黄
As of Android 11, this method no longer returns information about all apps; see https://g.co/dev/packagevisibility for details
Android R(11) 上获取到的应用数量也不全,adb shell pm list package 结果有 189 个,这样获取到的是 74 个(包括系统应用和自己,但不包括安装的第三方应用)。
在注册文件 AndroidManifest.xml 里添加 <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
后,获取到 189 个了。涉及用户隐私,慎用!!
AS 也报黄
A <queries> declaration should generally be used instead of QUERY_ALL_PACKAGES; see https://g.co/dev/packagevisibility for details
最终写法:
- 删除
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
; - 在注册文件 AndroidManifest.xml 里添加
queries
标签,填入需要查询的包名。
<manifest xmlns:android="http://schemas.android/apk/res/android"
package="com.test.luodemo">
<queries>
<package android:name="com.test.verticalgridviewdemo"/>
<package android:name="com.test.androidopencvdemo"/>
<package android:name="com.example.navigationdemo"/>
</queries>
<uses-permission android:name="..." />
<application
...
</application>
</manifest>
这样就获取到 77 个了(原来的74个 加上 queries里的 3 个)。
判断应用是否已安装
可以通过 获取已安装应用列表,然后过滤包名判断,
也就可以直接判断,
private boolean isPkgInstall(String pkgName){
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return false;
}
PackageInfo packageInfo = null;
try {
packageInfo = getPackageManager().getPackageInfo(pkgName, 0);
if (null != packageInfo) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
判断是否是系统应用
private boolean isSystemApp(String pkgName) {
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return false;
}
try {
PackageInfo info = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
判断是否是persist应用
private boolean isPersistApp(String pkgName) {
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return false;
}
try {
PackageInfo info = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
if ((info.applicationInfo.flags &
(ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT)) ==
(ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT)) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
获取应用信息
通过 PackageInfo
拿到,
代码 | 说明 |
---|---|
packageInfo.applicationInfo.loadLabel(PackageManager pm) | 应用名称 ,对应 AndroidManifest.xml 里的 android:label |
packageInfo.applicationInfo.loadIcon(PackageManager pm) | Drawable 类型的应用图标 |
packageInfo.packageName | 包名,如 com.android.settings |
packageInfo.versionName | 版本,如 1.0 |
packageInfo.sharedUserId | 对应 AndroidManifest.xml 里的 android:sharedUserId , Setings 的是 android.uid.system , Bluetooth 的是 com.android.bluetooth , 自己写的 Demo 未添加 android:sharedUserId 标签获取到的是 null |
packageInfo.sharedUserLabel | 获取到的是 0 |
packageInfo.lastUpdateTime | 上次更新时间,是 System.currentTimeMillis() 形式的 |
代码示例,
private void getPkgInfo(String pkgName){
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return;
}
PackageInfo packageInfo = null;
PackageManager packageManager = getPackageManager();
try {
packageInfo = packageManager.getPackageInfo(pkgName, 0);
if (null != packageInfo) {
String label = (String) packageInfo.applicationInfo.loadLabel(packageManager);
Drawable icon = (Drawable) packageInfo.applicationInfo.loadIcon(packageManager);
String strPName = packageInfo.packageName;
String strVersionName = packageInfo.versionName;
String sharedUserId = packageInfo.sharedUserId;
int sharedUserLabel = packageInfo.sharedUserLabel;
long uTime = packageInfo.lastUpdateTime;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
获取应用申请的权限
通过 PackageInfo
获取。
注意,实例化 PackageInfo 时传入的参数是 PackageManager.GET_PERMISSIONS
。
private void getPkgPermissionInfo(String pkgName){
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return;
}
PackageInfo pInfo = null;
PackageManager packageManager = getPackageManager();
try {
pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_PERMISSIONS);
if (null != pInfo) {
String[] permissions = pInfo.requestedPermissions;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
打印出的结果就是注册文件 AndroidManifest.xml 里声明的,如
android.permission.WAKE_LOCK 、android.permission.READ_EXTERNAL_STORAGE 、android.permission.WRITE_EXTERNAL_STORAGE 等,
模拟器测试,这三个权限我没申请也有 ,
android.permission.ACCESS_NETWORK_STATE
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.FOREGROUND_SERVICE
获取签名信息
通过 PackageInfo
获取。
注意,实例化 PackageInfo 时传入的参数是 PackageManager.GET_SIGNATURES
。
try {
PackageInfo info = getPackageManager().getPackageInfo("com.android.settings", PackageManager.GET_SIGNATURES);
Signature[] signatures = info.signatures;
String str = signatures[0].toString();
} catch (Exception e){
e.printStackTrace();
}
str 输出为 :android.content.pm.Signature@b2d95fc0
获取 MD5、 SHA1、 SHA256
通过 PackageInfo
获取。
注意,实例化 PackageInfo 时传入的参数是 PackageManager.GET_SIGNATURES
。
获取方法是一样的,只是初始化 MessageDigest
时传的值不同,
/**
* 获取 MD5 、SHA1 、SHA256
* @param algorithm : 只能是 MD5 、SHA1 、SHA256
* 返回值是这种形式:22:13:F1:91:ED:BC:92:E7:44:1B:34:9E:45:90:AB:76
* */
private String getAppSignatureAlgorithm(String pkgName, String algorithm){
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return null;
}
try {
PackageInfo info = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
Signature[] signatures = info.signatures;
byte[] signBytes = signatures[0].toByteArray();
MessageDigest mDigest = MessageDigest.getInstance(algorithm);
byte[] bytesMd5 = mDigest.digest(signBytes);
StringBuilder builder = new StringBuilder();
for (byte b : bytesMd5) {
builder.append(String.format("%02X", b)).append(":");// byte 转 16 进制
}
builder.replace(builder.length()-1, builder.length(),"");// 删除最后的 :
return builder.toString();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
使用示例,
String PKG_SETTINGS = "com.android.settings";
String sMd5 = getAppSignatureAlgorithm(PKG_SETTINGS, "MD5");
String sSha1 = getAppSignatureAlgorithm(PKG_SETTINGS, "SHA1");
String sSha256 = getAppSignatureAlgorithm(PKG_SETTINGS, "SHA256");
结果可以参考 AndroidStudio查看apk签名信息_android studio查看apk签名版本-CSDN博客 验证
判断两个应用签名是否一致
使用 checkSignatures
方法,源码注释,
/**
* Compare the signatures of two packages to determine if the same
* signature appears in both of them. If they do contain the same
* signature, then they are allowed special privileges when working
* with each other: they can share the same user-id, run instrumentation
* against each other, etc.
*
* @param packageName1 First package name whose signature will be compared.
* @param packageName2 Second package name whose signature will be compared.
*
* @return Returns an integer indicating whether all signatures on the
* two packages match. The value is >= 0 ({@link #SIGNATURE_MATCH}) if
* all signatures match or < 0 if there is not a match ({@link
* #SIGNATURE_NO_MATCH} or {@link #SIGNATURE_UNKNOWN_PACKAGE}).
*
* @see #checkSignatures(int, int)
*/
@CheckResult
@SignatureResult
public abstract int checkSignatures(@NonNull String packageName1,
@NonNull String packageName2);
示例,
int ret = getPackageManager().checkSignatures("com.android.settings","com.test.luodemo");// -3
int ret1 = getPackageManager().checkSignatures("com.android.settings", "com.android.bluetooth");// 0
ret = -3 ,对应 PackageManager.SIGNATURE_NO_MATCH 。
ret1 = 0 ,对应 PackageManager.SIGNATURE_MATCH 。
获取四大组件信息
看 PackageInfo
源码,获取四大组件需要传对应的 flag ,
/**
* Array of all {@link android.R.styleable#AndroidManifestActivity
* <activity>} tags included under <application>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_ACTIVITIES} was set.
*/
public ActivityInfo[] activities;
/**
* Array of all {@link android.R.styleable#AndroidManifestReceiver
* <receiver>} tags included under <application>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_RECEIVERS} was set.
*/
public ActivityInfo[] receivers;
/**
* Array of all {@link android.R.styleable#AndroidManifestService
* <service>} tags included under <application>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_SERVICES} was set.
*/
public ServiceInfo[] services;
/**
* Array of all {@link android.R.styleable#AndroidManifestProvider
* <provider>} tags included under <application>,
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_PROVIDERS} was set.
*/
public ProviderInfo[] providers;
示例代码,
本例只是拿到 name ,所以用 ComponentInfo
,
要拿到启动模式(launchMode)等信息,还是用 ActivityInfo
、ServiceInfo
、ProviderInfo
。
/**
* @param type : 0 activity , 1 service , 2 provider , 3 receiver
* */
private void getPkgComponent(String pkgName, int type){
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return;
}
PackageInfo pInfo = null;
PackageManager packageManager = getPackageManager();
try {
switch (type){
case 1:
pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_SERVICES);
break;
case 2:
pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_PROVIDERS);
break;
case 3:
pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_RECEIVERS);
break;
case 0:
default:
pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES);break;
}
if (null != pInfo) {
ComponentInfo[] componentInfos = null;
switch (type){
case 1:
componentInfos = pInfo.services;
break;
case 2:
componentInfos = pInfo.providers;
break;
case 3:
componentInfos = pInfo.receivers;
break;
case 0:
default:
componentInfos = pInfo.activities;
break;
}
if (null != componentInfos && componentInfos.length > 0) {
Log.d(TAG, "getPkgComponent -- componentInfos.length : " + componentInfos.length);
for (ComponentInfo info : componentInfos) {
Log.d(TAG , "getPkgComponent -- info : " + info.name);
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
输出结果是这样的 :
com.test.luodemo.packagemanager.PackageManagerTestActivity
com.test.luodemo.alarmtest.MyIntentService
com.test.luodemo.alarmtest.MyWakefulReceiver
com.test.luodemo.provider.LContentProvider
清除应用缓存
PackageManager.deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer)
源码说明,
/**
* Attempts to delete the cache files associated with an application.
* Since this may take a little while, the result will
* be posted back to the given observer. A deletion will fail if the calling context
* lacks the {@link android.Manifest.permission#DELETE_CACHE_FILES} permission, if the
* named package cannot be found, or if the named package is a "system package".
*
* @param packageName The name of the package to delete
* @param observer An observer callback to get notified when the cache file deletion
* is complete.
* {@link android.content.pm.IPackageDataObserver#onRemoveCompleted(String, boolean)}
* will be called when that happens. observer may be null to indicate that
* no callback is desired.
*
* @hide
*/
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract void deleteApplicationCacheFiles(@NonNull String packageName,
@Nullable IPackageDataObserver observer);
这个方法使用由限制:
- 需要 android.permission.DELETE_CACHE_FILES 权限;
- 只有系统应用才能申请 android.permission.DELETE_CACHE_FILES 权限,需要是系统应用;
- 标注为 @hide ,无法直接调用。
使用方法:
- 1.系统应用直接调用。
参考安卓原生 Settings 应用的代码,
public void clearCache() {
mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_SETTINGS_CLEAR_APP_CACHE);
mClearCachePreference.setClearingCache(true);
mPackageManager.deleteApplicationCacheFiles(mEntry.info.packageName,
new IPackageDataObserver.Stub() {
public void onRemoveCompleted(final String packageName,
final boolean succeeded) {
mHandler.post(new Runnable() {
@Override
public void run() {
mClearCachePreference.setClearingCache(false);
cacheCleared(succeeded);
}
});
}
});
mClearCachePreference.refresh();
}
- 2.反射调用。
AS 编译,导入 framework.jar ,添加依赖,
compileOnly files('libs/framework.jar')
反射调用,mark 下反射的写法
private final IPackageDataObserver iPackageDataObserver = new IPackageDataObserver.Stub() {
@Override
public void onRemoveCompleted(String packageName, boolean isSucceed) throws RemoteException {
}
};
private void clearPkgCacheData(String pkgName){
if (null == pkgName || TextUtils.isEmpty(pkgName)) {
return;
}
PackageManager packageManager = getPackageManager();
Class<?> classPm = null;
try {
classPm = Class.forName("android.content.pm.PackageManager");
Method method = classPm.getDeclaredMethod("deleteApplicationCacheFiles", String.class, IPackageDataObserver.class);
method.setAccessible(true);
method.invoke(packageManager, pkgName, iPackageDataObserver);
} catch (Exception e) {
e.printStackTrace();
}
}
不是系统应用,反射可以调用到,但是会报错,
Caused by: java.lang.SecurityException: Neither user 10156 nor current process has
android.permission.INTERNAL_DELETE_CACHE_FILES.
设置为系统应用就可以了。
本文标签: 妙用androidPackageManager
版权声明:本文标题:Android PackageManager的妙用 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1729149198a1187899.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论