获取APK相关信息

Android中APK部分信息通过adb无法直接获取,可以通过Android中的ApplicationInfo进行获取。

获取内容

  • packageName:包名
  • appName:用户可见的名称
  • isSystemApp:是否是系统应用
  • launcherActivity:启动的活动名
  • processName:运行的进程的名称
  • compileSdkVersion:编译SDK版本
  • targetSdkVersion:目标SDK版本
  • icon:图标数据

功能实现

权限

1
2
3
4
5
6
7
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32"
tools:ignore="ScopedStorage" />

MainActivity(入口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int REQUEST_PERMISSIONS = 1122;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);

// 获取应用程序的名称和图标
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSIONS);
}
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
// 权限已经被授予,可以保存应用信息
try {
AppInfoManager.saveInstalledAppsInfo(MainActivity.this);
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
});
backgroundThread.start();
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSIONS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被授予,可以保存应用信息
} else {
// 权限被拒绝,提示用户权限未被授予
Log.d(TAG, "权限被拒绝");
}
}
}
}

AppInfoManager(应用获取核心部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
public class AppInfoManager {
public static String TAG = "APPInfoManager";

public static void saveInstalledAppsInfo(Context context) throws ClassNotFoundException, NoSuchMethodException {
PackageManager packageManager = context.getPackageManager();
List<ApplicationInfo> apps = packageManager.getInstalledApplications(0);

DatabaseHelper dbHelper = new DatabaseHelper(context);
SQLiteDatabase db = dbHelper.getWritableDatabase();
for (ApplicationInfo app : apps) {
// 获取包名
String packageName = app.packageName;
Log.d(TAG, app.packageName);
String name = (String) app.loadLabel(packageManager); // 应用的用户可见名称
// 检查应用名是否已经存在于数据库中
if (dbHelper.appNameExists(name)) {
Log.d(TAG, String.format("%s exists", name));
continue;
}
// 获取应用的各种标志
int flags = app.flags;
// 是否是系统应用
boolean isSystemApp = (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
int compileSdkVersion = app.compileSdkVersion;
int targetSdkVersion = app.targetSdkVersion;
// 进程名称
String processName = app.processName;

// 获取启动活动名
String launcherActivity = "";
Intent intent = packageManager.getLaunchIntentForPackage(packageName);
if (intent != null) {
// 从Intent中获取启动Activity的组件信息
ComponentName componentName = intent.getComponent();
launcherActivity = componentName.getClassName(); // 启动Activity的类名
}
Drawable icon = app.loadIcon(packageManager);
// 获取应用图标
Bitmap bitmap = getBitmapFromDrawable(icon);
// 将图标转换为可以存储到数据库中的字节数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] iconBytes = baos.toByteArray();

// 将数据保存到数据库
ContentValues values = new ContentValues();
values.put("packageName", packageName);
values.put("appName", name);
values.put("isSystemApp", isSystemApp);
values.put("launcherActivity", launcherActivity);
values.put("processName", processName);
values.put("compileSdkVersion", compileSdkVersion);
values.put("targetSdkVersion", targetSdkVersion);
values.put("icon", iconBytes);
db.insert("apps", null, values);
Log.d(TAG, String.format("%s saved", name));
}

db.close();
writeFinishedFlag(context);
}

private static Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof AdaptiveIconDrawable) {
Drawable backgroundDr = ((AdaptiveIconDrawable) drawable).getBackground();
Drawable foregroundDr = ((AdaptiveIconDrawable) drawable).getForeground();
// 添加非空检查
if (backgroundDr != null && foregroundDr != null) {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
backgroundDr.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
foregroundDr.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
backgroundDr.draw(canvas);
foregroundDr.draw(canvas);
return bitmap;
} else{
// 创建一个完全透明的 ARGB_8888 格式的 Bitmap
return Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888);
}
} else {
// Fallback for other types of drawables (e.g., ColorDrawable, VectorDrawable)
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}

private static void writeFinishedFlag(Context context) {
File file = new File(Objects.requireNonNull(context.getExternalFilesDir(null)).getAbsolutePath(), "result.txt");
try (FileWriter writer = new FileWriter(file)) {
writer.write("1");
} catch (IOException e) {
Log.d(TAG, "writeFinishedFlag: ", e);
}
}
}

DatabaseHelper(数据保存)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class DatabaseHelper extends SQLiteOpenHelper {

private static final String DATABASE_NAME = "appInfo.db";
private static final String TABLE_NAME = "apps";
private static final int DATABASE_VERSION = 1;

private static final String CREATE_TABLE_APPS =
"CREATE TABLE " + TABLE_NAME +
"(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"packageName TEXT, " +
"appName TEXT," +
"isSystemApp INTEGER," +
"launcherActivity TEXT," +
"processName TEXT," +
"compileSdkVersion INTEGER," +
"targetSdkVersion INTEGER," +
"icon BLOB);";


public DatabaseHelper(Context context) {
super(context, context.getExternalFilesDir(null).getAbsolutePath() +
"/" + DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_APPS);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Handle database version upgrades if necessary
}

public boolean appNameExists(String appName) {
SQLiteDatabase db = getReadableDatabase();
String query = "SELECT * FROM " + TABLE_NAME + " WHERE appName = ?";
Cursor cursor = db.rawQuery(query, new String[]{appName});
boolean exists = cursor.moveToFirst();
cursor.close();
return exists;
}

}

使用方法

安装APK

1
adb install -g app-debug.apk

通过-g参数授予全部权限

启动APK

1
adb shell am start -n com.tt.appinfo/.MainActivity

等待获取完成

1
adb shell cat /sdcard/Android/data/com.tt.appinfo/files/result.txt

当cat到的结果为1时,表示获取完成

导出数据

1
adb pull /sdcard/Android/data/com.tt.appinfo/files/appInfo.db

解析数据

可通过SQLite在线查看 - StrErr.com临时查看数据

python解析示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
if __name__ == "__main__":
conn = sqlite3.connect('appInfo.db')
cursor = conn.cursor()
cursor.execute("SELECT _id, packageName, appName, isSystemApp, launcherActivity, processName, compileSdkVersion, targetSdkVersion, icon FROM apps")
rows = cursor.fetchall()
appInfos = {}
for row in rows:
packageName = row[1]
appName = row[2]
isSystemApp = row[3]
launcherActivity = row[4]
processName = row[5]
compileSdkVersion = row[6]
targetSdkVersion = row[7]
icon = row[8]
if packageName in appInfos.keys():
continue
appInfos[packageName] = {
"appName": appName,
"isSystemApp": isSystemApp,
"launcherActivity": launcherActivity,
"processName": processName,
"compileSdkVersion": compileSdkVersion,
"targetSdkVersion": targetSdkVersion,
"icon": icon,
}

if not os.path.exists("Icons"):
os.mkdir("Icons")

for package in appInfos.keys():
appName = appInfos[package]["appName"]
isSystemApp = appInfos[package]["isSystemApp"]
launcherActivity = appInfos[package]["launcherActivity"]
processName = appInfos[package]["processName"]
compileSdkVersion = appInfos[package]["compileSdkVersion"]
targetSdkVersion = appInfos[package]["targetSdkVersion"]

print(f"[{package}]appName: {appName}")
print(f"[{package}]isSystemApp: {isSystemApp}")
print(f"[{package}]launcherActivity: {launcherActivity}")
print(f"[{package}]processName: {processName}")
print(f"[{package}]compileSdkVersion: {compileSdkVersion}")
print(f"[{package}]targetSdkVersion: {targetSdkVersion}")

iconData = appInfos[package]['icon']
with open(f'Icons/{package}.png', 'wb') as f:
f.write(iconData)
print(f"{package} saved")

C#解析示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
private void LoadAppInfo()
{
string connectionString = "Data Source=appInfo.db;";
using var conn = new SqliteConnection(connectionString);
conn.Open();
string sql = "SELECT _id, packageName, appName, isSystemApp, launcherActivity, processName, compileSdkVersion, targetSdkVersion, icon FROM apps";
using var cmd = new SqliteCommand(sql, conn);
using (var reader = cmd.ExecuteReader())
{
var appInfos = new Dictionary<string, Dictionary<string, object>>();

while (reader.Read())
{
string packageName = reader.GetString(reader.GetOrdinal("packageName"));
string appName = reader.GetString(reader.GetOrdinal("appName"));
bool isSystemApp = reader.GetBoolean(reader.GetOrdinal("isSystemApp"));
string launcherActivity = reader.GetString(reader.GetOrdinal("launcherActivity"));
string processName = reader.GetString(reader.GetOrdinal("processName"));
int compileSdkVersion = reader.GetInt32(reader.GetOrdinal("compileSdkVersion"));
int targetSdkVersion = reader.GetInt32(reader.GetOrdinal("targetSdkVersion"));
byte[]? iconData = reader.IsDBNull(reader.GetOrdinal("icon")) ? null : (byte[])reader.GetValue(reader.GetOrdinal("icon"));

if (appInfos.ContainsKey(packageName))
{
continue;
}

appInfos[packageName] = new Dictionary<string, object>
{
{"appName", appName},
{"isSystemApp", isSystemApp},
{"launcherActivity", launcherActivity},
{"processName", processName},
{"compileSdkVersion", compileSdkVersion},
{"targetSdkVersion", targetSdkVersion},
{"iconData", iconData}
};

Log.Info($"[{packageName}]appName: {appName}");
Log.Info($"[{packageName}]isSystemApp: {isSystemApp}");
Log.Info($"[{packageName}]launcherActivity: {launcherActivity}");
Log.Info($"[{packageName}]processName: {processName}");
Log.Info($"[{packageName}]compileSdkVersion: {compileSdkVersion}");
Log.Info($"[{packageName}]targetSdkVersion: {targetSdkVersion}");

string iconsDir = "Icons";
if (!Directory.Exists(iconsDir))
{
Directory.CreateDirectory(iconsDir);
}

string iconPath = Path.Combine(iconsDir, $"{packageName}.png");
if (iconData != null)
{
File.WriteAllBytes(iconPath, iconData);
}
Log.Info($"{packageName} saved");
}
}
}