Android ProgressBar不再更新

时间:2014-08-01 09:42:32

标签: android multithreading android-asynctask android-ui

我正在尝试使用我的应用程序的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);
    }
}

1 个答案:

答案 0 :(得分:1)

很难调查它只看这个代码,但我发现潜在的问题。

Retrofit回调方法:在主线程上调用failure()success(),最终在主线程上调用publishProgress();。根据AsyncTask文档,应该从doInBackground()方法调用此方法,该方法在专用后台线程上调用。你绝对应该考虑重构代码。为了便于在不同的地方打印Logcat当前线程名称:Thread.currentThread().getName()

此外,我注意到您在Activity内强烈引用了AsyncTask。这是一个非常糟糕的做法。要轻松修复它,请使用Activity类包装WeakReference引用。

如果您决定进行更大的更改,我建议您使用ThreadExecutor将后台操作移至专用Service,或使用IntentService提供开箱即用的排队后台操作。最后,将进度更改传达给Activity使用BroadcastReceiver以及LocalBroadcastManager

<强>更新

还有一件事要补充。如果您自己在后台处理网络操作,则无需使用Retrofit的回调。这对于直接从UI执行的请求很有用。而是同步调用改造请求。