Android中使用OkHttp3无法连接WebSocket问题求助
Android端OkHttp3连接WebSocket失败无日志问题排查
我在服务器上部署了一个已验证可用的WebSocket,但用OkHttp3在Android端连接时失败,日志里完全没报错。我是Android开发新手,下面是我的Android端代码、Manifest文件,另外相同的WebSocket连接逻辑在普通Java项目里能正常运行。
Android端代码
package kean.pathfinder; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.PersistableBundle; import android.widget.Button; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.MapView; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Polyline; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; import kean.pathfinder.websocket.WebListener; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.WebSocket; public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, LocationListener { private MapView map; private final Path path = new Path(); private GoogleMap googleMap; private LocationManager locationManager; private WebSocket ws; private OkHttpClient client; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); map = findViewById(R.id.map); Button start = findViewById(R.id.start); Button undo = findViewById(R.id.undo); Button clear = findViewById(R.id.clear); client = new OkHttpClient(); Request request = new Request.Builder().url("ws://myServer.ip").build(); WebListener listener = new WebListener(); ws = client.newWebSocket(request,listener); ws.send("Hi from android"); ws.close(1000,"Session end"); start.setOnClickListener(l -> { try { JSONObject object = new JSONObject(); JSONArray array = new JSONArray(); for (LatLng latLng : path.getPoints()){ JSONObject point = new JSONObject(); point.put("lat",latLng.latitude); point.put("lng",latLng.longitude); array.put(point); } object.put("points",array); ws.send(object.toString()); } catch (JSONException e){ e.printStackTrace(); } }); undo.setOnClickListener(l -> { path.undo(); updatePoly(); }); clear.setOnClickListener(l -> { path.clear(); updatePoly(); }); map.getMapAsync(this); map.onCreate(savedInstanceState); locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); requestLocation(); } public void onMapReady(@NonNull GoogleMap googleMap) { googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); this.googleMap = googleMap; googleMap.setOnMapClickListener(l -> { path.add(l); updatePoly(); }); } private final AtomicReference<Polyline> polyline = new AtomicReference<>(); private void updatePoly(){ Polyline p = polyline.get(); if (p != null) p.remove(); polyline.set(googleMap.addPolyline(path.getPoly())); } public void onLocationChanged(@NonNull Location location) { LatLng latLng = new LatLng(location.getLatitude(),location.getLongitude()); googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng,50)); locationManager.removeUpdates(this); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1){ requestLocation(); } } private void requestLocation() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},1); return; } locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 400L, 1000f, this); } @Override protected void onStart() { super.onStart(); map.onStart(); } @Override protected void onResume() { super.onResume(); map.onResume(); } @Override protected void onPause() { super.onPause(); map.onPause(); } @Override protected void onStop() { super.onStop(); map.onStop(); ws.close(1000,"Session ended"); client.dispatcher().executorService().shutdown(); } @Override protected void onDestroy() { super.onDestroy(); map.onDestroy(); } @Override public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) { super.onSaveInstanceState(outState, outPersistentState); map.onSaveInstanceState(outState); } @Override public void onLowMemory() { super.onLowMemory(); map.onLowMemory(); } }
Manifest文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.PathFinder" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="token" /> </application> </manifest>
普通Java项目测试代码(可正常运行)
package org.example; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.WebSocket; public class Main { public static void main(String[] args) { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url("ws://myServer.ip").build(); // Empty class WebListener listener = new WebListener(); WebSocket ws = client.newWebSocket(request,listener); ws.send("Hello World"); ws.close(1000,"End"); client.dispatcher().executorService().shutdown(); } }
问题解决要点
- 移除过早关闭WebSocket的代码:
onCreate里创建WebSocket后立刻调用ws.close会直接终止连接,仅保留onStop中的关闭逻辑即可,避免连接未建立就被中断。 - 实现完整的WebSocketListener:空的
WebListener无法反馈连接状态,必须重写关键方法获取日志:
public class WebListener extends WebSocketListener { private static final int NORMAL_CLOSURE_STATUS = 1000; @Override public void onOpen(WebSocket webSocket, Response response) { Log.d("WebSocket", "连接成功"); webSocket.send("Hi from android"); // 连接成功后再发送消息 } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { Log.e("WebSocket", "连接失败", t); } @Override public void onMessage(WebSocket webSocket, String text) { Log.d("WebSocket", "收到消息: " + text); } @Override public void onClosing(WebSocket webSocket, int code, String reason) { webSocket.close(NORMAL_CLOSURE_STATUS, null); Log.d("WebSocket", "正在关闭: " + code + " / " + reason); } @Override public void onClosed(WebSocket webSocket, int code, String reason) { Log.d("WebSocket", "已关闭: " + code + " / " + reason); } }
- 添加明文网络许可:Android 9+默认禁止明文WS请求,在Manifest的
application标签里添加:
android:usesCleartextTraffic="true"
- 调整消息发送时机:不要在
onCreate里直接调用ws.send,必须在onOpen回调确认连接成功后再发送,避免因连接未建立导致消息丢失。
内容的提问来源于stack exchange,提问作者KZB
相关产品推荐
相关产品推荐

