程序锁的实现
看到程序锁,想到的当然就是360手机卫生等等一系列的软件管理应用了,这里将程序锁的那个输入密码的界面当成看门狗:
看门狗: 监视系统程序的运行状态(这里假设这个activity是属于360手机卫士里面的),
每打开一个程序,系统就会分配一个任务栈,每个程序一个任务栈,程序锁的原理:
当你打开一个软件,这个看门狗就能检测到你开启的是哪个软件,并可以获取包名,如果这个软件是被保护的,需要你
输入密码才能进入,其实就是在你打开这个软件后,看门狗开启另一个活动界面(比如显示输入密码),当密码输入正确,
就销毁这个页面,最后显示的自然就是你刚打开的软件页面了.
由上可知看门狗肯定是需要长期在后台运行的,需要一直监视用户当前打开的软件具体是哪个
看门狗服务类,WatchDogService.java
/** * 看门狗服务,监视系统程序的运行状态(这里假设这个activity是属于360手机卫士里面的),= */ public class WatchDogService extends Service { private boolean flag;//看门狗运行的标记 private ActivityManager am; private InnerReceiver innerReceiver; private String tempStopPackname;//临时停止保护的程序包名 @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); //监听自定义广播 innerReceiver = new InnerReceiver(); registerReceiver(innerReceiver, new IntentFilter("com.mybroadcast")); am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); flag=true;//让看门狗一直运行 new Thread(){ public void run() { while(flag){ //获取运行的任务栈,100代表最多获取100个任务栈,最近打开的在list的前面,后打开的在后面 List<RunningTaskInfo> tasks = am.getRunningTasks(100); //获取第一个任务栈的顶部activity,也就是获取最近打开的程序的当前打开的活动所在的包名 String packageName = tasks.get(0).topActivity.getPackageName(); System.out.println("当前打开的程序包名为:"+packageName); if("这个软件是加锁了的,且临时不保护" && !packageName.equals(tempStopPackname)){ Intent intent=new Intent(getApplicationContext(),LockActivity.class);//输入密码的活动 //由于服务是没有任务栈信息的,在服务开启activity要指定这个activity运行的任务栈 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packname", packageName);//告诉锁的界面锁住的是哪个应用 startActivity(intent); } try { Thread.sleep(50);//必须稍微睡一下,不然回收机制都没时间回收垃圾了 } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); } /** * 接收自定义的广播,来取消看门狗的临时保护 */ private class InnerReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { System.out.println("接收到临时停止保护的自定义广播"); tempStopPackname = intent.getStringExtra("packname"); } } @Override public void onDestroy() { super.onDestroy(); flag=false;//结束子线程 unregisterReceiver(innerReceiver); } }
显示输入密码的界面:LockActivity.java
/** * 作为程序锁显示的界面:必须设置该活动的启动模式为:singleInstance,并且不能在任务列表中显示(按住小房子键不会显示这个活动界面) */ public class LockActivity extends Activity { private EditText editText; private String packname; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lock);//布局为一个编辑框和一个确定按钮 editText = (EditText) findViewById(R.id.editText); Intent intent = getIntent(); packname = intent.getStringExtra("packname");//得到当前被锁住的应用的包名 PackageManager pm = getPackageManager(); try { //通过包名获取图标和软件名 ApplicationInfo info = pm.getApplicationInfo(packname, 0); Drawable appIcon = info.loadIcon(pm); String appName = info.loadLabel(pm).toString(); } catch (NameNotFoundException e) { e.printStackTrace(); } } /** * 提交按钮的单击时间,假设密码为12345 */ public void btnOnClick(View v){ String password = editText.getText().toString(); if(TextUtils.isEmpty(password)){ Toast.makeText(this, "请输入密码", 0).show(); }else if(password.equals("12345")){ Toast.makeText(this, "密码正确", 0).show(); /*告诉看门狗这个程序密码输入正确了,可以临时的停止保护了,不然销毁后,看门狗服务又监测到 * 这个应用需要上锁,就又弹出输入密码的界面了,通过自定义广播来通知看门狗 */ Intent intent=new Intent(); intent.setAction("com.mybroadcast"); intent.putExtra("packname", packname);//告诉看门狗停止保护的应用 sendBroadcast(intent); finish();//销毁界面 }else{ Toast.makeText(this, "密码错误,请重新输入", 0).show(); } } /** * 当在输入密码的界面按返回键时,应该直接回到桌面,不然会显示被锁住的软件 */ @Override public void onBackPressed() { Intent intent=new Intent(); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.HOME"); intent.addCategory("android.intent.category.DEFAULT"); intent.addCategory("android.intent.category.MONKEY"); startActivity(intent);//回到桌面 } /** * 界面不可见的时候就要销毁掉这个输入密码的界面 */ @Override protected void onStop() { super.onStop(); finish(); } }
<!-- singleInstance使得手机里只有一个实例存在,避免了按返回键软件显示出现错乱问题 excludeFromRecents代表当前这个输入密码的活动不会出现在最近任务的列表里了 --> <activity android:name=".LockActivity" android:launchMode="singleInstance" android:excludeFromRecents="true" android:label="@string/title_activity_lock" > </activity>
还会出现的问题:
1.当屏幕锁定黑屏后再去开启软件不会要求输入密码,所以还需要在看门狗服务类中注册锁屏的监听,见:锁屏与亮屏广播的注意点
2.还需要解决出现输入密码界面的速度问题,:1)任务栈的查询没必要放在子线程,所以就需要定义一个集合来装用户上锁的所有程序,2)上面获取任务栈的个数设置的100,其实设置为1个就可以了,因为只需要最新打开的软件信息,3)子线性没必要睡50ms,睡20ms就差不多了