点击连接树莓派按钮时App崩溃,求原因及解决方案
当点击按钮连接树莓派时,App出现崩溃现象:TextView会显示“Checking Inventory”,但随即崩溃,同时树莓派端的程序也终止。我推测问题可能与AsyncTask相关,但无法确定。以下是我的代码:
public class MainActivity extends AppCompatActivity { TextView results; public String response; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); results = findViewById(R.id.countTextView); Button thingBtn = findViewById(R.id.thingBtn); thingBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { results.setText("Checking Inventory"); new ConnectTask().execute(); } }); } public String Authentication = "we gucci"; public class ConnectTask extends AsyncTask<String, Void, String>{ @Override protected void onPreExecute() { Log.d(TAG, "AsyncTask starting"); super.onPreExecute(); } @Override protected String doInBackground(String... message) { try { Log.d(TAG, "Attempting to Connect to Socket"); InetAddress serverAddress = InetAddress.getByName("192.168.1.11"); //must change later!!! Socket house = new Socket(serverAddress, 5060); Log.d(TAG, "Connected to Socket on (" + serverAddress + ", " + house.getPort() + ")"); BufferedOutputStream bos =new BufferedOutputStream(house.getOutputStream()); bos.write(Authentication.getBytes("UTF-8")); bos.flush(); BufferedReader serverResponse = new BufferedReader(new InputStreamReader(house.getInputStream())); response = serverResponse.readLine(); Log.d(TAG, "Server response: " + response); house.close(); Log.d(TAG, "socket closed"); }catch (IOException e) { System.exit(1); } return response; } @Override protected void onPostExecute(String response){ Log.d(TAG, response); results.setText(response); } } }
嘿,我来帮你搞定这个崩溃问题!你猜的没错,问题确实和AsyncTask的使用以及异常处理有关,主要有这几个关键点:
1. 致命错误:System.exit(1)直接终止应用进程
在doInBackground的异常捕获块里,你调用了System.exit(1)——这会直接杀死整个Android应用进程,这就是App瞬间崩溃的核心原因!当网络连接出现任何IO异常(比如树莓派没开机、IP不对、端口没监听),你的代码直接强制退出了应用,这完全不符合Android的异常处理逻辑。
解决方法:
移除System.exit(1),改为捕获异常并返回错误信息,比如:
catch (IOException e) { Log.e(TAG, "Socket connection failed: " + e.getMessage()); return "Connection failed: " + e.getMessage(); }
2. 成员变量response存在线程安全隐患
你把response定义为MainActivity的成员变量,在doInBackground里赋值,然后在onPostExecute里使用。但AsyncTask的doInBackground是在后台线程执行,onPostExecute在主线程,虽然这里不会有并发赋值,但依赖成员变量传递结果是不规范的,而且如果多次执行AsyncTask,成员变量可能被覆盖,导致结果混乱。
解决方法:
直接使用doInBackground的返回值作为onPostExecute的参数,完全不需要成员变量response。修改后,doInBackground里直接返回读取到的结果或者错误信息,onPostExecute直接用传入的参数即可。
3. 未处理网络操作的其他潜在异常
比如InetAddress.getByName可能抛出UnknownHostException(属于IOException的子类,但最好明确处理),还有读取流时可能出现的异常,另外,Socket连接应该放在try-with-resources块里,确保资源自动关闭,避免内存泄漏。
优化后的完整代码示例:
public class MainActivity extends AppCompatActivity { TextView results; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); results = findViewById(R.id.countTextView); Button thingBtn = findViewById(R.id.thingBtn); thingBtn.setOnClickListener(v -> { results.setText("Checking Inventory"); new ConnectTask().execute(); }); } private final String Authentication = "we gucci"; public class ConnectTask extends AsyncTask<String, Void, String>{ @Override protected void onPreExecute() { super.onPreExecute(); Log.d(TAG, "AsyncTask starting"); } @Override protected String doInBackground(String... message) { // 使用try-with-resources自动关闭Socket、流资源 try (Socket house = new Socket("192.168.1.11", 5060); BufferedOutputStream bos = new BufferedOutputStream(house.getOutputStream()); BufferedReader serverResponse = new BufferedReader(new InputStreamReader(house.getInputStream()))) { Log.d(TAG, "Connected to Socket on (" + house.getInetAddress() + ", " + house.getPort() + ")"); bos.write(Authentication.getBytes("UTF-8")); bos.flush(); String response = serverResponse.readLine(); Log.d(TAG, "Server response: " + response); return response != null ? response : "Empty response from server"; } catch (UnknownHostException e) { Log.e(TAG, "Unknown host: " + e.getMessage()); return "Error: Could not find the device"; } catch (IOException e) { Log.e(TAG, "Connection error: " + e.getMessage()); return "Error: Failed to connect to device"; } } @Override protected void onPostExecute(String result){ super.onPostExecute(result); Log.d(TAG, "Received result: " + result); results.setText(result); } } }
额外建议
- 从Android API 30开始,AsyncTask已经被标记为废弃,推荐使用
Coroutines(协程)或者ExecutorService来处理后台任务,代码会更简洁且易于维护。 - 确保你的App已经申请了
INTERNET权限,在AndroidManifest.xml里添加:
<uses-permission android:name="android.permission.INTERNET" />
- 树莓派端的程序终止,可能是因为App崩溃时Socket连接被强制关闭,导致树莓派端的程序没有处理断开的情况,建议在树莓派的代码里添加异常捕获,处理客户端断开的场景。
内容的提问来源于stack exchange,提问作者Zachary Acosta

