Java後端部署以及與Android通信注意事項

1 概述

本文列舉了一些Android+後端Java通信/部署時的問題以及注意事項,覆蓋的問題包括但不限於安全組、數據庫、路徑等,如果各位讀者的Android端不能正常訪問Java後端,希望這裏的解決方案能幫助到您。

2 分類

這裏將問題分爲三類:

  • Java端問題
  • Android端問題
  • 其他雜項問題

先來看一下Java端可能出現的問題。

3 Java

包括:

  • 數據庫
  • 安全組/防火牆
  • 404

3.1 數據庫

3.1.1 驅動

注意MySQL5.7MySQL8註冊驅動時是不一樣的,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

MavenWAR打包插件不能將新修改的文件編譯爲字節碼並打包,因此,如果修改了數據庫的密碼後,需要重新編譯,一般步驟是先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來說,本地測試不能使用localhost127.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 最後

希望讀者看到最後能解決問題,另外如果還有其他問題或一些解決問題的其他方案歡迎評論補充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章