add log4j to upload logs.
authorkfl_614 <kfl_614@163.com>
Sat, 9 Jan 2021 07:29:25 +0000 (15:29 +0800)
committerkfl_614 <kfl_614@163.com>
Sat, 9 Jan 2021 07:29:25 +0000 (15:29 +0800)
14 files changed:
app/IcL02WorkService/release/IcL02WorkService_V57_202101061638_release.apk [deleted file]
app/IcL02WorkService/release/IcL02WorkService_V57_202101091515_release.apk [new file with mode: 0644]
app/IcL02WorkService/release/output.json
app/build.gradle
app/libs/android-logging-log4j-1.0.3.jar [new file with mode: 0644]
app/libs/ftp4j-1.7.2.jar [new file with mode: 0644]
app/libs/log4j-1.2.17.jar [new file with mode: 0644]
app/libs/okio-2.4.3.jar [new file with mode: 0644]
app/proguard-rules.pro
app/src/main/AndroidManifest.xml
app/src/main/java/com/ic/work/SensorDataService.java
app/src/main/java/com/ic/work/utils/ICLogger.java [new file with mode: 0644]
app/src/main/java/com/ic/work/utils/IcLog.java
app/src/main/java/com/ic/work/utils/StorageUtils.java [new file with mode: 0644]

diff --git a/app/IcL02WorkService/release/IcL02WorkService_V57_202101061638_release.apk b/app/IcL02WorkService/release/IcL02WorkService_V57_202101061638_release.apk
deleted file mode 100644 (file)
index 064c7b2..0000000
Binary files a/app/IcL02WorkService/release/IcL02WorkService_V57_202101061638_release.apk and /dev/null differ
diff --git a/app/IcL02WorkService/release/IcL02WorkService_V57_202101091515_release.apk b/app/IcL02WorkService/release/IcL02WorkService_V57_202101091515_release.apk
new file mode 100644 (file)
index 0000000..4a571cb
Binary files /dev/null and b/app/IcL02WorkService/release/IcL02WorkService_V57_202101091515_release.apk differ
index eb8754e..6888c7a 100644 (file)
@@ -1 +1 @@
-[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.1","enabled":true,"outputFile":"IcL02WorkService_V57_202101061638_release.apk","fullName":"IcL02WorkServiceRelease","baseName":"IcL02WorkService-release"},"path":"IcL02WorkService_V57_202101061638_release.apk","properties":{}}]
\ No newline at end of file
+[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.1","enabled":true,"outputFile":"IcL02WorkService_V57_202101091515_release.apk","fullName":"IcL02WorkServiceRelease","baseName":"IcL02WorkService-release"},"path":"IcL02WorkService_V57_202101091515_release.apk","properties":{}}]
\ No newline at end of file
index cac408f..382d8d2 100644 (file)
@@ -62,9 +62,9 @@ android {
         }
     }
 
-    productFlavors{
+    productFlavors {
 
-        IcA9WorkService{
+        IcA9WorkService {
             flavorDimensions "IcA9WorkService"
             versionCode 1
             versionName "5.0"
@@ -72,14 +72,14 @@ android {
 
         }
 
-        IcAkWorkService{
+        IcAkWorkService {
             flavorDimensions "IcAkWorkService"
             versionCode 1
             versionName "1.0"
             buildConfigField "int", "productType", "1001"
         }
 
-        IcL02WorkService{
+        IcL02WorkService {
             flavorDimensions "IcAkWorkService"
             versionCode 1
             versionName "1.1"
@@ -133,12 +133,15 @@ dependencies {
 
     implementation 'org.greenrobot:eventbus:3.2.0'
 
-    implementation 'com.squareup.okio:okio:2.4.3'
-
     implementation 'com.alibaba:fastjson:1.2.61'
 
     implementation "androidx.room:room-runtime:2.2.5"
     annotationProcessor "androidx.room:room-compiler:2.2.5"
 
     implementation 'com.jakewharton.threetenabp:threetenabp:1.2.2'
+
+    implementation files('libs/android-logging-log4j-1.0.3.jar',
+            'libs/ftp4j-1.7.2.jar',
+            'libs/log4j-1.2.17.jar',
+            'libs/okio-2.4.3.jar')
 }
diff --git a/app/libs/android-logging-log4j-1.0.3.jar b/app/libs/android-logging-log4j-1.0.3.jar
new file mode 100644 (file)
index 0000000..7d4146f
Binary files /dev/null and b/app/libs/android-logging-log4j-1.0.3.jar differ
diff --git a/app/libs/ftp4j-1.7.2.jar b/app/libs/ftp4j-1.7.2.jar
new file mode 100644 (file)
index 0000000..fca26ec
Binary files /dev/null and b/app/libs/ftp4j-1.7.2.jar differ
diff --git a/app/libs/log4j-1.2.17.jar b/app/libs/log4j-1.2.17.jar
new file mode 100644 (file)
index 0000000..068867e
Binary files /dev/null and b/app/libs/log4j-1.2.17.jar differ
diff --git a/app/libs/okio-2.4.3.jar b/app/libs/okio-2.4.3.jar
new file mode 100644 (file)
index 0000000..6289cf6
Binary files /dev/null and b/app/libs/okio-2.4.3.jar differ
index ca4936c..ab3b35f 100644 (file)
@@ -208,4 +208,24 @@ public protected <methods>;
     <init>(java.lang.Throwable);
 }
 
+
+-keep class com.facebook.** { *; }
+-keep class com.mediatek.** { *; }
+-keep class org.apache.** { *; }
+-keep class okio.Okio.** { *; }
+-keep class java.nio.** { *; }
+-keep class com.android.** { *; }
+-keep class android.** { *; }
+-keep class android.ddm.** { *; }
+-keep class com.alibaba.fastjson.** { *; }
+-keep class com.eqc.watch.launcher.WatchApplication { *; }
+#-keep class com.enqualcomm.support.EqcAlgorithmNative { *; }
+-dontwarn com.facebook.**
+-dontwarn com.mediatek.**
+-dontwarn java.nio.**
+-dontwarn com.alibaba.fastjson.**
+-dontwarn android.**
+-dontwarn com.android.**
+-dontwarn rx.internal.**
+
 -keep class com.mapbox.mapboxsdk.**{ *; }
\ No newline at end of file
index cecec34..53a38da 100644 (file)
@@ -25,6 +25,7 @@
 
     <!--用于申请调用A-GPS模块-->
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 
 
     <application
index 22bee1b..770c460 100644 (file)
@@ -248,11 +248,7 @@ public class SensorDataService extends Service implements TemperatureHelper.Temp
             IcLog.d(TAG, "from is not invalid..." + from);
             return;
         }
-        TestTaskEvent taskEvent = new TestTaskEvent(
-                censorType,
-                fromByValue,
-                System.currentTimeMillis()
-        );
+        TestTaskEvent taskEvent = new TestTaskEvent(censorType, fromByValue, System.currentTimeMillis());
         IcLog.d(TAG, "create new task == > " + taskEvent);
         //判断队列里是否有同样的任务,如果有的话不用发了,
         if (mTaskList.contains(taskEvent)) {
@@ -263,7 +259,6 @@ public class SensorDataService extends Service implements TemperatureHelper.Temp
             }
             return;
         }
-
         if (taskEvent.equals(currentTask)) {
             IcLog.d(TAG, "current task running...");
             //通知正在执行
@@ -272,9 +267,7 @@ public class SensorDataService extends Service implements TemperatureHelper.Temp
             }
             return;
         }
-
-        IcLog.d(TAG, "is running =-== > " + isRunning +
-                "  queue size == > " + mTaskList.size());
+        IcLog.d(TAG, "is running =-== > " + isRunning + "  queue size == > " + mTaskList.size());
         //判断是否繁忙
         //如果没有的话则发送一下
         if (!isRunning && mTaskList.size() == 0) {
diff --git a/app/src/main/java/com/ic/work/utils/ICLogger.java b/app/src/main/java/com/ic/work/utils/ICLogger.java
new file mode 100644 (file)
index 0000000..1bbfcce
--- /dev/null
@@ -0,0 +1,324 @@
+package com.ic.work.utils;
+
+import android.content.Context;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.RollingFileAppender;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import de.mindpipe.android.logging.log4j.LogConfigurator;
+
+import static org.apache.log4j.Logger.getLogger;
+
+
+public class ICLogger {
+
+    private static boolean isInitConfig = false;
+    private static Context mContext = null;
+
+    private static Logger Log = null;
+    private static Logger GPS = null;
+    private static Logger Nmea = null;
+    private static Logger APILog = null;
+    private static Logger alarmCycle = null;
+    private static Logger Battery = null;
+    private static Logger LBS = null;
+    //  private static java.util.logging.Logger originLog= getLogger(ICLogger.class.getSimpleName());
+    private static final boolean DEBUG = true;    //BuildConfig.DEBUG;
+
+    public static void setContext(Context context) {
+        mContext = context;
+    }
+
+    private static String getFunctionName() {
+        StackTraceElement[] currentStack = Thread.currentThread().getStackTrace();
+        if (currentStack == null) {
+            return null;
+        } else {
+            int stackLen = currentStack.length;
+
+            for (int i = 0; i < stackLen; ++i) {
+                StackTraceElement stackElement = currentStack[i];
+                if (!stackElement.isNativeMethod() &&
+                        !stackElement.getClassName().equals(Thread.class.getName()) &&
+                        !stackElement.getClassName().equals(ICLogger.class.getName())) {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("[ ");
+                    sb.append(Thread.currentThread().getName());
+                    sb.append(": ");
+                    sb.append(stackElement.getFileName());
+                    sb.append(":");
+                    sb.append(stackElement.getLineNumber());
+                    sb.append(" ");
+                    sb.append(stackElement.getMethodName());
+                    sb.append(" ]");
+                    return sb.toString();
+                }
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * 初始化与配置Logger   Log4j
+     * 日志信息格式中符号所代表的含义
+     * (1)-X号: X信息输出时左对齐。
+     * (2)%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL。
+     * (3)%d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921。
+     * (4)%r: 输出自应用启动到输出该log信息耗费的毫秒数。
+     * (5)%c: 输出日志信息所属的类目,通常就是所在类的全名。
+     * (6)%t: 输出产生该日志事件的线程名。
+     * (7)%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)。
+     * (8)%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
+     * (9)%%: 输出一个"%"字符。
+     * (10)%F: 输出日志消息产生时所在的文件名称。
+     * (11)%L: 输出代码中的行号。
+     * (12)%m: 输出代码中指定的消息,产生的日志具体信息。
+     * (13)%n: 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"输出日志信息换行。
+     * 可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如:
+     * (1)%20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
+     * (2)%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
+     * (3)%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
+     * (4)%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边交远销出的字符截掉。
+     */
+    private static void initConfig() {
+        if (!isInitConfig) {
+
+            LogConfigurator logConfigurator = new LogConfigurator();
+
+            String filename = StorageUtils.getExternalStorageLogDir() + File.separator + "log.txt";
+            logConfigurator.setFileName(filename);                // 日志存储路径,如果该路径没有会自动创建文件
+            logConfigurator.setRootLevel(Level.ALL);
+
+            logConfigurator.setLevel("log", Level.ALL);
+//        logConfigurator.setFilePattern("%d %-5p [%c{2}]-[%L] %m%n");// 设置文件输出模式
+            logConfigurator.setFilePattern("%d %m%n ");// 设置文件输出模式
+//        02-29 16:28:38.195 4629-4658/com.enqualcomm.support E/com.enqualcomm.support.utils.ICLogger:
+            logConfigurator.setMaxBackupSize(2); // 设置最大备份数量
+            logConfigurator.setMaxFileSize(1024 * 1024 * 20);// 设置最大文件大小  20MB
+            logConfigurator.setImmediateFlush(true);// 设置是否立即刷新
+            logConfigurator.setUseFileAppender(true); // 设置用户文件输出器
+            logConfigurator.setInternalDebugging(false); //控制console是否输出
+            logConfigurator.setUseLogCatAppender(true);
+            logConfigurator.setLogCatPattern("%m%n");
+            logConfigurator.configure();// 设置提交
+
+
+//        String logfilelist[]={"Log","GPS","Nmea","APILog","alarmCycle","Battery","LBS"};
+
+            //Log 和log目前是同一个配置
+            Log = getLogger("log");
+            Log.info("init Logger " + Log.getName());
+//        Log.addAppender( getRollingFileAppender("log","%d %m%n "));
+//        Log.info("init Logger "+Log.getName()+" "+((RollingFileAppender)Log.getAppender("log")).getFile());
+
+            GPS = getLogger("GPS");
+            GPS.addAppender(getRollingFileAppender("GPS", "%d %m%n "));
+            GPS.info("init Logger " + GPS.getName() + " " + ((RollingFileAppender) GPS.getAppender("GPS")).getFile());
+
+            Nmea = getLogger("Nmea");
+            Nmea.addAppender(getRollingFileAppender("Nmea", "%m%n"));
+            Nmea.info("init Logger " + Nmea.getName() + " " + ((RollingFileAppender) Nmea.getAppender("Nmea")).getFile());
+
+            APILog = getLogger("APILog");
+            APILog.addAppender(getRollingFileAppender("APILog", "%d %m%n "));
+            APILog.info("init Logger " + APILog.getName() + " " + ((RollingFileAppender) APILog.getAppender("APILog")).getFile());
+
+            alarmCycle = getLogger("alarmCycle");
+            alarmCycle.addAppender(getRollingFileAppender("alarmCycle", "%d %m%n "));
+            alarmCycle.info("init Logger " + alarmCycle.getName() + " " + ((RollingFileAppender) alarmCycle.getAppender("alarmCycle")).getFile());
+
+            Battery = getLogger("Battery");
+            Battery.addAppender(getRollingFileAppender("Battery", "%d %m%n "));
+            Battery.info("init Logger " + Battery.getName() + " " + ((RollingFileAppender) Battery.getAppender("Battery")).getFile());
+
+
+            LBS = getLogger("LBS");
+            LBS.addAppender(getRollingFileAppender("LBS", "%d %m%n "));
+            LBS.info("init Logger " + LBS.getName() + " " + ((RollingFileAppender) LBS.getAppender("LBS")).getFile());
+            isInitConfig = true;
+        }
+    }
+
+    public static void logRAWtoFile(String filename, String msg) {
+        if (filename.equals("Nmea")) {
+            Nmea.info(msg);
+        } else if (filename.equals("LBS")) {
+            LBS.info(msg);
+        } else if (filename.equals("GPS")) {
+            GPS.info(msg);
+        } else if (filename.equals("APILog")) {
+            APILog.info(msg);
+        } else if (filename.equals("alarmCycle")) {
+            alarmCycle.info(msg);
+        } else if (filename.equals("Battery")) {
+            Battery.info(msg);
+        } else {
+            Log.info(msg);
+        }
+    }
+
+    public static void logToFile(String filename, String msg) {
+        if (filename.equals("Nmea")) {
+            Nmea.info(msg);
+        } else if (filename.equals("LBS")) {
+            LBS.info(msg);
+        } else if (filename.equals("GPS")) {
+            GPS.info(msg);
+        } else if (filename.equals("APILog")) {
+            APILog.info(msg);
+        } else if (filename.equals("alarmCycle")) {
+            alarmCycle.info(msg);
+        } else if (filename.equals("Battery")) {
+            Battery.info(msg);
+        } else {
+            Log.info(msg);
+        }
+    }
+
+    private static RollingFileAppender getRollingFileAppender(String name, String layoutpattern) {
+        PatternLayout fileLayout = new PatternLayout(layoutpattern);
+        String filename = StorageUtils.getExternalStorageLogDir() + File.separator + name + ".txt";
+        RollingFileAppender rollingFileAppender = null;
+        try {
+            rollingFileAppender = new RollingFileAppender(fileLayout, filename);
+        } catch (IOException e) {
+            e.printStackTrace();
+            ICLogger.e(e.getMessage(), e);
+            return null;
+        }
+        rollingFileAppender.setName(name);
+        rollingFileAppender.setMaxBackupIndex(2);  //最多3个备份文件
+        rollingFileAppender.setMaximumFileSize(10 * 1024 * 1024); //10M 大小
+        rollingFileAppender.setImmediateFlush(true);
+
+        return rollingFileAppender;
+    }
+
+    public static void v(String msg) {
+        initConfig();
+        if (Log != null)
+            Log.debug(buildMessage(msg));
+    }
+
+    public static void v(String msg, Throwable thr) {
+        initConfig();
+        if (Log != null)
+            Log.debug(buildMessage(msg), thr);
+    }
+
+    public static void d(String msg) {
+        initConfig();
+        if (Log != null)
+            Log.debug(buildMessage(msg));
+    }
+
+    public static void d(String msg, Throwable thr) {
+        initConfig();
+        if (Log != null)
+            Log.debug(buildMessage(msg), thr);
+    }
+
+    public static void i(String msg) {
+        initConfig();
+        if (Log != null)
+            Log.info(buildMessage(msg));
+    }
+
+    public static void i(String msg, Throwable thr) {
+        initConfig();
+        if (Log != null)
+            Log.info(buildMessage(msg), thr);
+    }
+
+    public static void w(String msg) {
+        initConfig();
+        if (Log != null)
+            Log.warn(buildMessage(msg));
+    }
+
+    public static void w(String msg, Throwable thr) {
+        initConfig();
+        if (Log != null)
+            Log.warn(buildMessage(msg), thr);
+    }
+
+    public static void w(Throwable thr) {
+        initConfig();
+        if (Log != null)
+            Log.warn(buildMessage(""), thr);
+    }
+
+    public static void e(String msg) {
+        initConfig();
+        if (Log != null)
+            Log.error(buildMessage(msg));
+    }
+
+    public static void e(String msg, Throwable thr) {
+        initConfig();
+        if (Log != null)
+            Log.error(buildMessage(msg), thr);
+    }
+
+    public static void PrintExceptionStackTrace(Exception e) {
+
+        StringBuffer sb = new StringBuffer();
+        sb.append(e.getMessage()).append("\n");
+        e.printStackTrace(new PrintStream(new OutputStream() {
+            @Override
+            public void write(int b) throws IOException {
+                sb.append((char) b);
+            }
+        }));
+        i(sb.toString());
+    }
+
+    /**
+     * 生成消息
+     *
+     * @param msg
+     * @return
+     */
+    protected static String buildMessage(String msg) {
+        initConfig();
+        if (DEBUG && msg != null) {
+            String var1 = getFunctionName();
+            if (var1 != null) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(var1);
+                sb.append(" - ");
+                sb.append(msg);
+                return sb.toString();
+            } else {
+                return "functionName is null";
+            }
+        } else {
+            return "msg is null";
+        }
+    }
+
+    public static void log(String TAG, String msg) {
+        if (DEBUG) {
+            if (Log != null) Log.debug(TAG + " " + msg);
+
+        }
+    }
+
+    public static void log(String TAG, String funcName, String msg) {
+        if (DEBUG) {
+            if (Log != null)
+                Log.debug(TAG + " [" + funcName + "]" + msg);
+
+        }
+    }
+
+}
+
index 0c11184..23ef366 100644 (file)
@@ -1,7 +1,5 @@
 package com.ic.work.utils;
 
-import android.util.Log;
-
 /**
  * @author RyanLee
  * @date 2018/2/2
@@ -14,33 +12,23 @@ public class IcLog {
     private static String tagHead = "";
 
     public static void v(String tag, String msg) {
-        if (isDebug) {
-            Log.v(mergeHeadTag(tag), msg);
-        }
+        ICLogger.v(tag + " : " + msg);
     }
 
     public static void d(String tag, String msg) {
-        if (isDebug) {
-            Log.d(mergeHeadTag(tag), msg);
-        }
+        ICLogger.d(tag + " : " + msg);
     }
 
     public static void i(String tag, String msg) {
-        if (isDebug) {
-            Log.i(mergeHeadTag(tag), msg);
-        }
+        ICLogger.i(tag + " : " + msg);
     }
 
     public static void w(String tag, String msg) {
-        if (isDebug) {
-            Log.w(mergeHeadTag(tag), msg);
-        }
+        ICLogger.w(tag + " : " + msg);
     }
 
     public static void e(String tag, String msg, Throwable e) {
-        if (isDebug) {
-            Log.e(mergeHeadTag(tag), msg, e);
-        }
+        ICLogger.e(tag + " : " + msg);
     }
 
 
diff --git a/app/src/main/java/com/ic/work/utils/StorageUtils.java b/app/src/main/java/com/ic/work/utils/StorageUtils.java
new file mode 100644 (file)
index 0000000..ae72a50
--- /dev/null
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.ic.work.utils;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.StatFs;
+
+import java.io.File;
+import java.io.IOException;
+
+import static android.os.Environment.MEDIA_MOUNTED;
+
+/**
+ * Provides application storage paths
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public final class StorageUtils {
+
+       private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE";
+       private static final String INDIVIDUAL_DIR_NAME = "uil-images";
+
+       private StorageUtils() {
+       }
+
+       public static String getExternalStorageLogDir() {
+
+               if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                       File dir = new File(Environment.getExternalStorageDirectory().getPath(), "Android/ic/icprotocol");
+                       String path = dir.getPath();
+                       if (!dir.exists()) {
+                               dir.mkdirs();
+                       }
+                       return path;
+               }
+               return "/storage/emulated/0/Android/ic/icprotocol";
+       }
+       /**
+        * Returns application cache directory. Cache directory will be created on SD card
+        * <i>("/Android/data/[app_package_name]/cache")</i> if card is mounted and app has appropriate permission. Else -
+        * Android defines cache directory on device's file system.
+        *
+        * @param context Application context
+        * @return Cache {@link File directory}.<br />
+        * <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
+        * {@link Context#getCacheDir() Context.getCacheDir()} returns null).
+        */
+       public static File getCacheDirectory(Context context) {
+               return getCacheDirectory(context, true);
+       }
+
+       /**
+        * Returns application cache directory. Cache directory will be created on SD card
+        * <i>("/Android/data/[app_package_name]/cache")</i> (if card is mounted and app has appropriate permission) or
+        * on device's file system depending incoming parameters.
+        *
+        * @param context        Application context
+        * @param preferExternal Whether prefer external location for cache
+        * @return Cache {@link File directory}.<br />
+        * <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
+        * {@link Context#getCacheDir() Context.getCacheDir()} returns null).
+        */
+       public static File getCacheDirectory(Context context, boolean preferExternal) {
+               File appCacheDir = null;
+               if (preferExternal && MEDIA_MOUNTED
+                               .equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context) && haveFreeSpace()) {
+                       appCacheDir = getExternalCacheDir(context);
+               }
+               if (appCacheDir == null) {
+                       appCacheDir = context.getCacheDir();
+               }
+               if (appCacheDir == null) {
+                       String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/";
+                       appCacheDir = new File(cacheDirPath);
+               }
+               return appCacheDir;
+       }
+
+       /**
+        * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
+        * created on SD card <i>("/Android/data/[app_package_name]/cache/uil-images")</i> if card is mounted and app has
+        * appropriate permission. Else - Android defines cache directory on device's file system.
+        *
+        * @param context Application context
+        * @return Cache {@link File directory}
+        */
+       public static File getIndividualCacheDirectory(Context context) {
+               File cacheDir = getCacheDirectory(context);
+               File individualCacheDir = new File(cacheDir, INDIVIDUAL_DIR_NAME);
+               if (!individualCacheDir.exists()) {
+                       if (!individualCacheDir.mkdir()) {
+                               individualCacheDir = cacheDir;
+                       }
+               }
+               return individualCacheDir;
+       }
+
+       /**
+        * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
+        * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
+        *
+        * @param context  Application context
+        * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
+        * @return Cache {@link File directory}
+        */
+       public static File getOwnCacheDirectory(Context context, String cacheDir) {
+               File appCacheDir = null;
+               if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
+                       appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
+               }
+               if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
+                       appCacheDir = context.getCacheDir();
+               }
+               return appCacheDir;
+       }
+
+       private static File getExternalCacheDir(Context context) {
+               File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
+               File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
+               if (!appCacheDir.exists()) {
+                       if (!appCacheDir.mkdirs()) {
+                               return null;
+                       }
+                       try {
+                               new File(appCacheDir, ".nomedia").createNewFile();
+                       } catch (IOException e) {
+                       }
+               }
+               return appCacheDir;
+       }
+
+       private static boolean hasExternalStoragePermission(Context context) {
+               int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION);
+               return perm == PackageManager.PERMISSION_GRANTED;
+       }
+
+       private static boolean haveFreeSpace() {
+        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+            StatFs st = new StatFs(Environment.getExternalStorageDirectory().getPath());
+            int blockSize = st.getBlockSize();
+            long available = st.getAvailableBlocks();
+            long availableSize = (blockSize * available);
+            if (availableSize < 1024 * 1024 * 10) {// //sd卡空间如果小于10M,就认为sd卡空间不足
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+}