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>
查看系统设置源码, 查看清理缓存逻辑
- 导入Setting源码
查找清除缓存的逻辑
Clear cache->clear_cache_btn_text->installed_app_details->InstalledAppDetails->cache_size_text-> mAppEntry.cacheSize->stats.cacheSize->stats->mStatsObserver->getPackageSizeInfo->查看PackageManager源码,跟踪方法getPackageSizeInfo,发现改方法隐藏
通过反射方式,调用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>
小锁图片显示上面