WiFi P2P文件传输APP选择文件时卡顿无响应问题排查
WiFi P2P文件传输卡顿问题排查与修复
核心问题分析
- 主线程阻塞:
sendFile直接在onActivityResult中调用,而onActivityResult运行在主线程。网络连接、文件读写都是耗时操作,会导致主线程被阻塞,引发界面卡顿甚至ANR(应用无响应)。 - 内容Uri处理错误:
ACTION_OPEN_DOCUMENT返回的是系统内容Uri,并非真实文件路径。用fileUri.getPath()创建File对象无法正确获取文件信息(文件名、大小),虽暂时无报错,但属于逻辑隐患。 - Socket端口错误:
socket.connect中用本地端口作为远程连接端口,完全不符合TCP连接逻辑,会导致连接失败,主线程阻塞在连接超时环节。
修复方案
1. 后台线程执行耗时操作
将文件发送逻辑移到子线程,避免阻塞主线程。
2. 正确解析内容Uri
通过ContentResolver查询获取文件名和文件大小,替代错误的File对象方式。
3. 修正Socket连接端口
指定对方设备监听的固定端口(比如12345),确保两端端口一致。
修正后代码示例
选择文件按钮点击事件(保持原样)
btnSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.setType("*/*"); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent, PICK_FILE_REQUEST_CODE); } });
onActivityResult(改用子线程调用发送方法)
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_FILE_REQUEST_CODE && resultCode == RESULT_OK && data != null) { Uri selectedFileUri = data.getData(); HostInfo info = new HostInfo(); info.groupOwnerAddress = "192.168.49.1"; // 启动子线程执行文件发送 new Thread(() -> sendFile(selectedFileUri, info)).start(); } }
修正后的sendFile方法
private void sendFile(Uri fileUri, HostInfo info) { checkPermission(); String hostAddress = null; if (info != null && info.groupOwnerAddress != null) { hostAddress = info.groupOwnerAddress; } if (hostAddress == null) { Log.e(TAG, "Host address is null"); return; } // 对方设备监听的固定端口 final int REMOTE_PORT = 12345; int buffersize = 8192; // 增大缓冲区提升传输效率 // 从ContentResolver获取文件名和大小 String fileName = null; long fileSize = 0; Cursor cursor = getContentResolver().query(fileUri, new String[]{ OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }, null, null, null); if (cursor != null && cursor.moveToFirst()) { fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)); cursor.close(); } if (fileName == null) { Log.e(TAG, "Failed to get file name"); return; } try (Socket socket = new Socket()) { socket.bind(null); // 连接到对方指定端口 socket.connect(new InetSocketAddress(hostAddress, REMOTE_PORT), 5000); try (InputStream inputStream = getContentResolver().openInputStream(fileUri); OutputStream outputStream = socket.getOutputStream()) { if (inputStream == null) { Log.e(TAG, "Failed to open input stream"); return; } // 发送文件名长度与文件名 byte[] nameBuffer = fileName.getBytes(); // 若文件名长度可能超过255,需改用int(4字节)存储长度 outputStream.write(nameBuffer.length); outputStream.write(nameBuffer, 0, nameBuffer.length); // 发送文件大小(4字节大端格式) outputStream.write((byte) ((fileSize >> 24) & 0xFF)); outputStream.write((byte) ((fileSize >> 16) & 0xFF)); outputStream.write((byte) ((fileSize >> 8) & 0xFF)); outputStream.write((byte) (fileSize & 0xFF)); // 发送文件内容 byte[] buffer = new byte[buffersize]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.flush(); } } catch (IOException e) { Log.e(TAG, "File send failed", e); } }
额外注意点
- 确保对方设备在
REMOTE_PORT端口开启ServerSocket监听,否则连接会失败。 - Android 10及以下需申请
READ_EXTERNAL_STORAGE动态权限;Android 11+建议使用SAF(存储访问框架),避免申请宽泛的存储权限。 - 大文件传输时可添加进度回调,避免用户误以为应用卡顿。
内容的提问来源于stack exchange,提问作者AlexGoooner
相关产品推荐
相关产品推荐

