Android小应用——监控屏幕使用时间
Android小应用——监控屏幕使用时间
idea来源
这个idea是蔡小亦童鞋提出来的。她说看到一条报道说有人看手机看太久眼睛怎么怎么了,所以想弄个应用来监控屏幕使用时间。答应帮她做已经答应很久了,刚好这周没什么事了,于是就开始做。从开始找资料到写代码到美工到调试完成,只花了1天时间,不错不错~因为我觉得这个做得很粗糙别人不可能会怎么用,所以我就针对蔡小亦童鞋定制了流氓兔形象,哦哈哈是不是该感谢我~
预期目标
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 @Override31 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 @Override65 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 else71 {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 @Override102 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 <TextView17 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 <TextView25 android:id="@+id/preset"26 android:layout_width="fill_parent"27 android:layout_height="wrap_content"28 android:textColor="#000000"/>29 30 <EditText31 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 <Button38 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 <application10 android:icon="@drawable/ic_launcher"11 android:label="@string/app_name" >12 <activity13 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 android:name="com.legend.SERVICE_DEMO" /> 25 <category android:name="android.intent.category.default" /> 26 </intent-filter> 27 </service>28 </application>29 30 </manifest>
项目分析
我遇到的第一个问题是:如何监控?
经过查资料,我发现当屏幕启用或者锁屏时,系统会分别发送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);//执行
最后,上一下截图:
--------------------------------------后记-------------------------------------------
本来以为很简单的,没想到修改了两次才能正常运作:不知道为什么系统老是把资源回收了,结果凌晨都无法自动清空数据。
第一次修改,是取消守护线程了。之前对这个不了解,查了下资料,原来所谓的守护线程,就是当线程要守护的资源不存在时,这个线程也就退出了。所以我想这就是原因了吧,修改,还信心满满地以为不用测试了。
结果零点就不行了,严重被打击==!
无奈之下,只好用最原始的方法了:监听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 }
更多相关文章
- Android(安卓)弹无虚发之第四弹:你应该掌握的Notification(Notific
- MonoDroid学习笔记(十二)—— 您有一条新短信息,请注意查收。状态栏
- Dump Window
- android 唤醒屏幕并解锁
- 运用IntentService类下载文件
- 自定义app锁屏页
- Android悬浮贴边按钮实现(含动画效果)
- android 8.0 兼容问题
- 屏幕亮屏、熄屏监听代码