终于,所有的考试都考完了,可以安安心心的敲敲代码了。上次记录程序锁只是搭建了一个界面,具体的业务逻辑还没有实现,然后昨晚看了视频之后,自己也敲了一遍代码,所以今天就抽个空记录一下。推荐黑马74期视频,确实很不错。老师们都讲的很详细。
思路:
1.程序锁是功能要时时刻刻对我们开启的应用进行监听,它是脱离Activity的存在,所以根据这一需求,我们可以将业务逻辑写在Service中。
2.程序锁的实现:也就是在点击加锁应用的时候,要提前弹出一个锁屏界面,也就是一个Activity,然后输入密码,如果密码正确,才进入应用。
3.有硬件方面知识的应该知道watchDog,顾名思义,看门狗。比如说在16位的单片机里面,就有这个东西存在,它存在的目的就是对某一个引脚时时刻刻进行监控。根据这一思想,可以在程序中也设定一watchDog,说白了就是一个可控制的死循环。对我们启动的程序进行时时刻刻的监控。
问题:
1.在应用启动之前,我们设置一个拦截Activity,那么久需要考虑回退栈的问题。
2.在看门狗程序里,过滤已经解锁的应用,通过发送广播来实现。
3.当我们开启了服务之后,再去添加上锁的程序,不能生效。这时候需要用到 内容观察者,观察数据库变化,一旦数据库变化,则放置包名的集合需要重新获取数据
4.挂起拦截界面的时候,不需要手机卫士的图标
5.回退按钮的处理,在拦截界面点击回退按钮时,直接回退到桌面。
这些问题都是机上敲了代码之后,才总计出来的
1.watchDog代码
private void watch() {
//1.在子线程中开启可控的死循环
new Thread(new Runnable() {
@Override
public void run() {
mPackageNameList = mDao.findAll();
while (mIsWatch) {
//2检测现在正在开启的应用,任务栈
//3获取activity给管理者对象
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
//4.获取正在运行的任务栈的方法
List<ActivityManager.RunningTaskInfo> runningTasks = activityManager.getRunningTasks(1);
ActivityManager.RunningTaskInfo runningTaskInfo = runningTasks.get(0);
//5.获取栈顶的activity
String topPackageName = runningTaskInfo.topActivity.getPackageName();
//6拿此报名和已加数据库或者已加锁的集合进行比较
if (mPackageNameList.contains(topPackageName)){
//弹出拦截界面 这个拦截的Activity要在一个单独的任务栈中,这样当密码正确的时候才会跳转到相应的程序界面
//如果现在检测的程序包名已经解锁,则不需要弹出拦截界面了
if (!topPackageName.equals(mSkipPackageName)){
Intent intent = new Intent(getApplicationContext(), enterPsdActivity.class);
intent.putExtra("topPackageName",topPackageName);
//在服务中开启Activity,需要设置图下的Flags
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
//即使死循环再子线程中,也会消耗大量的内存,所以需要睡一下
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
2.拦截界面处,处理返回键,以及记录已经解锁的报名,传送回watchDog,免有一次的拦截;
public class enterPsdActivity extends Activity {
private String mTopPackageName;
private TextView tv_app_name;
private ImageView iv_app_icon;
private EditText et_psd;
private Button bt_submit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//拿到Intent中所携带的信息
mTopPackageName = getIntent().getStringExtra("topPackageName");
setContentView(R.layout.activity_enter_psd);
initUI();
initData();
}
private void initData() {
//通过传递过来的包名获取拦截应用的图标以及名称
PackageManager packageManager = getPackageManager();
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(mTopPackageName, 0);
//设置图标
iv_app_icon.setBackgroundDrawable(applicationInfo.loadIcon(packageManager));
//设置名称
tv_app_name.setText(applicationInfo.loadLabel(packageManager).toString());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
//注册密码确定按钮的监听事件
bt_submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String psd = et_psd.getText().toString();
if (!TextUtils.isEmpty(psd)){
if (psd.equals("123")){
//解锁进入应用
//关闭拦截的应用 告知watchDog不要再去监听已经解锁的应用,发送广播
Intent intent = new Intent("android.intent.action.SKIP");
intent.putExtra("topPackageName", mTopPackageName);
sendBroadcast(intent);
finish(); //结束掉拦截界面
}else{
ToastUtil.show(getApplicationContext(),"密码错误");
}
}else{
ToastUtil.show(getApplicationContext(),"请输入密码");
}
}
});
}
private void initUI() {
tv_app_name = (TextView)findViewById(R.id.tv_app_name);
iv_app_icon = (ImageView)findViewById(R.id.iv_app_icon);
et_psd = (EditText) findViewById(R.id.et_psd);
bt_submit = (Button) findViewById(R.id.bt_submit);
}
@Override //按回退键的时候,直接跳转到桌面而不是
public void onBackPressed() {
// 跳转到桌面 隐士意图
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
super.onBackPressed();
}
}
3.在广播接收者中,接收到从拦截界面传回的已经解锁了的应用
private class innerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获取发送广播过程中传递过来的包名,跳过这个包名的检测
mSkipPackageName = intent.getStringExtra("topPackageName");
}
}
3.内容观察者,用于当服务开启后,又添加了一个加锁的应用,则立即更新数据库
private class myContentObserver extends ContentObserver {
/** * Creates a content observer. * * @param handler The handler to run {@link #onChange} on, or null if none. */
public myContentObserver(Handler handler) {
super(handler);
}
@Override //一旦数据库发生改变的时候回调的方法 也就是执行了 数据库的 insert() delete()方法
public void onChange(boolean selfChange) {
super.onChange(selfChange);
new Thread(new Runnable() {
@Override
public void run() {
mPackageNameList = mDao.findAll();
}
}).start();
}
}
5.当服务关闭时,要释放所用到的资源。
@Override
public void onDestroy() {
//注销广播
if (mInnerReceiver!=null) {
unregisterReceiver(mInnerReceiver);
}
//停下看门狗
mIsWatch=false;
//注销内容观察者
if (mMyContentObserver!=null) {
getContentResolver().unregisterContentObserver(mMyContentObserver);
}
super.onDestroy();
}
零零散散的,我自己将就着看。这学期的期末考试终于结束了。能够安心的坐在图书馆敲敲代码,看看书了。接下来还有两周的生产实习和短学期实践。院里除了会做这样的事情还会做什么,找一个外地的培训机构来学校给我们上课,实际上就是变相招生。短学期实践的负责老师也根本就不管事,第一天开个会,然后就没有任何消息了。坑的只是我们这些学生,出去面试实习单位,对方一听说7月24之后才能报道,就直接pass,这就是他妈学院做的一点好事。如果真的可以学到什么,我相信大多数学生不会反抗,但事实上,就是挂着羊头卖狗肉,还美其名日:贯彻发展国家什么xxx人才培养计划。!! 简直就是扯淡。
Android—程序锁(1)展示页面的搭建