我正在尝试使用AutoCompleteTextView(ACTV)来显示我从网络资源中获取的结果。我已将完成阈值设置为2,并且当我输入字符时,我可以看到请求被触发。
我得到的结果是正确的。让我说我写“ca”,我得到结果“汽车”作为自动完成。我有一个回调函数,它接收来自AsyncTask的结果并将结果放入ArrayAdapter。然后我在ACTV上调用.showDropDown()并显示一个空的下拉列表(正常元素大小的一半)。然后,如果我输入最后一个字母“r”并且ACTV显示“car”,则显示下拉列表,结果突然出现在列表中。
如果我输入了两个字符(返回有效结果),则删除最后一个字母也会发生同样的情况。删除该字母后,“car”将显示为自动完成值。
有人有这个问题吗?看起来适配器已填充结果,但结果直到我执行的下一个操作才会显示。我已经尝试在将结果添加到适配器后运行.notifyDataSetChanged(),但是不应该这样做,或者?
答案 0 :(得分:16)
如果没有看到您的代码,很难说清楚可能会发生什么。但首先想到的是,您的网络请求发生在不同的线程上,因此您的performFiltering()
可能会过早地返回空结果集。此时,publishResults()
返回空结果,您的下拉列表为空。稍后,您的AsyncTask将返回结果,并将结果添加到适配器列表中,但出于某种原因,它仍未显示。
我认为你可能会错误地认识到对AsyncTask的需求。 Filter对象已经在做类似于AsyncTask的事情:performFiltering()
在后台线程中完成,并且在performFiltering()完成后从UI线程调用publishResults()
。因此,您可以直接在performFiltering()中执行网络请求,并将结果设置为FilterResults对象,您不必担心网络请求太慢并导致UI出现问题。
另一种解决方案,稍微复杂一点,但它是我在Filter对象中所做的事情(由于现有的架构在后台执行API调用,使用异步回调而不是阻塞/同步步骤,如果需要for performFiltering()),是使用带有wait()/ notify()的同步对象进行跨线程监控,因此效果与在performFiltering()中直接执行网络请求相同,但它实际上发生在多个线程:
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
synchronized APIResult waitForAPI(CharSequence constraint) {
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// wait() is a Java IPC technique that will block execution until another
// thread calls the same object's notify() method.
wait();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// notify() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
notify();
}
}
但是,我认为您可能会发现最简单的解决方案是直接在performFiltering()方法中同步执行您的网络请求。上面的代码示例只是一种可能性,如果您已经拥有适用于异步/回调驱动的API调用的体系结构,并且您不希望更改该行为以便在performFiltering()中获取同步结果。
答案 1 :(得分:1)
我认为Joe的答案是要走的路。但是,我认为您应该使用CountDownLatch而不是wait / notify。
原因是,使用wait / notify,如果你的API在启动“wait()”之前实际返回超级速度,你冒着竞争条件的风险......在这种情况下,notify会没有效果并且wait()将无限期地等待。 使用Latch,代码将如下所示(从Joe复制并修改):
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
CountDownLatch latch;
synchronized APIResult waitForAPI(CharSequence constraint) {
latch = new CountDownLatch(1);
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// Will wait till the count is 0...
// If the count is already 0, it'll return immediately.
latch.await();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// countDown() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
latch.countDown();
}
}
最后,我没有足够的功劳发表评论,否则我会......