2016-03-25

【Android】用 PING 偵測網際網路連線狀況

請改參考




2023.03.19
感覺這篇筆記的做法多做了一些不必要的事情,但這也是學習的歷程,所以還是保留......





參考資料 ----
ConnectivityManager
NetworkInfo
Process
ping(8) - Linux man page


或可透過 是否成功獲取網頁內容 做為判斷網際網路連線的依據,參考
【Kotlin】以 OkHttp3 偵測是否有網際網路連線能力


首先授予 app 權限

AndroidManifest.xml
  1.  
  2. ...
  3. ...
  4.  
  5. <uses-permission android:name="android.permission.INTERNET"/>
  6. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  7.  
  8. ...
  9. ...
  10.  

activity_main.xml
  1.  
  2. <?xml version="1.0" encoding="utf-8"?>
  3. <RelativeLayout
  4. xmlns:android="http://schemas.android.com/apk/res/android"
  5. xmlns:tools="http://schemas.android.com/tools"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent"
  8. android:paddingBottom="@dimen/activity_vertical_margin"
  9. android:paddingLeft="@dimen/activity_horizontal_margin"
  10. android:paddingRight="@dimen/activity_horizontal_margin"
  11. android:paddingTop="@dimen/activity_vertical_margin"
  12. tools:context="com.oldgrayduck.ping.MainActivity">
  13.  
  14.  
  15. <Button
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:text="PING"
  19. android:id="@+id/btnPing"
  20. android:layout_alignParentTop="true"
  21. android:layout_alignParentLeft="true"
  22. android:layout_alignParentStart="true"
  23. android:nestedScrollingEnabled="true"
  24. android:onClick="onbtnPingClicked"/>
  25. </RelativeLayout>
  26.  

一般偵測區域網路連線,只要 ConnectivityManager + NetworkInfo 即可,如下:
  1.  
  2. ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  3. NetworkInfo info = connManager.getActiveNetworkInfo();
  4.  
  5. // 可透過下列指令取得進一步的連線資訊
  6. info.getTypeName(); // 目前以何種方式連線 (WIFI, MOBILE)
  7. info.getState(); // 目前連線狀態 (DISCONNECTED, CONNECTING, CONNECTED)
  8. info.isAvailable(); // 目前網路是否可使用 (true/false)
  9. info.isConnected(); // 網路是否已連接 (true/false)
  10. info.isConnectedOrConnecting(); // 網路是否已連接 或 連線中 (true/false)
  11. info.isFailover(); // 網路目前是否有問題 (true/false)
  12. info.isRoaming(); // 網路目前是否在漫遊中 (true/false)
  13.  
  14.  

但這不足以得知是否連上網際網路,例如:
一般我們家裡都有無線路由器,當我們把路由器上 WAN 的線拔掉,家裡對外就斷線了,但 Android 設備仍可以連上路由器,則

info.isAvailable() 是 true
info.isConnected() 是 true

這樣無法判斷是否連上網際網路。

Android 的底層是 Linux,一般來說,在 /system/bin/ 下有 ping 這個程式,如果沒有,可能是被該 Android 裝置的製造商移除了;因為是 Linux 的指令,指令參數請參考 Linux;我們可以藉 ping 來偵測網路是否通。

  1.  
  2. ...
  3. ...
  4.  
  5. public void onbtnPingClicked(View v)
  6. {
  7. if (isConnected())
  8. Toast.makeText(this, "網路已連線", Toast.LENGTH_LONG).show();
  9. else
  10. Toast.makeText(this, "斷線中", Toast.LENGTH_LONG).show();
  11. }
  12.  
  13. private boolean isConnected()
  14. {
  15. boolean result = false;
  16. ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  17. NetworkInfo info = connManager.getActiveNetworkInfo();
  18. if (info==null || !info.isConnected())
  19. {
  20. Log.d(TAG, "網路 不通");
  21. result = false;
  22. }
  23. else
  24. {
  25. if(info.isConnected())
  26. Log.d(TAG, "info connected");
  27. else
  28. Log.d(TAG, "info disconnected");
  29. if (info.isAvailable())
  30. {
  31. try
  32. {
  33. Log.d(TAG, "info is available");
  34. // 不像 Windows 的 ping 預設執行 4 次
  35. // Linux 的 ping 預設是一直執行的,所以一定要下參數指定次數
  36. // 不然會造成您的 app 無回應
  37.  
  38. // 試著 ping 168.95.1.1, 4 次, 參數 -c 4
  39. Process process = new ProcessBuilder().command("/system/bin/ping","-c 4","168.95.1.1")
  40. .redirectErrorStream(true)
  41. .start();
  42. try
  43. {
  44. String strPing = "";
  45. InputStream in = process.getInputStream();
  46. OutputStream out = process.getOutputStream();
  47. InputStreamReader reader = new InputStreamReader(in, "utf-8");
  48. int i;
  49. Log.d(TAG, "read instream");
  50. while ((i = in.read()) != -1)
  51. {
  52. strPing = strPing + (char) i;
  53. }
  54. out.close();
  55. in.close();
  56. reader.close();
  57. Log.d(TAG, "strPing ==== "+strPing);
  58. if(strPing.indexOf("ttl")>=0)
  59. {
  60. Log.d(TAG, "strPing 字串中有 ttl");
  61. result = true;
  62. }
  63. else
  64. {
  65. Log.d(TAG, "Ping 沒回應");
  66. result = false;
  67. }
  68. }
  69. catch(Exception e)
  70. {
  71. result = false;
  72. }
  73. finally
  74. {
  75. // 記得要釋放掉 process
  76. process.destroy();
  77. }
  78. }
  79. catch (Exception e)
  80. {
  81. Log.d(TAG, "Exception--> " + e.getMessage());
  82. result = false;
  83. }
  84. }
  85. else
  86. {
  87. Log.d(TAG, "網路不可用");
  88. result = false;
  89. }
  90. }
  91. return result;
  92. }
  93.  
  94. ...
  95. ...
  96.  

如果網際網路是通的,則 strPing 字串會含有如下的內容:

64 bytes from 168.95.1.1: icmp_seq=1 ttl=249 time=4.67 ms
64 bytes from 168.95.1.1: icmp_seq=2 ttl=249 time=6.55 ms
64 bytes from 168.95.1.1: icmp_seq=3 ttl=249 time=4.29 ms
64 bytes from 168.95.1.1: icmp_seq=4 ttl=249 time=5.27 ms


提醒您:老人家這篇筆記偷懶,並未考慮 app 執行的程式合理性及最佳化,ping 這種耗時的指令,建議您還是放在 ThreadAsyncTask 執行比較好。



相關筆記 ----
【Android】AsyncTask - Thread 外的另一選擇

沒有留言:

張貼留言