使用Java Socket从Android向PC传图片时filePath返回null如何解决?
问题根源
你遇到的两个核心报错原因如下:
- 高版本Android系统中,
ACTION_GET_CONTENT返回的content://类型Uri无法通过查询MediaStore.Images.Media.DATA字段拿到本地文件路径,所以你的getPath方法返回null,后续new File(selectedImagePath)触发空指针导致崩溃 - 网络操作直接写在主线程中,Android 4.0及以上版本禁止主线程执行网络请求,会直接抛出
NetworkOnMainThreadException - 清单文件缺少存储读取权限,无法访问设备上的图片文件
修复方案
1. 修改AndroidManifest.xml添加权限
在manifest节点下添加对应存储权限:
<!-- 安卓13及以上版本图片读取权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 安卓12及以下版本存储读取权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
注意安卓6.0及以上版本需要在应用启动时动态申请上述权限,仅在清单文件声明无效。
2. 修改MainActivity逻辑
删除原有getPath方法,直接通过ContentResolver读取Uri的输入流,同时将网络请求放到子线程执行,修改后的完整代码如下:
package com.example.floweridentifier; import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class MainActivity extends Activity { private static final int SELECT_PICTURE = 1; private static final int PERMISSION_REQUEST_CODE = 100; private Uri selectedImageUri; // 替换原有路径字段,直接存Uri private ImageView img; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); img = findViewById(R.id.ivPic); // 启动时先申请权限 checkPermission(); findViewById(R.id.bBrowse).setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult( Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE); } }); Button send = findViewById(R.id.bSend); final TextView status = findViewById(R.id.tvStatus); send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { if (selectedImageUri == null) { Toast.makeText(MainActivity.this, "请先选择图片", Toast.LENGTH_SHORT).show(); return; } // 网络操作放到子线程执行 new Thread(new Runnable() { @Override public void run() { Socket sock = null; try { // 测试阶段优先用PC的内网IP,不要直接用公网IP,需要确保PC和模拟器在同一局域网 sock = new Socket("你的PC内网IP", 800); // 直接从Uri读输入流,不需要转文件路径 InputStream is = getContentResolver().openInputStream(selectedImageUri); BufferedInputStream bis = new BufferedInputStream(is); OutputStream os = sock.getOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { os.write(buffer, 0, len); } os.flush(); runOnUiThread(new Runnable() { @Override public void run() { status.setText("发送成功"); } }); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (sock != null) sock.close(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); } }); } private void checkPermission() { String permission; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { permission = Manifest.permission.READ_MEDIA_IMAGES; } else { permission = Manifest.permission.READ_EXTERNAL_STORAGE; } if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{permission}, PERMISSION_REQUEST_CODE); } } public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == SELECT_PICTURE) { selectedImageUri = data.getData(); TextView path = findViewById(R.id.tvPath); path.setText("Image Uri : " + selectedImageUri); img.setImageURI(selectedImageUri); } } } }
3. 测试注意事项
- 优先使用PC的内网IP测试,确保模拟器和PC连接在同一个WiFi/局域网下
- 确认PC端的接收服务已启动,且800端口已放开防火墙限制
- 如果一定要用公网IP测试,需要在路由器上配置800端口的端口映射,指向你PC的内网IP
内容的提问来源于stack exchange,提问作者user11964310
相关产品推荐
相关产品推荐

