0

I made this app to pull information from various rest APIs. It's not done yet but so far, the data being loaded by the AsyncTask is too slow for my liking and sometimes skips 1000+ frames. Is there a way I could speed it up?

On a side note, I would also appreciate it if someone could help me load images from urls for the news tab in the app.

Thank you!

Here's my repo if you prefer that to the code directly being posted: https://github.com/dayemsaeed/Covid-19

Here's my code:

Code

MainActivity.java

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.Style;

public class MainActivity extends AppCompatActivity {

    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Mapbox Access token
        Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_api_key));

        setContentView(R.layout.activity_main);

        mapView = findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(mapboxMap -> mapboxMap.setStyle(Style.DARK, style -> {


        }));

        ViewPager2 pager = findViewById(R.id.view_pager);

        ViewPageAdapter pageAdapter = new ViewPageAdapter(getSupportFragmentManager(), getLifecycle());
        pager.setAdapter(pageAdapter);

        pager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);

        TabLayout tabLayout = findViewById(R.id.tabLayout2);
        tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);

        new StatsLoader("https://api.covid19api.com/summary", pageAdapter, pager, tabLayout).execute();
        new NewsLoader("https://newsapi.org/v2/top-headlines?q=coronavirus&sortBy=popularity", pageAdapter, pager, tabLayout).execute();

    }
}

SortJSONArray.java

import android.util.Log;
import org.json.JSONObject;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class SortJsonArray {

    void sortArray(List list, final String keyName, final boolean ascending) {

        Collections.sort(list, new Comparator<JSONObject>() {

            private final String KEY_NAME = keyName;

            @Override
            public int compare(JSONObject o1, JSONObject o2) {

                String val1 = "";
                String val2 = "";

                try {
                    val1 = String.valueOf(o1.get(KEY_NAME));
                    val2 = String.valueOf(o2.get(KEY_NAME));
                } catch (Exception e) {
                    Log.e("Sort Exception", "Issue when sorting JSONArray", e);
                    e.printStackTrace();
                }

                if (IntCheckHelper.isInteger(val1)) {
                    if (ascending) {
                        return Integer.valueOf(val1).compareTo(Integer.valueOf(val2));
                    }
                    else {
                        return Integer.valueOf(val2).compareTo(Integer.valueOf(val1));
                    }
                }
                else {
                    if (ascending) {
                        return val1.compareToIgnoreCase(val2);
                    }
                    else {
                        return val2.compareToIgnoreCase(val1);
                    }
                }

            }

        });

    }

}

StatsFragment.java

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.json.JSONArray;

public class StatsFragment extends Fragment {

    View view;
    RecyclerView recyclerView;
    JSONArray covidData;
    String dataUrl;

    public StatsFragment(JSONArray data, String url) {
        covidData = data;
        dataUrl = url;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.view_page, container, false);
        recyclerView = view.findViewById(R.id.recyclerView2);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setHasFixedSize(false);
        RecyclerView.Adapter<CustomListAdapter.ListViewHolder> mAdapter = new CustomListAdapter(covidData, dataUrl);
        recyclerView.setAdapter(mAdapter);
        return view;
    }

}

CustomListAdapter.java

import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import org.json.JSONArray;
import org.json.JSONException;

import java.text.NumberFormat;

public class CustomListAdapter extends RecyclerView.Adapter<CustomListAdapter.ListViewHolder> {

    private JSONArray mDataset;
    private String murl;

    static class ListViewHolder extends RecyclerView.ViewHolder {

        TextView textView;
        TextView textView2;
        ImageView imageView;
        ListViewHolder(LinearLayout v) {
            super(v);
            textView = v.findViewById(R.id.name);
            textView2 = v.findViewById(R.id.cases);
            imageView = v.findViewById(R.id.image);
        }

    }

    CustomListAdapter(JSONArray myDataset, String url) {
        mDataset = myDataset;
        murl = url;
    }

    @NonNull
    @Override
    public CustomListAdapter.ListViewHolder onCreateViewHolder(ViewGroup parent,
                                                               int viewType) {
        // create a new view
        LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_view, parent, false);

        // TextView v = (TextView) linearLayout.findViewById(R.id.info_text);

        return new ListViewHolder(linearLayout);
    }

    @Override
    public void onBindViewHolder(ListViewHolder holder, int position) {

        String textViewString = "";
        String textView2String = "";

        try {
            if (murl.contains("api.covid19api.com")) {
                textViewString = mDataset.getJSONObject(position).getString("Country");
                textView2String = NumberFormat.getInstance().format(mDataset.getJSONObject(position).getInt("TotalConfirmed"));
            }
            else if (murl.contains("newsapi.org")) {
                textViewString = mDataset.getJSONObject(position).getString("title");
                textView2String = mDataset.getJSONObject(position).getString("description");
            }
            holder.textView.setText(textViewString);
            holder.textView2.setText(textView2String);
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

    @Override
    public int getItemCount() {
        return mDataset.length();
    }

}

StatsLoader.java

import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.viewpager2.widget.ViewPager2;

import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import javax.net.ssl.HttpsURLConnection;

public class StatsLoader extends AsyncTask<String, Void, JSONArray> {

    Exception exception;
    String urlString = "";
    static JSONArray covidData = new JSONArray();
    ViewPageAdapter pageAdapter;
    ViewPager2 viewPager2;
    TabLayout tabLayout;

    public StatsLoader(String url, ViewPageAdapter adapter, ViewPager2 pager, TabLayout tabs) {
        super();
        urlString = url;
        pageAdapter = adapter;
        viewPager2 = pager;
        tabLayout = tabs;
    }

    @Nullable
    @Override
    public JSONArray doInBackground(String ... urls) {
        HttpsURLConnection connection = null;
        BufferedReader reader = null;

        try {

            URL url = new URL(urlString);
            connection = (HttpsURLConnection) url.openConnection();
            connection.connect();
            InputStream inputStream = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream));

            StringBuilder buffer = new StringBuilder();
            String line = "";

            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }

            JSONObject json = new JSONObject(buffer.toString());
            covidData = json.getJSONArray("Countries");
            ArrayList<Object> list = new ArrayList<>();
            for (int i = 0; i < covidData.length(); i++) {
                list.add(covidData.get(i));
            }
            SortJsonArray sortJsonArray = new SortJsonArray();
            sortJsonArray.sortArray(list, "TotalConfirmed", false);
            covidData = new JSONArray();
            for (Object object : list) {
                covidData.put(object);
            }
            return covidData;

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    protected void onPostExecute(JSONArray coviddata) {

        if (this.exception == null) {
            Log.d("Check", "Works!");
            pageAdapter.addFragment(new StatsFragment(coviddata, urlString), "Stats");
            viewPager2.setAdapter(pageAdapter);
            new TabLayoutMediator(tabLayout, viewPager2,
                    (tab, position) -> {
                        switch (position) {
                            case 0:
                                tab.setText("Stats");
                                break;
                            case 1:
                                tab.setText("News");
                                break;
                            case 2:
                                tab.setText("Symptoms");
                                break;
                            case 3:
                                tab.setText("Safety");
                                break;
                            default:
                                break;
                        }
                    }).attach();
        }

    }


}

ViewPageAdapter.java

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import java.util.ArrayList;

public class ViewPageAdapter extends FragmentStateAdapter {

    private final ArrayList<Fragment> fragments = new ArrayList<>();
    private final ArrayList<String> titles = new ArrayList<>();

    public ViewPageAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);
    }

    public void addFragment(Fragment fragment, String title) {
        fragments.add(fragment);
        titles.add(title);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return fragments.get(position);
    }

    @Override
    public int getItemCount() {
        return fragments.size();
    }

}

NewsFragment.java

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import org.json.JSONArray;

public class NewsFragment extends Fragment {

    View view;
    RecyclerView recyclerView;
    JSONArray news;
    String newsUrl;

    public NewsFragment(JSONArray data, String url) {
        news = data;
        newsUrl = url;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.view_page, container, false);
        recyclerView = view.findViewById(R.id.recyclerView2);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setHasFixedSize(false);
        RecyclerView.Adapter<CustomListAdapter.ListViewHolder> mAdapter = new CustomListAdapter(news, newsUrl);
        recyclerView.setAdapter(mAdapter);
        return view;
    }

}

NewsLoader.java

import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

public class NewsLoader extends AsyncTask<String, Void, JSONArray> {

    Exception exception;
    String urlString = "";
    static JSONArray news = new JSONArray();
    ViewPageAdapter pageAdapter;
    ViewPager2 viewPager2;
    TabLayout tabLayout;

    public NewsLoader(String url, ViewPageAdapter adapter, ViewPager2 pager, TabLayout tabs) {
        super();
        urlString = url;
        pageAdapter = adapter;
        viewPager2 = pager;
        tabLayout = tabs;
    }

    @Nullable
    @Override
    public JSONArray doInBackground(String ... urls) {
        HttpURLConnection connection = null;
        BufferedReader reader = null;

        try {

            URL url = new URL(urlString);
            connection = (HttpsURLConnection) url.openConnection();
            connection.addRequestProperty("Authorization", "Bearer baef544b7dbe4ff8b15bb502d1fd5e1a");
            connection.connect();
            InputStream inputStream = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream));

            StringBuilder buffer = new StringBuilder();
            String line = "";

            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }

            JSONObject json = new JSONObject(buffer.toString());
            news = json.getJSONArray("articles");
            return news;

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    protected void onPostExecute(JSONArray newsData) {

        if (this.exception == null) {
            Log.d("Check", "Works!");
            pageAdapter.addFragment(new NewsFragment(newsData, urlString), "News");
            viewPager2.setAdapter(pageAdapter);
            new TabLayoutMediator(tabLayout, viewPager2,
                    (tab, position) -> {
                        switch (position) {
                            case 0:
                                tab.setText("Stats");
                                break;
                            case 1:
                                tab.setText("News");
                                break;
                            case 2:
                                tab.setText("Symptoms");
                                break;
                            case 3:
                                tab.setText("Safety");
                                break;
                            default:
                                break;
                        }
                    }).attach();
        }

    }
}

Layouts

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:statusBarScrim="@null"
            app:titleEnabled="false"
            android:minHeight="?attr/actionBarSize" >

            <fragment
                android:id="@+id/mapView"
                android:name="com.mapbox.mapboxsdk.maps.MapFragment"
                android:layout_width="match_parent"
                android:layout_height="350sp"
                android:apiKey="@string/mapbox_api_key"
                android:clickable="true"
                android:enabled="true"
                android:focusable="true"
                app:layout_collapseMode="parallax" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/appbar"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_marginBottom="48dp"
                android:gravity="top"
                app:layout_collapseMode="pin"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Dark"
                app:title="" />

            <com.google.android.material.tabs.TabLayout
                android:id="@+id/tabLayout2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="parent">

            </com.google.android.material.tabs.TabLayout>

        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="parent"
            tools:listitem="@layout/view_page" >

        </androidx.viewpager2.widget.ViewPager2>


    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.coordinatorlayout.widget.CoordinatorLayout>

list_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.cardview.widget.CardView
        android:id="@+id/card_view"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="14sp"
        android:layout_margin="15sp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="40sp">

            <ImageView
                android:id="@+id/image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toLeftOf="@+id/name"
                android:layout_toStartOf="@+id/name"
                android:contentDescription="Image of country flag" />

            <TextView
                android:id="@+id/name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingBottom="10sp" />

            <TextView
                android:id="@+id/cases"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/name" />

        </RelativeLayout>

    </androidx.cardview.widget.CardView>

</LinearLayout>

view_page.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        tools:listitem="@layout/list_view">

    </androidx.recyclerview.widget.RecyclerView>

</androidx.constraintlayout.widget.ConstraintLayout>
Dayem Saeed
  • 325
  • 5
  • 15
  • You should have put just a link to your repo instead of all this. – Walid Jul 05 '20 at 11:15
  • Sorry. I didn't know if people preferred this or going to my repo. Added it – Dayem Saeed Jul 05 '20 at 11:38
  • 1
    If it's skipping frames that means you're doing too many expensive operations on the main thread. With the amount of code you've posted it's hard to see where these expensive operations are in the code, but you should try to move them to a background thread. – Joni Jul 05 '20 at 11:59
  • @Joni so it only skips frames sometimes and the only stuff I'm doing on the main thread in regards to the async task is just inserting the information into the UI views in the "onPostExecute" part of NewsLoader and StatsLoader. I mean, it works well enough right now but I'm going to be getting more data from at least 2 more endpoints, which is where it'll really affect the performance. – Dayem Saeed Jul 05 '20 at 12:03

0 Answers0