手头的问题:
我必须创建一个持续运行的服务.这项服务监控5个应用程序说你的手机上安装了5个安卓游戏.该服务需要获取以下信息:
1.游戏开启和运行了多少次?
2.每场比赛的运行时间.
例如:说如果我在我的应用程序中安装了此服务.我让它运行了一个月.我需要在应用程序的屏幕上显示此类信息:
游戏游戏运行次数游戏持续时间
第1场比赛共20次,共15小时
游戏2共16次,总共玩25小时
..
..
第5场比赛共10次,共12小时
可能的方法:
当应用程序加载时,它会进入内存.注意到系统在应用程序启动时计时.当应用程序结束或放在后台注意时间再次.
所以说如果一个应用程序在晚上9点被带到内存并且在晚上9:30退出到后台,这给了我们30分钟的游戏时间.下次播放应用程序时,持续时间将从存储在某种变量中的上一个播放添加到30,依此类推.
每次将应用程序带入内存时,正在播放的计数器应该增加1.因此,给我们一个应用程序播放的次数.
编码:
我不知道Android中的服务,因为我从未真正使用过它们.任何与我手头的问题相关的教程都会非常有用.
其次,如果还有另一种方法可以做到这一点.我也想知道.我真的可以使用一些代码片段来启动这个项目.
解决方法:
正如您所写,任务是关于监控第三方应用程序,除了定期读取进程列表和检测前台进程之外,没有其他解决方案.你需要一个服务.不幸的是,AndroID没有为前台进程更改提供广播事件等方法.
该任务实际上需要大量代码,至少比普通答案所能包含的多得多.我在这里发布了它的一部分,但你应该解决幕后留下的许多细微差别,例如同步和发布之间的持久信息.这只是一个骨架.
首先,让代码编写一个应用程序对象,这是一个注册所有实例相关内容的好地方.
MonitorApp
public class MonitorApp extends Application{ // actual store of statistics private final ArrayList<HashMap<String,Object>> processList = new ArrayList<HashMap<String,Object>>(); // fast-access index by package name (used for lookup) private ArrayList<String> packages = new ArrayList<String>(); public ArrayList<HashMap<String,Object>> getProcessList() { return processList; } public ArrayList<String> getPackages() { return packages; } // Todo: you need to save and load the instance data // Todo: you need to address synchronization issues}
然后让我们起草一项活动.
MonitorActivity
import static ProcessList.ColUMN_PROCESS_name;import static ProcessList.ColUMN_PROCESS_PROP;import static ProcessList.ColUMN_PROCESS_COUNT;import static ProcessList.ColUMN_PROCESS_TIME;public class MonitorActivity extends Activity implements MonitorService.ServiceCallback{ private ArrayList<HashMap<String,Object>> processList; private MonitorService backgroundService; private MyCustomAdapter adapter = null; private ListVIEw ListVIEw = null; @OverrIDe public voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.main); // Todo: provIDe your layout ListVIEw = (ListVIEw)findVIEwByID(R.ID.ID_process_ListvIEw); createAdapter(); this.bindService( new Intent(this, MonitorService.class), serviceConnection, Context.BIND_auto_CREATE); } private voID createAdapter() { processList = ((MonitorApp)getApplication()).getProcessList(); adapter = new MyCustomAdapter(this, processList, R.layout.complex_List_item, new String[] { ColUMN_PROCESS_name, ColUMN_PROCESS_PROP, // Todo: you may calculate and pre-fill this fIEld // from ColUMN_PROCESS_COUNT and ColUMN_PROCESS_TIME // so eliminating the need to use the custom adapter }, new int[] { androID.R.ID.text1, androID.R.ID.text2 }); ListVIEw.setAdapter(adapter); } // callback method invoked by the service when foreground process changed @OverrIDe public voID sendResults(int resultCode, Bundle b) { adapter.notifyDataSetChanged(); } private class MyCustomAdapter extends SimpleAdapter { MyCustomAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) { super(context, data, resource, from, to); } @OverrIDe public VIEw getVIEw (int position, VIEw convertVIEw, VIEwGroup parent) { VIEw result = super.getVIEw(position, convertVIEw, parent); // Todo: customize process statistics display int count = (Integer)(processList.get(position).get(ColUMN_PROCESS_COUNT)); int seconds = (Integer)(processList.get(position).get(ColUMN_PROCESS_TIME)); return result; } } private ServiceConnection serviceConnection = new ServiceConnection() { @OverrIDe public voID onServiceConnected(Componentname classname, IBinder service) { LocalBinder binder = (LocalBinder)service; backgroundService = binder.getService(); backgroundService.setCallback(MonitorActivity.this); backgroundService.start(); } @OverrIDe public voID onServicedisconnected(Componentname classname) { backgroundService = null; } }; @OverrIDe public voID onResume() { super.onResume(); if(backgroundService != null) { backgroundService.setCallback(this); } } @OverrIDe public voID onPause() { super.onPause(); if(backgroundService != null) { backgroundService.setCallback(null); } }}
该活动启动后台工作服务,该服务实际上监视进程.您可以将服务注册从活动移动到应用程序实例中.服务本身就是这样的:
MonitorService
public class MonitorService extends Service{ private boolean initialized = false; private final IBinder mBinder = new LocalBinder(); private ServiceCallback callback = null; private Timer timer = null; private final Handler mHandler = new Handler(); private String foreground = null; private ArrayList<HashMap<String,Object>> processList; private ArrayList<String> packages; private Date split = null; public static int SERVICE_PERIOD = 5000; // Todo: customize (this is for scan every 5 seconds) private final ProcessList pl = new ProcessList(this) { @OverrIDe protected boolean isFilteredByname(String pack) { // Todo: filter processes by names, return true to skip the process // always return false (by default) to monitor all processes return false; } }; public interface ServiceCallback { voID sendResults(int resultCode, Bundle b); } public class LocalBinder extends Binder { MonitorService getService() { // Return this instance of the service so clIEnts can call public methods return MonitorService.this; } } @OverrIDe public voID onCreate() { super.onCreate(); initialized = true; processList = ((MonitorApp)getApplication()).getProcessList(); packages = ((MonitorApp)getApplication()).getPackages(); } @OverrIDe public IBinder onBind(Intent intent) { if(initialized) { return mBinder; } return null; } public voID setCallback(ServiceCallback callback) { this.callback = callback; } private boolean addToStatistics(String target) { boolean changed = false; Date Now = new Date(); if(!TextUtils.isEmpty(target)) { if(!target.equals(foreground)) { int i; if(foreground != null && split != null) { // Todo: calculate time difference from current moment // to the moment when prevIoUs foreground process was activated i = packages.indexOf(foreground); long delta = (Now.getTime() - split.getTime()) / 1000; Long time = (Long)processList.get(i).get(ColUMN_PROCESS_TIME); if(time != null) { // Todo: add the delta to statistics of 'foreground' time += delta; } else { time = new Long(delta); } processList.get(i).put(ColUMN_PROCESS_TIME, time); } // update count of process activation for new 'target' i = packages.indexOf(target); Integer count = (Integer)processList.get(i).get(ColUMN_PROCESS_COUNT); if(count != null) count++; else { count = new Integer(1); } processList.get(i).put(ColUMN_PROCESS_COUNT, count); foreground = target; split = Now; changed = true; } } return changed; } public voID start() { if(timer == null) { timer = new Timer(); timer.schedule(new MonitoringTimerTask(), 500, SERVICE_PERIOD); } // Todo: startForeground(srvcID, createNotification(null)); } public voID stop() { timer.cancel(); timer.purge(); timer = null; } private class MonitoringTimerTask extends TimerTask { @OverrIDe public voID run() { fillProcessList(); ActivityManager activityManager = (ActivityManager)MonitorService.this.getSystemService(ACTIVITY_SERVICE); List<RunningTaskInfo> taskInfo = activityManager.getRunningTasks(1); String current = taskInfo.get(0).topActivity.getPackagename(); // check if current process changed if(addToStatistics(current) && callback != null) { final Bundle b = new Bundle(); // Todo: pass necessary info to UI via bundle mHandler.post(new Runnable() { public voID run() { callback.sendResults(1, b); } }); } } } private voID fillProcessList() { pl.fillProcessList(processList, packages); }}
该服务使用帮助程序类来构建进程列表.
ProcessList中
public abstract class ProcessList{ // process package name public static final String ColUMN_PROCESS_name = "process"; // Todo: arbitrary property (can be user-fIEndly name) public static final String ColUMN_PROCESS_PROP = "property"; // number of times a process has been activated public static final String ColUMN_PROCESS_COUNT = "count"; // number of seconds a process was in foreground public static final String ColUMN_PROCESS_TIME = "time"; private Contextwrapper context; ProcessList(Contextwrapper context) { this.context = context; } protected abstract boolean isFilteredByname(String pack); public voID fillProcessList(ArrayList<HashMap<String,Object>> processList, ArrayList<String> packages) { ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); List<RunningAppProcessInfo> procInfo = activityManager.getRunningAppProcesses(); HashMap<String, Object> hm; final PackageManager pm = context.getApplicationContext().getPackageManager(); for(int i = 0; i < procInfo.size(); i++) { String process = procInfo.get(i).processname; String packageList = Arrays.toString(procInfo.get(i).pkgList); if(!packageList.contains(process)) { process = procInfo.get(i).pkgList[0]; } if(!packages.contains(process) && !isFilteredByname(process)) { ApplicationInfo ai; String applicationname = ""; for(int k = 0; k < procInfo.get(i).pkgList.length; k++) { String thisPackage = procInfo.get(i).pkgList[k]; try { ai = pm.getApplicationInfo(thisPackage, 0); } catch(final nameNotFoundException e) { ai = null; } if(k > 0) applicationname += " / "; applicationname += (String)(ai != null ? pm.getApplicationLabel(ai) : "(unkNown)"); } packages.add(process); hm = new HashMap<String, Object>(); hm.put(ColUMN_PROCESS_name, process); hm.put(ColUMN_PROCESS_PROP, applicationname); processList.add(hm); } } // optional sorting Comparator<HashMap<String, Object>> comparator = new Comparator<HashMap<String, Object>>() { public int compare(HashMap<String, Object> object1, HashMap<String, Object> object2) { return ((String)object1.get(ColUMN_PROCESS_name)).comparetoIgnoreCase((String)object2.get(ColUMN_PROCESS_name)); } }; Collections.sort(processList, comparator); packages.clear(); for(HashMap<String, Object> e : processList) { packages.add((String)e.get(ColUMN_PROCESS_name)); } }}
最后,清单.
AndroIDManifest.xml中
<?xml version="1.0" enCoding="utf-8"?><manifest xmlns:androID="http://schemas.androID.com/apk/res/androID" package="com.yourpackage" androID:versionCode="1" androID:versionname="1.0" > <uses-sdk androID:minSdkVersion="8" androID:targetSdkVersion="18" /> <uses-permission androID:name="androID.permission.GET_TASKS" /> <application androID:icon="@drawable/ic_launcher" androID:label="@string/app_name" > <activity androID:name=".MonitorActivity" androID:label="@string/app_name" androID:configChanges="orIEntation|keyboardHIDden" > <intent-filter> <action androID:name="androID.intent.action.MAIN" /> <category androID:name="androID.intent.category.LAUNCHER" /> </intent-filter> </activity> <service androID:name=".MonitorService" /> </application></manifest>
正如您所看到的,它已经是很多代码了.它部分地从一个正在运行的应用程序中提取,但我根据您的需求进行了快速更改,因此可能存在拼写错误,所有导入都被跳过等等.不过,我希望这会有所帮助.
ADDENDUM:Lollipop
注意:最新的AndroID版本打破了上述方法.以下是官方文档中关于getRunningTasks方法和其他方法的说明:
As of LolliPOP, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still retu rn a small subset of its data: at least the caller’s own tasks, and possibly some other tasks such as home that are kNown to not be sensitive.
我认为这是一种矫枉过正,可以更有选择性和方便的方式完成.更值得一提的是,考虑到Google的许多内置功能存在隐私问题,这似乎太戏剧化了.无论如何,我们无能为力.
唯一的解决方法是实现AndroID辅助功能服务(更多信息here和here),并拦截所有 *** 作,应用程序从那里获得和失去焦点.用户应手动启用该服务!您的应用程序应该以某种方式指导用户这样做.
总结以上是内存溢出为你收集整理的android – 如何创建一个持续监控应用程序使用信息的服务?全部内容,希望文章能够帮你解决android – 如何创建一个持续监控应用程序使用信息的服务?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)