我们之前已经把那个手机防盗的基本上做出来的了,但是还是有不少问题的,今天我们就把这些问题修复一下,首先我们就把那个最重要的来修复一下先,就是我们输入密码之后,又会弹出输入密码的界面,这是因为我们没有把那个监听的任务栈暂时的停止,所以它就会又进行判断了,所以就又进入到输入密码的界面了,解决的方法思路就是我们在服务那里新建一个list用来存放那些不用被阻止的应用,当输入完密码之后,我们就先把那个已经输入密码了的加锁了的应用加入到list里面,这样子,我们输入密码后,就不会再弹出输入密码的界面的了。
但是这样子,问题就来了,因为那个list是在服务里面的,那么我们就要调用服务里面的方法啦,这也就回归到我们一开始说程序锁的时候说的通过startService然后再通过bindService来调用服务里面的方法,这样开启的服务既可以长期在后台运行,也可以调用它里面的方法。
调用服务里面的方法很简单的,我们就不多说啦,有不明白的,可以去看看我们之前写的文章调用服务里面的方法
首先我们先新建一个接口
com.xiaobin.security.iservice.IService
package com.xiaobin.security.iservice; public interface IService { void startApp(String packageName); void stopApp(String packageName); }
接下来,我们就要在服务里面定义两个方法啦
//临时开启对某个应用的保护 private void invokeMethodStartApp(String packageName) { if(stopApps.contains(packageName)) { stopApps.remove(packageName); } } //临时停止对某个应用的保护 private void invokeMethodStopApp(String packageName) { stopApps.add(packageName); }
这个类,我们就写在Service里面,做成内部类的形式
private class MyBinder extends Binder implements IService { @Override public void startApp(String packageName) { invokeMethodStartApp(packageName); } @Override public void stopApp(String packageName) { invokeMethodStopApp(packageName); } }
接下来,我们就要重写一下我们的onBind方法啦
@Override public IBinder onBind(Intent intent) { return myBinder; }
好啦,在服务里面把我们要调用的Service方法也写好啦,那么我们就回到我们的输入密码的界面里面,进行方法的调用啦
首先我们会在onCreate里面先绑定服务
connection = new MyConnection(); // 绑定服务,主要是为了能够调用服务里面的方法 Intent intent = new Intent(this, WatchDogService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE);
private class MyConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 我们之前在Service里面已经实现了IService接口了 iService = (IService) service; } @Override public void onServiceDisconnected(ComponentName name) { } }
当然,我们还要不要忘记写一个解除绑定服务的方法啊,不然就会有问题的啦
@Override protected void onDestroy() { if (connection != null) { unbindService(connection); connection = null; } super.onDestroy(); }
最后,我们就可以在输入密码成功验证之后,调用服务里面的停止保护的方法啦
// 按钮的点击事件 public void confirm(View v) { String input = et_app_pwd.getText().toString().trim(); if (TextUtils.isEmpty(password)) { Toast.makeText(this, "您的密码还没有设置,请进入手机防盗进行设定", Toast.LENGTH_SHORT) .show(); } else if (TextUtils.isEmpty(input)) { Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show(); } else if (password.equals(MD5Encoder.encode(input))) { finish(); iService.stopApp(packageName);//调用服务里面的方法 } else { Toast.makeText(this, "密码错误", Toast.LENGTH_SHORT).show(); } }
那么我们再起来修复另一个bug,这个bug就是,我们开启了服务之后,如果我现在又锁定了一个应用,那么我们的程序锁是无法对它进行锁定的,因为我们锁定的那些数据都是存放在一个list里面的,在服务一开启的时候,我们就把那些要锁定的读取出来了,所以我们就要想办法对这个list进行更新,那样子,我们就可以修复这个bug了。
有人可能会结合到我们上面说的,调用服务里面的方法不就行啦,其实这个方法也是可以的,但是我们给大家讲另一个方法,那就是内容提供者与内容观察者
我们主要就是通过内容观察者来观察锁定的那个数据库,当观察到它里面的数据发生变化的时候,那就通知内容提供者进行更新
那么我们现在就要先有一个内容提供者啦,那我们就先建一个内容提供者
com.xiaobin.security.provider.AppLockProvider
package com.xiaobin.security.provider; import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; import com.xiaobin.security.dao.AppLockDao; public class AppLockProvider extends ContentProvider { //分别定义两个返回值 private static final int INSERT = 1; private static final int DELETE = 0; //先new一个UriMatcher出来,参数就是当没有匹配到的时候,返回的值是什么 private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); private static Uri URI = Uri.parse("content://com.xiaobin.security.applockprovider"); private AppLockDao dao; static { matcher.addURI("com.xiaobin.security.applockprovider", "insert", INSERT); matcher.addURI("com.xiaobin.security.applockprovider", "delete", DELETE); } @Override public boolean onCreate() { dao = new AppLockDao(getContext()); return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { //上面定义的返回值 int result = matcher.match(uri); if(result == INSERT) { String packageName = values.getAsString("packageName"); dao.add(packageName); //如果数据发生了改变就通知 getContext().getContentResolver().notifyChange(URI, null); } else { new IllegalArgumentException("URI地址不正确"); } return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { //上面定义的返回值 int result = matcher.match(uri); if(result == DELETE) { String packageName = selectionArgs[0]; dao.delete(packageName); //如果数据发生了改变就通知 getContext().getContentResolver().notifyChange(URI, null); } else { new IllegalArgumentException("URI地址不正确"); } return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
因为Provider是一个组件,所以我们要在AndroidMainfest里面注册它
<provider android:name="com.xiaobin.security.provider.AppLockProvider" android:authorities="com.xiaobin.security.applockprovider" android:exported="false"></provider>
// 注册一个内容观察者 getContentResolver().registerContentObserver( Uri.parse("content://com.xiaobin.security.applockprovider"), true, new MyObserver(new Handler()));
private class MyObserver extends ContentObserver { public MyObserver(Handler handler) { super(handler); // 重新更新apps里面的内容 apps = dao.getAllPackageName(); System.out.println("数据库的内容发生了改变"); } }
下面我们把完成的Service类粘出来
com.xiaobin.security.service.WatchDogService
package com.xiaobin.security.service; import java.util.ArrayList; import java.util.List; import android.app.ActivityManager; import android.app.KeyguardManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.Service; import android.content.Intent; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import com.xiaobin.security.dao.AppLockDao; import com.xiaobin.security.iservice.IService; import com.xiaobin.security.ui.LockActivity; public class WatchDogService extends Service { private AppLockDao dao; private List<String> apps; private ActivityManager activityManager; private Intent intent; private boolean flag = true; // 存放要停止保护的app private List<String> stopApps; private MyBinder myBinder; // 键盘的管理器 private KeyguardManager keyguardManager; @Override public IBinder onBind(Intent intent) { return myBinder; } // 临时开启对某个应用的保护 private void invokeMethodStartApp(String packageName) { if (stopApps.contains(packageName)) { stopApps.remove(packageName); } } // 临时停止对某个应用的保护 private void invokeMethodStopApp(String packageName) { stopApps.add(packageName); } @Override public void onCreate() { super.onCreate(); keyguardManager = (KeyguardManager) getSystemService(Service.KEYGUARD_SERVICE); myBinder = new MyBinder(); dao = new AppLockDao(this); apps = dao.getAllPackageName(); stopApps = new ArrayList<String>(); activityManager = (ActivityManager) getSystemService(Service.ACTIVITY_SERVICE); // 注册一个内容观察者 getContentResolver().registerContentObserver( Uri.parse("content://com.xiaobin.security.applockprovider"), true, new MyObserver(new Handler())); intent = new Intent(this, LockActivity.class); // 服务里面是没有任务栈的,所以要指定一个新的任务栈,不然是无法在服务里面启动activity的 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); new Thread() { public void run() { while (flag) { try { // 当解锁后,就再一次输入密码 if (keyguardManager.inKeyguardRestrictedInputMode()) { stopApps.clear(); } // 得到当前运行的任务栈,参数就是得到多少个任务栈,1就是只拿一个任务栈 // 1对应的也就是正在运行的任务栈啦 List<RunningTaskInfo> runningTaskInfos = activityManager .getRunningTasks(1); // 拿到当前运行的任务栈 RunningTaskInfo runningTaskInfo = runningTaskInfos .get(0); // 拿到要运行的Activity的包名 String packageName = runningTaskInfo.topActivity .getPackageName(); System.out.println("当前在运行:" + packageName); // 这样,我们就可以临时的把一个应用取消保护 if (stopApps.contains(packageName)) { sleep(1000); continue; } if (apps.contains(packageName)) { intent.putExtra("packageName", packageName); startActivity(intent); } else { } sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } @Override public void onDestroy() { super.onDestroy(); flag = false; } // ================================================================== private class MyBinder extends Binder implements IService { @Override public void startApp(String packageName) { invokeMethodStartApp(packageName); } @Override public void stopApp(String packageName) { invokeMethodStopApp(packageName); } } private class MyObserver extends ContentObserver { public MyObserver(Handler handler) { super(handler); // 重新更新apps里面的内容 apps = dao.getAllPackageName(); System.out.println("数据库的内容发生了改变"); } } }
最后,和大家说一下
为了方便大家的交流,我创建了一个群,这样子大家有什么疑问也可以在群上交流
群号是298440981
今天源码下载