@@ -51,6 +51,8 @@ public class BatteryReportActivity extends Activity {
private EditText etSearch ;
// 应用运行时长缓存( key: 包名, value: 时长ms)
private Map < String , Long > appRunTimeCache = new HashMap < String , Long > ( ) ;
// 新增:包名-应用名称映射缓存(用于搜索匹配名称,避免重复查询)
private Map < String , String > packageToAppNameCache = new HashMap < String , String > ( ) ;
// 包管理工具(全局持有,避免重复获取)
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 . getP ackageName( ) )) {
if ( TARGET_APP_PACKAGE . equals ( p ackageName) ) {
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 < android . app . usage . UsageStats > 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 < android . app . usage . UsageStats > 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 < BatteryReportAdapter . ViewHolder > {
private Context mContext ;
private List < AppBatteryModel > mDataList ;
private PackageManager mPm ; // 用于显示时查询应用名称
private PackageManager mPm ; // 兜底用:缓存无名称时查询
private Map < String , String > mPackageToNameCache ; // 新增:名称缓存映射
// Java7 显式构造(传入包管理工具 )
public BatteryReportAdapter ( Context context , List < AppBatteryModel > dataList , PackageManager pm ) {
// Java7 显式构造(新增名称缓存参数 )
public BatteryReportAdapter ( Context context , List < AppBatteryModel > dataList ,
PackageManager pm , Map < String , String > 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 ) {
// 包名不存在(如应用已卸载),用包名作为名称兜底
a ppName = 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 {
A pplicationInfo 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 显式构造