手机卫士12 13

1.已加锁和未加锁条目点击事件处理


//添加动画效果,动画默认是非阻塞的,所以执行动画的同时,动画以下的代码也会执行
animationView.startAnimation(mTranslateAnimation);//500毫秒
//对动画执行过程做事件监听,监听到动画执行完成后,再去移除集合中的数据,操作数据库,刷新界面
mTranslateAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始的是调用方法
}

@Override
public void onAnimationRepeat(Animation animation) {
//动画重复时候调用方法
}

//动画执行结束后调用方法
@Override
public void onAnimationEnd(Animation animation) {
if(isLock){
//已加锁------>未加锁过程
//1.已加锁集合删除一个,未加锁集合添加一个,对象就是getItem方法获取的对象
mLockList.remove(appInfo);
mUnLockList.add(appInfo);
//2.从已加锁的数据库中删除一条数据
mDao.delete(appInfo.packageName);
//3.刷新数据适配器
mLockAdapter.notifyDataSetChanged();
}else{
//未加锁------>已加锁过程
//1.已加锁集合添加一个,未加锁集合移除一个,对象就是getItem方法获取的对象
mLockList.add(appInfo);
mUnLockList.remove(appInfo);
//2.从已加锁的数据库中插入一条数据
mDao.insert(appInfo.packageName);
//3.刷新数据适配器
mUnLockAdapter.notifyDataSetChanged();
}
}
});
2.程序锁必须在服务中去维护
1.判断当前手机正在开启的应用(现在手机可见任务栈)
2.如果开启的应用在已加锁的列表中,弹出拦截界面
3.看门狗服务,一直(死循环(子线程,可控))对开启的应用做监听




问题:
1,栈


2,过滤已经解锁应用(发送广播)


3,开启服务后,再去添加加锁应用,不能生效
内容观察者,观察数据库变化,一旦数据库变化,则放置包名的集合需要重新获取数据


4,挂起输入密码(拦截)界面的时候,不需要去显示手机卫士的图标


5,回退按钮处理,在输入密码的activity中点击回退按钮需要回到桌面()



## Day13 ##
- 新建工程,获取缓存大小


- 布局文件开发


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >

   <EditText
       android:id="@+id/et_package"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:hint="请输入包名" >
   </EditText>

   <Button
       android:id="@+id/btn_ok"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="确定" />

   <TextView
       android:id="@+id/tv_result"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="查询结果" />

</LinearLayout>


- 查看系统设置源码, 查看清理缓存逻辑


1. 导入Setting源码
2. 查找清除缓存的逻辑


Clear cache->clear_cache_btn_text->installed_app_details->InstalledAppDetails->cache_size_text-> mAppEntry.cacheSize->stats.cacheSize->stats->mStatsObserver->getPackageSizeInfo->查看PackageManager源码,跟踪方法getPackageSizeInfo,发现改方法隐藏


3. 通过反射方式,调用PackageManager的方法


public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。


public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。


//需要权限:android.permission.GET_PACKAGE_SIZE


btnOk.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
String packageName = etPackage.getText().toString().trim();
if (!TextUtils.isEmpty(packageName)) {
PackageManager pm = getPackageManager();
try {
Method method = pm.getClass().getMethod(
"getPackageSizeInfo", String.class,
IPackageStatsObserver.class);
method.invoke(pm, packageName, new MyObserver());
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(getApplicationContext(), "输入内容不能为空!",
Toast.LENGTH_SHORT).show();
}
}
});


-------------------------------------


class MyObserver extends IPackageStatsObserver.Stub {


// 在子线程运行
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
throws RemoteException {
long cacheSize = pStats.cacheSize;
long dataSize = pStats.dataSize;
long codeSize = pStats.codeSize;

String result = "缓存:"
+ Formatter.formatFileSize(getApplicationContext(),
cacheSize)
+ "\n"
+ "数据:"
+ Formatter.formatFileSize(getApplicationContext(),
dataSize)
+ "\n"
+ "代码:"
+ Formatter.formatFileSize(getApplicationContext(),
codeSize);
System.out.println(result);
Message msg = Message.obtain();
msg.obj = result;
mHandler.sendMessage(msg);
}
}




- 缓存清理模块开发


- 新建页面CleanCacheActivity 
- 布局文件开发


<?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" >
   <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="50dp"
       android:background="#8866ff00" >

       <TextView
           android:id="@+id/textView1"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_centerVertical="true"
           android:layout_marginLeft="5dp"
           android:text="缓存清理"
           android:textColor="#000"
           android:textSize="22sp" />
       <Button
           android:id="@+id/button1"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentRight="true"
           android:layout_centerVertical="true"
           android:layout_marginRight="5dp"
           android:onClick="cleanCache"
           android:text="立即清理" />
   </RelativeLayout>
   <ProgressBar
       android:id="@+id/pb_progress"
       style="?android:attr/progressBarStyleHorizontal"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:progressDrawable="@drawable/custom_progress" />
   <TextView
       android:id="@+id/tv_status"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="正在扫描:" />
   <ScrollView
       android:layout_width="match_parent"
       android:layout_height="match_parent" >

       <LinearLayout
           android:id="@+id/ll_container"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="vertical" >
       </LinearLayout>
   </ScrollView>
</LinearLayout>


- 缓存页面逻辑


private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case SCANNING:
String name = (String) msg.obj;
tvStatus.setText("正在扫描:" + name);
break;
case SHOW_CACHE_INFO:
CacheInfo info = (CacheInfo) msg.obj;
View itemView = View.inflate(getApplicationContext(),
R.layout.list_cacheinfo_item, null);
TextView tvName = (TextView) itemView
.findViewById(R.id.tv_name);
ImageView ivIcon = (ImageView) itemView
.findViewById(R.id.iv_icon);
TextView tvCache = (TextView) itemView
.findViewById(R.id.tv_cache_size);
ImageView ivDelete = (ImageView) itemView
.findViewById(R.id.iv_delete);

tvName.setText(info.name);
ivIcon.setImageDrawable(info.icon);
tvCache.setText("缓存大小:"
+ Formatter.formatFileSize(getApplicationContext(),
info.cacheSize));

llContainer.addView(itemView);
break;
case SCANNING_FINISHED:
tvStatus.setText("扫描完成");
break;

default:
break;
}
};
};


/**
* 开始扫描
*/
private void startScan() {
new Thread() {
@Override
public void run() {
List<PackageInfo> packages = mPM
.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);

pbProgress.setMax(packages.size());// 设置进度条最大值为安装包的数量
int progress = 0;
for (PackageInfo packageInfo : packages) {
try {
Method method = mPM.getClass().getMethod(
"getPackageSizeInfo", String.class,
IPackageStatsObserver.class);
method.invoke(mPM, packageInfo.packageName,
new MyObserver());
} catch (Exception e) {
e.printStackTrace();
}

progress++;
pbProgress.setProgress(progress);

// 发送更新进度的消息
Message msg = Message.obtain();
msg.what = SCANNING;
msg.obj = packageInfo.applicationInfo.loadLabel(mPM)
.toString();
mHandler.sendMessage(msg);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

// 发送扫描结束的消息
mHandler.sendEmptyMessage(SCANNING_FINISHED);
}
}.start();
}

class MyObserver extends IPackageStatsObserver.Stub {

// 在子线程运行
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
throws RemoteException {
long cacheSize = pStats.cacheSize;// 获取缓存大小
if (cacheSize > 0) {
try {
CacheInfo info = new CacheInfo();
String packageName = pStats.packageName;
info.packageName = packageName;

ApplicationInfo applicationInfo = mPM.getApplicationInfo(
packageName, 0);
info.name = applicationInfo.loadLabel(mPM).toString();
info.icon = applicationInfo.loadIcon(mPM);
info.cacheSize = cacheSize;

// 扫描到缓存应用时发送消息
Message msg = Message.obtain();
msg.what = SHOW_CACHE_INFO;
msg.obj = info;
mHandler.sendMessage(msg);

} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
}
}


// 缓存对象的封装
class CacheInfo {
public String name;
public String packageName;
public Drawable icon;
public long cacheSize;
}


-------------------------


list_cacheinfo_item.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:padding="5dp" >

   <ImageView
       android:id="@+id/iv_icon"
       android:layout_width="40dp"
       android:layout_height="40dp"
       android:layout_alignParentLeft="true"
       android:layout_alignParentTop="true"
       android:src="@drawable/ic_launcher" />

   <TextView
       android:id="@+id/tv_name"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentTop="true"
       android:layout_marginLeft="20dp"
       android:layout_toRightOf="@+id/iv_icon"
       android:text="应用名称"
       android:textColor="#000"
       android:textSize="16sp" />

   <TextView
       android:id="@+id/tv_cache_size"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignLeft="@+id/tv_name"
       android:layout_below="@id/tv_name"
       android:layout_marginTop="5dp"
       android:text="缓存大小:"
       android:textColor="#000"
       android:textSize="16sp" />

   <ImageView
       android:id="@+id/iv_delete"
       android:layout_width="45dp"
       android:layout_height="45dp"
       android:layout_alignParentRight="true"
       android:layout_centerVertical="true"
       android:src="@drawable/btn_black_number_delete_selector" />

</RelativeLayout>


- 一键清理缓存


这是用的Android的一个BUG,就是你得程序去申请很大的内存,比如直接申请10G,但是你得内存总共才1G,这时候系统为了满足你得要求,会去全盘清理缓存,清理完了发现还是达不到你得要求,那么就返回失败!!!! 但是,我们的目的已经达成,就是要让他去清理全盘缓存


/**
* 一键清理

* @param view
*/
public void cleanAllCache(View view) {
try {
// 通过反射调用freeStorageAndNotify方法, 向系统申请内存
Method method = mPM.getClass().getMethod(
"freeStorageAndNotify", long.class,
IPackageDataObserver.class);
// 参数传Long最大值, 这样可以保证系统将所有app缓存清理掉
method.invoke(mPM, Long.MAX_VALUE, new IPackageDataObserver.Stub() {

@Override
public void onRemoveCompleted(String packageName,
boolean succeeded) throws RemoteException {
System.out.println("flag==" + succeeded);
System.out.println("packageName=" + packageName);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

-清理特定app缓存


查看Setting源码,分析清除缓存按钮的逻辑


实现代码:


/**
* 删除单个文件的缓存 需要权限:<uses-permission
* android:name="android.permission.DELETE_CACHE_FILES"/>

* @param packageName
*/
private void deleteCache(String packageName) {
try {
Method method = mPM.getClass().getMethod(
"deleteApplicationCacheFiles", String.class,
IPackageDataObserver.class);
method.invoke(mPM, packageName, new IPackageDataObserver.Stub() {
@Override
public void onRemoveCompleted(String packageName,
boolean succeeded) throws RemoteException {
System.out.println("succeeded" + succeeded);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}


注意加权限: <uses-permission android:name="android.permission.DELETE_CACHE_FILES"/>


知识拓展:
加上权限后仍然报错


- 跳转到某个系统应用界面清除缓存


1. 看一下腾讯管家跳转到系统应用界面时的日志,并且对于Settings源代码说明意图;

2. 代码实现:
//启动到某个系统应用页面
Intent intent = new Intent();
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.addCategory(Intent.CATEGORY_DEFAULT);//有无没影响
intent.setData(Uri.parse("package:"+cacheInfo.packName));
startActivity(intent);
- 流量统计


- 流量统计介绍, pc网络连接流量展示(已发送,已接受)
- 连接真机,查看文件proc/uid_stat,发现该目录下有很多以uid命名的文件夹
- 用户id是安装应用程序的时候 操作系统赋给应用程序的
- 获取uid方式


1. 进入AppInfoProvider, 
String name = packInfo.applicationInfo.loadLabel(pm).toString()+ packInfo.applicationInfo.uid;
2. 将应用名称和uid拼成一个字符串输出,真机查看主流应用(微信,QQ)的uid
3. 例如 QQ:10083, 进入QQ(10083)目录命令:cd 10083
4. 进入QQ10083:cd 10110
下载:168251
上传:23544
  tcp_rcv :代码下载的数据
  tcp_snd:代表上传的数据


- 手机安装360, 验证流量准确性
- 创建TrafficeManagerActivity
- 流量统计的api介绍


TrafficStats.getMobileRxBytes();// 3g/2g下载总流量
TrafficStats.getMobileTxBytes();// 3g/2g上传总流量

TrafficStats.getTotalRxBytes();// wifi+手机下载流量
TrafficStats.getTotalTxBytes();// wifi+手机上传总流量

TrafficStats.getUidRxBytes(10085);// 某个应用下载流量
TrafficStats.getUidTxBytes(10085);// 某个应用上传流量


这里需要注意的是,通过 TrafficStats 获取的数据在手机重启的时候会被清空,所以,如果要对流量进行持续的统计需要将数据保存到数据库中,在手机重启时将数据读出进行累加即可


- 流量报警原理简介


流量校准的工作原理就给运营商发短信


A:开启超额提醒
B:设置每月流量套餐300MB
  C:自动校准流量-流量短信设置
  D:演示发短信给运营商;


- 联网防火墙简介


在linux上有一款强大的防火墙软件iptable
360就是把这款软件内置了
如果手机有root权限,把防火墙软件装到手机的内部,并且开启起来。
以后就可以拦截某个应用程序的联网了。

如果允许某个软件上网就什么也不做。如果不允许某个软件上网,就把这个软件的所有的联网操作都定向到本地,这时就不会产生流量了。


- Android下的开源防火墙项目droidwall


登录code.google.com,搜索droidwall
svn地址: https://droidwall.googlecode.com/svn/
svn检出, 需要翻墙


常用开源代码网站: github.com, code.google.com


- 抽屉效果 SlidingDrawer


- 基本实现

   <SlidingDrawer
       android:id="@+id/slidingDrawer1"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:content="@+id/content"
       android:handle="@+id/handle" >

//指定抽屉把手
       <ImageView
           android:id="@id/handle"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:src="@drawable/lock" />

//指定抽屉内容
       <LinearLayout
           android:id="@id/content"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:background="#9e9e9e"
           android:gravity="center" >

           <TextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="我是小抽屉" />
       </LinearLayout>
   </SlidingDrawer>


- 把抽屉做成从右向左拉


android:orientation="horizontal"


- 实现腾讯抽屉竖直方向显示一小半功能


只需在抽屉上方增加一个空view


 <View
        android:layout_width="match_parent"
        android:layout_height="200dp" />


- 水平方向显示一小半


 <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="horizontal" >

       <View
           android:layout_width="100dp"
           android:layout_height="match_parent" />

       <SlidingDrawer
           android:id="@+id/slidingDrawer1"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:orientation="horizontal"
           android:content="@+id/content"
           android:handle="@+id/handle" >

           <ImageView
               android:id="@id/handle"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:src="@drawable/lock" />

           <LinearLayout
               android:id="@id/content"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:background="#9e9e9e"
               android:gravity="center" >

               <TextView
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:text="我是小抽屉" />
           </LinearLayout>
       </SlidingDrawer>
   </LinearLayout>


- 小锁图片显示上面


 <LinearLayout
                android:id="@id/handle"
                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>


- TabActivity的使用(知识拓展)


- 创建CleanActivity, 继承TabActivity
- 编写布局文件


<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@android:id/tabhost"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical" >

//内容体
       <FrameLayout
           android:id="@android:id/tabcontent"
           android:layout_width="match_parent"
           android:layout_height="0dp"
           android:layout_weight="1" >
       </FrameLayout>

//标签体
       <TabWidget
           android:id="@android:id/tabs"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" >
       </TabWidget>
   </LinearLayout>

</TabHost>


------------------------------------


/**
* 缓存清理主页面

* @author Kevin

*/
public class CleanActivity extends TabActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clean_base);

TabHost host = getTabHost();

TabSpec tab1 = host.newTabSpec("缓存清理").setIndicator("缓存清理");
TabSpec tab2 = host.newTabSpec("SD卡清理").setIndicator("SD卡清理");

tab1.setContent(new Intent(this, CleanCacheActivity.class));
tab2.setContent(new Intent(this, CleanSdcardActivity.class));

host.addTab(tab1);
host.addTab(tab2);
}
}


- sdcard清理


- 查看金山缓存文件夹数据库clearpath.db
- 原理介绍


1. 查询数据库中的所有缓存文件目录
2. 如果文件夹存在, 执行删除操作

- 自定义Application


- 写一个demo


/**
* 自定义全局Application 应用全局的初始化逻辑可以放在此处运行
* 这是一个单例类, 整个应用只有一个
* @author Kevin
*/
public class MyApplication extends Application {

@Override
public void onCreate() {
super.onCreate();
System.out.println("MyApplication onCreate");
}

public void doSomething() {
System.out.println("doSomething....");
}

}


----------------------


public class MainActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("MainActivity onCreate");

MyApplication app = (MyApplication) getApplication();// 获取自定义的Application
app.doSomething();
}
}


----------------------


清单文件中配置


 <application
        android:name=".MyApplication"




- 手机卫士自定义Application


MobileSafeApplication


- 全局捕获异常


- 模拟异常, 比如int i = 1/0, 演示崩溃情况
- 代码实现

/**
* 自定义全局Application

* @author Kevin

*/
public class MobileSafeApplication extends Application {

@Override
public void onCreate() {
super.onCreate();

// 设置未捕获异常处理器
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
}

class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {

// 未捕获的异常都会走到此方法中
// Throwable是Exception和Error的父类
@Override
public void uncaughtException(Thread thread, Throwable ex) {
System.out.println("产生了一个未处理的异常, 但是被哥捕获了...");
// 将异常日志输入到本地文件中, 找机会上传到服务器,供技术人员分析
File file = new File(Environment.getExternalStorageDirectory(),
"error.log");
try {
PrintWriter writer = new PrintWriter(file);
ex.printStackTrace(writer);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}

// 结束当前进程
android.os.Process.killProcess(android.os.Process.myPid());
}
}
}

- 代码混淆


- 代码未混淆的前提下,打包,并进行反编译, 发现源码都可以看到, 很不安全
- 找到项目根目录下的文件project.properties, 打开混淆注释

proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt


- 分析文件proguard-android.txt
- 将proguard-android.txt文件拷贝到项目根目录,方便以后修改


proguard.config=proguard-android.txt:proguard-project.txt


- 重新打包并反编译,查看效果
- 结论: 混淆后,会将类名,方法名编译成a,b,c,d等混乱的字母, 提高代码阅读成本,增强安全性


- 嵌入广告


- 分析app, 广告公司, 广告平台的关系


广告平台相当于中间商, 是app和广告公司的媒介, 抽成盈利


- 盈利方式


- 展示次数, 1000次 1毛5左右 , 1分钟展示3条广告
- 点击次数, 1次 1毛5左右
- 有效点击, 1次 1元左右


- 广告公司


有米, 百度, 360, 万普, panda


- 国外广告公司


StartApp



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值