1 概述
本文列舉了一些Android
+後端Java
通信/部署時的問題以及注意事項,覆蓋的問題包括但不限於安全組、數據庫、路徑等,如果各位讀者的Android
端不能正常訪問Java
後端,希望這裏的解決方案能幫助到您。
2 分類
這裏將問題分爲三類:
Java
端問題Android
端問題- 其他雜項問題
先來看一下Java
端可能出現的問題。
3 Java
端
包括:
- 數據庫
- 安全組/防火牆
404
3.1 數據庫
3.1.1 驅動
注意MySQL5.7
與MySQL8
註冊驅動時是不一樣的,MySQL5.7
是:
Class.forName("com.mysql.jdbc.Driver");
MySQL8
以上是:
Class.forName("com.mysql.cj.jdbc.Driver");
另外要注意JAR
包版本正確。
3.1.2 用戶名/密碼/權限
首先需要確保配置文件中的訪問數據庫的用戶名以及密碼要正確,不然的話可能會出現各種空指針錯誤,另外需要確保該用戶對目標數據庫或表具有對應的權限。
3.1.3 Spring Boot
中的加密配置
在application.yaml
/application.yml
/application.properties
中配置對應的用戶名以及密碼,一般明文是沒問題的,這裏是針對使用了Jasypt
加密庫(Jasypt
可以加密Spring Boot
中的配置文件,使用可以參考這裏)來說的。
因爲使用Jasypt
後配置文件爲密文,如果此時的加密口令不能正確讀取會直接報錯,因此可以針對Jasypt
的加密方式(簡單口令加密、非明文口令、非對稱加密等)查看配置文件是否有錯漏,或者是否配置了對應的環境變量等。
3.1.4 Maven
Maven
的WAR
打包插件不能將新修改的文件編譯爲字節碼並打包,因此,如果修改了數據庫的密碼後,需要重新編譯,一般步驟是先clean
,再編譯最後打包:
3.2 安全組/防火牆
3.2.1 不帶防火牆
查看防火牆可以使用:
systemctl status firewalld
# 或
systemctl status iptables
如果是沒有開啓,那就只需要處理安全組就可以了。
一般服務器廠商都會提供安全組,開放對應的端口以及IP
即可:
3.2.2 帶防火牆
3.2.2.1 firewalld
帶防火牆的話,可以先查看開啓了哪些服務以及規則:
firewall-cmd --list-services
firewall-cmd --list-ports
並根據需要添加相應規則,比如開放8080
端口給所有IP
:
# 添加規則
firewall-cmd --add-port=8080/tcp --permanent
# 重新加載
firewall-cmd --reload
移除規則的話可以使用:
# 移除規則
firewalld-cmd --remove-port=8080/tcp --permanent
# 也需要重新加載
firewall-cmd --reload
3.2.2.2 iptables
查看狀態:
systemctl status iptables
添加規則,以開啓8080
端口爲例,修改/etc/sysconfig/iptables
:
vim /etc/sysconfig/iptables
並添加:
-A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT
重啓服務生效:
systemctl restart iptables
3.3 404
出現404
的原因主要是因爲路徑出現問題,這裏分兩種情況討論:
- 使用
JavaWeb Servlet
形式 Spring Boot
3.3.1 Servlet
使用Servlet
的話,本地運行需要配置Tomcat
或其他Web
服務器,因此如果在IDEA
中配置Tomcat
的話,需要在Application context
這裏配置路徑:
當然不配置也不是不可以,默認有一個項目名的路徑,通常會很長難以記憶,建議修改爲一個容易記憶的路徑。
如果是部署在服務器上的Tomcat
,需要注意打包後的WAR
包名字,比如打包後爲demo.war
,放置webapps
後,啓動Tomcat
會自動解包爲webapps/demo
,這時候的路徑需要對應demo
進行訪問,比如:
http://www.example.com:8080/demo
http://xxx.xxx.xxx.xxx:8080/demo
3.3.2 Spring Boot
使用SpringBoot
的話基本上不會出現路徑錯誤的問題,得益於SpringBoot
強大的默認配置,一般在對應的方法上寫上:
@GetMapping("/demo")
@PostMapping("/demo")
就可以訪問了。
4 Android
端
排查完了Java
端問題再來看看Android
端的問題,包括:
HTTP
- 線程
AVD
- 權限
IP
4.1 HTTP
從Android P
開始默認要求使用HTTPS
,默認情況下使用HTTP
會出現如下異常:
W/System.err: java.io.IOException: Cleartext HTTP traffic to **** not permitted
java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by network security policy
有兩種解決辦法:
- 使用
HTTPS
- 修改
AndroidManifest.xml
使其允許HTTP
連接
使用後一種方法的話,在AndroidManifest.xml
添加如下語句即可:
<application
android:usesCleartextTraffic="true"
/>
4.2 線程
Android 4.0
開始要求聯網不能在主線程操作,所有有關聯網的操作都需要新開一個線程,因此處理比較簡單,將網絡操作放在新線程中即可,示例如下:
FutureTask<String> task = new FutureTask<>(new NetworkThread());
Thread thread = new Thread(task);
thread.start();
String result = task.get();
public class NetworkThread implements Callable<String> {
@Override
public String call(){
//網絡操作
return result;
}
}
利用FutureTask
,添加一個實現了Callable<T>
(T
的類型是線程操作的返回類型,比如這裏是返回String
)的類到其中,start()
線程後,get()
獲取結果即可。
4.3 權限
在AndroidManifest.xml
添加網絡權限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
4.4 AVD
出錯日誌如下:
StackOverflow
上的方案是卸載APP並重啓AVD
:
這個方法筆者測試是有效的但不確保百分百有效,估計這個是AVD
的問題。
4.5 IP
對於Android
來說,本地測試不能使用localhost
或127.0.0.1
,也就是不能使用:
"http://localhost:8080/demo/xxxx"
"http://127.0.0.1:8080/demo/xxxx"
而是使用內網IP
,比如192.168
開頭的,或10.253
開頭的,可以使用
ip addr
# 或
ifconfig
查看。
5 其他問題
5.1 HTTP
通信
這個主要是針對Java Web
來說的,因爲需要手動配置各種HTTP
配置,比如Content-Type
以及編碼等,因此如果設置錯誤或Android
端與Java
端不一致就會導致Android
端無法發送正確的參數或Java
端無法獲取正確的參數。
這裏以原生的HttpURLConnection
爲例,在Android
端,發送請求的設置如下:
String url = "http://xxx.xxx.xxx.xxx:xxxx/demo/xxxx";
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
String data = "name="+ URLEncoder.encode(name, StandardCharsets.UTF_8.toString())+"&password="+URLEncoder.encode(password,StandardCharsets.UTF_8.toString());
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.getOutputStream().write(data.getBytes(StandardCharsets.UTF_8));
byte [] bytes = new byte[1024];
int len = connection.getInputStream().read(bytes);
return new String(bytes,0,len,StandardCharsets.UTF_8);
開啓連接後,通過拼接以及URLEncoder.encode()
的方式得到要發送的數據,再獲取輸出流將其寫入。
在Java
端對應代碼如下:
@WebServlet("/xxxxx")
public class XXXServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
String name = request.getParameter("name");
String password = request.getParameter("password");
}
}
首先設置編碼UTF8
,然後獲取參數即可,返回之前需要設置返回類型,這裏是application/json
。
5.2 JSON
序列化
JSON
序列化框架有很多,常見的有:
Gson
Jackson
fastjson
這裏筆者測試的時候使用Jackson
同時配合Lombok
,注意需要加上@Setter
以及@Getter
,不然序列化/反序列化無法正常工作。
另外還需要存在默認的構造方法,使用序列化響應體時,示例如下:
@Setter
@Getter
public class ResponseBody{
private Object data;
private int code;
}
如果存在了其他的構造方法請添加@NoArgsConsturctor
,生成一個無參構造方法。
在Kotlin
中則需要指定默認值,比如:
class ResponseBody(var code:Int,var data:Any)
這樣是無法正常工作的,添加默認值就可以了:
class ResponseBody(var code:Int=0,var data:Any="")
6 最後
希望讀者看到最後能解決問題,另外如果還有其他問題或一些解決問題的其他方案歡迎評論補充。