Android 如何创建持续监控应用使用信息的服务?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20416610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:50:37  来源:igfitidea点击:

How to create a Service which continuously monitors app usage information?

androidserviceusage-statistics

提问by D'yer Mak'er

Problem at Hand: I have to create a Servicewhich runs continuously. This service monitors 5 apps say 5 android games installed on your phone. This service needs to get the information of: 1. How many times is the game opened and run? 2. For how much time each game has run.

手头的问题:我必须创建一个Service连续运行的。此服务会监控 5 个应用程序,其中显示您手机上安装了 5 个 Android 游戏。该服务需要获取以下信息: 1. 游戏打开运行了多少次?2. 每场比赛运行了多少时间。

for example: Say If I have this service installed in my app. And I let it run for a month. I need information of this kind on the screen of the app:

例如:说如果我在我的应用程序中安装了这个服务。我让它运行了一个月。我需要应用程序屏幕上的此类信息:

GameNumber of times the game is runDuration of Game play

游戏游戏运行次数 游戏持续时间

Game 1 20 times played for 15 hours in total

游戏 1 20 次共玩 15 小时

Game 2 16 times played for 25 hours in total

游戏 2 16 次共玩 25 小时

..

..

..

..

Game 5 10 times played for 12 hours in total

游戏 5 10 次共玩 12 小时

Possible Approach: When an application loads it comes in the memory. Noting the system clocks time while the application starts. And when the application ends or is put in the background noting the time again.

可能的方法:当应用程序加载时,它进入内存。在应用程序启动时注意系统时钟时间。当应用程序结束或被置于后台时,再次记录时间。

So say if an application is brought to memory at 9:00 pm and exits to background at 9:30 pm that gives us a gameplay time of 30 mins. Next time the application is played the duration will be added to 30 from the previous play stored in some sort of variable and so on. Each time an application is brought into the memory the counter of it being played should increase by one. Hence giving us the number of times an application is played.

假设一个应用程序在晚上 9:00 进入内存并在晚上 9:30 退出到后台,那么我们的游戏时间为 30 分钟。下一次播放应用程序时,持续时间将从存储在某种变量中的先前播放添加到 30,依此类推。每次将应用程序带入内存时,其正在播放的计数器应增加 1。因此,给我们一个应用程序的播放次数。

Coding:I have no idea about Servicein Android as I have never really worked on them. Any tutorials related to my problem at hand will be very helpful. Secondly, if there is another way in which this could be done. I would like to know that as well. I could really use some code snippet for me to start this project.

编码:我对ServiceAndroid一无所知,因为我从未真正研究过它们。任何与我手头问题相关的教程都会非常有帮助。其次,如果有另一种方法可以做到这一点。我也想知道。我真的可以使用一些代码片段来启动这个项目。

采纳答案by Stan

As you wrote that the task is about monitoring 3-rd party applications, there is no solution other than periodically read a list of processes and detecting foreground process. You need a service for this. Unfortunately, Android does not provide means such as broadcast events for foreground process change.

正如您所写的任务是关于监视 3-rd 方应用程序,除了定期读取进程列表和检测前台进程之外没有其他解决方案。您需要为此提供服务。遗憾的是,Android 并没有为前台进程更改提供广播事件等手段。

The task requires a lot of code in fact, at least much more than an ordinary answer could comprise. I'm posting a part of it here, but you should address many nuances left behind the scenes, such as synchronization and persisting information between launches. This is just a skeleton.

实际上,该任务需要大量代码,至少比普通答案所能包含的要多得多。我在这里发布了其中的一部分,但您应该解决许多幕后留下的细微差别,例如启动之间的同步和持久信息。这只是一个骨架。

First, lets code an application object, which is a good place to register all instance related stuff.

首先,让我们编写一个应用程序对象,这是注册所有与实例相关的东西的好地方。

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
}

Then lets draft an activity.

然后让我们起草一个活动。

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);
    }
  }

}

The activity launches a background worker service, which does actually monitor processes. You could possibly move the service registration from the activity into the application instance. The service itself is something like this:

该活动启动了一个后台工作服务,它实际上监控进程。您可以将服务注册从活动移到应用程序实例中。服务本身是这样的:

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);
  }

}

The service utilizes a helper class for building process lists.

该服务使用一个帮助程序类来构建进程列表。

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));
    }
  }

}

Finally, the manifest.

最后,清单。

AndroidManifest.xml

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>

As you may see, it's already a lot of code. It's partially extracted from a working application, but I made fast changes for your needs, so there may be typos, all imports are skipped, etc. Nevertheless, I hope this helps a bit.

如您所见,它已经有很多代码了。它部分提取自一个正在运行的应用程序,但我根据您的需要进行了快速更改,因此可能存在拼写错误、跳过所有导入等。不过,我希望这会有所帮助。

ADDENDUM: Lollipop+

附录:棒棒糖+

Beware: the latest Android versions broke the abovementioned approach. Here is what the official documentation says about getRunningTasksmethod and others:

注意:最新的 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.

从 LOLLIPOP 开始,这种方法不再适用于第三方应用程序:引入以文档为中心的最近记录意味着它可以将个人信息泄露给调用者。为了向后兼容,它仍将返回其数据的一小部分:至少是调用者自己的任务,可能还有一些其他任务,例如已知不敏感的 home。

I think this is an overkill and could be done in much more selective and convenient way. Not to mention that this seems too theatrical considering many built-in features from Google with privacy concerns. Anyway, we can do nothing with this.

我认为这是一种矫枉过正,可以通过更有选择性和更方便的方式来完成。更不用说考虑到谷歌的许多内置功能与隐私问题,这似乎太戏剧化了。无论如何,我们对此无能为力。

The only workaround is to implement Android accessibility service (more info hereand here) and intercept all actions with applications gaining and losing focus from there. The user should enable the service manually! Your application should somehow direct the user to do so.

唯一的解决方法是实现 Android 无障碍服务(更多信息在这里这里)并拦截所有应用程序从那里获得和失去焦点的操作。用户应手动启用该服务!您的应用程序应该以某种方式引导用户这样做。

回答by Javanator

My Thoughts,

我的想法,

  1. Develop application that makes some of their data public to other process to access them.

    Google "How to use content providers".

    Each of your application will need to keep record of all the activity they performed. This data will be public to be accessed.

  2. You dont need to run a service all the timefor this then. Whenever your monitoring app gets opened. It just need to fetch that data from all the applications you want and display them accordingly.

  3. For a case you want to submit that data on server periodically in background. Then you need a service. But still you dont need to run it forever. Do something like this.

    i) From service fetch data and upload to server.

    ii) Stop service and schedule an alarm after some time T1 to start your own service again to do step 1 again.

    iii) T1 depends on requirement. How refresh data you want on server.

  1. 开发应用程序,将他们的一些数据公开给其他进程以访问它们。

    谷歌“如何使用内容提供者”。

    您的每个应用程序都需要记录他们执行的所有活动。这些数据将被公开以供访问。

  2. 您不需要为此一直运行服务。每当您的监控应用程序打开时。它只需要从您想要的所有应用程序中获取该数据并相应地显示它们。

  3. 对于您希望在后台定期在服务器上提交该数据的情况。那么你需要一个服务。但是你仍然不需要永远运行它。做这样的事情。

    i) 从服务获取数据并上传到服务器。

    ii) 停止服务并在 T1 一段时间后安排警报以再次启动您自己的服务以再次执行步骤 1。

    iii) T1 取决于要求。您希望如何在服务器上刷新数据。

I can even tell you code pieces pointer if you want for how to implement what i said above. But i suggest you to find that on google and do it yourself first. Post another specific question on StackOverflow if you face any difficulty in that.

如果您想了解如何实现我上面所说的,我什至可以告诉您代码段指针。但我建议你先在谷歌上找到,然后自己做。如果您遇到任何困难,请在 StackOverflow 上发布另一个具体问题。

Hope this helps. :)

希望这可以帮助。:)

Edit:

编辑:

Firstly. Its only you who has to code in the end. Take pointer and help from here. Dont expect full code. Check more details below.

首先。最后只有你自己来编码。从这里获取指针和帮助。不要期望完整的代码。查看下面的更多详细信息。

A)

一种)

You need to make your apps as Content Provider. Check the link below. and follow the examples in it. You will end up in a app whose data will be available to others. Its easy go ahead.

你需要让你的应用程序成为内容提供者。检查下面的链接。并按照其中的示例进行操作。您最终将使用其数据可供其他人使用的应用程序。它很容易继续。

http://developer.android.com/guide/topics/providers/content-providers.html

http://developer.android.com/guide/topics/providers/content-providers.html

http://developer.android.com/guide/topics/providers/content-provider-creating.html

http://developer.android.com/guide/topics/providers/content-provider-creating.html

http://www.vogella.com/articles/AndroidSQLite/article.html

http://www.vogella.com/articles/AndroidSQLite/article.html

All your games app needs to make data public to be accessed. Now once you are done with this.

您所有的游戏应用都需要公开数据才能访问。现在一旦你完成了这个。

Now you just need to access all the data in your monitoring app and display it.

现在您只需要访问监控应用程序中的所有数据并显示它。

B)

乙)

As i said if you want to do this via service. You dont need to run it forever. Just start service for once, loads the data from content provider and do whatever you want to do. Set an alarm to invoke the service at a later stage to re do the same operation.

正如我所说,如果你想通过服务来做到这一点。你不需要永远运行它。只需启动一次服务,从内容提供者加载数据,然后做任何你想做的事情。设置一个警报,在稍后阶段调用该服务以重新执行相同的操作。

I assume if every 10 min your service can fetch data from content provider(Your games app that you want to monitor). It will be like following.

我假设您的服务是否每 10 分钟就可以从内容提供商(您要监控的游戏应用)获取数据。它会像下面一样。

public class MonitoringService extends Service {

    private static final String ACTION_FETCH = "action_fetch";
    private static final int REPEAT_DATALOAD_INTERVAL_MS = 10 * 60 * 1000; // 10 Min

    private static PendingIntent makeSelfPendingIntent(Context context) {
        PendingIntent intent = PendingIntent.getService(
                           context, 0, makeSelfIntent(context), 0);
        return intent;
    }

    private static Intent makeSelfIntent(Context context) {
        Intent intent = new Intent(context, MonitoringService.class);
        intent.setAction(ACTION_FETCH);
        return intent;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (intent != null && intent.getAction() != null) {
            String action = intent.getAction();
            if (action.equals(ACTION_FETCH)) {
                loadDataFromContentProviderDoWhateverYouWantThen();
                setAlarmToRedoLoad();
                stopSelf();

            }
        }
        return Super.onStartCommand(intent, flags, startId);
    }

    private void setAlarmToRedoLoad() {
        AlarmManager alarmManager = (AlarmManager) 
                                     getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP,
                     System.currentTimeMillis() + REPEAT_DATALOAD_INTERVAL_MS,
                     makeSelfPendingIntent(getApplicationContext()));
    }

    private void loadDataFromContentProviderDoWhateverYouWantThen(){
        check this link how to load data from content provider.
        as your games app are content providers. It should be loaded easily.
        then do whatever you want display, upload anything

        http://developer.android.com/guide/topics/providers/content-provider-basics.html
    }

    // Call this method from onCreate of your monitoring app
    public static void start(Context context) {
        Intent intent = makeSelfIntent(context);
        context.startService(intent);
    }

}

C) make sure you allow user of your monitoring app to stop this service anytime. Dont forget to cancel alarm in that case. so it dont run in background forever.

C) 确保您允许监控应用程序的用户随时停止此服务。在这种情况下不要忘记取消警报。所以它不会永远在后台运行。

D) You can also make things broadcast based. Whenever your games app saves data they should broadcast this event and let your service gets invoked after listening that broadcast. loads it then only.

D)你也可以让事情基于广播。每当您的游戏应用程序保存数据时,他们都应该广播此事件,并让您的服务在收听该广播后被调用。然后只加载它。

E) As Phill also stated you can go for Google Analytics as well.

E) 正如 Phill 所说,您也可以使用 Google Analytics。

How you want to actually do this is now depends on your requirement

您现在想如何实际执行此操作取决于您的要求

Hope this helps. Try it out and let me know what more issue you are facing

希望这可以帮助。尝试一下,让我知道您还面临什么问题

回答by insomniac

Use this example code it is for checking if the browser is running

使用此示例代码用于检查浏览器是否正在运行

ActivityManager activityManager = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE );
    List<RunningAppProcessInfo> procInfos = actvityManager.getRunningAppProcesses();
    for(int i = 0; i < procInfos.size(); i++){
        if(procInfos.get(i).processName.equals("com.android.browser")) {
            Toast.makeText(getApplicationContext(), "Browser is running", Toast.LENGTH_LONG).show();
        }
    }

To create a service you have to create a class that extends from Service base class and implement the function in an infinite loop such as while(true)in the onStartCommand()function. don't forget to add your service in the manifest file between <service></service>tags , a good tutorial is on http://www.vogella.com/articles/AndroidServices/article.html

要创建服务,您必须创建一个从 Service 基类扩展而来的类,并在无限循环中(例如while(true)onStartCommand()函数中)实现该函数。不要忘记在<service></service>标签之间的清单文件中添加您的服务,一个很好的教程在http://www.vogella.com/articles/AndroidServices/article.html

回答by peter.bartos

To get the current running application you can use:

要获取当前正在运行的应用程序,您可以使用:

ActivityManager am = (ActivityManager) mContext.getSystemService(Activity.ACTIVITY_SERVICE);
String packageName = am.getRunningTasks(1).get(0).topActivity.getPackageName();

You can check this periodically from your running service. You will need also define a required permission in your manifest: android.permission.GET_TASKS

您可以从正在运行的服务中定期检查这一点。您还需要在清单中定义所需的权限:android.permission.GET_TASKS

回答by minhaz

Following solutions based on the assumption that you own all five apps you are trying to monitor. If it other wise i will provide you another solution.

以下解决方案基于您拥有要监控的所有五个应用程序的假设。如果不这样做,我会为您提供另一种解决方案。

Let's breakdown the problem now.

现在让我们分解问题。

  1. Information service about app running or not.
  2. Designing service
  3. storing data
  1. 应用程序运行与否的信息服务。
  2. 设计服务
  3. 存储数据

You should use Intentto broadcast messages about your app running or not. Use activity onPauseand onResumeto broadcast messages.

您应该使用Intent广播关于您的应用程序运行与否的消息。使用活动onPauseonResume广播消息。

Now call this function from onPause and onResume,

现在从 onPause 和 onResume 调用这个函数,

   public void broadcastMessage(String method_name){
        Intent intent=new Intent("com.service.myservice");
        Bundle bundle=new Bundle();
        bundle.putString("app_name", this.getApplicationInfo().packageName);
        bundle.putLong("time", System.currentTimeMillis());
            //just to keep track if it is on pause or in on resume
        bundle.putString("method", method_name);
        intent.putExtras(bundle);
        this.startService(intent);
    }

and you can call this like the following way

你可以像下面这样调用它

public void onPause(){
        super.onPause();
        broadcastMessage(Thread.currentThread().getStackTrace()[1].getMethodName());
    }

Or

或者

public void onPause(){
        super.onPause();
        broadcastMessage("onPause");
    }

So why onPause and onResume because gamer is playing game only between these functions calls.

那么为什么 onPause 和 onResume 因为玩家只在这些函数调用之间玩游戏。

Designing Service

设计服务

Use IntentServiceand read thisif you wanna know why.

如果您想知道原因,请使用IntentService并阅读此内容

@Override
    protected void onHandleIntent(Intent intent) {
        // TODO Auto-generated method stub
        Bundle bundle=intent.getExtras();
        String app_name=bundle.getString("app_name");
        long time=bundle.getLong("time");
        //then do whatever you wanna do with this data

    }

Storing

收藏

ContentProvideris a good option for storing data, and it is also very powerful; integrate and scale well on Android, but in this case seems like you only want to store some very simple information and as such you can also pick something very simple.

ContentProvider是存储数据的好选择,功能也很强大;在 Android 上集成和扩展良好,但在这种情况下,您似乎只想存储一些非常简单的信息,因此您也可以选择一些非常简单的信息。

Code bellow uses SharedPreferences.

下面的代码使用SharedPreferences.

 protected void storeData(String app_name, long time) {
        if ((app_name==null)||(time<=0)) return;
        SharedPreferences sharedPreferences = PreferenceManager .getDefaultSharedPreferences(getApplicationContext());
        Editor editor = sharedPreferences.edit();
              //concatenating app name just to make key unique
        long start_time=sharedPreferences.getLong(app_name+"_start_time", -1);
        if (start_time==-1){
            //start time is empty means this is my start time
            // and user just started the app
            editor.putLong(app_name+"_start_time", time);
            editor.putLong(app_name+"_counter", sharedPreferences.getLong(app_name+"_counter", 0)+1);
        }else{
            //user is now closing the app. 
            long time_played=time-start_time;
            long total_time=sharedPreferences.getLong(app_name+"_total_time", 0);
            total_time=total_time+time_played;
            //saving total time
            editor.putLong(app_name+"_total_time", time);
            //removing start time for next pass
            editor.remove(app_name+"_start_time");
        }
        editor.commit();
    }
    public long getTotalTimeUserSpent(String app_name){
        SharedPreferences sharedPreferences = PreferenceManager .getDefaultSharedPreferences(getApplicationContext());
        return sharedPreferences.getLong(app_name+"_total_time", 0);
    }
    public long numberOfTimeUserPlayed(String app_name){
        SharedPreferences sharedPreferences = PreferenceManager .getDefaultSharedPreferences(getApplicationContext());
        return sharedPreferences.getLong(app_name+"_counter", 0);
    }

just call these method from onHandleIntent.

只需从onHandleIntent.

Last piece of puzzle would be distribution. You can distribute this on a separate app or part of these five apps. Separate apps is the easiest route to take. But if you choose to use this as a part of all five of these apps, read thisdiscussion.

最后一块拼图是分发。您可以在单独的应用程序或这五个应用程序的一部分上分发它。单独的应用程序是最简单的方法。但是,如果您选择将此作为所有五个应用程序的一部分,请阅读讨论。

回答by OrdinaryCPUKid

Installing Covenant Eyeson your Android device will monitor all Internet traffic on the phone's pre-installed browser and report additional apps that are being used onto the device.

在您的 Android 设备上安装Covenant Eyes将监控手机预装浏览器上的所有互联网流量,并报告设备上正在使用的其他应用程序。