-3

Hi I am learning Android App Development. For this, I wanted to make myself a simple wallpaper app. Hence, I wrote something roughly which is presented here. I want to get wallpaper urls from json. Unfortunately, I am unable to get data from my server. java.lang.NullPointerException: Attempt to read from null array How do I get the data correctly from the jsonParse asynctask? I am stuck on this the whole day. What could have gone wrong here? Here is my code:

myjson.json:

{
    "walls":[
                   {"ourUrl":"http://www.hdbloggers.net/wp-content/uploads/2016/01/Wallpapers-for-Android.jpg"},
                   {"ourUrl":"http://androidwallpape.rs/content/02-wallpapers/131-night-sky/wallpaper-2707591.jpg"},
                   {"ourUrl":"http://androidwallpape.rs/content/02-wallpapers/155-starrynight/starry-night-sky-star-galaxy-space-dark-9-wallpaper.jpg"}
            ]
}

MainActivity.java:

package regalstreak.me.wallpapers;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

public class MainActivity extends Activity {

    RecyclerView recyclerView;
    RecyclerView.LayoutManager layoutManager;
    RecyclerView.Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        adapter = new RecyclerAdapter(this);
        recyclerView.setAdapter(adapter);

    }

}

RecyclerAdapter.java:

package regalstreak.me.wallpapers;

import android.content.Context;
import android.os.AsyncTask;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;

// This is a recycleradapter which will set the correct images to the correct position in the recyclerview.

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {

    private Context myCtx1;
    String[] arr;
    String[] arrurl;
    String jsonURL = "http://dev.regalstreak.me/myjson.json";

    public RecyclerAdapter(Context ctx) {
        this.myCtx1 = ctx;
    }

    public ImageView Image;

    private String[] mText = {
            "Text 1",
            "Text 2",
            "Text 3"
    };

    public class ViewHolder extends RecyclerView.ViewHolder {

        public TextView Text;

        public ViewHolder(View itemView) {
            super(itemView);

            Image = (ImageView) itemView.findViewById(R.id.image_view);
            Text = (TextView) itemView.findViewById(R.id.text_view);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.wallpapers_list, viewGroup, false);

        ViewHolder viewHolder = new ViewHolder(v);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        viewHolder.Text.setText(mText[i]);
        new jsonParse().execute();
        new DownloadImageTask(Image).execute(arrurl[i]);
    }

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

    class jsonParse extends AsyncTask<String, Void, String[]> {

        protected String[] doInBackground(String[] urls) {
            String myText = null;
            String url = urls[0];
            String ourUrl;

            try {
                InputStream in = new java.net.URL(jsonURL).openStream();
                myText = IOUtils.toString(in, "utf-8");
                in.close();

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }


            try {
                // Parse the json
                List<String> allUrls = new ArrayList<String>();

                JSONObject jsonObjectRoot = new JSONObject(myText);
                JSONArray jsonArray = jsonObjectRoot.getJSONArray("walls");

                for (int i = 0; i < jsonArray.length(); i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    ourUrl = jsonObject.getString("ourUrl");
                    allUrls.add(ourUrl);
                }

                arr = allUrls.toArray(new String[allUrls.size()]);

            } catch (JSONException e) {
                e.printStackTrace();
            }

            return arr;
        }

        protected void onPostExecute(String[] result){
            arrurl = result;
        }
    }

}

DownloadImageTask.java:

package regalstreak.me.wallpapers;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;

import java.io.InputStream;

// Here, we will download the wallpapers obtained from jsonData with an asynctask.

public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>{

    ImageView bmImage;

    public DownloadImageTask(ImageView bmImage){
        this.bmImage = bmImage;
    }

    protected Bitmap doInBackground(String... urls) {
        String urldisplay = urls[0];
        Bitmap mIcon11 = null;
        try {
            InputStream in = new java.net.URL(urldisplay).openStream();
            mIcon11 = BitmapFactory.decodeStream(in);
            in.close();
        } catch (Exception e) {
            Log.e("Error getting images.", e.getMessage());
            e.printStackTrace();
        }

        return mIcon11;
    }

    protected void onPostExecute(Bitmap result){
        bmImage.setImageBitmap(result);
    }

}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="regalstreak.me.wallpapers.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/recycler_view" />

</RelativeLayout>

wallpaper_list.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/relative"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="5dp">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="150dp" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/image_view"
        android:alpha="0.6"
        android:background="@color/colorDivider"
        android:padding="9dp">

        <TextView
            android:id="@+id/text_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAlignment="center"
            android:textColor="@color/colorPrimaryText" />

    </RelativeLayout>

</RelativeLayout>
Neil Agarwal
  • 877
  • 1
  • 9
  • 10
  • Consider using `gson` for your JSON parsing. You can parse JSON into a POJO class using 2 lines of code, where one of those is a constructor. Also, please start your class name with a capital as that's the Java coding convention – vkislicins Nov 03 '16 at 12:08
  • Also if you need to create a POJO from json - there's an excellent tool online http://www.jsonschema2pojo.org/ – vkislicins Nov 03 '16 at 12:09
  • Thanks for the pointers @vkislicins. I will look forward to using gson asap after this. Just wanted to learn the basics and get going. – Neil Agarwal Nov 03 '16 at 12:12
  • Fair enough. Which line does your nullpointer points at? – vkislicins Nov 03 '16 at 12:15
  • RecyclerAdapter.java, line 75: ``` new DownloadImageTask(Image).execute(arrurl[i]);``` – Neil Agarwal Nov 03 '16 at 12:16
  • Are u getting json response at line myText = IOUtils.toString(in, "utf-8")? Can u debug and check mytext is null? – Raghavendra Nov 03 '16 at 12:18
  • Your json parse is an async task right? So you're attempting to use an object that should be produced as a result of an async task straight away. So at that time the object is still null, as your parser hasn't executed yet – vkislicins Nov 03 '16 at 12:18
  • @Raghavendra yes i am getting a response – Neil Agarwal Nov 03 '16 at 12:24
  • @vkislicins ooh. So any way i could execute the parser using the asynctask? – Neil Agarwal Nov 03 '16 at 12:25
  • The new DownloadImageTask(Image).execute(arrurl[i]); line will execute immediately after new jsonParse().execute(); statement, but arrurl not yet get the result. So u r getting NPE. – Raghavendra Nov 03 '16 at 12:30
  • Try this, do the jsonParse task in activity itself and pass the result as an argument to the adapter and then do the DownloadImageTask? – Raghavendra Nov 03 '16 at 12:33
  • Lets see @Raghavendra Ill report back – Neil Agarwal Nov 03 '16 at 12:34
  • @NeilAgarwal well, you don't really want to do any of that in your RecyclerView. RecyclerView is for presenting the data, not getting it and parsing. I'd suggest you look into `AsyncTaskLoader` - you can change your `AsyncTask` into loader, run it from your activity/fragment, do your tasks (fetch, parse, download, whatever) in the background, then return the data and pass it to your adapter. https://developer.android.com/reference/android/content/AsyncTaskLoader.html – vkislicins Nov 03 '16 at 12:48
  • @raghavendra it didn't work. Same npe. – Neil Agarwal Nov 03 '16 at 12:53
  • @NeilAgarwal can u post your updated code? – Raghavendra Nov 03 '16 at 12:57

1 Answers1

1

I have used HttpURLConnection class here for quick response and features like cache. The data received from the URL is being added to an input stream which we then convert to a String builder to get a string object which we can further use with the JSON classes.

PS - Add the AsyncTask code to your MainActivity itself, don't make a separate java file for this.

Tip - Always verify the json using this tool - jsonlint.com

MainActivity

/*

your code

*/

@Override
protected void onCreate(Bundle savedInstanceState) {

        new MyAsyncTask().execute("");

}

class MyAsyncTask extends AsyncTask<String, String, Void> {

    private ProgressDialog progressDialog = new ProgressDialog(StartScreen.this);
    InputStream inputStream = null;
    String result = "";
    ArrayList<String> list;

    protected void onPreExecute() {
        progressDialog.setTitle("Downloading JSON Data");
        progressDialog.show();
        // above code makes a dialog with a progress bar
    }

    @Override
    protected Void doInBackground(String... params) {
        ArrayList<String> param = new ArrayList<String>();
        URL url, url2;

        try{
            url = new URL("http://dev.regalstreak.me/myjson.json");
            // link to your json file
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setUseCaches(false);
            inputStream = new BufferedInputStream(urlConnection.getInputStream());

        }catch (MalformedURLException malle){
            Log.e("Mal", ""+malle);
            malle.printStackTrace();
        }catch (IOException ioe){
            Log.e("IO", ""+ioe);
            ioe.printStackTrace();
        }

        // Convert response to string using String Builder
        try {
            BufferedReader bReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"), 8);
            StringBuilder sBuilder = new StringBuilder();

            String line = null;
            while ((line = bReader.readLine()) != null) {
                sBuilder.append(line + "\n");
            }

            inputStream.close();
            result = sBuilder.toString();

        } catch (Exception e) {
            Log.e("StringBuilding", "Error converting result " + e.toString());
        }
        return null;
    }

    protected void onPostExecute(Void v) {

        //parse JSON data
        try {
            JSONObject jobj = new JSONObject(result);
            //Taking a JSON Array from the JSONObject created above

            String url = jobj.getString("ourUrl");

            // We are adding this string to the ArrayList

            list.add(url);

            progressDialog.dismiss();
            Context con = ListLoader.this.getApplication();
            adapter = new RecyclerAdapter(list,con);

            recyclerView.setAdapter(adapter);

        } catch (JSONException e) {
            Log.e("JSONException", "Error: " + e.toString());
        } // catch (JSONException e)
    }

}


/*

your code

*/

Now to display the images more effectively in the list, use the repo Universal image loader. It has a lot of features. You can get it here - https://github.com/nostra13/Android-Universal-Image-Loader

And then add this kind of code to display the images. Put it inside the onBindViewHolder

Adapter

@Override
public void onBindViewHolder(DataHolder holder, int position) {
    ImageLoaderConfiguration config;
    config = new ImageLoaderConfiguration.Builder(mContext).build();
    ImageLoader.getInstance().init(config);
    imageLoader = ImageLoader.getInstance();

    DisplayImageOptions options = new DisplayImageOptions.Builder()
    .showImageForEmptyUri(R.drawable.ic_error_black_48dp) // displays this image not found
    .showImageOnFail(R.drawable.ic_error_black_48dp) // Displays this on failure
    .showImageOnLoading(R.drawable.white) // Displays while loading
    .cacheInMemory(false)
    .cacheOnDisk(true)
    .build();

    imageLoader.displayImage(list.get(position), holder.imageView, options);    
    // We are feeding the urls here. 
}
Sanved
  • 921
  • 13
  • 19