监控Android屏幕使用时间

jopen 10年前

预期目标

1、能记录屏幕使用时间

2、每天凌晨清空数据,重新记录

3、用户可以自定义警戒线,当使用时间超过警戒线则在通知栏提醒。

 

主要代码

1 package com.legend;   2    3 import java.text.SimpleDateFormat;   4 import java.util.Date;   5    6 import android.app.Activity;   7 import android.content.Context;   8 import android.content.Intent;   9 import android.content.SharedPreferences;  10 import android.os.Bundle;  11 import android.view.View;  12 import android.view.View.OnClickListener;  13 import android.widget.Button;  14 import android.widget.EditText;  15 import android.widget.TextView;  16 import android.widget.Toast;  17   18 /**  19  * 目前先实现最小功能,只提取出总的屏幕亮的时间  20  * 通过广播来接收屏幕是否启动这个事件  21  * @author 林培东  22  */  23 public class MainActivity extends Activity   24 {  25     public TextView summary=null;  26     public TextView preset=null;  27     public EditText set=null;  28     public Button submit=null;  29       30     @Override  31     public void onCreate(Bundle savedInstanceState)   32     {  33         super.onCreate(savedInstanceState);  34         setContentView(R.layout.main);  35         startService(new Intent("com.legend.SERVICE_DEMO"));//启动服务  36           37         summary=(TextView)findViewById(R.id.summary);  38         preset=(TextView)findViewById(R.id.preset);  39         set=(EditText)findViewById(R.id.set);  40         submit=(Button)findViewById(R.id.submit);  41   42         //显示已使用屏幕时间  43           SharedPreferences sp=getSharedPreferences("actm", Context.MODE_PRIVATE);         44           int sum=(int)sp.getLong("sum", 0L)/1000;  45           int hour=sum/3600;  46           int minute=(sum-hour*3600)/60;  47           int second=sum%60;  48           //格式化输出日期  49           Date tmp=new Date();  50           tmp.setHours(hour);  51           tmp.setMinutes(minute);  52           tmp.setSeconds(second);  53           SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");  54           String result=sdf.format(tmp);  55           summary.setText(result);//最终显示  56             57           //显示已保存的设置  58           int limit=sp.getInt("limit", 24*60);  59           preset.setText(" 当前设定的预警分钟数为"+Integer.toString(limit));  60             61           //点击确定后重新设置  62           submit.setOnClickListener(new OnClickListener()  63           {  64             @Override  65             public void onClick(View v)  66             {  67                 String tmp=set.getText().toString();  68                 if(tmp.equals(""))  69                     Toast.makeText(MainActivity.this, "输入不能为空!", Toast.LENGTH_SHORT).show();  70                 else  71                 {  72                     SharedPreferences sp=getSharedPreferences("actm", Context.MODE_PRIVATE);  73                     SharedPreferences.Editor editor=sp.edit();  74                     editor.putInt("limit", Integer.parseInt(tmp));  75                     editor.commit();  76                     Toast.makeText(MainActivity.this, "已设定!", Toast.LENGTH_SHORT).show();  77                     preset.setText(" 当前设定的预警分钟数为"+Integer.parseInt(tmp));  78                 }  79             }               80           });  81             82     }  83       84 }
  1 package com.legend;    2     3 import java.util.Date;    4 import java.util.Timer;    5 import java.util.TimerTask;    6     7 import android.app.Notification;    8 import android.app.NotificationManager;    9 import android.app.PendingIntent;   10 import android.app.Service;   11 import android.content.BroadcastReceiver;   12 import android.content.Context;   13 import android.content.Intent;   14 import android.content.IntentFilter;   15 import android.content.SharedPreferences;   16 import android.os.IBinder;   17    18 /**   19  * 创建一个服务,该服务主要用来接收广播和创建定时器   20  * @author 林培东   21  */   22 public class LocalService extends Service   23 {   24     private static final int NOTIFY_ID=1234;//通知的唯一标识符   25        26     //主要功能,广播接收器   27     private final BroadcastReceiver receiver=new BroadcastReceiver()   28     {   29         @Override   30         public void onReceive(Context context, Intent intent)   31         {   32             SharedPreferences sp=getSharedPreferences("actm", Context.MODE_PRIVATE);   33             SharedPreferences.Editor editor=sp.edit();   34                35             if(intent.getAction().equals(Intent.ACTION_SCREEN_ON))   36             {   37                 //保存屏幕启动时的毫秒数                   38                 editor.putLong("lasttime", new Date().getTime());   39                 editor.commit();   40                    41                 //根据需要看是否需要在通知栏提醒   42                 int sum=(int)sp.getLong("sum", 0L)/1000;   43                 int limit=sp.getInt("limit", 1440)*60;   44                 if(limit<=sum)   45                 {   46                     final NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);//获取通知管理器   47                     Notification notification=new Notification(R.drawable.ic_launcher,"警告",System.currentTimeMillis());//通知的时机   48                     notification.flags = Notification.FLAG_AUTO_CANCEL;//点击一次通知就自动消失   49                     PendingIntent pIntent=PendingIntent.getActivity(context,0,new Intent(context,MainActivity.class),0);//跳转到主界面   50                     notification.setLatestEventInfo(context,"警告","本日使用屏幕已超过预设,如需取消该警告请重新设置!!!",pIntent);//通知栏显示内容   51                     manager.notify(NOTIFY_ID, notification);//执行   52                 }   53             }   54             else if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF))   55             {   56                 //保存屏幕总工作时间   57                 long lasttime=sp.getLong("lasttime", new Date().getTime());   58                 long sum=sp.getLong("sum", 0L);   59                 sum+=new Date().getTime()-lasttime;   60                 editor.putLong("sum", sum);   61                 editor.commit();   62             }   63         }   64        65     };   66    67     @Override   68     public void onCreate()   69     {   70         //添加过滤器并注册   71         final IntentFilter filter=new IntentFilter();   72         filter.addAction(Intent.ACTION_SCREEN_ON);   73         filter.addAction(Intent.ACTION_SCREEN_OFF);   74         registerReceiver(receiver, filter);   75                    76         //创建计划任务   77         TimerTask task=new TimerTask()   78         {   79             @Override   80             public void run()   81             {   82                 //每天凌晨自动更新数据   83                 SharedPreferences sp=getSharedPreferences("actm", Context.MODE_PRIVATE);   84                 SharedPreferences.Editor editor=sp.edit();   85                 editor.putLong("sum", 0L);   86                 editor.commit();   87                    88                 //取消通知栏通知   89                 final NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);   90                 manager.cancel(NOTIFY_ID);   91             }              92         };   93         Timer timer=new Timer(true);   94         int hour=new Date().getHours();   95         timer.schedule(task,(24-hour)*3600*1000, 24*3600*1000);   96         //timer.schedule(task,180*1000, 180*1000);//测试用   97            98         super.onCreate();   99     }  100       101     @Override  102     public IBinder onBind(Intent arg0)  103     {  104         return null;  105     }  106   107 }
 1 <?xml version="1.0" encoding="utf-8"?>   2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   3     android:layout_width="fill_parent"   4     android:layout_height="fill_parent"   5     android:background="@drawable/background"   6     android:orientation="vertical" >   7    8     <TextView   9         android:layout_width="fill_parent"  10         android:layout_height="wrap_content"  11         android:textSize="25dp"  12         android:text="今天屏幕总共使用"  13         android:textColor="#000000"  14         android:gravity="center" />  15       16     <TextView  17         android:id="@+id/summary"  18         android:layout_width="fill_parent"  19         android:layout_height="wrap_content"  20         android:textSize="50dp"  21         android:textColor="#000000"  22         android:gravity="center" />  23   24     <TextView  25         android:id="@+id/preset"  26         android:layout_width="fill_parent"  27         android:layout_height="wrap_content"  28         android:textColor="#000000"/>  29       30     <EditText  31         android:id="@+id/set"  32         android:layout_width="fill_parent"  33         android:layout_height="wrap_content"  34         android:hint="请输入预警提醒分钟数,如80"  35         android:inputType="number" />  36       37     <Button  38         android:id="@+id/submit"  39         android:layout_width="fill_parent"  40         android:layout_height="wrap_content"  41         android:text="确定提交" />  42   43 </LinearLayout>
1 <?xml version="1.0" encoding="utf-8"?>   2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"   3     package="com.legend"   4     android:versionCode="1"   5     android:versionName="1.0" >   6    7     <uses-sdk android:minSdkVersion="4" />   8    9     <application  10         android:icon="@drawable/ic_launcher"  11         android:label="@string/app_name" >  12         <activity  13             android:name=".MainActivity"  14             android:label="@string/app_name" >  15             <intent-filter>  16                 <action android:name="android.intent.action.MAIN" />  17   18                 <category android:name="android.intent.category.LAUNCHER" />  19             </intent-filter>  20         </activity>  21           22         <service android:name=".LocalService">   23             <intent-filter>   24   


项目分析

我遇到的第一个问题是:如何监控?

经过查资料,我发现当屏幕启用或者锁屏时,系统会分别发送ACTION_SCREEN_ON和ACTION_SCREEN_OFF这两个广播。我们只需要在接收这两个广播时记录时间就可以了。

注意:为了时程序退出后也能运行,必须使用Service。

注意:这两个广播是受保护的,只能在代码中注册。

下面是在Service中注册:

        //添加过滤器并注册          final IntentFilter filter=new IntentFilter();          filter.addAction(Intent.ACTION_SCREEN_ON);          filter.addAction(Intent.ACTION_SCREEN_OFF);          registerReceiver(receiver, filter);

在接收器receiver里,定义了onReceive()来处理这些数据,主要功能都在里面实现:

   //主要功能,广播接收器      private final BroadcastReceiver receiver=new BroadcastReceiver()      {          @Override          public void onReceive(Context context, Intent intent)          {              SharedPreferences sp=getSharedPreferences("actm", Context.MODE_PRIVATE);              SharedPreferences.Editor editor=sp.edit();                            if(intent.getAction().equals(Intent.ACTION_SCREEN_ON))              {                  //保存屏幕启动时的毫秒数                                  editor.putLong("lasttime", new Date().getTime());                  editor.commit();                                    //根据需要看是否需要在通知栏提醒                  int sum=(int)sp.getLong("sum", 0L)/1000;                  int limit=sp.getInt("limit", 1440)*60;                  if(limit<=sum)                  {                      final NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);//获取通知管理器                      Notification notification=new Notification(R.drawable.ic_launcher,"警告",System.currentTimeMillis());//通知的时机                      notification.flags = Notification.FLAG_AUTO_CANCEL;//点击一次通知就自动消失                      PendingIntent pIntent=PendingIntent.getActivity(context,0,new Intent(context,MainActivity.class),0);//跳转到主界面                      notification.setLatestEventInfo(context,"警告","本日使用屏幕已超过预设,如需取消该警告请重新设置!!!",pIntent);//通知栏显示内容                      manager.notify(NOTIFY_ID, notification);//执行                  }              }              else if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF))              {                  //保存屏幕总工作时间                  long lasttime=sp.getLong("lasttime", new Date().getTime());                  long sum=sp.getLong("sum", 0L);                  sum+=new Date().getTime()-lasttime;                  editor.putLong("sum", sum);                  editor.commit();              }          }            };

 

另一个问题是如何在每天凌晨自动把sum置零。一开始我查资料找到了ACTION_DATE_CHANGED这个广播,但测试时发现不可靠,网上也说了这个广播各种不可靠。

这里做了说明:http://4develop.in/csdn/Android/20111230_12_f516e79c-d732-4963-961b-4e0bd2f35437/1

于是,只能忍痛使用定时器来制定计划任务了:Timer和TimerTask。

        //创建计划任务          TimerTask task=new TimerTask()          {              @Override              public void run()              {                  //每天凌晨自动更新数据                  SharedPreferences sp=getSharedPreferences("actm", Context.MODE_PRIVATE);                  SharedPreferences.Editor editor=sp.edit();                  editor.putLong("sum", 0L);                  editor.commit();                                    //取消通知栏通知                  final NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);                  manager.cancel(NOTIFY_ID);              }                     };          Timer timer=new Timer(true);          int hour=new Date().getHours();          timer.schedule(task,(24-hour)*3600*1000, 24*3600*1000);          //timer.schedule(task,180*1000, 180*1000);//测试用

 

再有就是学习了如何使用通知栏来推送消息。

可以参考:http://fanwei51880.blog.163.com/blog/static/32406740201052754236166/

final NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);//获取通知管理器  Notification notification=new Notification(R.drawable.ic_launcher,"警告",System.currentTimeMillis());//通知的时机  notification.flags = Notification.FLAG_AUTO_CANCEL;//点击一次通知就自动消失  PendingIntent pIntent=PendingIntent.getActivity(context,0,new Intent(context,MainActivity.class),0);//跳转到主界面  notification.setLatestEventInfo(context,"警告","本日使用屏幕已超过预设,如需取消该警告请重新设置!!!",pIntent);//通知栏显示内容  manager.notify(NOTIFY_ID, notification);//执行

最后,上一下截图:

2012072411204894.png

--------------------------------------后记-------------------------------------------

本来以为很简单的,没想到修改了两次才能正常运作:不知道为什么系统老是把资源回收了,结果凌晨都无法自动清空数据。

第一次修改,是取消守护线程了。之前对这个不了解,查了下资料,原来所谓的守护线程,就是当线程要守护的资源不存在时,这个线程也就退出了。所以我想这就是原因了吧,修改,还信心满满地以为不用测试了。

结果零点就不行了,严重被打击==!

无奈之下,只好用最原始的方法了:监听Intent.ACTION_TIME_TICK这个广播,因为它一分钟就发送一次,是个可靠的广播,只要判断下时间点,就可以决定是否更新了。

其实这个方法我很早就想到了,只是我觉得这样每分钟都要做一次判断,太麻烦和太耗资源了。这算是程序员的通病吧。

所以,通过这个小软件,我也有了一点体会:功能第一,性能第二。因为用户最后用的是你的软件的功能,而性能是很难看出来的;只要影响不大的话。

所以,真的不应该在这个问题上钻牛角尖,一定要最优化。

最后,修改后的代码:

  1 package com.legend;    2     3 import java.util.Calendar;    4 import java.util.Date;    5     6 import android.app.Notification;    7 import android.app.NotificationManager;    8 import android.app.PendingIntent;    9 import android.app.Service;   10 import android.content.BroadcastReceiver;   11 import android.content.Context;   12 import android.content.Intent;   13 import android.content.IntentFilter;   14 import android.content.SharedPreferences;   15 import android.os.IBinder;   16    17 /**   18  * 创建一个服务,该服务主要用来接收广播和创建定时器   19  * @author 林培东   20  */   21 public class LocalService extends Service   22 {   23     private static final int NOTIFY_ID=1234;//通知的唯一标识符   24     private Calendar cal=null;   25        26     //主要功能,广播接收器   27     private final BroadcastReceiver receiver=new BroadcastReceiver()   28     {   29         @Override   30         public void onReceive(Context context, Intent intent)   31         {   32             SharedPreferences sp=getSharedPreferences("actm", Context.MODE_PRIVATE);   33             SharedPreferences.Editor editor=sp.edit();   34                35             if(intent.getAction().equals(Intent.ACTION_SCREEN_ON))   36             {   37                 //保存屏幕启动时的毫秒数                   38                 editor.putLong("lasttime", new Date().getTime());   39                 editor.commit();   40                    41                 //根据需要看是否需要在通知栏提醒   42                 int sum=(int)sp.getLong("sum", 0L)/1000;   43                 int limit=sp.getInt("limit", 1440)*60;   44                 if(limit<=sum)   45                 {   46                     final NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);//获取通知管理器   47                     Notification notification=new Notification(R.drawable.ic_launcher,"警告",System.currentTimeMillis());//通知的时机   48                     notification.flags = Notification.FLAG_AUTO_CANCEL;//点击一次通知就自动消失   49                     PendingIntent pIntent=PendingIntent.getActivity(context,0,new Intent(context,MainActivity.class),0);//跳转到主界面   50                     notification.setLatestEventInfo(context,"警告","本日使用屏幕已超过预设,如需取消该警告请重新设置!!!",pIntent);//通知栏显示内容   51                     manager.notify(NOTIFY_ID, notification);//执行   52                 }   53             }   54             else if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF))   55             {   56                 //保存屏幕总工作时间   57                 long lasttime=sp.getLong("lasttime", new Date().getTime());   58                 long sum=sp.getLong("sum", 0L);   59                 sum+=new Date().getTime()-lasttime;   60                 editor.putLong("sum", sum);   61                 editor.commit();   62             }   63             else if(intent.getAction().equals(Intent.ACTION_TIME_TICK))   64             {   65                 cal=Calendar.getInstance();   66                 if(cal.get(Calendar.HOUR_OF_DAY)==0 && cal.get(Calendar.MINUTE)==0)   67                 {   68                     //每天凌晨自动更新数据   69                     editor.putLong("sum", 0L);   70                     editor.commit();   71                        72                     //取消通知栏通知   73                     final NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);   74                     manager.cancel(NOTIFY_ID);   75                 }   76             }   77                78         }   79        80     };   81    82     @Override   83     public void onCreate()   84     {   85         //添加过滤器并注册   86         final IntentFilter filter=new IntentFilter();   87         filter.addAction(Intent.ACTION_SCREEN_ON);   88         filter.addAction(Intent.ACTION_SCREEN_OFF);   89         filter.addAction(Intent.ACTION_TIME_TICK);   90         registerReceiver(receiver, filter);   91            92         super.onCreate();   93     }   94        95     @Override   96     public IBinder onBind(Intent arg0)   97     {   98         return null;   99     }  100   101 }