Error handling of empty json in android studio - android

I am trying to build a simple news app that fetches news headlines from the internet but I am facing some problems in doing this.
In my app if my if the json is not parsed I just get an empty recycler view. In this case I want to parse the json again can someone please help me in doing this
This is my MainActivity:
public class MainActivity extends AppCompatActivity {
private MainActivityViewModel mainActivityViewModel;
private RecyclerView recyclerView;
private RecyclerViewAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.news_view);
mainActivityViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
mainActivityViewModel.init();
mainActivityViewModel.getNews().observe(this, new Observer<List<NewsData>>() {
#Override
public void onChanged(List<NewsData> newsData) {
adapter.notifyDataSetChanged();
}
});
init();
}
private void init(){
Log.d("","=========================intializing Recycler view======================");
System.out.println("executing init()");
adapter = new RecyclerViewAdapter(mainActivityViewModel.mutableLiveData.getValue(), this);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
}
This is NewsRepository.java
public class NewsRepository {
MutableLiveData<List<NewsData>> liveNews = new MutableLiveData<>();
private static NewsRepository instance;
public static NewsRepository getInstance(){
if(instance != null)
return instance;
instance = new NewsRepository();
return instance;
}
private static final String LOG_TAG = "NewsRepository";
private static final String newsAPIurl = "https://newsapi.org/v2/top-headlines?sources=google-news-in&apiKey";
List<NewsData> newsDatalist = new ArrayList<>();
public void getLiveNews(){
new FetchJSONAsyncTask().execute();
}
public class FetchJSONAsyncTask extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... voids) {
Log.d("Backgroumd thread" , "Fetching json");
URL url = createUrl();
String jsonRespone = "";
try {
jsonRespone = makeHttpRequest(url);
}
catch (IOException e) {
Log.d("Background" , "Could not Load url");
}
extractNewsfromJSON(jsonRespone);
return null;
}
}
private URL createUrl() {
URL url = null;
try {
url = new URL(NewsRepository.newsAPIurl);
} catch (MalformedURLException exception) {
Log.e(LOG_TAG, "Error with creating URL", exception);
return null;
}
return url;
}
private String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(10000);
urlConnection.setConnectTimeout(15000);
urlConnection.connect();
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} catch (IOException e) {
Log.e(LOG_TAG,"there was an error in makeHTTPRequest" , e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
private String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
private void extractNewsfromJSON(String newsJSON){
try{
JSONObject jsonObject = new JSONObject(newsJSON);
String a = jsonObject.getString("status");
System.out.println("=================================================");
System.out.println(a);
JSONArray newsArray = jsonObject.getJSONArray("articles");
if(newsArray.length() > 0){
for(int i =0 ;i< newsArray.length();i++){
JSONObject article = newsArray.getJSONObject(i);
String title = article.getString("title");
String desc = article.getString("description");
String urltoimg = article.getString("urlToImage");
String readmore = article.getString("url");
NewsData obj = new NewsData(title , desc, readmore , urltoimg);
newsDatalist.add(obj);
}
}
} catch (JSONException e) {
Log.e(LOG_TAG, "Problem parsing the JSON results", e);
}
}
}
And this is MainActivityViewModel
public class MainActivityViewModel extends ViewModel {
private NewsRepository instance;
MutableLiveData<List<NewsData>> mutableLiveData;
public void init(){
if(instance != null)
return;
instance = NewsRepository.getInstance();
instance.getLiveNews();
mutableLiveData = instance.liveNews;
}
public LiveData<List<NewsData>> getNews(){
return mutableLiveData;
}
public void refreshNews(){
instance.getLiveNews();
}
}

Problem with you code lies in this function:
public MutableLiveData<List<NewsData>> getLiveNews(){
new FetchJSONAsyncTask().execute();
MutableLiveData<List<NewsData>> liveNews = new MutableLiveData<>();
System.out.println("==========================================");
liveNews.setValue(newsData);
return liveNews;
}
FetchJSONAsyncTask is an asynchronous task, you are returning liveNews before the data is fetched from the API. It's blank and hence you are getting empty RecyclerView.
Here is what you can do, Create liveNews a class variable instead of creating a new object each time.
public class NewsRepository {
public MutableLiveData<List<NewsData>> liveNews = new MutableLiveData<>();
//rest of the code
Update your ViewModel to directly get the value from this variable:
public class MainActivityViewModel extends ViewModel {
private NewsRepository instance;
LiveData<List<NewsData>> LiveData;
public void init(){
if(instance != null)
return;
instance = NewsRepository.getInstance();
LiveData = instance.liveNews;
instance.getLiveNews() // call to fetch JSON first time
}
public LiveData<List<NewsData>> getNews(){
return LiveData;
}
//call this function from your activity to refresh the data
public void refreshNews(){
instance.getLiveNews();
}
}
Now you update your getLiveNews method to just call FetchJSONAsyncTask.
public void getLiveNews(){
new FetchJSONAsyncTask().execute();
}
Now in extractNewsfromJSON method set the value of liveNews:
private void extractNewsfromJSON(String newsJSON){
try{
//rest of the code
liveNews.postValue(newsData);
}
} catch (JSONException e) {
System.out.println(newsJSON);
Log.e(LOG_TAG, "Problem parsing the JSON results", e);
}
}

Related

Why is it not showing all the data?

I am creating this app which shows the latest news, which gets data from
https://newsapi.org/s/india-health-news-api
but it doesn't fetch all the data. Sometimes it just shows all but sometimes it just shows 2 or 3 news. Also, I don't see any log error message. What is the problem?
HealthNews.java
public class HealthNews extends AppCompatActivity {
private ArrayList urlList;
private NewsAdapter mNewsAdapter;
private static final String REQUEST_URL ="https://newsapi.org/v2/top-headlines?country=in&category=health&apiKey=3f7d99cdbb004766892bd239a4c099be";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_health_news);
Intent intent = getIntent();
HealthNews.NewsAsyncTask task = new HealthNews.NewsAsyncTask();
task.execute(REQUEST_URL);
urlList = QueryUtils.m;
ListView listView = (ListView)findViewById(R.id.listViewHealthNews);
mNewsAdapter = new NewsAdapter(this, new ArrayList<News>());
listView.setAdapter(mNewsAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Toast.makeText(getApplicationContext(), ""+ a.get(position), Toast.LENGTH_SHORT).show();
Object url = urlList.get(position);
Uri uri = (Uri) Uri.parse((String) url); // missing 'http://' will cause crashed
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
}
private class NewsAsyncTask extends AsyncTask<String, Void, ArrayList<News>> {
ProgressDialog p;
#Override
protected ArrayList<News> doInBackground(String... urls) {
if (urls.length < 1 || urls[0] == null) {
return null;
}
ArrayList<News> result = QueryUtils.fetchEarthquakeData(urls[0]);
return result;
//return null;
}
#Override
protected void onPostExecute(ArrayList<News> data) {
mNewsAdapter.clear();
if (data != null && !data.isEmpty()) {
p.hide();
mNewsAdapter.addAll(data);
}
}
#Override
protected void onPreExecute() {
super.onPreExecute();
p = new ProgressDialog(HealthNews.this);
p.setMessage("Latest News...");
p.setIndeterminate(false);
p.show();
}
}
}
QueryUtils.java
private static final String LOG_TAG = "";
private QueryUtils(){
}
private static URL createUrl(String stringUrl) {
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException e) {
Log.e(LOG_TAG, "Problem building the URL ", e);
}
return url;
}
private static String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(30000 /* milliseconds */);
urlConnection.setConnectTimeout(60000 /* milliseconds */);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e(LOG_TAG, "Error response code: " + urlConnection.getResponseCode());
}
} catch (IOException e) {
Log.e(LOG_TAG, "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
private static String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
static ArrayList<String> m = new ArrayList<String>();
public static ArrayList<News> extractNews(String SAMPLE_JSON){
if (TextUtils.isEmpty(SAMPLE_JSON)) {
return null;
}
ArrayList<News> news = new ArrayList<News>();
try {
JSONObject jsonObject1 = new JSONObject(SAMPLE_JSON);
JSONArray baseJSONArray = jsonObject1.getJSONArray("articles");
for (int i = 0; i < baseJSONArray.length(); i++) {
JSONObject jsonObject = baseJSONArray.getJSONObject(i);
JSONObject source = jsonObject.getJSONObject("source");
String name = source.getString("name");
String article = jsonObject.getString("title");
String url1 = jsonObject.getString("url");
String img = jsonObject.getString("urlToImage");
URL url = new URL(img);
Bitmap image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
News a = new News(image, article);
news.add(a);
m.add(url1);
}
} catch (JSONException j) {
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return news;
}
public static ArrayList<News> fetchEarthquakeData(String requestUrl) {
URL url = createUrl(requestUrl);
String jsonResponse = null;
try {
jsonResponse = makeHttpRequest(url);
} catch (IOException e) {
Log.e(LOG_TAG, "Problem making the HTTP request.", e);
}
ArrayList<News> news = extractNews(jsonResponse);
return news;
}
I went though your code and found some issues.
Some bad practices i found in your code are:
You are adding your urls in separate list with static specifier. Instead of this you should add the url variable in your News model directly. And you can directly retrieve the whole News model inside ListView > setOnItemClickListener.
You are creating Bitmap for all your images. It may cause OOM Exception. You should use any Image loading library instead.
I have fixed that all issues and created working code. Please do required changes which you want at your end.
HealthNews.java
public class HealthNews extends AppCompatActivity {
private Context context;
private NewsAdapter mNewsAdapter;
private ArrayList<News> listNews;
private static final String REQUEST_URL = "https://newsapi.org/v2/top-headlines?country=in&category=health&apiKey=3f7d99cdbb004766892bd239a4c099be";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.health_news);
context = this;
ListView list_news = findViewById(R.id.list_news);
listNews = new ArrayList<>();
mNewsAdapter = new NewsAdapter(context, listNews);
list_news.setAdapter(mNewsAdapter);
list_news.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
News selNews = (News) parent.getAdapter().getItem(position);
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(selNews.getUrl())));
} catch (Exception e) {
// Missing 'http://' or 'https://' will cause crash
e.printStackTrace();
}
}
});
new NewsAsyncTask().execute(REQUEST_URL);
}
private class NewsAsyncTask extends AsyncTask<String, Void, ArrayList<News>> {
private ProgressDialog p;
#Override
public void onPreExecute() {
super.onPreExecute();
p = new ProgressDialog(context);
p.setMessage("Latest News...");
p.setIndeterminate(false);
p.show();
}
#Override
public ArrayList<News> doInBackground(String... urls) {
return QueryUtils.fetchEarthquakeData(urls[0]);
}
#Override
public void onPostExecute(ArrayList<News> newsList) {
super.onPostExecute(newsList);
listNews.addAll(newsList);
p.hide();
mNewsAdapter.notifyDataSetChanged();
}
}
}
QueryUtils.java
public class QueryUtils {
public static ArrayList<News> fetchEarthquakeData(String apiUrl) {
ArrayList<News> listNews = new ArrayList<>();
try {
URL url = new URL(apiUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(30000);
conn.setConnectTimeout(60000);
conn.setRequestMethod("GET");
conn.connect();
int responseCode = conn.getResponseCode();
InputStream iStream;
if (responseCode == HttpURLConnection.HTTP_OK)
iStream = conn.getInputStream();
else
iStream = conn.getErrorStream();
BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
StringBuilder response = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
String jsonResponse = response.toString();
if (TextUtils.isEmpty(jsonResponse))
return null;
JSONObject jsonObject1 = new JSONObject(jsonResponse);
JSONArray baseJSONArray = jsonObject1.getJSONArray("articles");
for (int i = 0; i < baseJSONArray.length(); i++) {
JSONObject jsonObject = baseJSONArray.getJSONObject(i);
JSONObject source = jsonObject.getJSONObject("source");
News news = new News();
news.setArticle(jsonObject.optString("title"));
news.setUrl(jsonObject.optString("url"));
news.setUrlToImage(jsonObject.optString("urlToImage"));
listNews.add(news);
}
} catch (JSONException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return listNews;
}
}
News.java (Model)
public class News {
private String article;
private String url;
private String urlToImage;
public News() {
this.article = "";
this.url = "";
this.urlToImage = "";
}
public String getArticle() {
return article;
}
public void setArticle(String article) {
this.article = article;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUrlToImage() {
return urlToImage;
}
public void setUrlToImage(String urlToImage) {
this.urlToImage = urlToImage;
}
}
NewsAdapter.java (Change your item layout as per your code)
public class NewsAdapter extends BaseAdapter {
private Context context;
private LayoutInflater mInflater;
private List<News> listNews;
public NewsAdapter(Context context, List<News> listNews) {
this.context = context;
this.listNews = listNews;
mInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return listNews.size();
}
#Override
public News getItem(int position) {
return listNews.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
private class ViewHolder {
private ImageView item_img_news;
private TextView item_txt_article;
}
#SuppressLint("InflateParams")
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_news, null);
holder = new ViewHolder();
holder.item_img_news = convertView.findViewById(R.id.item_img_news);
holder.item_txt_article = convertView.findViewById(R.id.item_txt_article);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final News news = getItem(position);
holder.item_txt_article.setText(news.getArticle());
Glide.with(context).load(news.getUrlToImage()).into(holder.item_img_news);
return convertView;
}
}
app > build.gradle
implementation 'com.github.bumptech.glide:glide:4.9.0'

Android: How to add AsyncTaskLoader to this RecyclerView

[Update] Added repository link to download the project
I'm having this activity which connects to a URL to fetch data and display it using RecyclerView with a custom adapter. How can I edit this code to use AsyncTaskLoader instead of AsyncTask? here's the repository to download the very simple project Soonami tutorial app
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
public static QuakesAdapter quakesAdapter;
public static ArrayList<Event> eventsList = new ArrayList<>();
public static final String USGS_REQUEST_URL =
"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2018-01-01&endtime=2018-12-01&minmagnitude=6&limit=50";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
quakesAdapter = new QuakesAdapter(this, eventsList);
//defining recyclerView and setting the adapter
quakesAdapter.notifyDataSetChanged();
FetchData fetchData= new FetchData();
fetchData.execute();
}
private class FetchData extends AsyncTask<String, Void, ArrayList<Event>> {
String myDdata = "";
String line = "";
#Override
protected ArrayList<Event> doInBackground(String... params) {
try {
//opening the connection
if (httpURLConnection.getResponseCode() == 200) {
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
while(line != null){
line = bufferedReader.readLine();
myDdata = myDdata + line;
}
JSONObject jsonObject = new JSONObject(myDdata);
eventsList.clear();
JSONArray jsonArray = jsonObject.getJSONArray("features");
for(int i = 0; i < jsonArray.length(); i++){
//getting values of the 3 attributes
eventsList.add(new Event(title, time, tsunamiAlert));
}
if (inputStream != null) {
inputStream.close();
}
} else {
Log.e("Connection Error: ", "Error response code: " + httpURLConnection.getResponseCode());
}
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
catch (MalformedURLException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(ArrayList<Event> result) {
super.onPostExecute(result);
quakesAdapter.notifyDataSetChanged();
}
}
}
I have tested multiple examples but they have different codes and triggers multiple errors with my code like this one and still looking for a solution which makes my code works fine.
Set adpter in your recyclerview and then call the loader like this way:
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Event>> {
{
private RecyclerView recyclerView;
public static QuakesAdapter quakesAdapter;
public static ArrayList<Event> eventsList = new ArrayList<>();
public static final String USGS_REQUEST_URL =
"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2018-01-01&endtime=2018-12-01&minmagnitude=6&limit=50";
#Override
protected void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
quakesAdapter = new QuakesAdapter(this, eventsList);
//defining recyclerView and setting the adapter
recyclerView.setAdapter(quakesAdapter);
getSupportLoaderManager().initLoader(1, null, this).forceLoad();
}
#Override
public Loader<List<Event>> onCreateLoader ( int id, Bundle args){
return new FetchData(MainActivity.this);
}
#Override
public void onLoadFinished (Loader < List < Event >> loader, List < Event > data){
quakesAdapter.setData(data);
}
#Override
public void onLoaderReset (Loader < List < Event >> loader) {
quakesAdapter.setData(new ArrayList<Event>());
}
Performs actual task in background and returns the result.
private static class FetchData extends AsyncTaskLoader<List<Event>>{
String myDdata = "";
String line = "";
public FetchData(Context context) {
super(context);
}
#Override
public List<Event> loadInBackground () {
try {
List<Event> list = new ArrayList<Event>();
//opening the connection
if (httpURLConnection.getResponseCode() == 200) {
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
while (line != null) {
line = bufferedReader.readLine();
myDdata = myDdata + line;
}
JSONObject jsonObject = new JSONObject(myDdata);
JSONArray jsonArray = jsonObject.getJSONArray("features");
for (int i = 0; i < jsonArray.length(); i++) {
//getting values of the 3 attributes
eventsList.add(new Event(title, time, tsunamiAlert));
}
if (inputStream != null) {
inputStream.close();
}
} else {
Log.e("Connection Error: ", "Error response code: " + httpURLConnection.getResponseCode());
}
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return eventsList;
}
}
Add a method in your adapter like this:
public void setData(List<Event> data) {
this.data=data;
notifyDataSetChanged();
}
Which kind of error do you encounter?
I suggest using Java Interface and CallBack method for your AsynkTask, in this scenario, whenever your AsynkTask task is done, it notify the Activity with that callback method and you can execute notifyDataSetChange method of the adapter.

Android, Handling multiple URL for parsing

I am making a simple news reader app. The news need to be shown in one RecyclerView, like a list of news. The problem is that there are a multiple URLs from whom i extract data and i know only how to parse one but dont know how to handle more of them. Here is my code:
public class NewsActivity extends AppCompatActivity {
public static final String LOG_TAG = NewsActivity.class.getSimpleName();
public static final String newsUrl1 = "http://tests.intellex.rs/api/v1/news/list?page=1";
public static final String newsUrl2 = "http://tests.intellex.rs/api/v1/news/list?page=2";
public static final String newsUrl3 = "http://tests.intellex.rs/api/v1/news/list?page=3";
public static final String newsUrl4 = "http://tests.intellex.rs/api/v1/news/list?page=4";
private NewsAdapter adapter;
private RecyclerView recyclerView;
private ArrayList<NewsModel> newsArray;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.a_news_recycler_view__);
newsArray = new ArrayList<>();
adapter = new NewsAdapter(this, newsArray);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView = (RecyclerView) findViewById(R.id.newsRecyclerView);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.addItemDecoration(new SimpleDividerItemDecoration(getApplicationContext()));
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
NewsAsyncTask task = new NewsAsyncTask();
task.execute();
}
private class NewsAsyncTask extends AsyncTask<URL, Void, ArrayList<NewsModel>> {
#Override
protected ArrayList<NewsModel> doInBackground(URL... urls) {
URL url = createUrl(newsUrl1);
String jsonResponse = "";
try {
jsonResponse = makeHttpRequest(url);
} catch (IOException e) {
e.printStackTrace();
}
return extractFeatureFromJson(jsonResponse);
}
#Override
protected void onPostExecute(ArrayList<NewsModel> news) {
if (news == null) {
return;
}
adapter.addAll(news);
}
private URL createUrl(String stringUrl) {
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException exception) {
Log.e(LOG_TAG, "Error with creating URL", exception);
return null;
}
return url;
}
private String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(10000);
urlConnection.setConnectTimeout(15000);
urlConnection.connect();
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e(LOG_TAG, "Error response code: " + urlConnection.getResponseCode());
}
} catch (IOException e) {
Log.e(LOG_TAG, "Problem retrieving the JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
// function must handle java.io.IOException here
inputStream.close();
}
}
return jsonResponse;
}
private String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
private ArrayList<NewsModel> extractFeatureFromJson(String newsJson) {
if (TextUtils.isEmpty(newsJson)) {
return null;
}
ArrayList<NewsModel> news_information = new ArrayList<>();
try {
JSONObject baseJsonResponse = new JSONObject(newsJson);
JSONArray newsArray = baseJsonResponse.getJSONArray("list");
for (int i = 0; i < newsArray.length(); i++) {
JSONObject news = newsArray.getJSONObject(i);
try {
news = newsArray.getJSONObject(i);
String newsImage = news.getString("image");
String newsTitle = news.getString("title");
String newsPublished = news.getString("published");
String newsAuthor = news.getString("author");
String newsID = news.getString("id");
NewsModel newsModel = new NewsModel(newsImage, newsTitle, newsPublished, newsAuthor, newsID);
news_information.add(newsModel);
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return news_information;
}
}
}
Any help would be appreciated. Thanks in advance.
Why don't you use "links" as array ?
In case you will use an array:
JSONObject jsonObject = new JSONObject();
JSONArray keys = jsonObject.getJSONArray("links");
int length = keys.length();
for (int i = 0; i < length; i++) {
new ReadJSON().execute(keys.getString(i));
}
Anyway, you take all the keys and go one after the other, and then query each
EDIT:
JSONObject jsonObject = new JSONObject(/*Your links json*/);
JSONObject links = jsonObject.get("links");
Iterator<String> keys = links.keys();
while (keys.hasNext()) {
new ReadJSON().execute(links.getString(keys.next()));
}

Retrieving JSON Data using extras as a parameter of a constructor

Hi guys I am trying to retrieve some movie information in JSON format but I cannot seem to work out what the problem of my code is. The data retrieving and processing itself all works but the problem is that when I pass my title input in the EditText and retrieve that data from another activity, I cannot seem to be able to utilize it. I passed the extra retrieved into the parameter of my data processing class ParseJsonData. However, I get a null pointer exception at where I set title.setText(parseJsonData.getMovie().getTitle()). The strange aspect of this is that if I just run ParseJsonData in the MainActivity by passing in the title myself, I am able to retrieve the title of the data, observed through log. Is there anything that I should be aware of when I am passing an extra as a parameter of a constructor?
public class ResultsPage extends AppCompatActivity {
private final String LOG_TAG = getClass().getSimpleName();
private TextView title;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_results_page);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setTextViews();
}
private void setTextViews () {
Bundle bundle = getIntent().getExtras();
String movieTitle = bundle.getString("title");
Log.v(LOG_TAG, "title recieved is : " + movieTitle);
ParseJsonData parseJsonData = new ParseJsonData(movieTitle);
parseJsonData.execute();
title.setText(parseJsonData.getMovie().getTitle());
}
}
Below is ParseJsonData
public class ParseJsonData extends GetRawData{
private String mUrl;
private String title;
private static final String LOG_TAG = "ParseJsonData";
private Movie movie;
public ParseJsonData(String title) {
this.title = title;
processUrl();
}
#Override
public void execute() {
super.setUrl(mUrl);
ParseJsonDataBackground parseJsonDataBackground = new ParseJsonDataBackground();
parseJsonDataBackground.execute(mUrl);
}
public Movie getMovie() {
return movie;
}
private void processUrl () {
final String BASE_URL = "http://www.omdbapi.com/";
final String MOVIE_TITLE = "t";
final String MOVIE_YEAR = "y";
final String MOVIE_PLOT = "plot";
final String MOVIE_DATA_TYPE = "r";
mUrl = Uri.parse(BASE_URL).buildUpon().appendQueryParameter(MOVIE_TITLE, title).appendQueryParameter(MOVIE_YEAR, "").appendQueryParameter(MOVIE_PLOT, "short").appendQueryParameter(MOVIE_DATA_TYPE, "json").build().toString();
Log.v(LOG_TAG, "New Url address is : " + mUrl);
}
public class ParseJsonDataBackground extends GetRawDataBackground {
#Override
protected String doInBackground(String... params) {
return super.doInBackground(params);
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
processData(getmData());
}
private void processData (String mData){
try {
final String MOVIE_TITLE = "Title";
JSONObject jsonObject = new JSONObject(mData);
Log.v(LOG_TAG, mData);
String title = jsonObject.getString(MOVIE_TITLE);
movie = new Movie(title);
Log.v(LOG_TAG, "Title of the movie is " + movie.getTitle());
}catch (JSONException e){
Log.e(LOG_TAG, "Error retrieving JsonData");
e.printStackTrace();
}
}
}
}
This is an extension of GetRawData which is below
public class GetRawData {
private String url;
private String mData;
private static final String LOG_TAG = "GetRawData";
public GetRawData() {
}
public String getmData() {
return mData;
}
public void setUrl(String url) {
this.url = url;
}
public void execute () {
GetRawDataBackground getRawDataBackground = new GetRawDataBackground();
getRawDataBackground.execute(url);
}
public class GetRawDataBackground extends AsyncTask<String, Void, String>{
private StringBuffer stringBuffer;
#Override
protected String doInBackground(String... params) {
mData = processDownloads (params[0]);
if (mData == null){
Log.e(LOG_TAG, "Null returned during processing");
return null;
}
return mData;
}
#Override
protected void onPostExecute(String s) {
Log.v(LOG_TAG, "Data retrieved is : " + s);
super.onPostExecute(s);
}
private String processDownloads (String mUrl){
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
if (mUrl == null){
return null;
}
URL url = new URL(mUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
int responseCode = connection.getResponseCode();
Log.d(LOG_TAG, "Response code is : " + responseCode);
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
stringBuffer = new StringBuffer();
String line = new String();
while ((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
return stringBuffer.toString();
} catch (MalformedURLException e) {
Log.e(LOG_TAG, "MalformedURLException");
return null;
} catch (IOException e){
Log.e(LOG_TAG, "IOException in making connection");
return null;
} finally {
if (connection != null) {
connection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
Log.e(LOG_TAG, "Error attempting to close reader");
}
}
}
}
}
}
It's because you're instantiating movie in the background task. It happens in a parallel thread (Thread 2). Your main thread calls getMovite().getTitle(); but movie is not set yet as Thread 2 is still running.
You should pass a callback to ParseJsonData from MainActivity and call the callback in onPostExecute. Make sure you return to the MainThread when you update the text view though.
public class ParseJsonDataBackground extends GetRawDataBackground {
public interface ParseJsonCallback{
void onJsonReady(Movie movie);
}
private ParseJsonCallback callback;
ParseJsonDataBackground(ParseJsonCallback callback){
this.callback = callback;
}
.....
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
processData(getmData());
callback.onJsonReady(movie);
}
.....
}
And in MainActivity
....
ParseJsonData parseJsonData = new ParseJsonData(movieTitle, new ParseJsonCallback(){
void onJsonReady(Movie movie){
runOnUiThread(new Runnable() {
#Override
public void run() {
title.setText(movie.getTitle());
}
});
}
});
parseJsonData.execute();
....

Android Bug: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path in android

This is my JSON
[
{
"id":1,
"media":{
"name":"ABC",
"url":"abc.org/"
},
"published":"2016-01-24T16:00:00.000Z",
"_links":{
"self":{
"href":"acb.net"
}
}
}
]
Class ApiInterface
public interface ApiServiceInterface {
#GET("/api/feed/channels/current/entries")
ApiFeedCurrentRequest getAllApiFeedCurrent();
}
Class ApiFeedCurrentRequest
public class ApiFeedCurrentRequest {
#SerializedName("id")
private int mId;
#SerializedName("media")
private Media mMedia;
#SerializedName("published")
private String mPublished;
#SerializedName("_links")
private Link mLinks;
Class ApiService
private static final String TAG = "__API__Service";
private final ApiServiceInterface mApiService;
public ApiService(Context context) {
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(30, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(30, TimeUnit.SECONDS);
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constant.BASE_URL)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setClient(new OkClient(okHttpClient))
.setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
.setLog(new AndroidLog(TAG))
.setConverter(new CleanGsonConverter(gson))
.setErrorHandler(new CustomErrorHandler(context))
.build();
this.mApiService = restAdapter.create(ApiServiceInterface.class);
}
public ApiFeedCurrentRequest getAllData() {
if (mApiService != null) {
return mApiService.getAllApiFeedCurrent();
} else {
return null;
}
}
Class CleanGsonConverter
public class CleanGsonConverter extends GsonConverter {
private Gson mGson;
public CleanGsonConverter(Gson gson) {
super(gson);
mGson = gson;
}
public CleanGsonConverter(Gson gson, String encoding) {
super(gson, encoding);
mGson = gson;
}
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
boolean willCloseStream = false; // try to close the stream, if there is no exception thrown using tolerant JsonReader
try {
String mDirty = toString(body);
if (TextUtils.isEmpty(mDirty)) return null;
String clean = mDirty.replaceAll("(^\\(|\\)$)", "");
body = new JsonTypedInput(clean.getBytes(Charset.forName("UTF-8")));
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.in()));
jsonReader.setLenient(true);
Object o = mGson.fromJson(jsonReader, type);
willCloseStream = true;
return o;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (willCloseStream) {
closeStream(body);
}
}
}
private String toString(TypedInput body) {
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(body.in()));
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
private void closeStream(TypedInput body) {
try {
InputStream in = body.in();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In Activity.
private class GetDataAsync extends AsyncTask<Void, Void, Void> {
private WeakReference<SplashActivity> mWeakReference;
private ProgressDialog mDialog;
private boolean mErrorInternet = false;
private ApiFeedCurrentRequest mApiFeedCurrent;
public GetDataAsync(SplashActivity splashActivity) {
mWeakReference = new WeakReference<SplashActivity>(splashActivity);
mDialog = new ProgressDialog(splashActivity);
mDialog.setMessage(splashActivity.getString(R.string.message_loading));
mDialog.setCancelable(false);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mDialog.show();
}
#Override
protected Void doInBackground(Void... params) {
SplashActivity activity = mWeakReference.get();
if (activity != null) {
if (Utils.isInternetAvailable()) {
try {
mApiFeedCurrent = activity.mApiService.getAllData();
} catch (RetrofitError error) {
DebugTool.logD("ERROR = " + error.toString());
} catch (Exception e) {
e.printStackTrace();
}
} else {
mErrorInternet = true;
}
}
return null;
}
This is my Error.
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:200)
at com.google.gson.Gson.fromJson(Gson.java:810)
at com.seesaa.newsaudiocast.api.CleanGsonConverter.fromBody(CleanGsonConverter.java:60)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:367)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at $Proxy0.getAllApiFeedCurrent(Native Method)
at com.seesaa.newsaudiocast.api.ApiService.getAllData(ApiService.java:50)
at com.seesaa.newsaudiocast.activity.SplashActivity$GetDataAsync.doInBackground(SplashActivity.java:75)
at com.seesaa.newsaudiocast.activity.SplashActivity$GetDataAsync.doInBackground(SplashActivity.java:49)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
Please. Help me fix bug. Thanks all!
Class ApiInterface
public interface ApiServiceInterface {
#GET("/api/feed/channels/current/entries")
ApiFeedCurrentRequest getAllApiFeedCurrent();
}
replace with
public interface ApiServiceInterface {
#GET("/api/feed/channels/current/entries")
List<ApiFeedCurrentRequest> getAllApiFeedCurrent();
}
AND :in Activity
private List<ApiFeedCurrentRequest> mApiFeedCurrent = new ArrayList<>();
#Override
protected Void doInBackground(Void... params) {
SplashActivity activity = mWeakReference.get();
if (activity != null) {
if (Utils.isInternetAvailable()) {
try {
mApiFeedCurrent = activity.mApiService.getAllData();
} catch (RetrofitError error) {
DebugTool.logD("ERROR = " + error.toString());
} catch (Exception e) {
e.printStackTrace();
}
} else {
mErrorInternet = true;
}
}
return null;
}
Hope it will help you my friend ! :)
change this
ApiFeedCurrentRequest getAllApiFeedCurrent();
to this
ApiFeedCurrentRequest[] getAllApiFeedCurrent();
or
List<ApiFeedCurrentRequest> getAllApiFeedCurrent();

Categories

Resources