原理:周期性的ping一个网络地址,计算出连接时间,估算出当前的网络延迟状况。
工具类代码:
import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.text.TextUtils; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by tonglin on 2018/3/27. */ public class NetPingManager { private String mDomain; // 接口域名 private InetAddress[] mAddress; private ListmAddressIpList; private IOnNetPingListener mIOnNetPingListener; // 将监控日志上报到前段页面 private HandlerThread mHandlerThread; private static int DELAY_TIME = 3000; private ConnectivityManager manager; private final Handler mHandleMessage; private static final String url = "www.baidu.com"; /** * 延迟 */ public void setDuration(int delay) { DELAY_TIME = delay; } /** * 初始化网络诊断服务 */ public NetPingManager(Context context, String domain, IOnNetPingListener theListener) { if (TextUtils.isEmpty(domain)) { this.mDomain = url; } else { this.mDomain = domain; } this.mIOnNetPingListener = theListener; this.mAddressIpList = new ArrayList<>(); if (null != context) this.manager = (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); this.mHandlerThread = new HandlerThread("ping"); this.mHandlerThread.start(); this.mHandleMessage = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0: //每次请求清空上传集合 if (null != mAddressIpList) { if (null != mAddressIpList) { mAddressIpList.clear(); startNetDiagnosis(); } } if (null != mHandlerThread) mHandleMessage.sendEmptyMessageDelayed(0, DELAY_TIME); break; } } }; } public void getDelay() { if (null != this.mHandleMessage) { this.mHandleMessage.sendEmptyMessage(0); } } public void release() { synchronized (NetPingManager.class) { if (null != this.manager) this.manager = null; if (null != this.mHandleMessage) { this.mHandleMessage.removeMessages(0); } if (null != mHandlerThread) { Looper looper = this.mHandlerThread.getLooper(); if (looper != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { looper.quitSafely(); } else { looper.quit(); } } } this.mHandlerThread = null; this.mIOnNetPingListener = null; if (null != mAddressIpList) mAddressIpList.clear(); this.mAddressIpList = null; this.manager = null; } } /** * 监控网络诊断的跟踪信息 */ public interface IOnNetPingListener { void onDelay(long log, int type); void onError(); } /** * 开始诊断网络 */ private void startNetDiagnosis() { if (!TextUtils.isEmpty(this.mDomain)) { // 网络状态 if (isNetworkConnected()) { parseDomain(this.mDomain);// 域名解析 // TCP三次握手时间测试 execUseJava(); } else { if (null != mIOnNetPingListener) mIOnNetPingListener.onError(); LogUtil.e("netping", "当前主机未联网,请检查网络!"); } } } /** * 使用java执行connected */ private boolean execUseJava() { if (mAddress != null && mAddressIpList != null && !mAddressIpList.isEmpty()) { int len = mAddress.length; if (len > 0) { execIP(mAddress[0], mAddressIpList.get(0)); } } return false; } private static final int PORT = 80; private static final int CONN_TIMES = 4; // 设置每次连接的timeout时间 private int TIME_OUT = 6000; private final long[] RttTimes = new long[CONN_TIMES];// 用于存储三次测试中每次的RTT值 /** * 返回某个IP进行5次connect的最终结果 */ private boolean execIP(InetAddress inetAddress, String ip) { boolean isConnected = true; InetSocketAddress socketAddress; if (inetAddress != null && ip != null) { socketAddress = new InetSocketAddress(inetAddress, PORT); int flag = 0; for (int i = 0; i < CONN_TIMES; i++) { execSocket(socketAddress, i); if (RttTimes[i] == -1) {// 一旦发生timeOut,则尝试加长连接时间 TIME_OUT += 4000; if (i > 0 && RttTimes[i - 1] == -1) {// 连续两次连接超时,停止后续测试 flag = -1; break; } } else if (RttTimes[i] == -2) { if (i > 0 && RttTimes[i - 1] == -2) {// 连续两次出现IO异常,停止后续测试 flag = -2; break; } } } long time = 0; int count = 0; if (flag == -1) { isConnected = false; } else if (flag == -2) { isConnected = false; } else { for (int i = 0; i < CONN_TIMES; i++) { if (RttTimes[i] > 0) { time += RttTimes[i]; count++; } } if (count > 0) { if (mIOnNetPingListener != null) { int delay = (int) (time / count); int type; if (delay <= 100) { type = 1; } else if (delay <= 200) { type = 2; } else { type = 3; } mIOnNetPingListener.onDelay(delay, type); } } } } else { isConnected = false; } return isConnected; } /** * 针对某个IP第index次connect */ private void execSocket(InetSocketAddress socketAddress, int index) { long start; long end; Socket mSocket = new Socket(); try { start = System.currentTimeMillis(); mSocket.connect(socketAddress, TIME_OUT); end = System.currentTimeMillis(); RttTimes[index] = end - start; } catch (SocketTimeoutException e) { RttTimes[index] = -1;// 作为TIMEOUT标识 e.printStackTrace(); } catch (IOException e) { RttTimes[index] = -2;// 作为IO异常标识 e.printStackTrace(); } finally { if (mSocket != null) { try { mSocket.close(); } catch (IOException io) { io.printStackTrace(); } } } } /** * 判断网络是否连接 */ private Boolean isNetworkConnected() { if (manager == null) return false; NetworkInfo networkinfo = manager.getActiveNetworkInfo(); return !(networkinfo == null || !networkinfo.isAvailable()); } /** * 域名解析 */ private boolean parseDomain(String domain) { boolean flag = false; Map , Object> map = getDomainIp(domain); String useTime = (String) map.get("useTime"); mAddress = (InetAddress[]) map.get("remoteInet"); if (mAddress != null && mAddressIpList != null && mAddress.length > 0) {// 解析正确 if (!TextUtils.isEmpty(mAddress[0].getHostAddress())) { mAddressIpList.add(mAddress[0].getHostAddress()); flag = true; } } else {// 解析不到,判断第一次解析耗时,如果大于10s进行第二次解析 if (Integer.parseInt(useTime) > 10000) { map = getDomainIp(domain); mAddress = (InetAddress[]) map.get("remoteInet"); if (mAddress != null && mAddressIpList != null && mAddress.length > 0) { if (!TextUtils.isEmpty(mAddress[0].getHostAddress())) { mAddressIpList.add(mAddress[0].getHostAddress()); flag = true; } } } } return flag; } /** * 解析IP */ private Map , Object> getDomainIp(String domain) { Map , Object> map = new HashMap<>(); long start = 0; long end; String time = null; InetAddress[] remoteInet = null; try { start = System.currentTimeMillis(); remoteInet = InetAddress.getAllByName(domain); if (remoteInet != null) { end = System.currentTimeMillis(); time = (end - start) + ""; } } catch (UnknownHostException e) { end = System.currentTimeMillis(); time = (end - start) + ""; remoteInet = null; e.printStackTrace(); } finally { map.put("remoteInet", remoteInet); map.put("useTime", time); } return map; } }
使用方法:实例化NetPingManager,然后调用getDelay()方法
NetPingManager net = new NetPingManager(getApplicationContext(), "www.baidu.com",
new NetPingManager.IOnNetPingListener() { @Override public void onDelay(long delay, int type) { if (!isDestroyed() && !isFinishing() && tvdelay != null) { runOnUiThread(new Runnable() { @Override public void run() { switch (type) { case 1: tvdelay.setTextColor(Color.parseColor("#A7FC3F")); ABViewUtil.setTextViewLeftDrawable(R.drawable.nvb_icon_speed1, tvdelay); break; case 2: tvdelay.setTextColor(Color.parseColor("#FFF34A")); ABViewUtil.setTextViewLeftDrawable(R.drawable.nvb_icon_speed2, tvdelay); break; case 3: tvdelay.setTextColor(Color.parseColor("#FF0000")); ABViewUtil.setTextViewLeftDrawable(R.drawable.nvb_icon_speed3, tvdelay); break; default: ABViewUtil.setTextViewLeftDrawable(R.drawable.nvb_icon_speed1, tvdelay); break; } tvdelay.setText(String.valueOf(delay) + " ms"); } }); } } @Override public void onError() { LogUtil.i("tag", "无网络"); } }); if (netPingManager != null) { netPingManager.getDelay(); }