Android项目实战--手机卫士26--内容提供者与内容观察者

我们之前已经把那个手机防盗的基本上做出来的了,但是还是有不少问题的,今天我们就把这些问题修复一下,首先我们就把那个最重要的来修复一下先,就是我们输入密码之后,又会弹出输入密码的界面,这是因为我们没有把那个监听的任务栈暂时的停止,所以它就会又进行判断了,所以就又进入到输入密码的界面了,解决的方法思路就是我们在服务那里新建一个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("数据库的内容发生了改变");
		}

	}

}

好啦,现在我们的程序锁就全部讲完的啦,下一次我们就讲下一个功能,就是进程的管理


最后,和大家说一下

为了方便大家的交流,我创建了一个群,这样子大家有什么疑问也可以在群上交流

群号是298440981



今天源码下载


你可能感兴趣的:(项目实战,手机卫士,内容观察者内容提供者,程序锁)