2017-09-30 09:59:39 qq_29078329 阅读数 2674

Android清除缓存功能包含两个部分,一是计算缓存大小,二是清除缓存,并且由于计算缓存大小、清除缓存会比较耗时,要放在子线程执行。计算缓存大小、清除缓存,通过下面的工具类实现:

package com.xi.liuliu.topnews.utils;

import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;

import java.io.File;
import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * 清除缓存工具类,清除内/外缓存,清除数据库,清除sharedPreference,清除files和清除自定义目录
 * Created by zhangxb171 on 2017/9/30.
 */

public class FileCacheUtil {
    private static DecimalFormat df;

    /**
     * * 清除本应用内部缓存(/data/data/com.xxx.xxx/cache)
     *
     * @param context
     */
    public static void cleanInternalCache(Context context) {
        deleteFilesByDirectory(context.getCacheDir());
    }

    /**
     * * 清除本应用所有数据库(/data/data/com.xxx.xxx/databases)
     *
     * @param context
     */
    public static void cleanDatabases(Context context) {
        deleteFilesByDirectory(new File("/data/data/"
                + context.getPackageName() + "/databases"));
    }

    /**
     * * 清除本应用SharedPreference(/data/data/com.xxx.xxx/shared_prefs)
     *
     * @param context
     */
    public static void cleanSharedPreference(Context context) {
        deleteFilesByDirectory(new File("/data/data/"
                + context.getPackageName() + "/shared_prefs"));
    }

    /**
     * * 按名字清除本应用数据库
     *
     * @param context
     * @param dbName
     */
    public static void cleanDatabaseByName(Context context, String dbName) {
        context.deleteDatabase(dbName);
    }

    /**
     * * 清除/data/data/com.xxx.xxx/files下的内容
     *
     * @param context
     */
    public static void cleanFiles(Context context) {
        deleteFilesByDirectory(context.getFilesDir());
    }

    /**
     * * 清除外部cache下的内容(/mnt/sdcard/android/data/com.xxx.xxx/cache)
     *
     * @param context
     */
    public static void cleanExternalCache(Context context) {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            deleteFilesByDirectory(context.getExternalCacheDir());
        }
    }

    /**
     * * 清除自定义路径下的文件
     *
     * @param filePath
     */
    public static void cleanCustomCache(String filePath) {
        deleteFilesByDirectory(new File(filePath));
    }

    /**
     * * 清除本应用所有的数据
     *
     * @param context
     * @param filepath
     */
    public static void cleanApplicationData(Context context, String... filepath) {
        cleanInternalCache(context);
        cleanExternalCache(context);
        cleanDatabases(context);
        cleanSharedPreference(context);
        cleanFiles(context);
        if (filepath == null) {
            return;
        }
        for (String filePath : filepath) {
            cleanCustomCache(filePath);
        }
    }

    /**
     * * 删除方法 这里只会删除某个文件夹下的文件,如果传入的directory是个文件,将不做处理
     *
     * @param directory
     */
    private static void deleteFilesByDirectory(File directory) {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            for (File item : directory.listFiles()) {
                item.delete();
            }
        }
    }

    // 获取文件大小
    //Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据
    //Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据
    public static long getFolderSize(File file) throws Exception {
        long size = 0;
        try {
            File[] fileList = file.listFiles();
            for (int i = 0; i < fileList.length; i++) {
                // 如果下面还有文件
                if (fileList[i].isDirectory()) {
                    size = size + getFolderSize(fileList[i]);
                } else {
                    size = size + fileList[i].length();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return size;
    }

    /**
     * 删除指定目录下文件及目录
     *
     * @param filePath
     * @param deleteThisPath
     */
    public static void deleteFolderFile(String filePath, boolean deleteThisPath) {
        if (!TextUtils.isEmpty(filePath)) {
            try {
                File file = new File(filePath);
                if (file.isDirectory()) {// 如果下面还有文件
                    File files[] = file.listFiles();
                    for (int i = 0; i < files.length; i++) {
                        deleteFolderFile(files[i].getAbsolutePath(), true);
                    }
                }
                if (deleteThisPath) {
                    if (!file.isDirectory()) {// 如果是文件,删除
                        file.delete();
                    } else {// 目录
                        if (file.listFiles().length == 0) {// 目录下没有文件或者目录,删除
                            file.delete();
                        }
                    }
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    /**
     * 格式化单位
     *
     * @param size
     * @return
     */
    public static String getFormatSize(double size) {
        //保留两位小数
        df = new DecimalFormat("0.00");
        double kiloByte = size / 1024;
        String format = df.format(size);
        if (kiloByte < 1) {
            return format + "B";
        }

        double megaByte = kiloByte / 1024;
        if (megaByte < 1) {
            BigDecimal result1 = new BigDecimal(Double.toString(kiloByte));
            return result1.setScale(2, BigDecimal.ROUND_HALF_UP)
                    .toPlainString() + "K";
        }

        double gigaByte = megaByte / 1024;
        if (gigaByte < 1) {
            BigDecimal result2 = new BigDecimal(Double.toString(megaByte));
            return result2.setScale(2, BigDecimal.ROUND_HALF_UP)
                    .toPlainString() + "M";
        }

        double teraBytes = gigaByte / 1024;
        if (teraBytes < 1) {
            BigDecimal result3 = new BigDecimal(Double.toString(gigaByte));
            return result3.setScale(2, BigDecimal.ROUND_HALF_UP)
                    .toPlainString() + "G";
        }
        BigDecimal result4 = new BigDecimal(teraBytes);
        return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString()
                + "T";
    }

    /***
     * 获取应用缓存大小
     * @param file
     * @return
     * @throws Exception
     */
    public static String getCacheSize(File file) throws Exception {
        return getFormatSize(getFolderSize(file));
    }
}

上面的工具类可清除内部(data/data/com.xxx.xxx/xxx)、外部(mnt/sdcard/android/data/com.xxx.xxx/xx)、自定义的缓存文件。

计算缓存在子线程中执行,缓存计算完成后,通过监听的接口回调,更新UI:

public void calculateCacheSize() {
        CacheSizeCalculateListener listener = new CacheSizeCalculateListener() {
            @Override
            public void onCalculateFinished(long cacheSize) {
                String size = FileUtils.formatFileSize(cacheSize, FileUtils.SIZETYPE_MB) + "MB";
                mCacheSize.setText(size);
            }
        };
        CaculateCacheSizeRunnable runnable = new CaculateCacheSizeRunnable(listener);
        Thread thread = new Thread(runnable);
        thread.start();
    }

    static class CaculateCacheSizeRunnable implements Runnable {
        CacheSizeCalculateListener listener;

        public CaculateCacheSizeRunnable(CacheSizeCalculateListener listener) {
            this.listener = listener;
        }

        @Override
        public void run() {
            long cacheSize = 0;
            try {
                cacheSize = FileUtils.getCacheSize();
            } catch (Exception e) {
                e.printStackTrace();
            }
            listener.onCalculateFinished(cacheSize);
        }
    }

    interface CacheSizeCalculateListener {
        void onCalculateFinished(long cacheSize);
    }
同样,清除缓存也在子线程中执行,清除完成后通过回调监听的接口,弹出Toast:

 private void clearCache() {
        SettingsActivity.ClearCacheRunnable runnable = new SettingsActivity.ClearCacheRunnable(mClearCacheListener);
        Thread thread = new Thread(runnable);
        thread.start();
    }

    public void setClearCacheListener(SettingsActivity.ClearCacheListener listener) {
        this.mClearCacheListener = listener;
    }

public static class ClearCacheRunnable implements Runnable {
        ClearCacheListener listener;

        public ClearCacheRunnable(ClearCacheListener listener) {
            this.listener = listener;
        }

        @Override
        public void run() {
            FileUtils.clearCache();
            listener.onClearCacheFinished();
        }
    }


    public interface ClearCacheListener {
        void onClearCacheFinished();
    }

注:我的项目中主要是清除外部存储的图片,并未清除cache、files、sharePref、database等目录。地址:https://github.com/xiyy/TopNews



2017-06-07 15:36:18 mythmayor 阅读数 546
转载请注明出处:https://blog.csdn.net/mythmayor/article/details/72900395

1.本地数据库自动更新的工作机制

  1. 开启一个服务,定时访问服务器
  2. 进行版本对比,如果最新版本比较高,获取需要更新的内容
  3. 将新内容插入到本地数据库中

2.如何处理横竖屏切换

  1. 指定屏幕朝向
    在清单文件对应的Activity中配置android:screenOrientation=”landscape”(横屏,portrait是竖屏);
  2. 设置屏幕旋转时不重新创建Activity
    在清单文件对应的Activity中配置android:configChanges=”keyboardHidden|orientation|screenSize”,最好这三个都配置,否则不能适配所有机型或sdk版本。
    横竖屏切换时会走Activity的onConfigurationChanged()方法

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        // 当新设置中,屏幕布局模式为横排时
        if(newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
            //TODO 某些操作
        }else{
            //TODO 某些操作
        }
        super.onConfigurationChanged(newConfig);
    }
    

3.系统控件的样式在哪里定义

  1. 找到文件:sdk/platforms/某个版本/data/res/values/styles.xml
  2. 搜索关注的控件,如ProgressBar

    <style name="Widget.ProgressBar.Horizontal">
        //indeterminate不确定
        <item name="android:indeterminateOnly">false</item>
        //进度条使用的图片
        <item name="android:progressDrawable">@android:drawable/progress_horizontal</item>
        //进度不确定的时候使用的图片,如安装APK时系统的进度条
        <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
        <item name="android:minHeight">20dip</item>
        <item name="android:maxHeight">20dip</item>
    </style>
    

4.Fragment添加到Activity上有哪些步骤?

//获得管理器
FragmentManager fm = getSupportFragmentManager();

//获得切换Fragment的帮助类,有add添加、delete删除、replace替换、hide隐藏、show显示
FragmentTransaction ft = fm.beginTransaction();

//创建Fragment
CleanCacheFragment f1 = new CleanCacheFragment();
ft.replace(R.id.fl_container, f1);

//提交切换Fragment的事务
ft.commit();

5.Fragment的生命周期

  1. onAttach():Fragment对象跟Activity关联时

  2. onCreate():Fragment对象的初始创建时

  3. onCreateView():创建Fragment的View对象时

  4. onActivityCreate():所依附的Activity对象已经完成了Activity.onCreate()方法时

  5. onStart():界面即将显示给用户,当Activity的onStart方法调用时

  6. onResume():可以获取焦点与用户交互,当Activity的onResume调用时

  7. onPause():Activity被遮挡不可获取焦点调用了onPause时,或者Activity(或其他容器)打开另一个Fragment,当前Fragemnt无法获取焦点时

  8. onStop():Activity不可见调用了onStop()时,或者Activity(或其他容器)打开另一个Fragment当前Fragment不再显示给用户时

  9. onDestroyView():Fragment中的View对象即将被从当前界面中移除时

  10. onDestroy():Fragment对象被销毁时

  11. onDetach():在Fragment对象不再跟它依附的Activity关联的时候,方法会立即被调用

6.缓存

    路径:data/data/包名/cache。用于存放临时文件,当系统空间不足时会清空该目录数据

//获取缓存文件夹
context.getCacheDir(); 

7.如何从应用源码中定位实现逻辑

  1. 通过UI上一些字符串进行全局搜索,Ctrl+H 中的 FileSearch
  2. 通过string.xml中对应字符串id定位layout布局
  3. 通过layout布局中的id,定位代码中该View的事件逻辑

8.如何获取应用的缓存

  1. 经过分析源码,发现获取应用的缓存可以使用packageManager.getPackageSizeInfo()方法
  2. pm的getPackageSizeInfo()方法为hide,无法直接调用,所以使用反射调用该方法
  3. getPackageSizeInfo()需要两个参数,第一个为包名,第二个为一个回调接口IPackageStatsObserver.Stub这种形式一般为远程调用系统服务,需要使用aidl,将aidl文件拷贝到本项目中,注意要使用aidl文件中的原始包名
  4. 代码实现,需要权限

    //1.获得packageManager对象
    PackageManager pm = getPackageManager();
    
    //2.pm的getPackageSizeInfo()方法为hide,使用反射调用
    Method[] methods = PackageManager.class.getDeclaredMethods();
    for(Method method:methods){
        if("getPackageSizeInfo".equals(method.getName())){
            try {
    
                //3.第二个参数需要使用aidl创建
                method.invoke(pm, "com.mythmayor.testcache",new IPackageStatsObserver.Stub(){
                    @Override
                    public void onGetStatsCompleted(PackageStats pStats,
                            boolean succeeded) throws RemoteException {
                        long cacheSize = pStats.cacheSize;
                        System.out.println("缓存大小:"+cacheSize);
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
            return;
        }
    }
    

9.如何清除一个应用的缓存

需要应用为系统应用

packageManager.deleteApplicationCacheFiles(String packageName,IPackageDataObserver observer)
权限:要求系统应用
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />

打开应用详细信息界面

Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:"+info.packName));
startActivity(intent);

10.清除全部应用缓存

利用系统漏洞,google忘记把CLEAR_APP_CACHE权限声明为只有系统应用才可以申请的权限

//建议Long.MAX_VALUE。
PackageManager.freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer);
//权限
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
2015-11-19 23:26:41 zsg592713711 阅读数 51
众所周知 Volley框架可以帮助开发者访问网络数据 ,并对数据进行缓存

创建RequestQueue 时 设置缓存路径

// 缓存 (这使用的是磁盘缓存)
Cache cache=new DiskBasedCache(context.getCacheDir(),1024*1024*10);
// 网络栈 http数据通信的具体实现
Network network=new BasicNetwork(new HurlStack());

//创建并启动请求队列
requestQueue=new RequestQueue(cache,network);


Volley会将数据保存到磁盘缓存中
但有时下次访问同样的地址时 不会读缓存 因为这与服务器传过来的头文件有关
头文件会申明缓存记录的生命周期 当过了申明周期就不会读缓存
若头文件没有申明生命周期,则Volley就不会读取缓存,这时候就要我们手动的读取Volley存放在磁盘中的缓存了


if(!isNetworkAvailable(context)){
getFromDiskCache(url); //如果没网,则调取缓存数据‘
if (requestQueue.getCache().get(commentUrl) != null) {
//通过url的到缓存的数据
byte[] data = requestQueue.getCache().get(commentUrl).data;
}


}else{
//有网则从网上更新数据
//……(省略)
}
}

判断网络是否可用

public static boolean isNetworkAvailable(Context context) {
try {
ConnectivityManager manger = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manger.getActiveNetworkInfo();
//return (info!=null && info.isConnected());
if(info != null){
return info.isConnected();
}else{
return false;
}
} catch (Exception e) {
return false;
}
}
2020-01-22 10:33:57 Fantasy_Lin_ 阅读数 1319

1. 前言

用过WebView的同学都清楚,WebView默认是会自动缓存网页资源的。虽然前端H5网页有自己一套缓存机制(不懂的同学,可以看看这篇文章 手把手教你构建 Android WebView 的缓存机制 & 资源预加载方案),但是在某些场景下,还是需要原生主动去做清除缓存操作,即清除WebView的缓存。大多数文章都会说,使用下列的方法就可以清除了。

new WebView(context).clearCache(true);

但实际上是这种清除方式,只是清除了一部分缓存而已。像Cookie,使用这种方式是清除不了的。

2. 解决方案

经过自己的摸索,整理出了一个有效的清除方式,具体代码如下:

/**
 * 清除缓存
 *
 * @param context 上下文
 */
public static void clearCache(Context context) {
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // 清除cookie
            CookieManager.getInstance().removeAllCookies(null);
        } else {
            CookieSyncManager.createInstance(context);
            CookieManager.getInstance().removeAllCookie();
            CookieSyncManager.getInstance().sync();
        }

        new WebView(context).clearCache(true);

        File cacheFile = new File(context.getCacheDir().getParent() + "/app_webview");
        clearCacheFolder(cacheFile, System.currentTimeMillis());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private static int clearCacheFolder(File dir, long time) {
    int deletedFiles = 0;
    if (dir != null && dir.isDirectory()) {
        try {
            for (File child : dir.listFiles()) {
                if (child.isDirectory()) {
                    deletedFiles += clearCacheFolder(child, time);
                }
                if (child.lastModified() < time) {
                    if (child.delete()) {
                        deletedFiles++;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return deletedFiles;
}

3. 验证

  • 调用上面clearCache方法后,可以重新打开网页,比较网页加载的速度
  • 使用Android Studio的DeviceFileExplorer工具,进入到“/dada/data/APP的包名”目录下,比较前后文件内容的变化,如下图所示
清除缓存前
清除缓存后

 

完整的demo可以查看 AndroidWebView

 

如果想进一步交流和学习的同学,可以加一下QQ群哦!

2019-09-17 10:05:22 u014714188 阅读数 65

Android AS中清空缓存中的第三方包或者gradle

在实际利用AS开发android应用的时候,我们不可避免的会自动引用第三方架包或者各个版本的gradle,很多时候我们在引用的时候并没有去指定引用的版本,这就可能出现我们引用的版本不对,导致功能无法实现。我是在集成Mob推送的时候发现华为手机测试推送功能一切正常,但测试小米手机推送的时候就发现这个功能不正常,时灵时不灵的感觉,困了三四天才把问题解决。最后的原因基本有这个几方面:1.mob自动同步的版本不对;2.gradle有多个版本

首先,我们来看怎么删除第三方架包,此处我以MOB同步的版本为例

我们的AS中工程目录的External Libraries目录下就是我们sync的所有包,如下图:
在这里插入图片描述
我们在该目录下,能看到所有集成MOB所引用的包的版本,之前MobPush引用的版本是1.0.2,而正确的是现在的2.2.0。如下图:
在这里插入图片描述
在找到了版本不对的包后,现在正式开始去删除。关闭AS,找到如下目录:C:\Users\Administrator.gradle,看到这个目录下面有一个caches文件,我们所有的缓存都在这个目录里面了。继续往下找caches\modules-2\files-2.1这个目录,这个里面就是我们引用的第三方架包,找到对应的包(我此处为mob的包),删除即可,如下:
在这里插入图片描述
然后,返回到上一级目录,在下图所示的目录中去删除各个版本所引用包。
在这里插入图片描述
具体如下:
在这里插入图片描述
都删除完了后,重启AS,重新同步即可到对应的版本

上面的是删除架包,现在是删除gradle版本

如下图,图中的4.4就是我当前的gradle版本,删除其余的版本,只保留当前的版本:
在这里插入图片描述
那么我们如何知道我们当前的版本是多少呢,在AS打开我们的工程目录,找到如下目录下的文件:
在这里插入图片描述
打开对应的文件,里面就有当前项目的gradle版本了。

distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

ps:当然,这是最后的办法了,如果我们能通过AS菜单栏的Build 目录下的Clean Project和File目录下的Invalidate Caches/Restart来解决问题就最好了。

没有更多推荐了,返回首页