获取本应用和全部app缓存以及清理全解
来源:互联网 发布:android 判断移动网络 编辑:程序博客网 时间:2024/05/12 04:20
转载注明出处:http://blog.csdn.net/u014239140/article/details/53239797
纵观Aandroid 应用市场App简直是泛滥了,对于app的要求也是越来越高了,对于app的缓存和清理都是屡见不鲜的一项功能了,今天的主题就是解决获取app的缓存和清理工作做下讲解,做过开发的老手这方面肯定不是什么问题了,这篇文章主要是对刚接触不久的童鞋们准备的,说老实的我刚接触Android时,第一次做项目就有这样一项需求,也是折腾了一两天,才搞出来,今天如果你看了这篇文章,就可以大大省出这部分时间了,当然聪明的你可能比我厉害,说不定一两个小时就搞出来了!嘿嘿,反正我是觉得我笨笨的。
好了回归主题,首先讲下获取当前app的缓存和清理,这部分稍微简单的多。
先来看下呆萌吧:
已经接触android的你,android的缓存目录你肯定是知道的吧,还是提下目录结构:/data/data/package_name/cache 是不是看到很熟悉,对如果你的缓存没手动移动到sd上,那当前的app的缓存就在该目录下,你只需清除这下面的东西是不是就该可以了?android在2.2版本后是不是就有将缓存移动到sd上的功能了,当然只做了前一步还是不够的,那我们是不是还得把/data/data/package_name/files这下面的东西给干掉,恩,是的!
我们知道了缓存的位置了大致在哪里,然而在屏幕上是不是显示的缓存大小啊,这么办?是不是要计算大小啊,好吧,小算法来了! 怎么算呢?首先是不是的获取到缓存目录长度,然后才开始计算啊!恩,是这样的,那就先这样搞下吧!
怎么写才方便以后使用呢,是不是支持可以供外部使用,当完全写完后做下封装,以后这样的功能是不是就不用再写,O(∩_∩)O哈哈~!
这里简单的封装了一个工具类,在需要的地方创建一个对象就开始调用方法是我最喜欢的,在View层上看起来也最直观,不会有看起来臃肿的感觉。
package com.zzh.appcachesizeclear.LocaAppUtil;import android.content.Context;import java.io.File;/** * Created by zhengzaihong on 2016/11/20 0020. */public class LocaUtil { private Context context; public LocaUtil(Context context){ this.context=context; } /** * 计算缓存的大小, * * @return */ public String getCacheSize() { long fileSize = 0; String cacheSize = "0KB"; File filesDir = context.getApplicationContext().getFilesDir();// /data/data/package_name/files File cacheDir = context.getCacheDir();// /data/data/package_name/cache fileSize += getDirSize(filesDir); fileSize += getDirSize(cacheDir); // 2.2版本才有将应用缓存转移到sd卡的功能 if (isMethodsCompat(android.os.Build.VERSION_CODES.FROYO)) { File externalCacheDir = getExternalCacheDir(context);// "<sdcard>/Android/data/<package_name>/cache/" fileSize += getDirSize(externalCacheDir); } if (fileSize > 0) cacheSize = formatFileSize(fileSize); return cacheSize; } /** * 清除app缓存 */ public void clearAppCache() { // 清除数据缓存 clearCacheFolder(context.getFilesDir(), System.currentTimeMillis()); clearCacheFolder(context.getCacheDir(), System.currentTimeMillis()); // 2.2版本才有将应用缓存转移到sd卡的功能 if (isMethodsCompat(android.os.Build.VERSION_CODES.FROYO)) { clearCacheFolder(getExternalCacheDir(context), System.currentTimeMillis()); } } /** * 清除缓存目录 * * @param dir 目录 * @param curTime 当前系统时间 * @return */ private int clearCacheFolder(File dir, long curTime) { int deletedFiles = 0; if (dir != null && dir.isDirectory()) { try { for (File child : dir.listFiles()) { if (child.isDirectory()) { deletedFiles += clearCacheFolder(child, curTime); } if (child.lastModified() < curTime) { if (child.delete()) { deletedFiles++; } } } } catch (Exception e) { e.printStackTrace(); } } return deletedFiles; } /** * 获取目录文件大小 * * @param dir * @return */ public static long getDirSize(File dir) { if (dir == null) { return 0; } if (!dir.isDirectory()) { return 0; } long dirSize = 0; File[] files = dir.listFiles(); for (File file : files) { if (file.isFile()) { dirSize += file.length(); } else if (file.isDirectory()) { dirSize += file.length(); dirSize += getDirSize(file); // 递归调用继续统计 } } return dirSize; } /** * 将二进制长度转换成文件大小 * * @param length * @return */ public static String formatFileSize(long length) { String result = null; int sub_string = 0; if (length >= 1073741824) { sub_string = String.valueOf((float) length / 1073741824).indexOf( "."); result = ((float) length / 1073741824 + "000").substring(0, sub_string + 3) + "GB"; } else if (length >= 1048576) { sub_string = String.valueOf((float) length / 1048576).indexOf("."); result = ((float) length / 1048576 + "000").substring(0, sub_string + 3) + "MB"; } else if (length >= 1024) { sub_string = String.valueOf((float) length / 1024).indexOf("."); result = ((float) length / 1024 + "000").substring(0, sub_string + 3) + "KB"; } else if (length < 1024) result = Long.toString(length) + "B"; return result; } /** * 判断当前版本是否兼容目标版本的方法 * * @param VersionCode * @return */ public static boolean isMethodsCompat(int VersionCode) { int currentVersion = android.os.Build.VERSION.SDK_INT; return currentVersion >= VersionCode; } public static File getExternalCacheDir(Context context) { // return context.getExternalCacheDir(); API level 8 // e.g. "<sdcard>/Android/data/<package_name>/cache/" return context.getExternalCacheDir(); }}
好了,上面就是我们的获取当前app缓存以及清理的工具类了。那怎么使用呢?简单的很,看下面!
先创建一个工具类对象出来:
LocaUtil locaUtil = new LocaUtil(this);
获取缓存大小使用方法:locaUtil.getCacheSize();
清除缓存大小方法:
locaUtil.clearAppCache();
嘿嘿,这样是不是感觉黑爽啊!提下,有的人喜欢使用静态方法,于是就把很多方法整成静态了的,并往静态方法里传入上下文和其他监听什么的,那么问题就来了,比如你的Activity使用了该方法,往该方法里传入了上下文和监听对象,用上去感觉是很爽的样子,其实这里有个潜在的危险,你声明的static方法,一般是比你Activity的生命周期更长,假入你当退出了该Activity的时候,系统要回收掉该Activity,然而你的静态方法却还持有Activity的引用,其实该组件就不能被回收掉,如果大量的这样使用,可想而知,内存肯定会泄露了!好了! 获取当前应用的缓存以及清理工作就结束了!下面看下来获取系统的缓存以及清理工作吧!
《*******************************************华丽的分割线*************************************》
获取系统的缓存,很多人肯定都遇到过问题,Android的API里没这玩意呀。那怎么搞!其实是有的,只是该方法被系统给hide了,嘿嘿!想一下,有什么解决办法没有可以使用呢?立马就想到了反射机制,对,没错就是这样的!
由于需要获得系统级的服务或类,我们必须加入Android系统形成的AIDL文件,共三个:IPackageStatsObserver.aidl ,IPackageDataObserver.aidl和 PackageStats.aidl文件。并将其放android.content.pm包路径下。
IPackageStatsObserver.aidl 这样写:
package android.content.pm;import android.content.pm.PackageStats;/** * API for package data change related callbacks from the Package Manager. * Some usage scenarios include deletion of cache directory, generate * statistics related to code, data, cache usage(TODO) * {@hide} */oneway interface IPackageStatsObserver { void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);}
IPackageDataObserver.aidl写法: package android.content.pm; /** * API for package data change related callbacks from the Package Manager. * Some usage scenarios include deletion of cache directory, generate * statistics related to code, data, cache usage(TODO) * {@hide} */ oneway interface IPackageDataObserver { void onRemoveCompleted(in String packageName, boolean succeeded); }
PackageStats.aidl写法:package android.content.pm;parcelable PackageStats;
好了我们的AIDL就写完了,顺便说下AIDL是什么?其实就是android的接口定义语言准备工作做完了,下面就来考虑怎么写,以后方便使用呢?我这里就整成一个Service,以后绑定这个Service实现相应的监听就可以得到应有的数据和结果,这样挺方便的,下面先来个Presenter辅助类,减少Activit中的代码下! 以前写代码都是喜欢MVC模式来搞,这样写起来爽,后期维护可能就有点坑了,代码量上来,可能耦合性就非常高了,怎么避免,可以采用MVP模式了,好处就是有效的解决了耦合性,弊端就是你要创建大量的Java类了。有利有弊,自己选择了!
这个类比较简单,就不做过多介绍了!
package com.zzh.appcachesizeclear.Presenter;import android.app.Activity;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import com.zzh.appcachesizeclear.Services.CleanerService;/** * Created by zhengzaihong on 2016/11/20 0020. */public class BindConnectionPresenter{ private Activity context; private ServiceConnection mServiceConnection; private CleanerService mCleanerService; public BindConnectionPresenter(Activity context){ this.context=context; initServiceConnection(); } //创建ServiceConnection用于绑定Service public void initServiceConnection(){ mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mCleanerService = ((CleanerService.CleanerServiceBinder) service).getService(); //添加绑定状态回调 bindServiceLisetener.OnSuccess(mCleanerService); //绑定Service处理的监听 mCleanerService.setOnActionListener((CleanerService.OnActionListener) context); } @Override public void onServiceDisconnected(ComponentName name) { mCleanerService.setOnActionListener(null); mCleanerService = null; } }; } //绑定Service public ServiceConnection bindService(){ context.bindService(new Intent(context, CleanerService.class), mServiceConnection, Context.BIND_AUTO_CREATE); return mServiceConnection; } //解绑 public void unBindService(){ context.unbindService(mServiceConnection); } private BindServiceStatusListener bindServiceLisetener; //设置绑定事件的监听结果 public void setBindServiceLisetener(BindServiceStatusListener bindServiceLisetener){ if(null != bindServiceLisetener){ this.bindServiceLisetener=bindServiceLisetener; } } //定义一个接口 public interface BindServiceStatusListener{ void OnSuccess(CleanerService cleanerService); }}
这步就完成了,下面黑心的东西就来了,这个里面东西稍微多点,关键处打上了注释
package com.zzh.appcachesizeclear.Services;import android.app.Service;import android.content.Context;import android.content.Intent;import android.content.pm.ApplicationInfo;import android.content.pm.IPackageDataObserver;import android.content.pm.IPackageStatsObserver;import android.content.pm.PackageManager;import android.content.pm.PackageStats;import android.graphics.drawable.Drawable;import android.os.AsyncTask;import android.os.Binder;import android.os.Environment;import android.os.Handler;import android.os.IBinder;import android.os.RemoteException;import android.os.StatFs;import android.os.UserHandle;import android.text.format.Formatter;import android.util.Log;import android.widget.Toast;import com.zzh.appcachesizeclear.R;import com.zzh.appcachesizeclear.Bean.CacheListItem;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;import java.util.concurrent.CountDownLatch;public class CleanerService extends Service { public static final String ACTION_CLEAN_AND_EXIT = "CLEAN_AND_EXIT"; private static final String TAG = "CleanerService"; private Method mGetPackageSizeInfoMethod, mFreeStorageAndNotifyMethod; private PackageManager pm; private OnActionListener mOnActionListener; private boolean mIsScanning = false; private boolean mIsCleaning = false; private long mCacheSize = 0; public static interface OnActionListener { public void onScanStarted(Context context); public void onScanProgressUpdated(Context context, int current, int max); public void onScanCompleted(Context context, List<CacheListItem> apps); public void onCleanStarted(Context context); public void onCleanCompleted(Context context, long cacheSize); } public class CleanerServiceBinder extends Binder { public CleanerService getService() { return CleanerService.this; } } private CleanerServiceBinder mBinder = new CleanerServiceBinder(); private class TaskClean extends AsyncTask<Void, Void, Long> { @Override protected void onPreExecute() { if (mOnActionListener != null) { mOnActionListener.onCleanStarted(CleanerService.this); } } @Override protected Long doInBackground(Void... params) { final CountDownLatch countDownLatch = new CountDownLatch(1); StatFs stat = new StatFs(Environment.getDataDirectory().getAbsolutePath()); try { mFreeStorageAndNotifyMethod.invoke(getPackageManager(), (long) stat.getBlockCount() * (long) stat.getBlockSize(), new IPackageDataObserver.Stub() { @Override public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException { countDownLatch.countDown(); } } ); countDownLatch.await(); } catch (InvocationTargetException | InterruptedException | IllegalAccessException e) { e.printStackTrace(); } return mCacheSize; } @Override protected void onPostExecute(Long result) { mCacheSize = 0; if (mOnActionListener != null) { mOnActionListener.onCleanCompleted(CleanerService.this, result); } mIsCleaning = false; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { try { //public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。 //public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。 pm = getPackageManager(); //得到pm对象 Class<?> loadClass=getClassLoader().loadClass("android.content.pm.PackageManager"); //通过反射机制获得该隐藏函数 4.2之前反射的写法 mGetPackageSizeInfoMethod = pm.getClass().getMethod("getPackageSizeInfo", String.class,IPackageStatsObserver.class); // mGetPackageSizeInfoMethod = pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class,int.class,IPackageStatsObserver.class); //通过反射机制获得该隐藏函数 4.2之后写法 新增UserHandle这个类 //Method myUserId = UserHandle.class.getDeclaredMethod("myUserId"); //int userID = (Integer) myUserId.invoke(pm,null); //mGetPackageSizeInfoMethod.invoke(pm, pkgName, userID, new IPackageStatsObserver.Stub()); // mGetPackageSizeInfoMethod = pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class, int.class, IPackageStatsObserver.class); mFreeStorageAndNotifyMethod = pm.getClass().getMethod("freeStorageAndNotify", long.class, IPackageDataObserver.class); // mFreeStorageAndNotifyMethod = pm.getClass().getMethod("freeStorageAndNotify", long.class,int.class, IPackageDataObserver.class); } catch (NoSuchMethodException e) { e.printStackTrace(); } } private class TaskScan extends AsyncTask<Void, Integer, List<CacheListItem>> { private int mAppCount = 0; @Override protected void onPreExecute() { if (mOnActionListener != null) { mOnActionListener.onScanStarted(CleanerService.this); } } @Override protected List<CacheListItem> doInBackground(Void... params) { mCacheSize = 0; final List<ApplicationInfo> packages = getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA); publishProgress(0, packages.size()); final CountDownLatch countDownLatch = new CountDownLatch(packages.size()); final List<CacheListItem> apps = new ArrayList<CacheListItem>(); try { Method myUserId = UserHandle.class.getDeclaredMethod("myUserId"); //int userID = (Integer) myUserId.invoke(pm,null); for (ApplicationInfo pkg : packages) { //4.2之前的写法 mGetPackageSizeInfoMethod.invoke(pm, pkg.packageName, new IPackageStatsObserver.Stub() { //4.2之后的写法 // mGetPackageSizeInfoMethod.invoke(pm, pkg.packageName,userID, new IPackageStatsObserver.Stub() { @Override public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException { synchronized (apps) { publishProgress(++mAppCount, packages.size()); if (succeeded && pStats.cacheSize > 0) { try { String packageName =pStats.packageName; String appname =getPackageManager().getApplicationLabel(getPackageManager().getApplicationInfo(pStats.packageName, PackageManager.GET_META_DATA)).toString(); Drawable icon =getPackageManager().getApplicationIcon(pStats.packageName); long cachsize =pStats.cacheSize; apps.add(new CacheListItem(packageName, appname, icon,cachsize)); mCacheSize += pStats.cacheSize; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } } synchronized (countDownLatch) { countDownLatch.countDown(); } } } ); } countDownLatch.await(); } catch (InvocationTargetException | InterruptedException | IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return new ArrayList<>(apps); } @Override protected void onProgressUpdate(Integer... values) { if (mOnActionListener != null) { mOnActionListener.onScanProgressUpdated(CleanerService.this, values[0], values[1]); } } @Override protected void onPostExecute(List<CacheListItem> result) { if (mOnActionListener != null) { mOnActionListener.onScanCompleted(CleanerService.this, result); } mIsScanning = false; } } @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getAction(); if (action != null) { if (action.equals(ACTION_CLEAN_AND_EXIT)) { setOnActionListener(new OnActionListener() { @Override public void onScanStarted(Context context) { } @Override public void onScanProgressUpdated(Context context, int current, int max) { } @Override public void onScanCompleted(Context context, List<CacheListItem> apps) { if (getCacheSize() > 0) { cleanCache(); } } @Override public void onCleanStarted(Context context) { } @Override public void onCleanCompleted(Context context, long cacheSize) { String msg = getString(R.string.cleaned, Formatter.formatShortFileSize(CleanerService.this, cacheSize)); Log.d(TAG, msg); Toast.makeText(CleanerService.this, msg, Toast.LENGTH_LONG).show(); new Handler().postDelayed(new Runnable() { @Override public void run() { stopSelf(); } }, 5000); } }); scanCache(); } } return START_NOT_STICKY; } public void scanCache() { mIsScanning = true; //开启异步任务开始扫描 new TaskScan().execute(); } public void cleanCache() { mIsCleaning = true; //开启异步任务开始清除 new TaskClean().execute(); } public void setOnActionListener(OnActionListener listener) { mOnActionListener = listener; } public boolean isScanning() { return mIsScanning; } public boolean isCleaning() { return mIsCleaning; } public long getCacheSize() { return mCacheSize; }}
代码已经很清晰简单了,是不是?这样写后就可以了?当然不是你还得在你的AndroidManifest.xml文件上把该服务给注册上!<service android:name=".Services.CleanerService" android:exported="false" />
添加必须的权限<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /><uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
然后来看下怎么使用呢?先来看下获取缓存在你需要的地方调用该方法就可以了!
public void scanCache(){ if (!mCleanerService.isScanning() && !mAlreadyScanned) { mCleanerService.scanCache(); }}
清除缓存:if (mCleanerService != null && !mCleanerService.isScanning() && !mCleanerService.isCleaning() && mCleanerService.getCacheSize() > 0) { mCleanerService.cleanCache();}
再来看下完整的代码吧!package com.zzh.appcachesizeclear;import android.content.Context;import android.text.format.Formatter;import android.view.View;import android.widget.TextView;import android.widget.Toast;import com.zzh.appcachesizeclear.Bean.CacheListItem;import com.zzh.appcachesizeclear.LocaAppUtil.LocaUtil;import com.zzh.appcachesizeclear.Presenter.BindConnectionPresenter;import com.zzh.appcachesizeclear.Services.CleanerService;import com.zzh.appcachesizeclear.Tools.StorageUtil;import com.zzh.appcachesizeclear.model.StorageSize;import java.util.List;public class MainActivity extends BaseActiviy implements CleanerService.OnActionListener,BindConnectionPresenter.BindServiceStatusListener { private TextView tv_curentapp; private TextView tv_clearthisapp; private TextView tv_allapp; private TextView tv_clearSysCache; private TextView tv_cachesize; private TextView tv_alllist; private TextView tv_allapplistsize; private LocaUtil locaUtil; private CleanerService mCleanerService; private BindConnectionPresenter presenter; private boolean mAlreadyScanned = false; @Override public void intview() { tv_curentapp = (TextView) findViewById(R.id.tv_curentapp); tv_allapp = (TextView) findViewById(R.id.tv_allapp); tv_cachesize = (TextView) findViewById(R.id.tv_cachesize); tv_clearthisapp = (TextView) findViewById(R.id.tv_clearthisapp); tv_clearSysCache = (TextView) findViewById(R.id.tv_clearSysCache); tv_alllist = (TextView) findViewById(R.id.tv_alllist); tv_allapplistsize = (TextView) findViewById(R.id.tv_allapplistsize); locaUtil = new LocaUtil(this); presenter = new BindConnectionPresenter(MainActivity.this); presenter.bindService(); presenter.setBindServiceLisetener(MainActivity.this); } @Override public void binlisetener() { tv_curentapp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tv_cachesize.setText("当前缓存:"+locaUtil.getCacheSize()); } }); tv_clearthisapp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { locaUtil.clearAppCache(); tv_cachesize.setText("当前缓存:"+locaUtil.getCacheSize()); } }); tv_allapp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { scanCache(); } }); tv_clearSysCache.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mCleanerService != null && !mCleanerService.isScanning() && !mCleanerService.isCleaning() && mCleanerService.getCacheSize() > 0) { mCleanerService.cleanCache(); } scanCache(); } }); } @Override public int getlayout() { return R.layout.activity_main; } public void scanCache(){ if (!mCleanerService.isScanning() && !mAlreadyScanned) { mCleanerService.scanCache(); } } @Override public void onScanStarted(Context context) { Toast.makeText(context, "扫描中", Toast.LENGTH_SHORT).show(); } @Override public void onScanProgressUpdated(Context context, int current, int max) { System.out.println("当前进度:"+current+"/"+max); } @Override public void onScanCompleted(Context context, List<CacheListItem> apps) { if (apps.size() > 0) { long medMemory = mCleanerService != null ? mCleanerService.getCacheSize() : 0; StorageSize mStorageSize = StorageUtil.convertStorageSize(medMemory); tv_alllist.setText("发现可清理app"+apps.size()+"个"); tv_allapplistsize.setText("可清理緩存:"+mStorageSize.value+"/"+mStorageSize.suffix); } if (!mAlreadyScanned) { mAlreadyScanned = true; } } @Override public void onCleanStarted(Context context) { Toast.makeText(context, "清除中请稍等....", Toast.LENGTH_SHORT).show(); } @Override public void onCleanCompleted(Context context, long cacheSize) { Toast.makeText(context, context.getString(R.string.cleaned, Formatter.formatShortFileSize(MainActivity.this, cacheSize)), Toast.LENGTH_LONG).show(); } @Override public void OnSuccess(CleanerService cleanerService) { mCleanerService = cleanerService; } @Override protected void onDestroy() { super.onDestroy(); presenter.unBindService(); }}
下面介绍下定义在Service中的方法
onScanStarted(); //开始扫描的时候会被触发
onScanProgressUpdated(); // 扫描时的进度,方便做Dialog 提示等。
onScanCompleted(); // 扫描完成时会被触发
onCleanStarted(); //执行清除时触发
onCleanCompleted();//清除完成时触发
注意的时在不使用的时候及时的进行解绑
@Override protected void onDestroy() { super.onDestroy(); presenter.unBindService(); }
好了,基本就整完了,愉快的周末过得太快了,整的有点匆忙,基友喊开始撸了,赶紧上线压压惊哒!O(∩_∩)O哈哈~
最后附上呆萌吧!http://download.csdn.net/detail/u014239140/9688016
- 获取本应用和全部app缓存以及清理全解
- 获取APP缓存大小以及清理APP缓存
- 获取缓存的大小 以及清理缓存
- 应用缓存获取和清理功能实现失败
- android 获取其他应用程序的缓存大小以及清理应用缓存
- iOS APP缓存清理
- 关于APP清理缓存
- android app缓存清理
- shell 清理app缓存
- android app清理缓存
- 清理app缓存
- Android 清理应用缓存
- Android 清理应用缓存
- 关于清理应用缓存
- ios-清理应用缓存
- android---清理应用缓存
- Android 清理应用缓存
- 获取IOS APP应用ipa、car文件全部图片
- hadoop之jobhistory
- 自定义侧边快速索引栏
- 走进UML世界(二):九种图介绍
- (ZT)《openssl 编程》之大数
- 数据库几大范式的理解
- 获取本应用和全部app缓存以及清理全解
- Oracle数据库编程工具PL/SQLS问题系列之——tables选项无表(我的解决之道)
- Apache—DBUtils框架
- Linux 线程同步
- Hibernate 学习笔记 之 多对多关系 及其 级联操作
- 色彩搭配
- Logger.getLogger()和LogFactory.getLog()的区别
- 使用gcc遇到的问题
- 数据结构中的四种树的概念