Android Studio开发猜球星APP在OPPO真机运行崩溃求助
问题排查与修复方案
核心崩溃原因:主线程被阻塞
你的代码在onCreate方法中直接调用了AsyncTask.execute().get(),这个方法会同步阻塞主线程,强制等待网络请求完成。Android系统对主线程阻塞有严格限制,尤其是OPPO这类定制化ROM,会直接判定应用无响应并终止进程。模拟器环境宽松,所以没触发崩溃。
修复步骤:
- 移除AsyncTask的.get()调用,改用回调机制
不要在主线程等待异步任务结果,重写AsyncTask的onPostExecute方法,在网络请求完成后再处理HTML解析和初始化UI:
public class DownloadTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... urls) { // 原网络请求逻辑不变 String result = ""; URL url; HttpURLConnection urlConnection = null; try { url = new URL(urls[0]); urlConnection = (HttpURLConnection) url.openConnection(); InputStream in = urlConnection.getInputStream(); InputStreamReader reader = new InputStreamReader(in); int data = reader.read(); while (data != -1) { char current = (char) data; result += current; data = reader.read(); } return result; } catch (Exception e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(String result) { super.onPostExecute(result); if (result == null) { Toast.makeText(MainActivity.this, "网络请求失败", Toast.LENGTH_SHORT).show(); return; } // 把原来onCreate里解析HTML、初始化列表、调用newQuestion的逻辑移到这里 try { String[] splitResult = result.split("<div class=\"organism contentStream slide\" id=\"slide68\">"); if (splitResult.length < 2) { Toast.makeText(MainActivity.this, "页面结构变更,解析失败", Toast.LENGTH_SHORT).show(); return; } // 解析图片URL逻辑 Pattern p = Pattern.compile("<img alt=(.*?)>"); Matcher m = p.matcher(splitResult[1]); while (m.find()) { Pattern p2 = Pattern.compile("src=\"(.*?)\""); Matcher m2 = p2.matcher(m.group(1)); while (m2.find()) { footballerURLs.add(m2.group(1)); } } // 解析球星名字逻辑 Pattern p3 = Pattern.compile("<div class=\"organism contentStream slide\"(.*?)/h2>"); Matcher m3 = p3.matcher(splitResult[1]); while (m3.find()) { Pattern p4 = Pattern.compile("<h2>(.*?)<"); Matcher m4 = p4.matcher(Objects.requireNonNull(m3.group(1))); while (m4.find()) { String footballerName = m4.group(1).trim(); footballerName = footballerName.replaceAll("[0-9]", ""); footballerName = footballerName.replaceAll("\\. ", ""); footballerNames.add(footballerName); } } if (!footballerNames.isEmpty()) { footballerNames.remove(1); } // 初始化完成后加载题目 newQuestion(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "初始化失败", Toast.LENGTH_SHORT).show(); } } }
然后在onCreate里启动异步任务即可,不要调用.get():
DownloadTask task = new DownloadTask(); task.execute("https://bleacherreport.com/articles/705591-the-100-best-soccer-players-of-all-time");
- 修复newQuestion中的主线程阻塞问题
同样,newQuestion里的imageTask.execute(...).get()也会阻塞主线程,改用onPostExecute回调:
public class ImageDownloader extends AsyncTask<String, Void, Bitmap> { @Override protected Bitmap doInBackground(String... urls) { // 原图片下载逻辑不变 try { URL url = new URL(urls[0]); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.connect(); InputStream inputStream = connection.getInputStream(); Bitmap myBitmap = BitmapFactory.decodeStream(inputStream); return myBitmap; } catch (Exception e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (bitmap != null) { imageView.setImageBitmap(bitmap); } } }
修改newQuestion方法,去掉.get():
public void newQuestion() { try { Random rand = new Random(); if (footballerURLs.isEmpty() || footballerNames.isEmpty()) { Toast.makeText(this, "数据未加载完成", Toast.LENGTH_SHORT).show(); return; } chosenPlayer = rand.nextInt(footballerURLs.size()); ImageDownloader imageTask = new ImageDownloader(); imageTask.execute(footballerURLs.get(chosenPlayer)); locationOfCorrectAnswer = rand.nextInt(4); int incorrectAnswerLocation; for (int i = 0; i < 4; i++) { if (i == locationOfCorrectAnswer) { answers[i] = footballerNames.get(chosenPlayer); } else { incorrectAnswerLocation = rand.nextInt(footballerURLs.size()); while (incorrectAnswerLocation == chosenPlayer) { incorrectAnswerLocation = rand.nextInt(footballerURLs.size()); } answers[i] = footballerNames.get(incorrectAnswerLocation); } } // 提前设置按钮文本,不用等图片加载 button0.setText(answers[0]); button1.setText(answers[1]); button2.setText(answers[2]); button3.setText(answers[3]); } catch (Exception e) { e.printStackTrace(); } }
其他潜在问题与优化
- HTML解析健壮性不足
依赖固定的HTML节点拆分(split("<div class=\"organism contentStream slide\" id=\"slide68\">")),一旦网站结构变更,会导致splitResult[1]数组越界抛异常。建议:
- 增加数组长度判断,避免下标越界
- 改用更可靠的HTML解析库替代正则,减少解析失败概率
- 网络安全配置
如果图片URL是HTTP明文链接,Android 9+默认禁止,需要在res/xml/network_security_config.xml中配置允许明文请求:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
然后在AndroidManifest.xml的<application>标签中添加:
android:networkSecurityConfig="@xml/network_security_config"
- 权限检查
确保AndroidManifest.xml中添加了网络权限:
<uses-permission android:name="android.permission.INTERNET" />
内容的提问来源于stack exchange,提问作者Mateo
相关产品推荐
相关产品推荐

