【问题标题】:Android app extremely slow to swipe viewpager and scroll through recyclerview, how to pintpoint cause?Android应用程序滑动viewpager和滚动recyclerview非常慢,如何查明原因?
【发布时间】:2019-06-03 09:45:07
【问题描述】:

因此,我在这个问题上被困了一个星期,我启动我的应用程序,它在白屏上冻结了近 10 秒,之后应用程序变得非常慢。我有一个带有 viewpager 的底部导航视图,它加载了四个片段,其中一些片段具有带有自定义适配器的 recyclerviews。每次我滑动或选择不同的标签时,应用程序选择新标签的速度非常慢。即使是 recyclerview 滚动也很慢。奇怪的是,当数据关闭时,应用程序运行正常,viewpager 滑动和 recyclerview 滚动速度足够快。

我看到了一些建议,建议对从在线接收的 json 数据使用 gson 解析,但性能提升可以忽略不计。我也试过 viewPager.setOffscreenPageLimit(4);但这没有帮助。我所有的网络调用都放在了一个 asycntask 上,我已经使用 StrictMode 来确认这一点。该应用程序在模拟器上也可以正常运行,因此问题仅在我测试过的所有 api 的真实设备上。

 //First Fragment
public void loadData(final Context context, final boolean b) {

    HurlStack hurlStack = new HurlStack() {
        @Override
        protected HttpURLConnection createConnection(URL url) {
            HttpsURLConnection httpsURLConnection = null;
            try {
                httpsURLConnection = CustomCAHttpsProvider.getHttpsUrlConnection(ServerConstants.LOAD_CHAT_URL, InfosylumApplication.getContext(), R.raw.certificate, false);
            } catch (Exception e) {
                e.printStackTrace();
                Helper.showErrorDialog(e.getMessage(), e.toString(), InfosylumApplication.getContext());
            }
            return httpsURLConnection;
        }
    };

    StringRequest stringRequest = new StringRequest(Request.Method.POST, ServerConstants.LOAD_CHAT_URL, new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            mSwipeRefreshLayout.setRefreshing(false);

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (isRunning) {
                        loadData(context, b);
                    }
                }
            }, 3000);
            try {
                JSONObject jsonObject = new JSONObject(response).getJSONObject("object");
                TinyDB tinyDB = new TinyDB(context);
                tinyDB.putString(TinyDBConstants.LOCAL_TYPING, "");


                if (jsonObject.has("groupsAndInd")) {
                    final JSONArray array = jsonObject.getJSONArray("groupsAndInd");
                    LinkedHashMap<String, ChatNotification> chatNotificationLinkedHashMap = new LinkedHashMap<>();

                    final JSONArray localChatArray;
                    if (!tinyDB.getString(TinyDBConstants.LOCAL_CHAT_OTHER).isEmpty()) {
                        localChatArray = new JSONArray(tinyDB.getString(TinyDBConstants.LOCAL_CHAT_OTHER));
                    } else {
                        localChatArray = new JSONArray();
                    }

                    ArrayList<String> localChatIds = new ArrayList<>();
                    final ArrayList<String> onlineChatIds = new ArrayList<>();


                    String idsString = "";
                    for (int i = 0; i < localChatArray.length(); i++) {
                        JSONObject foodJson = localChatArray.getJSONObject(i);
                        localChatIds.add(foodJson.getString("id"));
                        idsString += foodJson.getString("id") + "\n";
                    }


                    for (int i = 0; i < array.length(); i++) {
                        JSONObject object = array.getJSONObject(i);

                        List<String> urlList = Arrays.asList(object.getString("seenUsers").split(","));


                        if ((!urlList.contains(PatriceUser.getCurrentUser().getObjectId()) || object.getInt("messageStatus") < (Const.MESSAGE_STATUS_RECEIVED)) && !localChatIds.contains(object.getString("id")) && !object.getString("senderId").equals(PatriceUser.getCurrentUser().getObjectId())) {

                            String id = null, chatType = null;
                            switch (object.getString("chatType")) {
                                case Const.CHAT_TYPE_GROUP:
                                    id = object.getString("groupId");
                                    chatType = Const.CHAT_TYPE_GROUP;
                                    break;
                                case Const.CHAT_TYPE_INDIVIDUAL:
                                    id = object.getString(Const.SENDER_ID);
                                    chatType = Const.CHAT_TYPE_INDIVIDUAL;
                                    break;
                            }

                            boolean found = false;


                            if (chatNotificationLinkedHashMap.containsKey(id)) {
                                ChatNotification chatNotification = chatNotificationLinkedHashMap.get(id);
                                chatNotification.addChatObject(object);
                                chatNotificationLinkedHashMap.put(id, chatNotification);
                            } else {
                                ChatNotification chatNotification = new ChatNotification(context, chatType, id, object);
                                chatNotificationLinkedHashMap.put(id, chatNotification);
                            }


                        }

                        onlineChatIds.add(object.getString("id"));

                    }

                    tinyDB.putString(TinyDBConstants.LOCAL_CHAT_OTHER, array.toString());


                    for (Map.Entry<String, ChatNotification> entry : chatNotificationLinkedHashMap.entrySet()) {
                        String key = entry.getKey();
                        ChatNotification value = entry.getValue();

                        final InfosylumNotification infosylumNotification = new InfosylumNotification(context);
                        infosylumNotification.showNotification(value);
                    }


                }


                try {

                    if (jsonObject.has("groupsAndInd")) {
                        getActualMessages(jsonObject.getJSONArray("groupsAndInd"), jsonObject.getJSONArray("unread"));
                    }

                    if (jsonObject.has("notes")) {
                        loadNotes(jsonObject.getJSONArray("notes"));
                    }


                } catch (NullPointerException ignore) {
                    Helper.showErrorDialog("ignore", ignore.getMessage(), getActivity());
                }


            } catch (JSONException e) {

            } catch (NullPointerException e) {
                //When the user has logged out


        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {


        }
    }) {
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            Map<String, String> params = new HashMap<>();
            params.put("userId", PatriceUser.getCurrentUser().getObjectId());


            params.put("loadGroupsQuery", loadGroupsQuery);


            return params;
        }
    };

    stringRequest.setRetryPolicy(new DefaultRetryPolicy(
            40000,
            DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
            DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

   VolleySingleton.getInstance(activity).addToRequestQueue(stringRequest);
    stringRequest.setTag(REQUEST_TAG);
    if (requestQueue == null) {
        requestQueue = Volley.newRequestQueue(InfosylumApplication.getContext(), hurlStack);
    }

    requestQueue.add(stringRequest);


}

其他片段在获取数据的方式上非常相似,应用程序中也有后台作业,我认为最难做的事情是查明导致错误的方法。此外,使用分析器是不可能的,因为正在使用的设备是 API 17。我想问一下,有什么方法可以确定究竟是哪种方法导致了我收到的缓慢和偶尔的 ANR?

【问题讨论】:

  • 在运行应用程序时尝试在 android studio 中检查您的 logcat。它应该向您显示哪些活动需要很长时间才能响应。
  • 我可以推荐使用日志记录的代码(在进入或退出函数时)吗?甚至可以使用自定义的更智能的记录器来收集一些统计数据和资料。
  • 进展如何?它解决了你的问题吗?
  • 还没有。还在努力中

标签: java android android-viewpager android-strictmode


【解决方案1】:

查看您的代码,您每次调用loadData() 时都会创建新的HttpURLConnection,这是不必要的。尝试使用单例设计模式只创建一次httpConnection 实例。

这里:new Handler().postDelayed(new Runnable() { @Override public void run() { if (isRunning) { loadData(context, b); } } }, 3000);

您正在从 loadData() 执行此代码,因此存在递归调用(您每 3 秒一次又一次地调用此方法(如果 isRunning==true)。
在这种情况下,尝试在 this 方法之外创建所有变量(特别是列表),这样您就只会重复使用它们,而不是每次都创建新实例。

当您没有互联网时,它运行得很快,因为您没有执行onResponse(其中包含多个循环),而只是执行小onErrorResponse

你想用方法做什么?也许我可以通过提供一些替代解决方案来帮助您。

更新您在哪里执行异步任务? Handler 在它创建的同一个线程上执行?是AsynchTask 还是主线程正在调用loadData()

【讨论】:

  • 您好,感谢您的帮助。我一定会尝试你的解决方案。我正在使用代码构建一个聊天应用程序,例如 whatsapp,它会不断检查新的聊天内容,因此需要每隔几秒重新加载一次。 loadData 是从主线程调用的,但我使用的是 volley,它在发出请求时是异步的。
  • @PatriceAndala 是的,我不确定 volley 是否默认是异步的,你也可以尝试在后台线程上执行所有计算,如(for 循环),如果它们很耗时(它肯定会帮助你减少主线程的使用)。小建议尝试将您的代码解耦为更小的方法(它将帮助您更快地测试和调试它)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多