新項目的app,可通過內網和外網的服務器ip進行請求訪問,但是客戶提供了專業終端,終端在wifi情況下走外網內網都可以,但關閉wifi則只能走4G專網,也就是只能走內網。
可前往我的小站查看:Android根據內網外網連接情況配置服務器訪問IP
方案
Android中可以直接調用底層的shell,執行相應的命令,因此只需要執行ping命令即可。Android可以通過 Process p = Runtime.getRuntime().exec(/system/bin/ping -c 1 -w 1 " + ip)
執行。
然后通過if (p.waitFor() == 0)
判斷是否ping通,這里的兩個1
表示參數,第一個表示ping 1次,第二個表示操作1s即為失敗。
完整實現
- 首先聲明權限,這一步非常重要,在AndroidManifes文件中
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />
- 維持幾個全局變量
String outer_ip = "183.230.XXX.XXX"; // 服務器外網IP
String inner_ip = "192.168.XXX.XXX"; // 服務器內網IP
boolean outerIpAvilable = false; // 外網可用
boolean innerIpAvialable = false; // 內網可用
- 開啟兩個線程去ping兩個ip,並通過CountDownLatch控制同步。因為
要在兩個ping結束之后,配置了ip之后才能做接下來的操作
。
private void initNetworkConfig() {
try {
final int totalThread = 2;
CountDownLatch countDownLatch = new CountDownLatch(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new PingNetwork(outer_ip, countDownLatch, false));
executorService.execute(new PingNetwork(inner_ip, countDownLatch, true));
countDownLatch.await(); // 等待二者執行完畢
Log.d(TAG, "end");
if (innerIpAvialable && outerIpAvilable)
Toast.makeText(this, "內外都可使用", Toast.LENGTH_SHORT).show();
else if (outerIpAvilable)
Toast.makeText(this, "外網可使用", Toast.LENGTH_SHORT).show();
else
Toast.makeText(this, "內網可使用", Toast.LENGTH_SHORT).show();
executorService.shutdown();
}catch (Exception e) {
e.printStackTrace();
}
}
- 實現ping的異步線程,
class PingNetwork implements Runnable {
String ip; // 需要ping的ip
CountDownLatch countDownLatch;
boolean isCheckInner;
public PingNetwork(String ip, CountDownLatch countDownLatch, boolean isCheckInner) {
this.ip = ip;
this.countDownLatch = countDownLatch;
this.isCheckInner = isCheckInner;
}
@Override
public void run() {
try {
Process p = Runtime.getRuntime().exec("/system/bin/ping -c 1 -w 1 " + ip);// ping網址3次
// ping的狀態
final int status = p.waitFor();
if (status == 0) {
Log.d(TAG, "ping onSuccess");
if (isCheckInner){
innerIpAvialable = true;
outerIpAvilable = false;
}
else{
outerIpAvilable = true;
innerIpAvialable = false;
}
} else {
// 讀取ping的error內容,查看無法ping通的原因
InputStream errorStream = p.getErrorStream();
BufferedReader errIn = new BufferedReader(new InputStreamReader(errorStream));
StringBuilder sb = new StringBuilder();
String err = "";
while ((err = errIn.readLine()) != null) {
sb.append(err);
}
Log.d(TAG, "result err : " + sb.toString());
Log.d(TAG, "ping onFailure");
}
} catch (Exception e) {
Log.d(TAG, "ping onFailure");
} finally {
countDownLatch.countDown();
}
}
}
這里在ping失敗時候可以打印錯誤信息查看,還記得第一步是聲明權限,本人沒有聲明第二個權限,在這里得到了一個錯誤信息Pemission denied
,網上說什么root的都有,其實不然。
完善
通過以上的實現,可以實現通過內網外網連接情況配置訪問服務器的ip,但是設想一下,如果在app啟動時,手機可以訪問外網,所以程序配置了外網的ip(因為外網速度快),但是在使用的過程中,關閉了外網訪問,比如說wifi,此時走了專網,即內網,則無法再訪問服務器了。所以在切換網絡時,需要從新配置訪問ip。
因此,需要再app里面通過廣播的方式,在android N(android 7)之前,可以通過android.net.conn.CONNECTIVITY_CHANGE
廣播,可以靜態注冊和動態注冊,然而在7之后,改廣播無效了,可以使用以下方案替換。
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.requestNetwork(new NetworkRequest.Builder().build(),
new ConnectivityManager.NetworkCallback() {
@Override public void onAvailable(Network network) {
super.onAvailable(network);
LogUtil.d("網絡發生改變,更改配置");
NetworkUtils.initNetworkConfig();
}
});
NetworkUtils.initNetworkConfig();
是我對上面通過ping配置ip的封裝。