AndroID手机上安装的很多应用都会频繁唤醒手机(唤醒系统、唤醒屏幕),造成手机耗电等现象。良好的对齐唤醒管理方案,就是对后台应用待机时不频繁唤醒,智能节省电量。
实现原理:APK作为该功能的入口,勾选应用后,将勾选的应用写入黑名单,并通知framework黑名单内容变化;framework接收到通知后,自动获取黑名单中的应用,保存到列表中;在framework调用接口中检测应用是否在黑名单中,如果在黑名单中则检测闹钟类型,如果闹钟类型是0或2,对应修改为1或3。
应用层功能实现APK界面初始化在ForbitAlarmlogic构造方法中初始化了数组列表ListPkgs、forbitPkgs、allowPkgs、showPkgs。
ListPkgs:表示需要设置对齐唤醒的应用,如果这些应用已经安装,就会显示在对齐唤醒设置的界面上。初始数据从/data/data/com.***.androID.security/app_bin/forbitappList.xml中获取,如果文件不存在,则从本地资源数组security_array_savepower_forbitalarms中获取。
forbitPkgs:表示对齐唤醒名单,即禁止唤醒的名单,界面勾选的应用。初始数据从SharedPreference数据库名ManagerUtil.PRE_name(com.***.androID.savepowermanager_preferences)中获取键值ManagerUtil.FORBIT_ALARM_APP_List_KEY中保存的数据,将获取的数据保存到forbitPkgs数组中,如果没有数据则返回null。
allowPkgs:表示允许唤醒的名单,界面没有勾选的应用。初始数据从SharedPreference数据库ManagerUtil.PRE_name(com.***.androID.savepowermanager_preferences)中获取键值为ManagerUtil.ALLOW_ALARM_APP_List_KEY中保存的数据,将获取的数据保存到allowPkgs数组列表中;如果没有数据则返回null。
showPkgs:表示要显示在对齐唤醒设置界面的数组应用列表,在数据初始化之前先将该数组清空。对齐唤醒方案优化之前,该数组保存的是ListPkgs列表与已安装应用的交集。优化之后,同时还保存了已安装的第三方应用。
public ForbitAlarmlogic(Context ctx) { this.mCtx = ctx; pm = ctx.getPackageManager(); xmlAppList = Util.getDefaultDataPath(ctx) + "/app_bin/appList.xml"; String xmlfile = Util.getDefaultDataPath(ctx)+"/app_bin/forbitappList.xml"; file f = new file(xmlfile); if (!f.exists()) { Log.e("forbitappList not exist!"); String[] strs = mCtx.getResources().getStringArray(R.array.security_array_savepower_forbitalarms); for (String str : strs) { ListPkgs.add(str); } } else { readFromXmlWithfilename(xmlfile, ListPkgs); }// readFromXml(); Set<String> forbitset = (Set<String>)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_name, ManagerUtil.FORBIT_ALARM_APP_List_KEY, null, 4); if (forbitset != null) { Iterator<String> forbitir = forbitset.iterator(); while(forbitir.hasNext()) { String forbit = forbitir.next(); forbitPkgs.add(forbit); } } Set<String> allowset = (Set<String>)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_name, ManagerUtil.ALLOW_ALARM_APP_List_KEY, null, 4); if (allowset != null) { Iterator<String> allowir = allowset.iterator(); while(allowir.hasNext()) { String allow = allowir.next(); allowPkgs.add(allow); } }}
public ArrayList<DroIDApp> getListApps() { if (forbitPkgs.size() == 0) { Set<String> forbitset= (Set<String>)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_name, ManagerUtil.FORBIT_ALARM_APP_List_KEY, null, 4); if (forbitset == null) { readFromXml(); HashSet<String> forbitPkgsSet = new HashSet<String>(); for (String pkg : forbitPkgs) { forbitPkgsSet.add(pkg); } ManagerUtil.savePreferenceValue(mCtx, ManagerUtil.PRE_name, ManagerUtil.FORBIT_ALARM_APP_List_KEY, forbitPkgsSet, 4); } else { Iterator<String> forbitir = forbitset.iterator(); while(forbitir.hasNext()) { String forbit = forbitir.next(); forbitPkgs.add(forbit); } } } showPkgs.clear(); ArrayList<DroIDApp> apps = new ArrayList<DroIDApp>(); final List<PackageInfo> installed = pm.getInstalledPackages(0); String name = null; for (final PackageInfo appInfo : installed){ String pkg = appInfo.packagename; if (ListPkgs.contains(pkg)) { DroIDApp app = new DroIDApp(); name = pm.getApplicationLabel(appInfo.applicationInfo).toString(); app.name = name; app.icon = appInfo.applicationInfo.loadIcon(pm); if (forbitPkgs.contains(pkg)) { app.online_switch = true; } else if (allowPkgs.contains(pkg)) { app.online_switch = false; } else { app.online_switch = true; } app.pkg = pkg; apps.add(app); showPkgs.add(pkg); Log.d("in white List and installed package is : "+pkg); } else {// 已经安装的第三方应用 if ((appInfo.applicationInfo.uID > 10000) && (appInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYstem) == 0 && (appInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYstem_APP) == 0) { String pkgname = appInfo.packagename; DroIDApp app = new DroIDApp(); app.name = pm.getApplicationLabel(appInfo.applicationInfo).toString(); app.icon = appInfo.applicationInfo.loadIcon(pm);// app.online_switch = true; if (forbitPkgs.contains(pkg)) { app.online_switch = true; } else if (allowPkgs.contains(pkg)) { app.online_switch = false; } else { app.online_switch = true; } app.pkg = pkgname; apps.add(app); showPkgs.add(pkgname); Log.d("not in white List and installed third package is : "+pkgname); } } } return apps; }
private class GetListDataThread implements Runnable { @OverrIDe public voID run() { // Todo auto-generated method stub appList = mFbAmLogic.getListApps(); resultList.clear(); for (DroIDApp app : appList) { Log.d("getListApps appname = " + app.pkg); if (app.online_switch) { if (app.pkg != null && app.pkg.length() > 0) { resultList.add(app.pkg); saveList.add(app.pkg); } } } Message msg = Message.obtain(); msg.what = MSG_SHOWList; handler.sendMessage(msg); }}
ForbitAlarmlogic类的getListApps()方法中重新为forbitPkgs数组赋值
如果forbitPkgs为空,即在构造方法中没有获取到数据,重新从上面数据库中获取数据;如果仍然是空,则从/data/data/com.***.androID.security/app_bin/appList.xml文件中获取,保存到forbitPkgs数组中。
手机管家中显示的对齐唤醒名单主要有:
(1)、forbitappList.xml文件与已安装应用的交集应用;
(2)、已安装的第三方应用。
APK响应机制APK在启动之后,就已经设置好了黑白名单,初始化过程就是加载界面的过程。
响应点击事件界面初始化完毕之后,将处于勾选状态的应用保存到两个数组列表:resultList、saveList。响应点击事件时,将应用移除resultList列表,或添加到resultList列表中。
界面退出机制在onPause()方法中判断resultList与saveList是否相同,如果不相同则重新保存对齐唤醒名单,并通知AlarmManagerService。
public voID onPause() { // Todo auto-generated method stub super.onPause(); new Thread(new Runnable() { @OverrIDe public voID run() { // Todo auto-generated method stub boolean isSameContent = true; for (int i = 0; i < saveList.size(); i++) { Log.d("saveList "+ i + " = "+saveList.get(i)); } for (int j = 0; j < resultList.size(); j++) { Log.d("resultList "+ j + " = "+resultList.get(j)); } if (saveList.size() == resultList.size()) { Log.i("saveList == resultList"); for (String result : resultList) { String xmlAppList = "/data/data/com.***.androID.security/app_bin/appList.xml"; ArrayList<String> forbitPkgs = new ArrayList<String>(); ForbitAlarmlogic.readFromXmlWithfilename(xmlAppList, forbitPkgs); if (!forbitPkgs.contains(result)) { Log.i(result + "Not In appList.xml"); isSameContent = false; break; } if (!saveList.contains(result)) { Log.i(result + "Not In SaveList"); isSameContent = false; break; } } } else { Log.i("saveList Changed"); isSameContent = false; } if (!isSameContent) { Log.i("ForbitAlarmSetting save Data"); mFbAmLogic.saveAlarmAppMap(resultList); } } }).start(); }
(1)、如何重新保存名单?
首先,清空allowPkgs和forbitPkgs,即先清空允许启动的应用列表和禁止启动的应用列表。
其次,将禁止唤醒的应用(即界面上处于勾选状态的应用)添加到forbitPkgs中,并写入/data/data/com.***.androID.security/app_bin/appList.xml文件中。同时写入对应键值为ManagerUtil.FORBIT_ALARM_APP_List_KEY数据库中。
再次,将允许唤醒的应用(界面上没有勾选的应用)添加到allowPkgs中,并写入对应键值为ManagerUtil.ALLOW_ALARM_APP_List_KEY数据库中。
最后,通知AlarmManagerService。
(2)、如何通知AlarmManagerService?
上面数据保存完毕后,发送广播:com.***.androID.savepower.forbitalarmappListchanged,通知AlarmManagerService。
public static voID notifyFramework(final Context ctx) { new Thread(){ public voID run() { try { Thread.sleep(200); Intent intent = new Intent(); intent.setAction(ManagerUtil.INTENT_FORBITALARM_List_CHANGED); ctx.sendbroadcast(intent); } catch (InterruptedException e) { Log.e("appList.xml send broadcast error"); } }; }.start();}
流程图如下:
安装第三方应用在PackageReceiver类中接收到包安装的广播后,将第三方应用添加到白名单,重新获取对齐唤醒数据。
new Thread(new Runnable() { @OverrIDe public voID run() { // Todo auto-generated method stub Log.d("automatically add newly installed applications into blackList." + " packagename = " + packagename); synchronized (PackageReceiver.this) { mForbitAlarmlogic = ForbitAlarmlogic .getInstance(mCtx); mForbitAlarmlogic .packageReceiverApkAdded(packagename); } } }).start();
AlarmManagerService实现机制接收广播当对齐唤醒名单发生变化时,会发送forbitalarmappListchanged 广播。AlarmManagerService定义了该广播的接收器,用来接收APK发送的广播。从appList.xml(/data/data/com.***.androID.security/app_bin/appList.xml)文件中读取应用保存到全局变量mHashtable中。
class UpdateXmlReceiver extends broadcastReceiver { public UpdateXmlReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_SAVEPOWER_UPDATEXML); getContext().registerReceiver(this, filter); } @OverrIDe public voID onReceive(Context context, Intent intent) { synchronized (mlock) { // Todo auto-generated method stub if(YulongFeature.FEATURE_REDUCE_RTC_WAKEUP){ mHashtable.clear(); Slog.d(TAG, "Receive savepower broadcast, read xml again."); getPackagenameFromXml(); } } }}
private voID getPackagenameFromXml() { fileReader permReader = null; try { permReader = new fileReader(xmlNewfile); Slog.d(TAG, "getPackagenameFromXml : read xmlNewfile "); } catch (fileNotFoundException e) { try { permReader = new fileReader(xmlfile); Slog.d(TAG, "getPackagenameFromXml : read xmlfile "); } catch (fileNotFoundException e1) { // Todo auto-generated catch block Slog.d(TAG, "getPackagenameFromXml, can not find config xml "); return; } } try { XmlPullParser parser = Xml.newPullParser(); parser.setinput(permReader); XmlUtils.begindocument(parser, "channel"); while (true) { XmlUtils.nextElement(parser); if (parser.getEventType() == XmlPullParser.END_document) { break; } String name = parser.getname(); if ("item".equals(name)) { int ID = Integer.parseInt(parser.getAttributeValue(null, "ID")); if (ID <= 0) { Slog.w(TAG, "<item> without name at " + parser.getpositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } String packagename = parser.getAttributeValue(null, "name"); if (packagename == null) { Slog.w(TAG, "<item> without name at " + parser.getpositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } Slog.d(TAG, "getPackagenameFromXml : ID is " + ID + " name is " + packagename); mHashtable.put(ID, packagename); XmlUtils.skipCurrentTag(parser); } else { XmlUtils.skipCurrentTag(parser); continue; } } permReader.close(); } catch (XmlPullParserException e) { Slog.w(TAG, "Got execption parsing permissions.", e); } catch (IOException e) { Slog.w(TAG, "Got execption parsing permissions.", e); }}
修改闹钟类型在调用setImpl方法设置闹钟时,我们通过修改闹钟的类型来实现对齐唤醒功能。
if (type == AlarmManager.RTC_WAKEUP || type == AlarmManager.ELAPSED_REALTIME_WAKEUP) { if(mHashtable.containsValue(callingPackage)){ if (AlarmManager.RTC_WAKEUP == type) { type = AlarmManager.RTC; Slog.v(TAG, "change alarm type RTC_WAKEUP to RTC for " + callingPackage); } if (AlarmManager.ELAPSED_REALTIME_WAKEUP == type) { type = AlarmManager.ELAPSED_REALTIME; Slog.v(TAG, "change alarm type ELAPSED_REALTIME_WAKEUP to ELAPSED_REALTIME for " + callingPackage); } } }
对齐唤醒添加机制(1)、第三方应用全部添加到对齐唤醒名单;
(2)、禁止系统应用验证前添加到对齐唤醒名单,避免导致系统异常。
A. 系统核心应用不允许加入对齐唤醒名单,即位于system/priv-app目录下的应用不可以加入对齐唤醒名单;
总结以上是内存溢出为你收集整理的android功耗优化(2)--对齐唤醒全部内容,希望文章能够帮你解决android功耗优化(2)--对齐唤醒所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)