Android中AsyncTask调用API返回旧结果问题求助
解决DialogFlow API返回旧值的AsyncTask异步问题
你遇到的这个问题核心是AsyncTask的异步执行特性导致的,咱们一步步拆解问题并给出修复方案:
问题根源分析
在你的data(View v)方法里,启动AsyncTask后立刻就去读取DATA的值,但此时AsyncTask还在后台执行网络请求,onPostExecute还没来得及更新MainActivity.DATA,所以你拿到的必然是上一次请求留下的旧值。另外还有两个潜在小问题:
QUERY作为静态变量,虽然能拿到新值,但如果包含空格或特殊字符,直接拼URL会导致请求异常- 异常日志太简略,不利于排查隐藏问题
修复方案
1. 把UI更新逻辑移到AsyncTask的onPostExecute中
这是解决异步问题的核心——onPostExecute是AsyncTask后台任务完成后唯一安全的UI更新入口,把设置TextView的代码放在这里,确保拿到的是最新的返回值:
// 修改AsyncTask,把返回类型从Void改为String,直接返回请求结果 class Async extends AsyncTask<Void, Void, String>{ private Context context; // 传入Activity上下文,用于更新UI public Async(Context context) { this.context = context; } @Override protected String doInBackground(Void... voids) { String data = ""; try { Log.e("Query in url", MainActivity.QUERY); // 对QUERY做URL编码,避免特殊字符破坏请求格式 String encodedQuery = URLEncoder.encode(MainActivity.QUERY, "UTF-8"); URL url = new URL ("https://api.dialogflow.com/v1/query?v=20150910&contexts=[]&lang=en&query=" + encodedQuery +"&sessionId=bee67580-d05c-47f6-8d64-a6218c3913e1"); URLConnection httpURLConnection = url.openConnection (); httpURLConnection.setRequestProperty ("Authorization", "Bearer CONFIDENTIAL KEY"); InputStream inputStream = httpURLConnection.getInputStream (); BufferedReader bufferedReader = new BufferedReader (new InputStreamReader (inputStream)); String line; while ((line = bufferedReader.readLine ()) != null) { data += line; } } catch (MalformedURLException e) { Log.i ("PROBLEM", "URL错误: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { Log.i ("PROBLEM", "IO异常: " + e.getMessage()); e.printStackTrace(); } return data; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); MainActivity.DATA = result; // 后台任务完成后再更新UI if(result != null){ ((MainActivity)context).textView1.setText(result); } } }
然后修改data方法,去掉提前读取DATA的逻辑:
public void data(View v){ Toast.makeText (this, QUERY, Toast.LENGTH_SHORT).show (); // 传入当前Activity上下文启动AsyncTask Async async = new Async(this); async.execute (); }
2. 优化静态变量(可选但推荐)
静态变量QUERY和DATA容易引发内存泄漏,建议改成MainActivity的成员变量:
// 替换原来的静态变量 private String query = null; private String data = null;
然后在语音识别回调里更新成员变量:
@Override public void onResults(Bundle bundle) { ArrayList<String> arrayList = bundle.getStringArrayList (SpeechRecognizer.RESULTS_RECOGNITION); if(arrayList!=null){ textView.setText (arrayList.get (0)); query = arrayList.get (0); }else { Toast.makeText (MainActivity.this, "Array List is null", Toast.LENGTH_SHORT).show (); } }
同时修改AsyncTask的构造方法,直接传入query值,避免依赖静态变量:
class Async extends AsyncTask<Void, Void, String>{ private Context context; private String query; public Async(Context context, String query) { this.context = context; this.query = query; } @Override protected String doInBackground(Void... voids) { // 后续代码里直接用this.query代替MainActivity.QUERY Log.e("Query in url", query); String encodedQuery = URLEncoder.encode(query, "UTF-8"); // ... 其余请求逻辑不变 } }
启动AsyncTask时传入query:
Async async = new Async(this, query); async.execute();
总结
这个问题的核心就是你在异步任务未完成时提前读取结果,导致拿到旧值。把UI更新逻辑移到onPostExecute就能解决根本问题,加上URL编码和详细异常日志,能让你的代码更健壮。
内容的提问来源于stack exchange,提问作者Pratham Khurana
相关产品推荐
相关产品推荐

