-3

I am working on implementing low internet speed detection in my app. However, it is not functioning as expected—it reports "low internet" even when the internet speed is good. I would like to implement this without using any file downloads, and I would greatly appreciate your guidance on how to reliably detect low internet speed.

Please find the code I have implemented below.

Thank you in advance.

public class NetworkQualityMonitor {
 public interface NetworkQualityCallback {
 void onQualityChecked(Quality quality, long latency, double jitter, double packetLoss);
 }
 public enum Quality {
 GOOD, MODERATE, POOR
 }
 private final OkHttpClient client;
 private final String pingUrl = "https://www.google.com";
 private final int pingCount;
 private final ExecutorService executor;
 private final Handler mainHandler;
 // --- Caching state ---
 private long lastCheckTime = 0;
 private Quality lastQuality = Quality.POOR;
 private long lastLatency = -1;
 private double lastJitter = -1;
 private double lastPacketLoss = -1;
 private static NetworkQualityMonitor networkQualityMonitor;
 private static final long CACHE_DURATION_MS = 7000; // 5 sec
 private boolean isChecking = false; // avoid parallel execution
 private boolean isNavigatingToLowNetwork = false; // avoid multiple LowNetworkActivity launches
 private NetworkQualityMonitor() {
 this.client = new OkHttpClient.Builder().connectTimeout(3, TimeUnit.SECONDS).readTimeout(2, TimeUnit.SECONDS).build();
 this.pingCount = 4;
 this.executor = Executors.newSingleThreadExecutor();
 this.mainHandler = new Handler(Looper.getMainLooper());
 }
 public synchronized static NetworkQualityMonitor getInstance() {
 if (networkQualityMonitor == null) {
 networkQualityMonitor = new NetworkQualityMonitor();
 }
 return networkQualityMonitor;
 }
 private boolean mapNetworkQuality(Quality quality, long latency, double jitter, double packetLoss) {
 return quality == Quality.POOR;
 }
 public synchronized void checkQuality(Activity activity, boolean isFreshCheck, NetworkChecker.NetworkCheckCallback networkCheckCallback) {
 long now = System.currentTimeMillis();
 // --- If cached result is still valid, return immediately ---
 if (now - lastCheckTime < CACHE_DURATION_MS && !isFreshCheck) {
 mainHandler.post(() -> networkCheckCallback.onNetworkCheckCompleted(
 lastQuality != Quality.POOR,
 lastQuality == Quality.POOR,
 0
 ));
 if (lastQuality == Quality.POOR) {
 retryCheck(activity, networkCheckCallback); // trigger retry
 }
 return;
 }
 runNetworkTest(activity, networkCheckCallback, false);
 }
 private void runNetworkTest(Activity activity, NetworkChecker.NetworkCheckCallback networkCheckCallback, boolean isRetry) {
 executor.execute(() -> {
 try {
 Quality quality;
 List<Long> rtts = new ArrayList<>();
 int successCount = 0;
 for (int i = 0; i < pingCount; i++) {
 long start = System.currentTimeMillis();
 Request request = new Request.Builder().url(pingUrl).tag("" + System.currentTimeMillis()).build();
 try (Response response = client.newCall(request).execute()) {
 if (response.isSuccessful()) {
 successCount++;
 long rtt = System.currentTimeMillis() - start;
 rtts.add(rtt);
 }
 } catch (IOException ignored) {}
 try { Thread.sleep(70); } catch (InterruptedException ignored) {}
 }
 // --- Latency ---
 long avgLatency = rtts.isEmpty() ? -1 : rtts.stream().mapToLong(Long::longValue).sum() / rtts.size();
 // --- Jitter ---
 double jitter = 0;
 for (int i = 1; i < rtts.size(); i++) {
 jitter += Math.abs(rtts.get(i) - rtts.get(i - 1));
 }
 jitter = rtts.size() > 1 ? jitter / (rtts.size() - 1) : -1;
 // --- Packet Loss ---
 double packetLoss = 100.0 * (pingCount - successCount) / pingCount;
 // --- Decide Quality ---
 quality = decideQuality(avgLatency, jitter, packetLoss);
 // --- Cache result ---
 lastCheckTime = System.currentTimeMillis();
 lastQuality = quality;
 lastLatency = avgLatency;
 lastJitter = jitter;
 lastPacketLoss = packetLoss;
 mainHandler.post(() -> {
 if (quality == Quality.POOR) {
 if (!isRetry) {
 // First time poor → retry check
 retryCheck(activity, networkCheckCallback);
 } else {
 // Second time also poor → navigate
 networkCheckCallback.onNetworkCheckCompleted(false, true, 0);
 navigateToLowNetworkActivity(activity);
 }
 } else {
 networkCheckCallback.onNetworkCheckCompleted(true, false, 0);
 }
 });
 } catch (Exception e) {
 mainHandler.post(() -> networkCheckCallback.onNetworkCheckCompleted(true, false, 0));
 }
 });
 }
 private void retryCheck(Activity activity, NetworkChecker.NetworkCheckCallback networkCheckCallback) {
 // Optional: Add a small delay before retry
 mainHandler.postDelayed(() -> runNetworkTest(activity, networkCheckCallback, true), 1000);
 }
 public void navigateToLowNetworkActivity(Activity activity) {
 if (!(activity instanceof SplashActivity) && !(activity instanceof LowNetworkActivity)) {
 if (isNavigatingToLowNetwork) return; // already navigating
 isNavigatingToLowNetwork = true;
 Intent intent = new Intent(activity, LowNetworkActivity.class);
 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 activity.startActivity(intent);
 // Reset flag after a delay (so navigation can happen again later if needed)
 mainHandler.postDelayed(() -> isNavigatingToLowNetwork = false, 5000);
 }
 }
 private Quality decideQuality(long latency, double jitter, double packetLoss) {
 if (latency == -1 || jitter == -1 || packetLoss == -1) {
 return Quality.POOR;
 }
 boolean good = latency < 1800 && jitter < 1500 && packetLoss < 1;
 boolean poor = latency > 1800 || jitter > 1500 || packetLoss > 5;
 if (BuildConfig.DEBUG) {
 Log.d("NetworkCheck", "Latency : " + latency + " Jitter : " + jitter + " PacketLoss : " + packetLoss);
 }
 if (good) return Quality.GOOD;
 if (poor) return Quality.POOR;
 return Quality.MODERATE;
 }
}
 NetworkQualityMonitor.getInstance().checkQuality(activity, isFreshCall, new NetworkCheckCallback() {
 @Override
 public void onNetworkCheckCompleted(boolean isConnected, boolean isLowSpeed, double speedKbps) {
 handleResult(activity, isRunOnUIThread, isSendToNetworkActivity, isLowSpeed, downSpeed, false, listener);
 }
 });
asked Oct 24 at 13:20
New contributor
Aakash Vishwakarma is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
5
  • 3
    Please edit the question to define what "low internet" is. Do you mean weak signal strength of a mobile device? Please also fix the formatting of your code, something apparently went wrong there. And finally, please remove the tags [android-studio] and [mobile-application]. The problem you have doesn't seem to be related to Android Studio, and it suffices to use the [android] tag. Instead, please have a look of some other tags refering to the actual network monitoring aspect might be more fitting. Commented Oct 24 at 13:42
  • In addition to "low internet" not being well defined (and having many possible interpretations) this doesn't do what you think it does. It doesn't measure packet loss- you need to work at the TCP level to measure that. It measures entire round trip HTTP connections, for which a lot of those measurements you're using aren't going to be anywhere near correct. Commented 2 days ago
  • Also- "without any file download". Every http transfer is a file download. The file could be big or could be small. In this case it would be giant html and js file consisting of Google's homepage. Its an unhappy medium size- it isn't big enough to really test throughput, but way too big to be a replacement for a ping (which sends less than 100 bytes) Commented 2 days ago
  • @GabeSechan i have editted the question, please check and its a request to reopen the question Commented yesterday
  • @tyg i have editted the question, please check and its a request to repone the question Commented yesterday

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.