diff --git a/powerbell/build.properties b/powerbell/build.properties index 1dfe650..99402ff 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 16:57:21 HKT 2025 +#Wed Oct 22 09:39:33 GMT 2025 stageCount=15 libraryProject= baseVersion=15.4 publishVersion=15.4.14 -buildCount=0 +buildCount=1 baseBetaVersion=15.4.15 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java index 42975b3..b824260 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java @@ -51,6 +51,8 @@ public class BatteryReportActivity extends Activity { private EditText etSearch; // 应用运行时长缓存(key:包名,value:时长ms) private Map appRunTimeCache = new HashMap(); + // 新增:包名-应用名称映射缓存(用于搜索匹配名称,避免重复查询) + private Map packageToAppNameCache = new HashMap(); // 包管理工具(全局持有,避免重复获取) private PackageManager mPackageManager; @@ -74,25 +76,27 @@ public class BatteryReportActivity extends Activity { // 1. 加载所有应用(仅存包名,不查名称) loadAllAppPackage(); - // 2. 强制添加目标应用(包名兜底) + // 2. 预缓存所有包名对应的应用名称(为搜索名称做准备) + preCacheAllAppNames(); + // 3. 强制添加目标应用(包名兜底) //forceAddTargetPackage(); - // 3. 首次获取运行时长(key:包名) + // 4. 首次获取运行时长(key:包名) appRunTimeCache = getAppRunTime(); - // 4. 更新时长到数据模型(用包名匹配) + // 5. 更新时长到数据模型(用包名匹配) updateAppRunTimeToModel(); - // 5. 初始化过滤列表和适配器(传入包管理工具供显示时查名称) + // 6. 初始化过滤列表和适配器(传入包管理工具+名称缓存) filteredList.addAll(dataList); - adapter = new BatteryReportAdapter(this, filteredList, mPackageManager); + adapter = new BatteryReportAdapter(this, filteredList, mPackageManager, packageToAppNameCache); rvBatteryReport.setAdapter(adapter); - // 搜索监听(按包名过滤,不涉及名称) + // 搜索监听:修改为“包名+应用名称”双匹配 etSearch.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - filterAppsByPackage(s.toString()); + filterAppsByPackageAndName(s.toString()); // 替换为新的双维度过滤方法 } @Override @@ -145,27 +149,51 @@ public class BatteryReportActivity extends Activity { dataList.clear(); // 清空列表避免重复 LogUtils.d(TAG, "开始加载应用包名列表,共找到" + appList.size() + "个应用"); - + // 仅遍历包名,不处理名称(避免名称获取异常影响) for (ApplicationInfo appInfo : appList) { String packageName = appInfo.packageName; - /*if (packageName.equals("cc.winboll.studio.powerbell.beta")) { - //if (packageName.equals("aidepro.top")) { - LogUtils.d(TAG, "aidepro.top OK5"); - }*/ - // 模型仅存包名、初始耗电0、初始时长0(名称显示时再查) + // 模型仅存包名、初始耗电0、初始时长0(名称显示/搜索时用缓存) dataList.add(new AppBatteryModel(packageName, "0.0", 0)); } - - /*for (AppBatteryModel appBatteryModel : dataList) { - if (appBatteryModel.getPackageName().equals("aidepro.top")) { - LogUtils.d(TAG, "aidepro.top OK4"); - } - }*/ LogUtils.d(TAG, "应用包名列表加载完成,共添加" + dataList.size() + "个包名。" ); } + /** + * 新增:预缓存所有包名对应的应用名称(为搜索提供支持,避免重复查询) + */ + private void preCacheAllAppNames() { + packageToAppNameCache.clear(); // 清空旧缓存 + LogUtils.d(TAG, "开始预缓存包名-应用名称映射"); + + for (AppBatteryModel model : dataList) { + String packageName = model.getPackageName(); + String appName = getAppNameByPackage(packageName); // 复用工具方法获取名称 + packageToAppNameCache.put(packageName, appName); // 缓存映射关系 + } + + LogUtils.d(TAG, "预缓存完成,共缓存" + packageToAppNameCache.size() + "个应用名称"); + } + + /** + * 工具方法:通过包名获取应用名称(带异常处理,避免崩溃) + */ + private String getAppNameByPackage(String packageName) { + try { + ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0); + return mPackageManager.getApplicationLabel(appInfo).toString(); + } catch (PackageManager.NameNotFoundException e) { + // 包名不存在(如应用已卸载),用包名兜底 + LogUtils.e(TAG, "包名" + packageName + "对应的应用未找到:" + e.getMessage()); + return packageName; + } catch (Exception e) { + // 其他异常(如权限问题),同样用包名兜底 + LogUtils.e(TAG, "查询应用名称失败(包名:" + packageName + "):" + e.getMessage()); + return packageName; + } + } + /** * 强制添加目标包名(兜底逻辑,仅操作包名) */ @@ -186,6 +214,8 @@ public class BatteryReportActivity extends Activity { // 2. 仅用包名创建模型,强制添加(不依赖名称查询) dataList.add(0, new AppBatteryModel(TARGET_APP_PACKAGE, "0.0", 0)); + // 同步更新名称缓存 + packageToAppNameCache.put(TARGET_APP_PACKAGE, getAppNameByPackage(TARGET_APP_PACKAGE)); LogUtils.d(TAG, "强制添加目标包名成功:" + TARGET_APP_PACKAGE); } @@ -193,57 +223,51 @@ public class BatteryReportActivity extends Activity { * 更新运行时长到模型(用包名匹配,不涉及名称) */ private void updateAppRunTimeToModel() { - /*if(appRunTimeCache.containsKey("aidepro.top")) - { - LogUtils.d(TAG, "aidepro.top OK2"); - }*/ - - int nCount = 0; + int nCount = 0; for (AppBatteryModel model : dataList) { String packageName = model.getPackageName(); - /*if(packageName.equals("aidepro.top")) - { - LogUtils.d(TAG, "aidepro.top OK3"); - }*/ - Long runTime; // Java7 显式判断包名是否在缓存中 if (appRunTimeCache.containsKey(packageName)) { - /*if(packageName.equals("cc.winboll.studio.powerbell.beta")) { - LogUtils.d(TAG, String.format("特殊查询 %s 查询有结果。", packageName)); - }*/ runTime = appRunTimeCache.get(packageName); - LogUtils.d(TAG, String.format("应用包 %s 运行时长已更新。", packageName)); - nCount++; + LogUtils.d(TAG, String.format("应用包 %s 运行时长已更新。", packageName)); + nCount++; } else { runTime = 0L; } model.setRunTime(runTime); } - LogUtils.d(TAG, String.format("dataList.size() %d, appRunTimeCache.size() %d。", dataList.size(), appRunTimeCache.size())); - LogUtils.d(TAG, String.format("updateAppRunTimeToModel() 更新的数据量为:%d", nCount)); + LogUtils.d(TAG, String.format("dataList.size() %d, appRunTimeCache.size() %d。", dataList.size(), appRunTimeCache.size())); + LogUtils.d(TAG, String.format("updateAppRunTimeToModel() 更新的数据量为:%d", nCount)); } /** - * 按包名过滤应用(搜索逻辑仅操作包名) + * 新增:双维度过滤(包名+应用名称) + * 搜索关键词同时匹配“包名(不区分大小写)”或“应用名称(不区分大小写)” */ - private void filterAppsByPackage(String keyword) { + private void filterAppsByPackageAndName(String keyword) { filteredList.clear(); if (keyword == null || keyword.isEmpty()) { filteredList.addAll(dataList); } else { - String lowerKeyword = keyword.toLowerCase(); + String lowerKeyword = keyword.toLowerCase(); // 统一转为小写,实现不区分大小写搜索 boolean isTargetMatched = false; for (AppBatteryModel model : dataList) { - String packageNameLower = model.getPackageName().toLowerCase(); - // 仅按包名包含关键词过滤 - boolean isMatched = packageNameLower.contains(lowerKeyword); + String packageName = model.getPackageName(); + String packageNameLower = packageName.toLowerCase(); + // 从缓存中获取应用名称(避免重复查询PackageManager) + String appName = packageToAppNameCache.get(packageName); + String appNameLower = appName.toLowerCase(); + + // 匹配规则:包名包含关键词 OR 应用名称包含关键词 + boolean isMatched = packageNameLower.contains(lowerKeyword) + || appNameLower.contains(lowerKeyword); if (isMatched) { filteredList.add(model); // 检查是否是目标包名 - if (TARGET_APP_PACKAGE.equals(model.getPackageName())) { + if (TARGET_APP_PACKAGE.equals(packageName)) { isTargetMatched = true; } } @@ -271,11 +295,11 @@ public class BatteryReportActivity extends Activity { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { try { android.app.usage.UsageStatsManager manager = - (android.app.usage.UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); + (android.app.usage.UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); long endTime = System.currentTimeMillis(); long startTime = endTime - 24 * 3600 * 1000; // 近24小时 List statsList = manager.queryUsageStats( - android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); + android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); // 仅存储包名和时长(不查名称) for (android.app.usage.UsageStats stats : statsList) { @@ -283,21 +307,16 @@ public class BatteryReportActivity extends Activity { String packageName = stats.getPackageName(); LogUtils.d(TAG, "包名" + packageName + "运行时长:" + formatRunTime(runTimeMs)); runTimeMap.put(packageName, runTimeMs); - if (packageName.equals("aidepro.top")) { - LogUtils.d(TAG, String.format("runTimeMap.put(packageName, runTimeMs) 特殊查询 %s 查询有结果。", packageName)); - } + if (packageName.equals("aidepro.top")) { + LogUtils.d(TAG, String.format("runTimeMap.put(packageName, runTimeMs) 特殊查询 %s 查询有结果。", packageName)); + } } } catch (Exception e) { LogUtils.e(TAG, "获取应用运行时长失败:" + e.getMessage()); } } - /*if(runTimeMap.containsKey("aidepro.top")) - { - LogUtils.d(TAG, "aidepro.top OK"); - }*/ - - LogUtils.d(TAG, String.format("应用运行时长列表数量%d。", runTimeMap.size())); + LogUtils.d(TAG, String.format("应用运行时长列表数量%d。", runTimeMap.size())); return runTimeMap; } @@ -359,8 +378,8 @@ public class BatteryReportActivity extends Activity { } }); - // 重新应用包名过滤 - filterAppsByPackage(etSearch.getText().toString()); + // 重新应用“包名+名称”双维度过滤 + filterAppsByPackageAndName(etSearch.getText().toString()); } /** @@ -372,7 +391,7 @@ public class BatteryReportActivity extends Activity { } android.app.usage.UsageStatsManager manager = - (android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); + (android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); if (manager == null) { return false; } @@ -380,7 +399,7 @@ public class BatteryReportActivity extends Activity { long endTime = System.currentTimeMillis(); long startTime = endTime - 1000 * 60; List statsList = manager.queryUsageStats( - android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); + android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); return statsList != null && !statsList.isEmpty(); } @@ -423,28 +442,31 @@ public class BatteryReportActivity extends Activity { } /** - * RecyclerView 适配器(仅在显示时通过包名查询应用名称) + * RecyclerView 适配器(支持从缓存获取名称,避免重复查询) */ public static class BatteryReportAdapter extends RecyclerView.Adapter { private Context mContext; private List mDataList; - private PackageManager mPm; // 用于显示时查询应用名称 + private PackageManager mPm; // 兜底用:缓存无名称时查询 + private Map mPackageToNameCache; // 新增:名称缓存映射 - // Java7 显式构造(传入包管理工具) - public BatteryReportAdapter(Context context, List dataList, PackageManager pm) { + // Java7 显式构造(新增名称缓存参数) + public BatteryReportAdapter(Context context, List dataList, + PackageManager pm, Map packageToNameCache) { this.mContext = context; this.mDataList = dataList; - this.mPm = pm; // 持有包管理工具,供显示时查名称 + this.mPm = pm; + this.mPackageToNameCache = packageToNameCache; // 接收名称缓存 } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(mContext) - .inflate(android.R.layout.simple_list_item_2, parent, false); + .inflate(android.R.layout.simple_list_item_2, parent, false); return new ViewHolder(itemView); } - // 绑定数据:核心修改——仅在此处通过包名查询应用名称并显示 + // 绑定数据:优先从缓存取名称,无缓存时兜底查询 @Override public void onBindViewHolder(ViewHolder holder, int position) { // Java7 显式非空判断(避免空指针) @@ -458,28 +480,35 @@ public class BatteryReportActivity extends Activity { String packageName = model.getPackageName(); String appName = ""; - // 关键步骤:通过包名查询应用名称(仅显示时执行,不影响数据计算) - try { - ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0); - appName = mPm.getApplicationLabel(appInfo).toString(); - } catch (PackageManager.NameNotFoundException e) { - // 包名不存在(如应用已卸载),用包名作为名称兜底 - appName = packageName; - LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage()); - } catch (Exception e) { - // 其他异常(如权限问题),同样用包名兜底 - appName = packageName; - LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage()); + // 优先从缓存获取名称(避免重复调用PackageManager,提升性能) + if (mPackageToNameCache != null && mPackageToNameCache.containsKey(packageName)) { + appName = mPackageToNameCache.get(packageName); + } else { + // 缓存无数据时,兜底查询名称(兼容异常场景) + try { + ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0); + appName = mPm.getApplicationLabel(appInfo).toString(); + // 同步更新到缓存,后续复用 + if (mPackageToNameCache != null) { + mPackageToNameCache.put(packageName, appName); + } + } catch (PackageManager.NameNotFoundException e) { + appName = packageName; + LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage()); + } catch (Exception e) { + appName = packageName; + LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage()); + } } - // 显示应用名称(查询到则显示名称,否则显示包名) + // 显示应用名称(缓存/兜底结果) holder.tvAppName.setText(appName); // 格式化运行时长(调用 Activity 的工具方法) String runTimeStr = ((BatteryReportActivity) mContext).formatRunTime(model.getRunTime()); // 显示耗电+运行时长(基于包名计算的结果) String consumptionText = String.format("耗电:%s mAh | 运行时长:%s", - model.getConsumption(), runTimeStr); + model.getConsumption(), runTimeStr); holder.tvConsumption.setText(consumptionText); // 优化显示:目标应用文字标蓝(通过包名匹配) @@ -504,7 +533,7 @@ public class BatteryReportActivity extends Activity { // ViewHolder(绑定系统布局的两个 TextView) public static class ViewHolder extends RecyclerView.ViewHolder { - TextView tvAppName; // 显示应用名称(通过包名查询) + TextView tvAppName; // 显示应用名称(缓存/兜底获取) TextView tvConsumption; // 显示耗电+运行时长(包名计算结果) // Java7 显式构造 diff --git a/powerbell/src/main/res/layout/activity_batteryreport.xml b/powerbell/src/main/res/layout/activity_batteryreport.xml index 70a6d3e..361b96f 100644 --- a/powerbell/src/main/res/layout/activity_batteryreport.xml +++ b/powerbell/src/main/res/layout/activity_batteryreport.xml @@ -6,19 +6,19 @@ android:orientation="vertical" android:background="@android:color/white"> - + - + -