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>