diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d5b1b84f7..6227eca50f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +* `19/07/28` [add] NetworkUtils#(un)registerNetworkStatusChangedListener. Publish v1.25.3. +* `19/07/27` [fix] ThreadUtils memory leak. * `19/07/26` [add] ContainerUtils. Publish v1.25.2. * `19/07/25` [fix] PermissionUtils' NullPointException. * `19/07/24` [fix] ZipUtils#unzipFile. diff --git a/README-CN.md b/README-CN.md index 00f811aee2..d9a991369f 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.2-brightgreen.svg +[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.3-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 de338f9347..c44d4cec62 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.2-brightgreen.svg +[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.3-brightgreen.svg [auc]: https://github.com/Blankj/AndroidUtilCode [apiSvg]: https://img.shields.io/badge/API-14+-brightgreen.svg diff --git a/buildSrc/src/main/groovy/Config.groovy b/buildSrc/src/main/groovy/Config.groovy index 85859adde4..b4d0d64ef6 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_002 - static versionName = '1.25.2'// E.g. 1.9.72 => 1,009,072 + static versionCode = 1_025_003 + static versionName = '1.25.3'// E.g. 1.9.72 => 1,009,072 // lib version static kotlin_version = '1.3.10' diff --git a/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/network/NetworkActivity.kt b/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/network/NetworkActivity.kt index d1617f15b4..f0ed896a1c 100644 --- a/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/network/NetworkActivity.kt +++ b/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/network/NetworkActivity.kt @@ -9,9 +9,9 @@ import com.blankj.common.CommonTitleActivity import com.blankj.utilcode.pkg.R import com.blankj.utilcode.util.NetworkUtils import com.blankj.utilcode.util.SpanUtils -import com.blankj.utilcode.util.Utils +import com.blankj.utilcode.util.ThreadUtils +import com.blankj.utilcode.util.ToastUtils import kotlinx.android.synthetic.main.activity_network.* -import java.util.concurrent.atomic.AtomicInteger /** * ``` @@ -21,10 +21,7 @@ import java.util.concurrent.atomic.AtomicInteger * desc : demo about NetworkUtils * ``` */ -class NetworkActivity : CommonTitleActivity() { - - var cur: Int = 0 - var count: AtomicInteger = AtomicInteger(); +class NetworkActivity : CommonTitleActivity(), NetworkUtils.OnNetworkStatusChangedListener { companion object { fun start(context: Context) { @@ -33,8 +30,6 @@ class NetworkActivity : CommonTitleActivity() { } } - private lateinit var spanSb: SpannableStringBuilder - override fun bindTitle(): CharSequence { return getString(R.string.demo_network) } @@ -52,6 +47,7 @@ class NetworkActivity : CommonTitleActivity() { NetworkUtils.setWifiEnabled(isChecked) updateAboutNetwork() } + NetworkUtils.registerNetworkStatusChangedListener(this) } override fun onResume() { @@ -66,18 +62,45 @@ class NetworkActivity : CommonTitleActivity() { when (view.id) { R.id.networkOpenWirelessSettingsBtn -> NetworkUtils.openWirelessSettings() } - updateAboutNetwork() } - private lateinit var ipV4AddressAsyncTask: Utils.Task - private lateinit var ipV6AddressAsyncTask: Utils.Task - private lateinit var wifiAvailableAsyncTask: Utils.Task - private lateinit var availableAsyncTask: Utils.Task - private lateinit var domainAddressAsyncTask: Utils.Task + private lateinit var task: ThreadUtils.SimpleTask private fun updateAboutNetwork() { - spanSb = SpanUtils.with(networkAboutTv) - .appendLine("isConnected: " + NetworkUtils.isConnected()) + + SpanUtils.with(networkAboutTv) + .append(getSpan()) + .appendLine("") + .appendLine("") + .appendLine("") + .appendLine("") + .appendLine("Loading...") + .create() + + task = object : ThreadUtils.SimpleTask() { + + override fun doInBackground(): String { + val sb: StringBuilder = StringBuilder(); + sb.appendln("getIPv4Address: ${NetworkUtils.getIPAddress(true)}") + sb.appendln("getIPv6Address: ${NetworkUtils.getIPAddress(false)}") + sb.appendln("isWifiAvailable: ${NetworkUtils.isWifiAvailable()}") + sb.appendln("isAvailable: ${NetworkUtils.isAvailable()}") + sb.appendln("getBaiduDomainAddress: ${NetworkUtils.getDomainAddress("baidu.com")}") + return sb.toString() + } + + override fun onSuccess(result: String) { + SpanUtils.with(networkAboutTv) + .append(getSpan()) + .append(result) + .create() + } + } + ThreadUtils.executeByCached(task) + } + + private fun getSpan(): SpannableStringBuilder { + return SpanUtils().appendLine("isConnected: " + NetworkUtils.isConnected()) .appendLine("getMobileDataEnabled: " + NetworkUtils.getMobileDataEnabled()) .appendLine("isMobileData: " + NetworkUtils.isMobileData()) .appendLine("is4G: " + NetworkUtils.is4G()) @@ -89,72 +112,23 @@ class NetworkActivity : CommonTitleActivity() { .appendLine("getIpAddressByWifi: " + NetworkUtils.getIpAddressByWifi()) .appendLine("getGatewayByWifi: " + NetworkUtils.getGatewayByWifi()) .appendLine("getNetMaskByWifi: " + NetworkUtils.getNetMaskByWifi()) - .append("getServerAddressByWifi: " + NetworkUtils.getServerAddressByWifi()) + .appendLine("getServerAddressByWifi: " + NetworkUtils.getServerAddressByWifi()) .create() - cur += 5 - - ipV4AddressAsyncTask = NetworkUtils.getIPAddressAsync(true) { data -> - val num = count.get() - if (num >= cur - 5) { - spanSb = SpanUtils().appendLine(spanSb) - .append("getIPv4Address: $data") - .create() - networkAboutTv.text = spanSb - } - count.addAndGet(1) - } + } - ipV6AddressAsyncTask = NetworkUtils.getIPAddressAsync(false) { data -> - val num = count.get() - if (num >= cur - 5) { - spanSb = SpanUtils().appendLine(spanSb) - .append("getIPv6Address: $data") - .create() - networkAboutTv.text = spanSb - } - count.addAndGet(1) - } + override fun onDisconnected() { + ToastUtils.showLong("onDisconnected") + } - wifiAvailableAsyncTask = NetworkUtils.isWifiAvailableAsync { data -> - val num = count.get() - if (num >= cur - 5) { - spanSb = SpanUtils().appendLine(spanSb) - .append("isWifiAvailable: $data") - .create() - networkAboutTv.text = spanSb - } - count.addAndGet(1) - } + override fun onConnected(networkType: NetworkUtils.NetworkType) { - availableAsyncTask = NetworkUtils.isAvailableAsync { data -> - val num = count.get() - if (num >= cur - 5) { - spanSb = SpanUtils().appendLine(spanSb) - .append("isAvailable: $data") - .create() - networkAboutTv.text = spanSb - } - count.addAndGet(1) - } - domainAddressAsyncTask = NetworkUtils.getDomainAddressAsync("baidu.com") { data -> - val num = count.get() - if (num >= cur - 5) { - spanSb = SpanUtils().appendLine(spanSb) - .append("getBaiduDomainAddress: $data") - .create() - networkAboutTv.text = spanSb - } - count.addAndGet(1) - } + ToastUtils.showLong("onConnected: ${networkType.name}") } override fun onDestroy() { - ipV4AddressAsyncTask.cancel() - ipV6AddressAsyncTask.cancel() - wifiAvailableAsyncTask.cancel() - availableAsyncTask.cancel() - domainAddressAsyncTask.cancel() + task.cancel() + NetworkUtils.unregisterOnNetworkChangedListener(this) super.onDestroy() } } diff --git a/lib/utilcode/README-CN.md b/lib/utilcode/README-CN.md index 8b27520ae3..63444bb5e3 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.2' +implementation 'com.blankj:utilcode:1.25.3' // if u use AndroidX, use the following -implementation 'com.blankj:utilcodex:1.25.2' +implementation 'com.blankj:utilcodex:1.25.3' ``` diff --git a/lib/utilcode/README.md b/lib/utilcode/README.md index 408cbba37b..322488a692 100644 --- a/lib/utilcode/README.md +++ b/lib/utilcode/README.md @@ -2,10 +2,10 @@ Gradle: ```groovy -implementation 'com.blankj:utilcode:1.25.2' +implementation 'com.blankj:utilcode:1.25.3' // if u use AndroidX, use the following -implementation 'com.blankj:utilcodex:1.25.2' +implementation 'com.blankj:utilcodex:1.25.3' ``` diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/BarUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/BarUtils.java index 7b5509b2b5..5137124495 100644 --- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/BarUtils.java +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/BarUtils.java @@ -57,7 +57,7 @@ private BarUtils() { * @return the status bar's height */ public static int getStatusBarHeight() { - Resources resources = Resources.getSystem(); + Resources resources = Utils.getApp().getResources(); int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); return resources.getDimensionPixelSize(resourceId); } @@ -453,7 +453,7 @@ private static void invokePanels(final String methodName) { * @return the navigation bar's height */ public static int getNavBarHeight() { - Resources res = Resources.getSystem(); + Resources res = Utils.getApp().getResources(); int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId != 0) { return res.getDimensionPixelSize(resourceId); diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java index f875ced39b..73fa19bba2 100644 --- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java @@ -306,13 +306,13 @@ private boolean isShouldHideKeyboard(View v, MotionEvent event) { } private static int getStatusBarHeight() { - Resources resources = Resources.getSystem(); + Resources resources = Utils.getApp().getResources(); int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); return resources.getDimensionPixelSize(resourceId); } private static int getNavBarHeight() { - Resources res = Resources.getSystem(); + Resources res = Utils.getApp().getResources(); int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId != 0) { return res.getDimensionPixelSize(resourceId); diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java index e0828aba66..dc23ae8fe4 100644 --- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java @@ -1,8 +1,10 @@ package com.blankj.utilcode.util; import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiManager; @@ -21,8 +23,10 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.util.Enumeration; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.ACCESS_WIFI_STATE; @@ -50,7 +54,8 @@ public enum NetworkType { NETWORK_4G, NETWORK_3G, NETWORK_2G, - NETWORK_UNKNOWN + NETWORK_UNKNOWN, + NETWORK_NO } /** @@ -376,6 +381,7 @@ public static String getNetworkOperatorName() { *
  • {@link NetworkUtils.NetworkType#NETWORK_3G }
  • *
  • {@link NetworkUtils.NetworkType#NETWORK_2G }
  • *
  • {@link NetworkUtils.NetworkType#NETWORK_UNKNOWN }
  • + *
  • {@link NetworkUtils.NetworkType#NETWORK_NO }
  • * */ @RequiresPermission(ACCESS_NETWORK_STATE) @@ -419,11 +425,15 @@ public static NetworkType getNetworkType() { || subtypeName.equalsIgnoreCase("WCDMA") || subtypeName.equalsIgnoreCase("CDMA2000")) { return NetworkType.NETWORK_3G; + } else { + return NetworkType.NETWORK_UNKNOWN; } } + } else { + return NetworkType.NETWORK_UNKNOWN; } } - return NetworkType.NETWORK_UNKNOWN; + return NetworkType.NETWORK_NO; } /** @@ -632,4 +642,99 @@ public static String getServerAddressByWifi() { if (wm == null) return ""; return Formatter.formatIpAddress(wm.getDhcpInfo().serverAddress); } + + /** + * Register the status of network changed listener. + * + * @param listener The status of network changed listener + */ + public static void registerNetworkStatusChangedListener(OnNetworkStatusChangedListener listener) { + NetworkChangedReceiver.getInstance().registerListener(listener); + } + + /** + * unregister the status of network changed listener. + * + * @param listener The status of network changed listener + */ + public static void unregisterOnNetworkChangedListener(OnNetworkStatusChangedListener listener) { + NetworkChangedReceiver.getInstance().unregisterListener(listener); + } + + public static final class NetworkChangedReceiver extends BroadcastReceiver { + + private static NetworkChangedReceiver getInstance() { + return LazyHolder.INSTANCE; + } + + private NetworkType mType; + private Set mListeners = new HashSet<>(); + + void registerListener(final OnNetworkStatusChangedListener listener) { + if (listener == null) return; + Utils.runOnUiThread(new Runnable() { + @SuppressLint("MissingPermission") + @Override + public void run() { + int preSize = mListeners.size(); + mListeners.add(listener); + if (preSize == 0 && mListeners.size() == 1) { + mType = getNetworkType(); + IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + Utils.getApp().registerReceiver(NetworkChangedReceiver.getInstance(), intentFilter); + } + } + }); + } + + void unregisterListener(final OnNetworkStatusChangedListener listener) { + if (listener == null) return; + Utils.runOnUiThread(new Runnable() { + @Override + public void run() { + int preSize = mListeners.size(); + mListeners.remove(listener); + if (preSize == 1 && mListeners.size() == 0) { + Utils.getApp().unregisterReceiver(NetworkChangedReceiver.getInstance()); + } + } + }); + } + + @SuppressLint("MissingPermission") + @Override + public void onReceive(Context context, Intent intent) { + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + // debouncing + Utils.runOnUiThreadDelayed(new Runnable() { + @Override + public void run() { + NetworkType networkType = NetworkUtils.getNetworkType(); + if (mType == networkType) return; + LogUtils.e(networkType); + mType = networkType; + if (networkType == NetworkType.NETWORK_NO) { + for (OnNetworkStatusChangedListener listener : mListeners) { + listener.onDisconnected(); + } + } else { + for (OnNetworkStatusChangedListener listener : mListeners) { + listener.onConnected(networkType); + } + } + } + }, 1000); + } + } + + private static class LazyHolder { + private static final NetworkChangedReceiver INSTANCE = new NetworkChangedReceiver(); + } + } + + public interface OnNetworkStatusChangedListener { + void onDisconnected(); + + void onConnected(NetworkType networkType); + } } 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 c5785c5910..b54633d94a 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 @@ -12,7 +12,6 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; @@ -35,9 +34,7 @@ public final class ThreadUtils { private static final Map> TYPE_PRIORITY_POOLS = new HashMap<>(); - private static final Map TASK_TIMERTASK_MAP = new ConcurrentHashMap<>(); - - private static final Map> POOL_TASK_MAP = new ConcurrentHashMap<>(); + private static final Map TASK_TASKINFO_MAP = new ConcurrentHashMap<>(); private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final Timer TIMER = new Timer(); @@ -881,8 +878,11 @@ public static void cancel(final List tasks) { */ public static void cancel(ExecutorService executorService) { if (executorService instanceof ThreadPoolExecutor4Util) { - List tasks = POOL_TASK_MAP.get(executorService); - cancel(tasks); + for (Map.Entry taskTaskInfoEntry : TASK_TASKINFO_MAP.entrySet()) { + if (taskTaskInfoEntry.getValue().mService == executorService) { + cancel(taskTaskInfoEntry.getKey()); + } + } } else { Log.e("LogUtils", "The executorService is not ThreadUtils's pool."); } @@ -904,11 +904,10 @@ private static void executeWithDelay(final ExecutorService pool, TimerTask timerTask = new TimerTask() { @Override public void run() { - execute(pool, task); + execute(pool, task, this); } }; TIMER.schedule(timerTask, unit.toMillis(delay)); - TASK_TIMERTASK_MAP.put(task, timerTask); } private static void executeAtFixedRate(final ExecutorService pool, @@ -920,24 +919,20 @@ private static void executeAtFixedRate(final ExecutorService pool, TimerTask timerTask = new TimerTask() { @Override public void run() { - execute(pool, task); + execute(pool, task, this); } }; TIMER.scheduleAtFixedRate(timerTask, unit.toMillis(initialDelay), unit.toMillis(period)); } private static void execute(final ExecutorService pool, final Task task) { - pool.execute(task); - recordTask(pool, task); + execute(pool, task, null); } - private static void recordTask(ExecutorService pool, Task task) { - List tasks = POOL_TASK_MAP.get(pool); - if (tasks == null) { - tasks = new CopyOnWriteArrayList<>(); - POOL_TASK_MAP.put(pool, tasks); - } - tasks.add(task); + private static void execute(final ExecutorService pool, final Task task, + final TimerTask timerTask) { + pool.execute(task); + TASK_TASKINFO_MAP.put(task, new TaskInfo(timerTask, pool)); } private static ExecutorService getPoolByTypeAndPriority(final int type) { @@ -1268,10 +1263,19 @@ public void execute(@NonNull Runnable command) { } private static void cancelTimerTask(final Task task) { - TimerTask timerTask = TASK_TIMERTASK_MAP.get(task); + TaskInfo timerTask = TASK_TASKINFO_MAP.get(task); if (timerTask != null) { - TASK_TIMERTASK_MAP.remove(task); - timerTask.cancel(); + TASK_TASKINFO_MAP.remove(task); + } + } + + private static class TaskInfo { + private TimerTask mTimerTask; + private ExecutorService mService; + + private TaskInfo(TimerTask timerTask, ExecutorService service) { + mTimerTask = timerTask; + mService = service; } } }