1、参照腾讯手机管家,看一看演示软件锁功能;设置密码,启动软件试试;
2、在高级工具里面添加“程序锁”enterApplock,添加点击事件。
3、创建新的类AppLockActivity,并在功能清单文件注册;
写布局文件
标题用线性布局:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal" >
<TextView
android:textColor="#ffffff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tab_left_pressed"
android:gravity="center"
android:text="未加锁" />
<TextView
android:textColor="#ffffff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tab_right_default"
android:gravity="center"
android:text="已加锁" />
LinearLayout
中间部分,显示软件的地方代码,有两个ListView;
<LinearLayout
android:id="@+id/ll_unlock"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未加锁软件 xx个" />
android:id="@+id/lv_unlock"
android:layout_width="match_parent"
android:layout_height="match_parent" >
LinearLayout>
<LinearLayout
android:id="@+id/ll_locked"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已加锁软件 xx个" />
android:id="@+id/lv_locked"
android:layout_width="match_parent"
android:layout_height="match_parent" >
LinearLayout>
4、初始化两个TextView和线性布局加上点击事件;
A:初始化TextView和线性布局
B:两个TextView 加上点击事件
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_unlock:
tv_unlock.setBackgroundResource(R.drawable.tab_left_pressed);
tv_locked.setBackgroundResource(R.drawable.tab_right_default);
ll_unlock.setVisibility(View.VISIBLE);
ll_locked.setVisibility(View.GONE);
break;
case R.id.tv_locked:
tv_unlock.setBackgroundResource(R.drawable.tab_left_default);
tv_locked.setBackgroundResource(R.drawable.tab_right_pressed);
ll_unlock.setVisibility(View.GONE);
ll_locked.setVisibility(View.VISIBLE);
break;
}
}
演示测试,看看效果;
5、未加锁模块的实现
A:初始化两个ListView
lv_unlock = (ListView) findViewById(R.id.lv_unlock);
lv_locked = (ListView) findViewById(R.id.lv_locked);
B:利用软件管理模块已经写过的工具得到系统所有的信息
//先得到安装的所有程序//建议在子线程去写
appInfos = AppInfoProvider.getAllAppInfos(this);
C:创建适配器AppLockAdapter 并自定义Item 的View
布局文件名叫list_applock_item.xml
使用相对布局比较方便
xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="@+id/iv_icon"
android:layout_width="50dip"
android:layout_height="50dip"
android:src="@drawable/app" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/iv_icon"
android:text="软件名称"
android:textSize="20sp" >
TextView>
<ImageView
android:id="@+id/iv_status"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dip"
android:src="@drawable/lock" />
RelativeLayout>
D:在适配器的getView()方法里实例化布局文件;
View view = null;
ViewHolder holder = null;
if(convertView != null&& convertView instanceof RelativeLayout){
view = convertView;
holder = (ViewHolder) view.getTag();
}else{
view = View.inflate(getApplicationContext(), R.layout.list_applock_item, null);
holder = new ViewHolder();
holder.iv_icon = (ImageView) view.findViewById(R.id.iv_icon);
holder.tv_name = (TextView) view.findViewById(R.id.tv_name);
view.setTag(holder);
}
AppInfo appInfo = appInfos.get(position);
holder.iv_icon.setImageDrawable(appInfo.getIcon());
holder.tv_name.setText(appInfo.getName());
return view;
E:设置未加锁个数
A:定义ID为tv_unlock_count、tv_locked_count初始TextView
tv_unlock_count = (TextView) findViewById(R.id.tv_unlock_count);
tv_locked_count = (TextView) findViewById(R.id.tv_locked_count);
B:在getCount()方法赋值;
public int getCount() {
tv_unlock_count.setText("未加锁软件:"+appInfos.size()+"个");
return appInfos.size();
}
1、创建数据库,用户保存已加锁数据;
A:基于BlackNumberDBOpenHelper修改名称AppLockDBOpenHelper
B:修改数据库名称blacknumber.db ---> applock.db
C:修改表明:改成如下
/**
* _id 主键 自增长 ,packname 包名
*/
private String sql = "create table applock(_id integer primary key autoincrement,packname varchar(20))";
D:基于BlackNumberDao 复制改名成 AppLockDao
public class AppLockDao {
private AppLockDBOpenHelper openHelper;
public AppLockDao(Context context){
openHelper = new AppLockDBOpenHelper(context);
}
/**
* 增加一条信息
*/
public void add(String packname ){
SQLiteDatabase db = openHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("packname", packname);
db.insert("applock", null, values);
db.close();
}
/**
* 删除一条信息
* @param packname
*/
public void delete(String packname) {
SQLiteDatabase db = openHelper.getWritableDatabase();
db.delete("applock", "packname=?", new String[]{packname});
db.close();
}
/**
* 查询一条信息是否存在
*/
public boolean find(String packname){
boolean result = false;
SQLiteDatabase db = openHelper.getReadableDatabase();
Cursor cursor = db.query("applock", null, "packname=?", new String[]{packname}, null, null, null);
if(cursor.moveToNext()){
result = true;
}
cursor.close();
db.close();
return result;
}
}
2、小锁上加上ID,设置点击事件
A:list_applock_item里面,小锁图标加上ID:iv_status
B:初始化并且设置点击事件,并实例化AppLockDao
holder.iv_status.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//从当前界面移除Item
appInfos.remove(position);
//把移除的Item对象对应的包名加到数据库里面
dao.add(appInfo.getPackageName());
//通知页面刷新
notifyDataSetChanged();
}
});
3、区分未加锁和已经加锁区分--未加锁的实现;
A:定义两个集合
//未加锁
private List
//已加锁
private List
B:数据初始化--数据区分开来
appInfos = AppInfoProvider.getAllAppInfos(this);
unlockappInfos = new ArrayList
lockedappInfos = new ArrayList
for(AppInfo appInfo : appInfos){
if(dao.find(appInfo.getPackageName())){
lockedappInfos.add(appInfo);
}else{
unlockappInfos.add(appInfo);
}
}
C:在适配器定义字段区分未加锁已加锁,在构造方法里传参数
/**
* true未加锁,false已加锁
*/
private boolean unlockflag = true;
public AppLockAdapter(boolean unlockflag) {
this.unlockflag = unlockflag;
}
D:在getCount()方法修改成如下:
public int getCount() {
if(unlockflag){
tv_unlock_count.setText("未加锁软件:"+unlockappInfos.size()+"个");
return unlockappInfos.size();
}else{
tv_locked_count.setText("已加锁软件:"+lockedappInfos.size()+"个");
return lockedappInfos.size();
}
}
E:在getView()处理
AppInfo appInfo = null;
if(unlockflag){
appInfo = unlockappInfos.get(position);
}else{
appInfo = lockedappInfos.get(position);
}
F:点击事件修改
if(unlockflag){
//从当前界面移除Item
unlockappInfos.remove(position);
//把移除的Item对象对应的包名加到数据库里面
dao.add(appInfo.getPackageName());
}
//通知页面刷新
notifyDataSetChanged();
4、已加锁的实现
A:初始化已加锁的适配器
lv_unlock.setAdapter(new AppLockAdapter(true));//未加锁
lv_locked.setAdapter(new AppLockAdapter(false));//已加锁
B:已加锁的点击事件
if(unlockflag){
//从当前界面移除Item
unlockappInfos.remove(position);
//把移除的Item对象对应的包名加到数据库里面
dao.add(appInfo.getPackageName());
}else{
lockedappInfos.remove(position);
dao.delete(appInfo.getPackageName());
}
运行看看演示效果
C:解决移除了,界面没有变化的问题
处理集合数据:
if(unlockflag){
//从当前界面移除Item
unlockappInfos.remove(position);
//把移除的Item对象对应的包名加到数据库里面
dao.add(appInfo.getPackageName());
lockedappInfos.add(appInfo);
}else{
lockedappInfos.remove(position);
dao.delete(appInfo.getPackageName());
unlockappInfos.add(appInfo);
}
//通知页面刷新
notifyDataSetChanged();
适配器定义成成员变量
unLockAdapter = new AppLockAdapter(true);
lockedAdapter = new AppLockAdapter(false);
lv_unlock.setAdapter(unLockAdapter);//已加锁
lv_locked.setAdapter(lockedAdapter);//未加锁
更新适配器
//通知页面刷新
//notifyDataSetChanged();
unLockAdapter.notifyDataSetChanged();
lockedAdapter.notifyDataSetChanged();
5、设置状态,如果是为加锁的话设置未加锁图片,如果是已加锁设置为加锁状态;
在getView()方法里
if(unlockflag){
holder.iv_status.setImageResource(R.drawable.lock);
appInfo = unlockappInfos.get(position);
}else{
appInfo = lockedappInfos.get(position);
holder.iv_status.setImageResource(R.drawable.unlock);
}
1、设置Item的动画效果
在未加锁地方,加锁右移动画
TranslateAnimation ta = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1.0f,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0);
ta.setDuration(500);
view.startAnimation(ta);
左移动画
TranslateAnimation ta = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, -1.0f,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0);
ta.setDuration(500);
2、解决动画移动问题
导的原因,动画没有开始播放,界面就刷新了。
动画播放需要时间的,动画没有播就变成了新的View对象。就播了新的View对象,
让动画播放完后,再去更新页面;
如何等待呢?
动画播放是不能放在子线程的。
view.startAnimation(ta);//通知动画开始播放
动画播放是每隔一段时间在主线程更新View,产生的动画效果;
//让主线程等待500毫米
postDelayed方法说明:
Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses. The runnable will be run on the thread to which this handler is attached(附属的).
使Runnable r添加到消息队列,是运行在指定时间的流逝之后。可运行的线程将运行在handler 附属线程。
//若干秒后在主线程执行逻辑
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//会在主线程执行
// 从当前界面移除Item
unlockappInfos.remove(position);
// 把移除的Item对象对应的包名加到数据库里面
dao.add(appInfo.getPackageName());
lockedappInfos.add(appInfo);
// 通知页面刷新
// notifyDataSetChanged();
unLockAdapter.notifyDataSetChanged();
lockedAdapter.notifyDataSetChanged();
}
}, 500);
完整代码:
if (unlockflag) {
TranslateAnimation ta = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1.0f,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0);
ta.setDuration(500);
view.startAnimation(ta);
//若干秒后在主线程执行逻辑
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//会在主线程执行
// 从当前界面移除Item
unlockappInfos.remove(position);
// 把移除的Item对象对应的包名加到数据库里面
dao.add(appInfo.getPackageName());
lockedappInfos.add(appInfo);
// 通知页面刷新
// notifyDataSetChanged();
unLockAdapter.notifyDataSetChanged();
lockedAdapter.notifyDataSetChanged();
}
}, 500);
} else {
holder.iv_status.setImageResource(R.drawable.unlock);
TranslateAnimation ta = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, -1.0f,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0);
ta.setDuration(500);
view.startAnimation(ta);
//若干秒后在主线程执行逻辑
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//会在主线程执行
lockedappInfos.remove(position);
dao.delete(appInfo.getPackageName());
unlockappInfos.add(appInfo);
// 通知页面刷新
// notifyDataSetChanged();
unLockAdapter.notifyDataSetChanged();
lockedAdapter.notifyDataSetChanged();
}
}, 500);
}
1、举例生活中:家里或者公司门口请一个保安。进来之前出事一下证件,如果认识的话,看一下脸就行了。请保安成本比较高。这时候就买了一条狗。我们叫这狗叫:看门狗
职责:监听手机的信息;
2、在服务包下创建新类WatchDogService 在功能清单文件配置,并实现onCreate()、onDestroy()方法
A:定义ActivityManager并实例化它
private ActivityManager am;
B:在onCreate()创建新线程,线程里循环监视当前运行的进程;
类的成员变量
private boolean flag = false;
new Thread(){
public void run() {
flag = true;
while (flag) {
//巡逻 运行当前运行的应用程序
}
};
}.start();
C:得到任务占最前面任务栈信息 ,也是当前要开启的程序
回顾知识:返回一个集合,当前运行的任务栈
Return a list of the tasks that are currently running, with the most recent being first and older ones after in order. Note that "running" does not mean any of the task's code is currently loaded or activity -- the task may have been frozen by the system, so that it can be restarted in its previous state when next brought to the foreground.
返回一个列表正在运行的任务,与最近的第一和旧后为。请注意,“跑”并不意味着任何任务的代码是当前加载的任务或活动——可能是由系统冻结,以便它可以重新启动在以前的状态下进入前台;
代码如下
new Thread(){
public void run() {
flag = true;
while (flag) {
//巡逻 运行当前运行的应用程序
//得到最近打开的任务栈信息,最近打开的应用程序
RunningTaskInfo taskInfo = am.getRunningTasks(1).get(0);
String packName = taskInfo.topActivity.getPackageName();
System.out.println("==packName="+packName);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}.start();
需要加上权限
<uses-permission android:name="android.permission.GET_TASKS"/>
D:在设置中心SettingActivity增加一条目,控制程序锁服务开启和关闭
<com.itheima.mobilesafe.ui.SettingItemView
android:id="@+id/siv_applock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
itheima:desc_off="程序锁已经关闭"
itheima:desc_on="程序锁已经开启"
itheima:title="设置程序锁" >
com.itheima.mobilesafe.ui.SettingItemView>
点击事件代码实现
//设置程序锁
siv_applock = (SettingItemView) findViewById(R.id.siv_applock);
watchDogIntent = new Intent(this,WatchDogService.class);
boolean isWatchDogRunning = ServiceUtils.isServiceRunning(this, "com.itheima.mobilesafe.service.WatchDogService");
if(isWatchDogRunning){
siv_applock.setChecked(true);
}else{
siv_applock.setChecked(false);
}
siv_applock.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(siv_applock.isChecked()){
siv_applock.setChecked(false);
stopService(watchDogIntent);
}else{
siv_applock.setChecked(true);
startService(watchDogIntent);
}
}
});
运行演示看效果,打开其他软件看看日志。
3、判断哪些应用需要保护
A:在WatchDogService里的创建AppLockDao类
private AppLockDao dao;
B:在onCreate()方法里实例化
dao = new AppLockDao(this);
C:查找数据库,那些程序需要加密,需要保护
if(dao.find(packName)){
//需要保护的的应用程序,弹出密码输入框
}
D:创建新的Activity界面EnterPwdActivity,并实现布局文件,在功能清单文件注册;
E:启动输入密码界面,在看门狗服务的onCreate()创建意图
watchIntent = new Intent(this, EnterPwdActivity.class);
watchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
在判断是需要保护的启动输入密码界面
if(dao.find(packName)){
//需要保护的的应用程序,弹出密码输入框
startActivity(watchIntent);
}
运行演示,看一下效果,和腾讯的对比;
1、在EnterPwdActivity里重写onBackPressed()方法,并进入手机桌面;
当按返回键调用的方法;
Called when the activity has detected the user's press of the back key. The default implementation simply finishes the current activity, but you can override this to do whatever you want.
当活动已检测到后退键的用户的新闻。默认的实现简单地完成当前的活动,但是你可以重写此来做你想做的事。
@Override
public void onBackPressed() {
//进入手机桌面
Intent intent = new Intent();
//
//
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.HOME");
startActivity(intent);
}
2、完善输入密码布局文件
xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_appname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你要打开的应用:" />
<ImageView
android:id="@+id/iv_appicon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/app" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword" />
<Button
android:onClick="enter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确定" />
LinearLayout>
3、初始化控件
private TextView tv_appname;
private ImageView iv_appicon;
private EditText et_password;
4、在WatchDogService里传包名信息
watchIntent.putExtra("packname", packName);
5、在EnterPwdActivity获取包名
Intent intent = getIntent();
String packname = intent.getStringExtra("packname");
6、设置Icon和名字
PackageManager pm = getPackageManager();
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(packname, 0);
Drawable icon = applicationInfo.loadIcon(pm);
iv_appicon.setImageDrawable(icon);
String name = applicationInfo.loadLabel(pm).toString();
tv_appname.setText(name);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
7、实现按钮点击事件
public void enter(View view){
String password = et_password.getText().toString().trim();
if(TextUtils.isEmpty(password)){
Toast.makeText(this, "对不起,密码不能为空", 1).show();
return;
}
if("123".equals(password)){
//密码正确
finish();
}else{
Toast.makeText(this, "密码错误", 1).show();
}
}
8、处理图标不变的问题
当界面看不到的时候回调的方法,把页面给关了
@Override
protected void onStop() {
super.onStop();
finish();
}
9、处理密码输入正确的时,又再次弹出页面的问题;
A:在密码正确的时候,发生广播,告诉看门狗该应用临时停止保护;
//发一广播,告诉看门狗,对某程序临时停止保护
Intent intent = new Intent();
intent.setAction("com.ithiema.mobilesafe.stopprotect");
intent.putExtra("packname", packname);
sendBroadcast(intent);
B:在看门狗服务里接收广播
private InnerReceiver receiver;
private class InnerReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
temStopProtectPackName = intent.getStringExtra("packname");
Log.i(TAG, "看门狗得到了消息,临时对某个应用程序保护=="+temStopProtectPackName);
}
}
C:注册广播
receiver = new InnerReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.ithiema.mobilesafe.stopprotect");
registerReceiver(receiver, filter);
取消注册:
unregisterReceiver(receiver);
receiver = null;
D:判断该不该保护
if(dao.find(packName)){
//需要保护的的应用程序,弹出密码输入框
if(packName.equals(temStopProtectPackName)){
//什么也不用做
}else{
watchIntent.putExtra("packname", packName);
startActivity(watchIntent);
}
}
E:相关优化
知识拓展:看门狗后台一直在运行,这样是比较耗电的。
例如:腾讯的在桌面显示占用内存显示百分比:在屏幕就显示,不是在桌面了就隐藏掉。这样节约电;
我们要优化的的话怎么做呢?
在看门狗服务里,监听锁屏事件,如果锁屏了我就把看门狗停止(flag = false;);屏幕开启了,我就让看门狗开始工作启动服务并且flag = true;;
避免一次输入密码了不再输入;防止别人在我使用的时候,接着使用不用输入密码的情形;
也可以在锁屏的时候temStopProtectPackName赋值为空,者就就行了。
1、演示复习bug
2、画图分析,正常情况下的任务栈和bug时的任务栈图;
3、解决问题;在功能清单文件EnterPwdActivity加上字段
<activity android:name="com.itheima.mobilesafe.EnterPwdActivity" android:launchMode="singleInstance"/>
standard:标准的开一个都会放在任务栈的栈顶;
singleTop:如果栈顶存在了,就不会创建一个新的了;
singleTask :整个任务栈里只有一个这样的Activity;
singleInstance:创建一个自己的单独的任务栈,把这个Activity放在里面。
singleInstance除此之外的应用场景,例如分享一个消息,后点击返回回到被分享的软件就需要用到,分享平台的Activty就需要改成singleInstance启动模式;
4、然后再画图分析正确的任务栈;
长按小房子键:弹出历史记录页面,就会列出最近打开的Activity;
1、演示由于最近打开的Activity导致的Bug;
一般的用户是理解不了的;
2、容易暴露用户的隐私
最近打开的Activity,是为了用户可以很快打开最近打开的应用而设计的;2.2、2.3普及后就把问题暴露出来了,很容易暴露用户的隐私。比如你玩一些日本开发的游戏:吹裙子、扒衣服这类游戏。你正在玩这些有些,这个时候,爸妈或者大学女辅导员过来了,赶紧按小房子,打开背单词的应用,这时大学女辅导员走过来说,干嘛呢,把手机交出来,长按一下小房子键,这个时候很尴尬的事情就产生了。
A:低版本是无法移除的。低版本记录最近8个;想要隐藏隐私,打开多个挤出去;
B:4.0以后高版本就可以直接移除了。考虑用户呼声比较高。
3、设置不再最近任务列表显示activity
<activity
android:excludeFromRecents="true"
android:name="com.itheima.mobilesafe.EnterPwdActivity"
android:launchMode="singleInstance" />
4、在装有腾讯管家的模拟器演示腾讯管理的程序锁功能;也没用现实最近的Activity,它也是这样做的。
知识拓展,以后开发带有隐私的软件,或者软件名称不好听的应用,就可以加载在最近打开列表不包括字段;
1、演示在高版本软件锁的情况,是很正常的;在在低版本演示,会发现看到保护的程序,如果来回关闭输入密码界面,会看到具体内容。这个时候需要优化速度效果。
2、优化
A:在看门狗服务WatchDogService 子线程休眠时间改为20毫秒;
B:查询数据库操作是比较耗时的,改成在内存中查询数据库;
在AppLockDao 类里增加查询所有要保护的包名的方法;
/**
* 查询所有的要保护的包名
* @return
*/
public List
List
SQLiteDatabase db = openHelper.getReadableDatabase();
Cursor cursor = db.query("applock", new String[]{"packname"}, null, null, null, null, null);
while(cursor.moveToNext()){
String packname = cursor.getString(0);
lists.add(packname);
}
cursor.close();
db.close();
return lists;
}
在WatchDogService服务定位集合成成员变量。
private List
在onCreate()初始化
protectedPacknames = dao.findAll();
查找数据库的代码改成在内存中找
//不要平方的查询数据库操作 ---改为查询内存;
if(protectedPacknames.contains(packName)){//查询内存
//需要保护的的应用程序,弹出密码输入框
if(packName.equals(temStopProtectPackName)){
//什么也不用做
}else{
watchIntent.putExtra("packname", packName);
startActivity(watchIntent);
}
}
改为查内存速度要高10倍以上;
演示效果,会比之前要快一些;
3、这个时候,增加另外一款软件进入程序锁。打开看看,是无法打开输入密码页面的;解析原因;
4、我们这个时候就需要根据数据库的数据变化而改变集合的信息了,就用到了观察者;
A:画图讲解观察者原理
B:在AppLockDao 类构造方法里增加上下文Context
private Context context;
public AppLockDao(Context context){
openHelper = new AppLockDBOpenHelper(context);
this.context = context;
}
C:在AppLockDao的add和delete方法里增加发通知内容改变的消息
Uri uri = Uri.parse("content://com.itheima.mobilesafe.applockdb");
//null 代表不关心谁接收消息
context.getContentResolver().notifyChange(uri, null);
D:在看门狗服务WatchDogService里注册内容观察者
Uri uri = Uri.parse("content://com.itheima.mobilesafe.applockdb");
observer = new MyContentObserver(new Handler());
getContentResolver().registerContentObserver(uri, true, observer);
E:在服务销毁的地方取消注册
getContentResolver().unregisterContentObserver(observer);
observer = null;
F:内容观察者实现
private class MyContentObserver extends ContentObserver{
public MyContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.i(TAG, "观察数据库数据变化了。。。。");
protectedPacknames = dao.findAll();
}
}
运行演示看效果,演示加锁、未加锁、服务关闭等;
演示前,需要装上360手机卫士;
1、简单看一下PC网络状态;
2、连上真机
连上后命令:adb shell或者 adb -s shouji shell
进入proc目录:cd proc
查看文件命令:ls -l
文件夹uid_stat,用户id的状态
进入该目录命令:cd uid_stat
查看里面有什么东西:ls -l
这些东西是什么呢?这些数字就是用户程序的用户ID;
如何去获取程序的用户ID呢?在我们开发的软件管理模块,得多所有用户信息AppInfoProvider类里增加;
Uid说明
The kernel user-ID that has been assigned to this application; currently this is not a unique ID (multiple applications can have the same uid).
内核的用户ID分配给该应用程序;目前这不是唯一的ID(多个应用程序可以有相同的UID)。
String name = packInfo.applicationInfo.loadLabel(pm).toString()+ packInfo.applicationInfo.uid;
用户id是安装应用程序的时候 操作系统赋给应用程序的;
例如:
QQ:10083
去哪网:10110
进入QQ(10083)目录命令:cd 10083
查看命令:cat tct_rcv
下载数据:367435
上传数据:94625
进入去哪网10110:cd 10110
下载:168251
上传:23544
tcp_rcv :代码下载的数据
tcp_snd:代表上传的数据
装上360手机卫士:
命令:
adb -s b1be17f0 install D:\手机卫士\day9\com.qihoo360.mob.apk
查看流量统计
计算值:
QQ下载和上传
下载:168251 0.160456657409668MB
上传:23544 0.0224533081054688MB
Android上得到流量信息是很重要的。从Android2.2开始提供,统计流量,但2.2支持得比较弱,2.3后支持就比较强一些。
3、创建新页面TrafficManagerActivity继承Activity
4、流量统计API接口介绍
手机上传和下载的总流量
// 3g/2g网络上传的总流量,单位为byte
TrafficStats.getMobileTxBytes();
// 3g/2g网络下载的总流量,单位为byte
TrafficStats.getMobileRxBytes();
// 手机+wifi的下载总流量
TrafficStats.getTotalRxBytes();
// 手机+wifi的上传总流量
TrafficStats.getTotalTxBytes();
计算某个应用程序的流量
//某个用户程序的下载流量
TrafficStats.getUidRxBytes(uid);
//某个应用程序的上传流量
TrafficStats.getUidTxBytes(uid)
这些值在模拟器打印都是-1;
知识拓展
Android流量统计TrafficStats类的使用
对于Android流量统计来说在2.2版中新加入了TrafficStats类可以轻松获取,其实本身TrafficStats类也是读取 Linux提供的文件对象系统类型的文本进行解析。android.net.TrafficStats类中,提供了多种静态方法,可以直接调用获取,返回 类型均为 long型,如果返回等于-1代表 UNSUPPORTED 当前设备不支持统计。
static long getMobileRxBytes() //获取通过Mobile连接收到的字节总数,不包含WiFi
static long getMobileRxPackets() //获取Mobile连接收到的数据包总数
static long getMobileTxBytes() //Mobile发送的总字节数
static long getMobileTxPackets() //Mobile发送的总数据包数
static long getTotalRxBytes() //获取总的接受字节数,包含Mobile和WiFi等
static long getTotalRxPackets() //总的接受数据包数,包含Mobile和WiFi等
static long getTotalTxBytes() //总的发送字节数,包含Mobile和WiFi等
static long getTotalTxPackets() //发送的总数据包数,包含Mobile和WiFi等
static long getUidRxBytes(int uid) //获取某个网络UID的接受字节数
static long getUidTxBytes(int uid) //获取某个网络UID的发送字节数
总接受流量TrafficStats.getTotalRxBytes(),
总发送流量TrafficStats.getTotalTxBytes());
不包含WIFI的手机GPRS接收量TrafficStats.getMobileRxBytes());
不包含Wifi的手机GPRS发送量TrafficStats.getMobileTxBytes());
某一个进程的总接收量TrafficStats.getUidRxBytes(Uid));
某一个进程的总发送量TrafficStats.getUidTxBytes(Uid));
这些都是从第一次启动程序到最后一次启动的统计量。并不是这篇文章里所说的“从本次开机到本次关机的统计量”!
用法举例,注意这里得到的单位都是"KB"
Java代码
5、流量报警原理简介(参照360流量设置页面)
工作原理就给运营商发短信
A:开启超额提醒
B:设置每月流量套餐300MB
C:自动校准流量-流量短信设置
D:演示法短信给运营商;211
当发短信后,过一会就会回复剩余流量等等信息了
6、联网防火墙简介
在linux 上有一款强大的防火墙软件iptable
360就是把这款软件内置了
如果手机有root权限,把防火墙软件装到手机的内部,并且开启起来。
以后就可以拦截某个应用程序的联网了。
如果允许某个软件上网就什么也不做。如果不允许某个软件上网,就把这个软件的所有的联网操作都定向到本地,这时就不会产生流量了。
7、Android下的开源防火墙项目droidwall
http://code.google.com/上搜索:droidwall可以下到代码
项目路径:
https://code.google.com/p/droidwall/
用SVN下载droidwall地址:
http://droidwall.googlecode.com/svn/
单位时间内的流量如何计算,用到定时器,不断去更新就可以了。
流量统计工具,比较耗电的。因为需要经常更新数据;
两个开源网站:
https://github.com/
http://code.google.com/
www.eoeandroid.cn
www.itlanbao.com
1、参照金山的抽屉效果效果。基于主页面布局文件创建布局文件activity_traffic_manager.xml,标题改为流量统计;
2、在布局文件里使用新控件Slidingdrawer(活动抽屉)
<SlidingDrawer
android:id="@+id/slidingdrawer01"
android:layout_width="match_parent"
android:layout_height="match_parent" >
SlidingDrawer>
预览是报错:下面的类不能被发现
需要配置把手和内容
<SlidingDrawer
android:handle="@+id/myHandle"
android:content="@+id/myContent"
android:id="@+id/slidingdrawer01"
android:layout_width="match_parent"
android:layout_height="match_parent" >
SlidingDrawer>
预览还是报错
Exception raised during rendering: The handle attribute is must refer to an existing child.
Exception details are logged in Window > Show View > Error Log
在渲染过程中引发的异常:手柄属性必须引用现有的孩子。
异常详细信息登录窗口>显示视图>错误日志
需要定义孩子
<SlidingDrawer
android:id="@+id/slidingdrawer01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:content="@+id/myContent"
android:handle="@+id/myHandle" >
<ImageView
android:id="@id/myHandle"
android:src="@drawable/lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
SlidingDrawer>
还是报错
Exception raised during rendering: The content attribute is must refer to an existing child.
在渲染过程中引发的异常:内容属性是必须引用现有的孩子。
定义内容
<SlidingDrawer
android:id="@+id/slidingdrawer01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:content="@+id/myContent"
android:handle="@+id/myHandle" >
<ImageView
android:id="@id/myHandle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/lock" />
<LinearLayout
android:id="@id/myContent"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是抽屉的内容" />
LinearLayout>
SlidingDrawer>
3、在主页面设置点击事件,进入流量统计页面;
Intent trafficIntent = new Intent(HomeActivity.this,
TrafficManagerActivity.class);
startActivity(trafficIntent);
运行才能拉动抽屉;
4、抽屉内容加上背景,效果要明显一些
android:background="#22000000"
5、把抽屉做成从右向左拉
在滑动抽屉SlidingDrawer里加上
android:orientation="horizontal"
就可以了
6、实现腾讯抽屉竖直方向显示一小半功能;
xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="fill_parent"
android:layout_height="55dip"
android:background="#8866ff00"
android:gravity="center"
android:text="流量统计"
android:textColor="#000000"
android:textSize="22sp" />
android:layout_width="fill_parent"
android:layout_height="200dip"
/>
<SlidingDrawer
android:orientation="vertical"
android:id="@+id/slidingdrawer01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:content="@+id/myContent"
android:handle="@+id/myHandle" >
<ImageView
android:id="@id/myHandle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/lock" />
<LinearLayout
android:id="@id/myContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#22000000"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是抽屉的内容" />
LinearLayout>
SlidingDrawer>
LinearLayout>
7、水平方向显示一小半
xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="fill_parent"
android:layout_height="55dip"
android:background="#8866ff00"
android:gravity="center"
android:text="流量统计"
android:textColor="#000000"
android:textSize="22sp" />
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<View
android:layout_width="200dip"
android:layout_height="match_parent" />
<SlidingDrawer
android:id="@+id/slidingdrawer01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:content="@+id/myContent"
android:handle="@+id/myHandle"
android:orientation="horizontal" >
<ImageView
android:id="@id/myHandle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/lock" />
<LinearLayout
android:id="@id/myContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#22000000"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是抽屉的内容" />
LinearLayout>
SlidingDrawer>
LinearLayout>
8、当小锁图片显示上面
xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="fill_parent"
android:layout_height="55dip"
android:background="#8866ff00"
android:gravity="center"
android:text="流量统计"
android:textColor="#000000"
android:textSize="22sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<View
android:layout_width="200dip"
android:layout_height="match_parent" />
<SlidingDrawer
android:id="@+id/slidingdrawer01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:content="@+id/myContent"
android:handle="@+id/myHandle"
android:orientation="horizontal" >
android:id="@id/myHandle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/lock" />
<LinearLayout
android:id="@id/myContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#22000000"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是抽屉的内容" />
LinearLayout>
SlidingDrawer>
LinearLayout>
LinearLayout>