我正在尝试使用我的应用程序的UI解决问题。
当输入特定的Activity
时,应用程序会在目录中查找某些文件,并且每个文件都会向REST API服务请求一些数据。
工作线程实现为AsyncTask
:
doInBackground
方法递归搜索文件夹及其子文件夹中的文件。
如果找到文件,则会将新请求提交给由Retrofit + OkHTTPClient处理的请求队列。
请求是异步的,Callback
函数,一旦检索到文件的信息,就会调用执行某些操作的函数,然后调用AsyncTask
publishProgress()
。
onProgressUpdate
来更新ProgressBar
的当前值和最大值。
我遇到的主要问题是,经过一段时间ProgressBar
停止更新后,我收到了“Choreographer lost frames”消息,但每次都会调用onProgressUpdate
并使用更新的值。
我认为许多HTTP请求是频繁更新UI的瓶颈。
我还尝试创建Thread
,优先级设置为MAX_PRIORITY
,以便从中调用publishProgress
,没有区别。
我应该遵循哪种模式才能使用户界面更具响应性?
这是一些简化的代码:
的AsyncTask
public class MyAsyncTask implements myCustomListener{
private int processed = 0;
private int toProcess = 0;
private MyCustomActivity mActivity;
public MyAsyncTask(MyCustomActivity mActivity){
this.mActivity = mActivity;
}
@Override
protected Void doInBackground(Void... arg0) {
doScan(mRootDir);
return null;
}
private void doScan(File rootDir){
for(File file: rootDir.listFiles()){
if(file.isDirectory()) doScan(file);
else if(isWantedFile(file)) {
MyQueues.addRequest(file,this);
toProcess++;
}
}
}
@Override
public void onCustomEventListened(File f){
//set f as processed
processed++;
publishProgress();
}
@Override
protected void onProgressUpdate(Void... voids) {
if (processed > 1)
mActivity.showProgressBar(); //If not visible, set visible
mActivity.updateProgressBar(processed, toProcess);
Log.i("Scan Progress", processed + "/" + toProcess); //this log print tells me that method is called properly
if (processed == toProcess) {
mActivity.hideProgressBar(); //If visible, set not visible
}
}
}
RequestQueues
public class MyQueues{
private static LinkedBlockingQueue<Runnable> mRequestQueue = new LinkedBlockingQueue<Runnable>();
private static ThreadPoolExecutor tpe = new ThreadPoolExecutor(4, 4, Long.MAX_VALUE, TimeUnit.NANOSECONDS, mRequestQueue);
private static OkClient mOkClient;
private static OkHttpClient mOkHttpClient;
static{
if(null==mOkHttpClient){
File cache = MyApp.getCacheDir();
HttpResponseCache resCache;
try {
resCache = new HttpResponseCache(cache, 10L * 1024 * 1024);
mOkHttpClient = new OkHttpClient();
mOkHttpClient.setResponseCache(resCache);
} catch (IOException e) {
}
}
if(null==mOkClient){
mOkClient = new OkClient(mOkHttpClient);
}
}
public static interface TheGamesDBService {
@GET("/getData.php")
void getData(@Query("id") String id, MyCallback<FileInfoClass> cb);
}
private static RestAdapter myAdapter = new RestAdapter.Builder()
.setClient(mOkClient).setConverter(myConverter())
.setExecutors(tpe, new MainThreadExecutor())
.setEndpoint(Constants.MYENDPOINT).build();
private static MyService mService = myAdapter
.create(MyService.class);
public static void addRequest(File f, MyCustomListener mListener) {
MyCallback cb = new MyCallback(f, mListener);
myService().getData(f.getName(),cb);
}
}
}
myCallBack函数
public class MyCallback implements Callback<FileInfoClass>{
private File f;
private MyCustomListener mListener;
public MyCallback(File f, MyCustomListener mListener){
this.f=f;
this.mListener=mListener;
}
@Override
public void failure(RetrofitError response) {
mListener.onCustomEventListened(f);
}
@Override
public void success(FileInfoClass fic, Response response) {
if(check(fic,f)){
//store fic data...
}
mListener.onCustomEventListened(f);
}
}
答案 0 :(得分:1)
很难调查它只看这个代码,但我发现潜在的问题。
Retrofit回调方法:在主线程上调用failure()
和success()
,最终在主线程上调用publishProgress();
。根据AsyncTask文档,应该从doInBackground()
方法调用此方法,该方法在专用后台线程上调用。你绝对应该考虑重构代码。为了便于在不同的地方打印Logcat当前线程名称:Thread.currentThread().getName()
此外,我注意到您在Activity
内强烈引用了AsyncTask
。这是一个非常糟糕的做法。要轻松修复它,请使用Activity
类包装WeakReference
引用。
如果您决定进行更大的更改,我建议您使用ThreadExecutor将后台操作移至专用Service
,或使用IntentService
提供开箱即用的排队后台操作。最后,将进度更改传达给Activity
使用BroadcastReceiver
以及LocalBroadcastManager
。
<强>更新强>
还有一件事要补充。如果您自己在后台处理网络操作,则无需使用Retrofit的回调。这对于直接从UI执行的请求很有用。而是同步调用改造请求。