0

So, I've been stuck on this issue for a week where I launch my app and it freezes for almost 10 seconds on a white screen and after that the app gets really slow. I have a bottomnavigationview with a viewpager that loads four fragments, some of which have recyclerviews with custom adapters. Everytime I swipe or select a different tab, the app is painfully slow to select the new tab. Even the recyclerview scrolling is really slow. The weird part is that when data is switched off, the app works just fine and the viewpager swipes and recyclerview scrolls are fast enough.

I have seen several suggestions which suggest using gson parsing for the json data received from online, but the performance increase was negligible. I have also tried viewPager.setOffscreenPageLimit(4); but that hasn't helped. All my network calls have been placed on an asycntask and I have used StrictMode to confirm that. The app also works ok on an emulator, so the problem is only on real devices of all apis that I have tested.

 //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);


}

The other fragments are quite similar in how they obtain data there are also background jobs within the app and I think the hardest thing to do is to pinpoint which method is causing the bug. Also, using the profiler is out of the question because the device being used is of API 17. I'd just like to ask, is there any way I can pinpoint which method exactly is causing the sluggishness and the occasional ANRs that I receive?

Patrice Andala
  • 184
  • 1
  • 14

1 Answers1

0

Looking at your code you are creating new HttpURLConnection every time you call loadData(), which is unnecessary.Try to use Singleton design pattern to create httpConnection instance only once.

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

this code you are executing from loadData() so there is recursive call (you call this method again and again every 3 seconds (if isRunning==true)).
In that case try to create all variable (specially lists) outside of the this method so you will just use them repeatedly not create new instance every time.

When you don't have an internet it is working fast because you aren't executing onResponse (which contains several loops) but only small onErrorResponse.

What are you trying to do with method? maybe I can help you by providing some alternative solution for it.

UPDATE where are you performing asynch tasks? Handler performs on the same thread it was created? is it AsynchTask or is it main thread are calling loadData()

mr.kostua
  • 696
  • 6
  • 12
  • Hi, thanks for all the help. I'll definitely try your solutions. I'm using the code to build a chat application such as whatsapp, which constantly checks for new chats, hence the need to reload every few seconds. loadData is called from the main thread but I'm using volley, which is asynchronous in making the requests. – Patrice Andala Jun 03 '19 at 11:48
  • @PatriceAndala yes I wasn't sure if volley was asynch by default, aslo you can try to perform all calculations like (for loops) on the background thread, if they are time consuming ( it will definitely help you to reduce the use of Main thread). Small advise try to decouple your code into smaller methods (it will help you test and debug it much faster). – mr.kostua Jun 03 '19 at 13:08