【问题标题】:Android ViewPager + custom adapter lagAndroid ViewPager + 自定义适配器滞后
【发布时间】:2017-09-20 07:14:28
【问题描述】:

我正在创建一个具有 ViewPager 的活动,并且在 ViewPager 内有 5 个选项卡/片段。我也在实现 FragmentPagerAdapter,如下所示:

public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return fragmentA;
                case 1:
                    return fragmentB;
                case 2:
                    return fragmentC;
                case 3:
                    return  fragmentD;
                case 4:
                    return fragmentE;
                default:
                    return fragmentA;
            }
        }

        @Override
        public int getCount() {
            return numberOfPage;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return tabTitle[position];
        }
    }

上面的代码只显示片段并初始化视图而不将数据填充到列表视图中,为了将数据填充到列表视图中,我使用了下面的代码:

    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

                }

                @Override
                public void onPageSelected(int position) {
                    mSectionsPagerAdapter.notifyDataSetChanged();
                    switch (position){
                        case 0:
                            fragmentA.requestDataFromServer();
                            break;
                        case 1:
                            fragmentB.requestDataFromServer();
                            break;
                        case 2:
                            fragmentC.requestDataFromServer();
                            break;
                        case 3:
                            break;
                        case 4:
                            break;
                        default:
                           break;
                    }
                }

                @Override
                public void onPageScrollStateChanged(int state) {

                }
            });
        mViewPager.setOffscreenPageLimit(4);

和 requestDataFromServer() :

public void requestDataFromServer(){
    new Thread(new Runnable{
        @Override
        public void run(){
            //below is volley request
            ApiServer.getInstance().requestData(getContext(),new JsonResponseCb{
                @Override
                public onSuccess(Object res){

                    new Thread(new Runnable{
                        list.addAll((List<Example>)res);
                    }).start();

                    adapter.notifyDataSetChanged();
                }
                @Override
                public onFail(Object res){}
                @Override
                public onError(Object res){}

            });
        }

    }).start();

}

requestData() 代码:

public void requestData(final Context context, final JsonResponseCb cb)
    {
        LogHelper.debug(getClass(),"Outside thread = "+Thread.currentThread().getName());
        if (cb == null || context == null)
            return;
        apiServerRequest(context, Constants.getNFUrl(), null, new ApiServer.ServerCallback() {
            @Override
            public void onSuccess(final JSONObject jsonObject) {
                NfResponse response = gson.fromJson(jsonObject.toString(), NfResponse.class);
                if(response.getStatus() != null && response.getStatus().equals("OK"))
                    cb.onSuccess(response.getNewsfeedPost());
                else
                    cb.onFail(null);

            }

            @Override
            public void onError(VolleyError error) {

            }
        },Request.Method.GET);
    }

apiServerRequest() 代码:

private void apiServerRequest(Context context, String url, Map<String, Object> bodyParameter, final ServerCallback serverCallback, int method) {
        final RequestQueue requestQueue = Volley.newRequestQueue(context);
        if (bodyParameter == null) {
            final JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(method, url, null, new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    serverCallback.onSuccess(response);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    serverCallback.onError(error);
                }
            });
            requestQueue.add(jsonObjectRequest);
            jsonObjectRequest.setRetryPolicy(new DefaultRetryPolicy(Constants.SERVER_TIMEOUT, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        } else {
            final JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(method, url, new JSONObject(bodyParameter), new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    serverCallback.onSuccess(response);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    serverCallback.onError(error);
                }
            });
            requestQueue.add(jsonObjectRequest);
            jsonObjectRequest.setRetryPolicy(new DefaultRetryPolicy(Constants.SERVER_TIMEOUT, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        }

    }

我已经运行了一个不同的线程来填充数据,但它仍然显示

Skipped 31 frames!  The application may be doing too much work on its main thread.

我为列表视图使用了自定义适配器,并在适配器上实现了 ViewHolder 模式。

有什么解决办法吗?

【问题讨论】:

  • ExampleList 是 List 的一些子类型吗?也许您在主线程上解析服务器响应?
  • @Droidman 我已经更新了我的问题,我在新线程中进行了所有解析
  • 不,从我在您的代码中看到的内容来看,您并没有。您实际上所做的是请求某个工作线程上的数据。由于请求是异步的,所以后面的 onSuccess 回调在主线程上运行
  • @Droidman 你能告诉我如何让回调在新线程上运行吗?
  • 看我的回答,希望我刚才测试的小例子能帮助你了解实际情况

标签: android listview android-fragments android-viewpager


【解决方案1】:

发生了什么?

您实际上只在后台Thread 上发送您的请求,而onSuccess 回调稍后在主Thread 上运行,从而导致延迟。为了说明这一点,这里有一个小例子:

final RequestQueue q = Volley.newRequestQueue(this);
q.start();
    new Thread((new Runnable() {
        @Override
        public void run() {
            Log.w("Thread:", Thread.currentThread().getName()); // Thread-13 in my test-case
            JsonObjectRequest request = new JsonObjectRequest(
                   Request.Method.GET, 
                   "https://jsonplaceholder.typicode.com/posts/1", 
                    new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) { // <-- your onSuccess callback
                    Log.e("Thread:", Thread.currentThread().getName()); // main
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("Thread:", Thread.currentThread().getName()); // main
                }
            });
            q.add(request); // roughly equivalent to your requestData call
        }
    })).start();

如何预防?

您应该在收到此响应之后启动一个工作器Thread 来处理服务器响应。

尝试以下方法:

public void requestData(final Context context, final JsonResponseCb cb)
{
    LogHelper.debug(getClass(),"Outside thread = "+Thread.currentThread().getName());
    if (cb == null || context == null)
        return;
    apiServerRequest(context, Constants.getNFUrl(), null, new ApiServer.ServerCallback() {
        @Override
        public void onSuccess(final JSONObject jsonObject) {
          new Thread(() -> {
            NfResponse response = gson.fromJson(jsonObject.toString(), NfResponse.class);
              if(response.getStatus() != null && response.getStatus().equals("OK"))
               cb.onSuccess(response.getNewsfeedPost());
              else
               cb.onFail(null);
      }).start();
    }

        @Override
        public void onError(VolleyError error) {

        }
    },Request.Method.GET);
}

注意,在这种情况下,onSuccess 回调将在后台线程上调用,您应该将需要从该回调执行的所有 UI 操作包装到

runOnUiThread(new Runnable() {
        @Override
        public void run() {
           // your UI code here
        }
    });

【讨论】:

  • 我尝试登录,回调确实在 main 上运行。所以我创建了一个新线程来解析回调响应,但滞后仍然存在:(
  • 请更新您的代码并显示您解析数据的所有位置
  • 我已经更新了我的问题,我没有将 notifyDataSetChanged() 放在新线程中,因为新线程无法触及视图
  • 请显示 ApiServer 类的 requestData 方法
  • 似乎延迟是由在主线程上运行的gson.fromJsonresponse.getNewsfeedPost(或两者)方法引起的。我要做的是找到实际的 JSON 解析发生在哪里,因为这肯定必须移动到后台线程
猜你喜欢
  • 2017-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多