From 6a836a06ad3ea7d5dd6562b52b2a9ac59e36793f Mon Sep 17 00:00:00 2001 From: Blankj <625783482@qq.com> Date: Fri, 26 Jul 2019 16:58:16 +0800 Subject: [PATCH] see 07/26 log --- CHANGELOG.md | 3 + README-CN.md | 2 +- README.md | 2 +- buildApp.gradle | 5 +- buildSrc/src/main/groovy/Config.groovy | 13 +- buildSrc/src/main/groovy/ConfigUtils.groovy | 1 - lib/base/build.gradle | 1 + .../java/com/blankj/base/BaseApplication.java | 2 + lib/utilcode/README-CN.md | 4 +- lib/utilcode/README.md | 4 +- .../blankj/utilcode/util/ContainerUtils.java | 172 ++++++++++++++++++ .../blankj/utilcode/util/PermissionUtils.java | 4 +- .../com/blankj/utilcode/util/ThreadUtils.java | 48 +++-- .../com/blankj/utilcode/util/ZipUtils.java | 6 +- .../com/blankj/utilcode/util/BaseTest.java | 23 +-- .../blankj/utilcode/util/ZipUtilsTest.java | 9 +- lib/utildebug/.gitignore | 1 + lib/utildebug/build.gradle | 15 ++ lib/utildebug/proguard-rules.pro | 21 +++ lib/utildebug/src/main/AndroidManifest.xml | 2 + lib/utildebug/src/main/res/values/strings.xml | 3 + .../com/blankj/utildebug/ExampleUnitTest.java | 17 ++ plugin/api-gradle-plugin/README.md | 2 +- 23 files changed, 294 insertions(+), 66 deletions(-) create mode 100644 lib/utilcode/src/main/java/com/blankj/utilcode/util/ContainerUtils.java create mode 100644 lib/utildebug/.gitignore create mode 100644 lib/utildebug/build.gradle create mode 100644 lib/utildebug/proguard-rules.pro create mode 100644 lib/utildebug/src/main/AndroidManifest.xml create mode 100644 lib/utildebug/src/main/res/values/strings.xml create mode 100644 lib/utildebug/src/test/java/com/blankj/utildebug/ExampleUnitTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 85a3fe045e..2d5b1b84f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +* `19/07/26` [add] ContainerUtils. Publish v1.25.2. +* `19/07/25` [fix] PermissionUtils' NullPointException. +* `19/07/24` [fix] ZipUtils#unzipFile. * `19/07/23` [fix] ThreadUtils of cache pool. Publish v1.25.1. * `19/07/18` [add] README of ApiUtils and BusUtils. * `19/07/15` [add] Publish v1.25.0. diff --git a/README-CN.md b/README-CN.md index a89513fc4f..00f811aee2 100644 --- a/README-CN.md +++ b/README-CN.md @@ -45,7 +45,7 @@ [frame]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame.png -[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.1-brightgreen.svg +[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.2-brightgreen.svg [auc]: https://github.com/Blankj/AndroidUtilCode [apiSvg]: https://img.shields.io/badge/API-14+-brightgreen.svg diff --git a/README.md b/README.md index 6c96d845dc..de338f9347 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ If this project helps you a lot and you want to support the project's developmen [frame]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame.png -[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.1-brightgreen.svg +[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.2-brightgreen.svg [auc]: https://github.com/Blankj/AndroidUtilCode [apiSvg]: https://img.shields.io/badge/API-14+-brightgreen.svg diff --git a/buildApp.gradle b/buildApp.gradle index d346b80d74..1c87fdae14 100644 --- a/buildApp.gradle +++ b/buildApp.gradle @@ -16,7 +16,7 @@ configApkName() android { compileSdkVersion Config.compileSdkVersion defaultConfig { - minSdkVersion Config.minSdkVersion + minSdkVersion 16 versionCode Config.versionCode versionName Config.versionName applicationId Config.applicationId + suffix @@ -55,6 +55,9 @@ dependencies { debugImplementation Config.depConfig.leakcanary.support_fragment.dep releaseImplementation Config.depConfig.leakcanary.android_no_op.dep + debugImplementation 'com.didichuxing.doraemonkit:doraemonkit:1.1.8' + releaseImplementation 'com.didichuxing.doraemonkit:doraemonkit-no-op:1.1.8' + // 根据 Config.pkgConfig 来依赖所有 pkg for (def entrySet : ConfigUtils.getApplyPkgs().entrySet()) { api entrySet.value.dep diff --git a/buildSrc/src/main/groovy/Config.groovy b/buildSrc/src/main/groovy/Config.groovy index c3ff6c373f..85859adde4 100644 --- a/buildSrc/src/main/groovy/Config.groovy +++ b/buildSrc/src/main/groovy/Config.groovy @@ -14,8 +14,8 @@ class Config { static compileSdkVersion = 27 static minSdkVersion = 14 static targetSdkVersion = 27 - static versionCode = 1_025_001 - static versionName = '1.25.1'// E.g. 1.9.72 => 1,009,072 + static versionCode = 1_025_002 + static versionName = '1.25.2'// E.g. 1.9.72 => 1,009,072 // lib version static kotlin_version = '1.3.10' @@ -71,10 +71,11 @@ class Config { ], lib : [ - base : new DepConfig(":lib:base"), - common : new DepConfig(":lib:common"), - subutil : new DepConfig(":lib:subutil"), - utilcode: new DepConfig(true/*是否本地调试*/, ":lib:utilcode", "com.blankj:utilcode:$versionName"), + base : new DepConfig(":lib:base"), + common : new DepConfig(":lib:common"), + subutil : new DepConfig(":lib:subutil"), + utilcode : new DepConfig(true/*是否本地调试*/, ":lib:utilcode", "com.blankj:utilcode:$versionName"), + utildebug: new DepConfig(true/*是否本地调试*/, ":lib:utildebug", "com.blankj:utildebug:$versionName"), ], support : [ diff --git a/buildSrc/src/main/groovy/ConfigUtils.groovy b/buildSrc/src/main/groovy/ConfigUtils.groovy index 90b9254b2a..f202f25a35 100644 --- a/buildSrc/src/main/groovy/ConfigUtils.groovy +++ b/buildSrc/src/main/groovy/ConfigUtils.groovy @@ -88,7 +88,6 @@ class ConfigUtils { void beforeEvaluate(Project project) { if (project.subprojects.isEmpty()) { if (project.path.contains(":plugin:")) { - // 插件的话自己写 build.gradle return } if (project.name == "app") { diff --git a/lib/base/build.gradle b/lib/base/build.gradle index 3b9c007384..9ac90bd0b2 100644 --- a/lib/base/build.gradle +++ b/lib/base/build.gradle @@ -12,4 +12,5 @@ dependencies { api Config.depConfig.swipe_panel.dep api Config.depConfig.eventbus.lib.dep compileOnly Config.depConfig.leakcanary.android_no_op.dep + compileOnly 'com.didichuxing.doraemonkit:doraemonkit-no-op:1.1.8' } \ No newline at end of file diff --git a/lib/base/src/main/java/com/blankj/base/BaseApplication.java b/lib/base/src/main/java/com/blankj/base/BaseApplication.java index 32fcff1126..d2a9a4d5d6 100644 --- a/lib/base/src/main/java/com/blankj/base/BaseApplication.java +++ b/lib/base/src/main/java/com/blankj/base/BaseApplication.java @@ -8,6 +8,7 @@ import com.blankj.utilcode.util.CrashUtils; import com.blankj.utilcode.util.LogUtils; import com.blankj.utilcode.util.ProcessUtils; +import com.didichuxing.doraemonkit.DoraemonKit; import com.squareup.leakcanary.LeakCanary; import java.util.ArrayList; @@ -41,6 +42,7 @@ protected void attachBaseContext(Context base) { public void onCreate() { super.onCreate(); sInstance = this; + DoraemonKit.install(this); initLeakCanary(); initLog(); initCrash(); diff --git a/lib/utilcode/README-CN.md b/lib/utilcode/README-CN.md index cfd73581f1..8b27520ae3 100644 --- a/lib/utilcode/README-CN.md +++ b/lib/utilcode/README-CN.md @@ -2,10 +2,10 @@ Gradle: ```groovy -implementation 'com.blankj:utilcode:1.25.1' +implementation 'com.blankj:utilcode:1.25.2' // if u use AndroidX, use the following -implementation 'com.blankj:utilcodex:1.25.1' +implementation 'com.blankj:utilcodex:1.25.2' ``` diff --git a/lib/utilcode/README.md b/lib/utilcode/README.md index 27c9e10ec0..408cbba37b 100644 --- a/lib/utilcode/README.md +++ b/lib/utilcode/README.md @@ -2,10 +2,10 @@ Gradle: ```groovy -implementation 'com.blankj:utilcode:1.25.1' +implementation 'com.blankj:utilcode:1.25.2' // if u use AndroidX, use the following -implementation 'com.blankj:utilcodex:1.25.1' +implementation 'com.blankj:utilcodex:1.25.2' ``` diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ContainerUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ContainerUtils.java new file mode 100644 index 0000000000..747ecabbf7 --- /dev/null +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ContainerUtils.java @@ -0,0 +1,172 @@ +package com.blankj.utilcode.util; + +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.v4.util.SimpleArrayMap; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; +import android.util.SparseLongArray; + +import java.util.Collection; +import java.util.Collections; + +/** + *
+ *     author: blankj
+ *     blog  : http://blankj.com
+ *     time  : 2019/07/26
+ *     desc  : utils about container
+ * 
+ */ +public final class ContainerUtils { + + private ContainerUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + public static boolean isEmpty(T[] arr) { + return arr == null || arr.length == 0; + } + + public static boolean isEmpty(final Collection obj) { + return obj == null || obj.isEmpty(); + } + + public static boolean isEmpty(final java.util.Map obj) { + return obj == null || obj.isEmpty(); + } + + public static boolean isEmpty(final SimpleArrayMap obj) { + return obj == null || obj.isEmpty(); + } + + public static boolean isEmpty(final SparseArray obj) { + return obj == null || obj.size() == 0; + } + + public static boolean isEmpty(final SparseBooleanArray obj) { + return obj == null || obj.size() == 0; + } + + public static boolean isEmpty(final SparseIntArray obj) { + return obj == null || obj.size() == 0; + } + + public static boolean isEmpty(final android.support.v4.util.LongSparseArray obj) { + return obj == null || obj.size() == 0; + } + + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean isEmpty(final android.util.LongSparseArray obj) { + return obj == null || obj.size() == 0; + } + + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static boolean isEmpty(final SparseLongArray obj) { + return obj == null || obj.size() == 0; + } + + public static boolean isNotEmpty(V[] obj) { + return !isEmpty(obj); + } + + public static boolean isNotEmpty(final Collection obj) { + return !isEmpty(obj); + } + + public static boolean isNotEmpty(final java.util.Map obj) { + return !isEmpty(obj); + } + + public static boolean isNotEmpty(final SimpleArrayMap obj) { + return !isEmpty(obj); + } + + public static boolean isNotEmpty(final SparseArray obj) { + return !isEmpty(obj); + } + + public static boolean isNotEmpty(final SparseBooleanArray obj) { + return !isEmpty(obj); + } + + public static boolean isNotEmpty(final SparseIntArray obj) { + return !isEmpty(obj); + } + + public static boolean isNotEmpty(final android.support.v4.util.LongSparseArray obj) { + return !isEmpty(obj); + } + + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean isNotEmpty(final android.util.LongSparseArray obj) { + return !isEmpty(obj); + } + + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static boolean isNotEmpty(final SparseLongArray obj) { + return !isEmpty(obj); + } + + + public static final class Array { + + private Array() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + public static int getSize(final T[] arr) { + if (isEmpty(arr)) return 0; + return arr.length; + } + + public static void reverse(final T[] arr) { + int size = getSize(arr); + if (size <= 1) return; + int mid = size >> 1; + T tmp; + for (int i = 0; i < mid; i++) { + tmp = arr[i]; + arr[i] = arr[size - i - 1]; + arr[size - i - 1] = tmp; + } + } + } + + + public static final class List { + + private List() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + public static int getSize(java.util.List list) { + if (isEmpty(list)) return 0; + return list.size(); + } + + public static void reverse(java.util.List list) { + if (getSize(list) <= 1) return; + Collections.reverse(list); + } + + } + + + public static final class Map { + + private Map() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + public static int getSize(java.util.Map map) { + if (isEmpty(map)) return 0; + return map.size(); + } + + + } + + +} diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/PermissionUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/PermissionUtils.java index d35cbcc620..4f20f3f31d 100755 --- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/PermissionUtils.java +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/PermissionUtils.java @@ -410,7 +410,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - sInstance.onRequestPermissionsResult(this); + if (sInstance != null && sInstance.mPermissionsRequest != null) { + sInstance.onRequestPermissionsResult(this); + } finish(); } diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ThreadUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ThreadUtils.java index 2782d7f7cd..c5785c5910 100644 --- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ThreadUtils.java +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ThreadUtils.java @@ -1,12 +1,12 @@ package com.blankj.utilcode.util; -import android.annotation.SuppressLint; import android.os.Handler; import android.os.Looper; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.util.Log; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Timer; @@ -33,7 +33,7 @@ */ public final class ThreadUtils { - private static final Map> TYPE_PRIORITY_POOLS = new ConcurrentHashMap<>(); + private static final Map> TYPE_PRIORITY_POOLS = new HashMap<>(); private static final Map TASK_TIMERTASK_MAP = new ConcurrentHashMap<>(); @@ -944,23 +944,24 @@ private static ExecutorService getPoolByTypeAndPriority(final int type) { return getPoolByTypeAndPriority(type, Thread.NORM_PRIORITY); } - @SuppressLint("UseSparseArrays") - private synchronized static ExecutorService getPoolByTypeAndPriority(final int type, final int priority) { - ExecutorService pool; - Map priorityPools = TYPE_PRIORITY_POOLS.get(type); - if (priorityPools == null) { - priorityPools = new ConcurrentHashMap<>(); - pool = ThreadPoolExecutor4Util.createPool(type, priority); - priorityPools.put(priority, pool); - TYPE_PRIORITY_POOLS.put(type, priorityPools); - } else { - pool = priorityPools.get(priority); - if (pool == null) { + private static ExecutorService getPoolByTypeAndPriority(final int type, final int priority) { + synchronized (TYPE_PRIORITY_POOLS) { + ExecutorService pool; + Map priorityPools = TYPE_PRIORITY_POOLS.get(type); + if (priorityPools == null) { + priorityPools = new ConcurrentHashMap<>(); pool = ThreadPoolExecutor4Util.createPool(type, priority); priorityPools.put(priority, pool); + TYPE_PRIORITY_POOLS.put(type, priorityPools); + } else { + pool = priorityPools.get(priority); + if (pool == null) { + pool = ThreadPoolExecutor4Util.createPool(type, priority); + priorityPools.put(priority, pool); + } } + return pool; } - return pool; } static final class ThreadPoolExecutor4Util extends ThreadPoolExecutor { @@ -986,9 +987,9 @@ private static ExecutorService createPool(final int type, final int priority) { new UtilsThreadFactory("io", priority) ); case TYPE_CPU: - return new ThreadPoolExecutor4Util(CPU_COUNT + 1, CPU_COUNT + 1, + return new ThreadPoolExecutor4Util(CPU_COUNT + 1, 2 * CPU_COUNT + 1, 30, TimeUnit.SECONDS, - new LinkedBlockingQueue4Util(), + new LinkedBlockingQueue4Util(true), new UtilsThreadFactory("cpu", priority) ); default: @@ -1046,7 +1047,7 @@ private static final class LinkedBlockingQueue4Util extends LinkedBlockingQueue< private volatile ThreadPoolExecutor4Util mPool; - private boolean mIsAddSubThreadFirstThenAddQueue = false; + private int mCapacity = Integer.MAX_VALUE; LinkedBlockingQueue4Util() { super(); @@ -1054,12 +1055,19 @@ private static final class LinkedBlockingQueue4Util extends LinkedBlockingQueue< LinkedBlockingQueue4Util(boolean isAddSubThreadFirstThenAddQueue) { super(); - mIsAddSubThreadFirstThenAddQueue = isAddSubThreadFirstThenAddQueue; + if (isAddSubThreadFirstThenAddQueue) { + mCapacity = 0; + } + } + + LinkedBlockingQueue4Util(int capacity) { + super(); + mCapacity = capacity; } @Override public boolean offer(@NonNull Runnable runnable) { - if (mIsAddSubThreadFirstThenAddQueue && + if (mCapacity <= size() && mPool != null && mPool.getPoolSize() < mPool.getMaximumPoolSize()) { // create a non-core thread return false; diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ZipUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ZipUtils.java index 9cdc0d54be..d2a301c63b 100644 --- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ZipUtils.java +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ZipUtils.java @@ -293,7 +293,7 @@ public static List unzipFileByKeyword(final File zipFile, if (isSpace(keyword)) { while (entries.hasMoreElements()) { ZipEntry entry = ((ZipEntry) entries.nextElement()); - String entryName = entry.getName(); + String entryName = entry.getName().replace("\\", "/"); if (entryName.contains("../")) { Log.e("ZipUtils", "entryName: " + entryName + " is dangerous!"); continue; @@ -303,7 +303,7 @@ public static List unzipFileByKeyword(final File zipFile, } else { while (entries.hasMoreElements()) { ZipEntry entry = ((ZipEntry) entries.nextElement()); - String entryName = entry.getName(); + String entryName = entry.getName().replace("\\", "/"); if (entryName.contains("../")) { Log.e("ZipUtils", "entryName: " + entryName + " is dangerous!"); continue; @@ -378,7 +378,7 @@ public static List getFilesPath(final File zipFile) ZipFile zip = new ZipFile(zipFile); Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { - String entryName = ((ZipEntry) entries.nextElement()).getName(); + String entryName = ((ZipEntry) entries.nextElement()).getName().replace("\\", "/");; if (entryName.contains("../")) { Log.e("ZipUtils", "entryName: " + entryName + " is dangerous!"); paths.add(entryName); diff --git a/lib/utilcode/src/test/java/com/blankj/utilcode/util/BaseTest.java b/lib/utilcode/src/test/java/com/blankj/utilcode/util/BaseTest.java index f30ee7cca4..06c360cffc 100644 --- a/lib/utilcode/src/test/java/com/blankj/utilcode/util/BaseTest.java +++ b/lib/utilcode/src/test/java/com/blankj/utilcode/util/BaseTest.java @@ -10,9 +10,7 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLog; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; /** *
@@ -39,25 +37,6 @@ public void execute(@NonNull Runnable command) {
 
     @Test
     public void test() throws Exception {
-        CountDownLatch latch = new CountDownLatch(1);
-
-        for (int i = 0; i < 100; i++) {
-            final int finalI = i;
-            ThreadUtils.executeByCpu(new ThreadUtils.SimpleTask() {
-                @Override
-                public Void doInBackground() throws Throwable {
-                    System.out.println("" + Thread.currentThread() + finalI);
-                    Thread.sleep(100);
-                    return null;
-                }
-
-                @Override
-                public void onSuccess(Void result) {
-
-                }
-            });
-        }
-
-        latch.await(1, TimeUnit.SECONDS);
+
     }
 }
diff --git a/lib/utilcode/src/test/java/com/blankj/utilcode/util/ZipUtilsTest.java b/lib/utilcode/src/test/java/com/blankj/utilcode/util/ZipUtilsTest.java
index 558d1062c0..aad1ef6789 100644
--- a/lib/utilcode/src/test/java/com/blankj/utilcode/util/ZipUtilsTest.java
+++ b/lib/utilcode/src/test/java/com/blankj/utilcode/util/ZipUtilsTest.java
@@ -1,6 +1,5 @@
 package com.blankj.utilcode.util;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -59,8 +58,8 @@ public void getComments() throws Exception {
         System.out.println(ZipUtils.getComments(zipFile));
     }
 
-    @After
-    public void tearDown() {
-        FileUtils.deleteAllInDir(PATH_TEMP);
-    }
+//    @After
+//    public void tearDown() {
+//        FileUtils.deleteAllInDir(PATH_TEMP);
+//    }
 }
\ No newline at end of file
diff --git a/lib/utildebug/.gitignore b/lib/utildebug/.gitignore
new file mode 100644
index 0000000000..796b96d1c4
--- /dev/null
+++ b/lib/utildebug/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/lib/utildebug/build.gradle b/lib/utildebug/build.gradle
new file mode 100644
index 0000000000..5a5bb7e07f
--- /dev/null
+++ b/lib/utildebug/build.gradle
@@ -0,0 +1,15 @@
+apply {
+    plugin "com.github.dcendents.android-maven"
+    plugin "com.jfrog.bintray"
+    from "${rootDir.path}/gradle/upload/bintrayUploadAndroid.gradle"
+}
+
+dependencies {
+    implementation Config.depConfig.lib.utilcode.dep
+    compileOnly Config.depConfig.support.appcompat_v7.dep
+    compileOnly Config.depConfig.support.design.dep
+
+    testImplementation Config.depConfig.test.junit.dep
+    testImplementation Config.depConfig.test.robolectric.dep
+    testImplementation Config.depConfig.support.appcompat_v7.dep
+}
\ No newline at end of file
diff --git a/lib/utildebug/proguard-rules.pro b/lib/utildebug/proguard-rules.pro
new file mode 100644
index 0000000000..f1b424510d
--- /dev/null
+++ b/lib/utildebug/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/lib/utildebug/src/main/AndroidManifest.xml b/lib/utildebug/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..11a29aa41f
--- /dev/null
+++ b/lib/utildebug/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/lib/utildebug/src/main/res/values/strings.xml b/lib/utildebug/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..f11f7450a8
--- /dev/null
+++ b/lib/utildebug/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/lib/utildebug/src/test/java/com/blankj/utildebug/ExampleUnitTest.java b/lib/utildebug/src/test/java/com/blankj/utildebug/ExampleUnitTest.java
new file mode 100644
index 0000000000..ea1f901d70
--- /dev/null
+++ b/lib/utildebug/src/test/java/com/blankj/utildebug/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.blankj.utildebug;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}
\ No newline at end of file
diff --git a/plugin/api-gradle-plugin/README.md b/plugin/api-gradle-plugin/README.md
index 57b9cfdb85..80ab54dc90 100644
--- a/plugin/api-gradle-plugin/README.md
+++ b/plugin/api-gradle-plugin/README.md
@@ -45,7 +45,7 @@ apply plugin: "com.blankj.api"
 api "com.blankj:utilcode:latest_version"
 ```
 
-如果你单纯只想引入 `ApiUtils` 也是可以的,需要你自己拷贝一份这个类放到你工程里,然后在 app 下的 `build.gradle` 中 配置 api 的 SDL 域如下所示:
+如果你单纯只想引入 `ApiUtils` 也是可以的,需要你自己拷贝一份这个类放到你工程里,然后在 app 下的 `build.gradle` 中 配置 api 的 DSL 域如下所示:
 
 ```groovy
 api {