当前位置: 代码迷 >> Android >> Android项目实战-手机卫士26-内容提供者与内容观察者
  详细解决方案

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

热度:69   发布时间:2016-04-28 07:42:35.0
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



今天源码下载


  相关解决方案