我们之前已经把那个手机防盗的基本上做出来的了,但是还是有不少问题的,今天我们就把这些问题修复一下,首先我们就把那个最重要的来修复一下先,就是我们输入密码之后,又会弹出输入密码的界面,这是因为我们没有把那个监听的任务栈暂时的停止,所以它就会又进行判断了,所以就又进入到输入密码的界面了,解决的方法思路就是我们在服务那里新建一个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);
}
里面有两个很简单的方法,一个是输入密码之后,停止对它的阻止 stopApp,一个是要重新输入密码 重新开启对它的保护 startApp
接下来,我们就要在服务里面定义两个方法啦
//临时开启对某个应用的保护
private void invokeMethodStartApp(String packageName)
{
if(stopApps.contains(packageName))
{
stopApps.remove(packageName);
}
}
//临时停止对某个应用的保护
private void invokeMethodStopApp(String packageName)
{
stopApps.add(packageName);
}
接下来,我们就要写一个自己的Binder类啦,当服务一但绑定的时候,就返回这个类的对象
这个类,我们就写在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;
}
这个myBinder对象就是我们上面写的那个内部类的对象来的,我在onCreate那里把它初始化了
好啦,在服务里面把我们要调用的Service方法也写好啦,那么我们就回到我们的输入密码的界面里面,进行方法的调用啦
首先我们会在onCreate里面先绑定服务
connection = new MyConnection();
// 绑定服务,主要是为了能够调用服务里面的方法
Intent intent = new Intent(this, WatchDogService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
把我们的connection对象的类写成内部类的形式
private class MyConnection implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
// 我们之前在Service里面已经实现了IService接口了
iService = (IService) service;
}
@Override
public void onServiceDisconnected(ComponentName name)
{
}
}
因为服务一但用bindService进行绑定之后,就会马上返回一个Binder对象的,而我们之前在服务里面返回的Binder对象就是我们实现了IService接口的对象啦,所以我们就在绑定的时候,拿到那个Binder对象,然后把它转换成IService,这样我们就可以调用服务里面的方法啦
当然,我们还要不要忘记写一个解除绑定服务的方法啊,不然就会有问题的啦
@Override
protected void onDestroy()
{
if (connection != null)
{
unbindService(connection);
connection = null;
}
super.onDestroy();
}
在Activity的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,这个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;
}
}
为了简单起见,我们只处理了insert和delete这两个方法,其实的方法我们都没有处理,各位有兴趣的可以处理一下,
因为Provider是一个组件,所以我们要在AndroidMainfest里面注册它
<provider
android:name="com.xiaobin.security.provider.AppLockProvider"
android:authorities="com.xiaobin.security.applockprovider"
android:exported="false"></provider>
好啦,现在内容提供者,我们也有啦,现在我们要做的就是在Service的onCreate方法里面注册一个内容的观察者,当数据发生变化的时候,我们就对list进行更新一下
// 注册一个内容观察者
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("数据库的内容发生了改变");
}
}
好啦,到这里为止我们的bug修复就完成的啦,今天主要就是讲了内容的提供者与内容的观察者, 这些是很重要的,大家可以多看看,
下面我们把完成的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("数据库的内容发生了改变");
}
}
}
好啦,现在我们的程序锁就全部讲完的啦,下一次我们就讲下一个功能,就是进程的管理