Search code examples
javaandroidxmlandroid-fragmentsandroid-arrayadapter

Android Xml Pull Parser error - SitesDownloadTask.onPostExecute


I have different three Adapters and three XML Parsers in my application for three Fragments sliding on ViewPager.

My application works fine until I start sliding a little faster between first and second, or second and third fragment.

Log Cat messagess:

When sliding between first and second:

FATAL EXCEPTION: main
Process: com.intera.eronetmarket, PID: 3326
java.lang.NullPointerException
    at android.widget.ArrayAdapter.init(ArrayAdapter.java:310)
    at android.widget.ArrayAdapter.<init>(ArrayAdapter.java:153)
    at adapters.KatAdapter.<init>(KatAdapter.java:32)
    at com.intera.eronetmarket.Kategorije$SitesDownloadTask.onPostExecute(Kategorije.java:98)
    at com.intera.eronetmarket.Kategorije$SitesDownloadTask.onPostExecute(Kategorije.java:1)
    at android.os.AsyncTask.finish(AsyncTask.java:632)
    at android.os.AsyncTask.access$600(AsyncTask.java:177)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5017)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
    at dalvik.system.NativeStart.main(Native Method)

When Sliding between second and third:

FATAL EXCEPTION: main
Process: com.intera.eronetmarket, PID: 3359
java.lang.NullPointerException
    at com.intera.eronetmarket.Preporuceno$AppDownloadTask.onPostExecute(Preporuceno.java:85)
    at com.intera.eronetmarket.Preporuceno$AppDownloadTask.onPostExecute(Preporuceno.java:1)
    at android.os.AsyncTask.finish(AsyncTask.java:632)
    at android.os.AsyncTask.access$600(AsyncTask.java:177)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5017)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
    at dalvik.system.NativeStart.main(Native Method)

My first fragment where AppDownlodTask class is. It is same in all 3 fragments (only adapters are different for each fragment):

public class Preporuceno extends Fragment  {

private AppAdapter mAdapter;
private ListView siteList;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i("mobAppModel", "OnCreate()");    
View rootView = inflater.inflate(R.layout.activity_preporuceno, container, false);


siteList = (ListView) rootView.findViewById(R.id.listView1);   

siteList.setOnItemClickListener(new OnItemClickListener(){

    @Override
    public void onItemClick(AdapterView<?> parent, View v, int pos,long id) {
        String url = mAdapter.getItem(pos).getstoreURL();
        Intent i = new Intent(Intent.ACTION_VIEW);
        i.setData(Uri.parse(url));
        startActivity(i);

    }
});

    if(isNetworkAvailable()){
        Log.i("mobAppModel", "starting download Task");
        AppDownloadTask download = new AppDownloadTask();
        download.execute();
    }else{
        mAdapter = new AppAdapter(getActivity().getApplicationContext(), -1, XMLsourcePullParser.getmobAppModel(getActivity()));
        siteList.setAdapter(mAdapter);
    }

    return rootView;
}

private boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager 
          = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null && activeNetworkInfo.isConnected();
} 
private class AppDownloadTask extends AsyncTask<Void, Void, Void>{
    @Override
    protected Void doInBackground(Void... arg0) {
        //Download the file
        try {
            Downloader.DownloadFromUrl("https://dl.dropboxusercontent.com/s/te0c0s7y7zr79tm/kategorijeXML.xml", getActivity().openFileOutput("XMLsource.xml", Context.MODE_PRIVATE));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    } 
    @Override
    protected void onPostExecute(Void result){
        //setup our Adapter and set it to the ListView.

        mAdapter = new AppAdapter(getActivity().getApplicationContext(), -1, XMLsourcePullParser.getmobAppModel(getActivity()));

            siteList.setAdapter(mAdapter);

        Log.i("mobAppModel", "adapter size = "+ mAdapter.getCount());
    }

}

}

My Adapters:

package adapters;

import java.util.List;
import models.KatModel;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.intera.eronetmarket.R;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;


/*
 * Custom Adapter class that is responsible for holding the list of sites after they
 * get parsed out of XML and building row views to display them on the screen.
 */
public class KatAdapter extends ArrayAdapter<KatModel> {

    ImageLoader imageLoader;
    DisplayImageOptions options;


    public KatAdapter(Context ctx, int textViewResourceId, List<KatModel> sites) {

        super(ctx, textViewResourceId, sites);

        //Setup the ImageLoader, we'll use this to display our images
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx).build();
        imageLoader = ImageLoader.getInstance();
        if (!imageLoader.isInited()) {
            imageLoader.init(config);
        }

        //Setup options for ImageLoader so it will handle caching for us.
        options = new DisplayImageOptions.Builder()
        .cacheInMemory()
        .cacheOnDisc()
        .build();}





    /*
     * (non-Javadoc)
     * @see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)
     * 
     * This method is responsible for creating row views out of a StackSite object that can be put
     * into our ListView
     */
    @SuppressLint("InflateParams")
    @Override
    public View getView(int pos, View convertView, ViewGroup parent){
        RelativeLayout row = (RelativeLayout)convertView;
        Log.i("StackSites", "getView pos = " + pos);
        if(null == row){
            //No recycled View, we have to inflate one.
            LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = (RelativeLayout)inflater.inflate(R.layout.row_site, null);
        }

        //Get our View References

        TextView nameTxt = (TextView)row.findViewById(R.id.nameTxt);



        //Initially we want the progress indicator visible, and the image invisible




        //Load the image and use our options so caching is handled.


        //Set the relavent text in our TextViews
        nameTxt.setText(getItem(pos).getcategoryName());




        return row;


    }

}

--

package adapters;

import java.util.List;

import models.mobAppModel;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; 
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;

import com.intera.eronetmarket.R;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.ImageLoadingListener;

@SuppressLint("InflateParams") public class AppAdapter extends ArrayAdapter<mobAppModel>{
    ImageLoader imageLoader;
    DisplayImageOptions options;
    public AppAdapter(Context ctx,int textViewResourceId, List<mobAppModel> appModel){
        super(ctx,textViewResourceId,appModel);

        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx).build();
        imageLoader = ImageLoader.getInstance();
        if (!imageLoader.isInited()) {
            imageLoader.init(config);
         }



        options= new DisplayImageOptions.Builder()
        .cacheInMemory()
        .cacheOnDisc()
        .build();
    }

    public View getView(int pos, View convertView, ViewGroup parent){
        RelativeLayout row = (RelativeLayout)convertView;
        Log.i("mobAppModels", "getView pos = " + pos);
        if(null == row){
            //No recycled View, we have to inflate one.
            LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = (RelativeLayout)inflater.inflate(R.layout.row_app, null);
        }

        //Get our View References
        final ImageView iconImg = (ImageView)row.findViewById(R.id.iconUrl);
        final ProgressBar indicator = (ProgressBar)row.findViewById(R.id.progress);

        //Initially we want the progress indicator visible, and the image invisible
        indicator.setVisibility(View.VISIBLE);
        iconImg.setVisibility(View.INVISIBLE);

        //Setup a listener we can use to swtich from the loading indicator to the Image once it's ready
        ImageLoadingListener listener = new ImageLoadingListener(){



            @Override
            public void onLoadingStarted(String arg0, View arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onLoadingCancelled(String arg0, View arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {
                indicator.setVisibility(View.INVISIBLE);
                iconImg.setVisibility(View.VISIBLE);
            }

            @Override
            public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {
                // TODO Auto-generated method stub

            }

        };

        //Load the image and use our options so caching is handled.
        imageLoader.displayImage(getItem(pos).getimageUrl(),iconImg,options,listener);

        //Set the relavent text in our TextViews




        return row;


    }

}

--

package adapters;

import java.util.List;
import models.mobAppModel;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.RatingBar;
import com.intera.eronetmarket.R;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.ImageLoadingListener;

public class AppAdapterNajpop extends ArrayAdapter<mobAppModel>{
    ImageLoader imageLoader;
    DisplayImageOptions options;
    public AppAdapterNajpop(Context ctx,int textViewResourceId, List<mobAppModel> appModel){
        super(ctx,textViewResourceId,appModel);

        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx).build();
        imageLoader = ImageLoader.getInstance();
        if (!imageLoader.isInited()) {
            imageLoader.init(config);
         }


        options= new DisplayImageOptions.Builder()
        .cacheInMemory()
        .cacheOnDisc()
        .build();
    }

    @SuppressLint("InflateParams")
    public View getView(int pos, View convertView, ViewGroup parent){
        RelativeLayout row = (RelativeLayout)convertView;
        Log.i("mobAppModels", "getView pos = " + pos);
        if(null == row){
            //No recycled View, we have to inflate one.
            LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = (RelativeLayout)inflater.inflate(R.layout.row_app_najpopularnije, null);
        }

        //Get our View References
        final ImageView iconImg = (ImageView)row.findViewById(R.id.iconUrl);
        TextView appHeadline = (TextView)row.findViewById(R.id.textView1);
        TextView developer = (TextView)row.findViewById(R.id.textView2);
        RatingBar ratingpoints =(RatingBar)row.findViewById(R.id.ratingBar);
        final ProgressBar indicator = (ProgressBar)row.findViewById(R.id.progress);

        //Initially we want the progress indicator visible, and the image invisible
        indicator.setVisibility(View.VISIBLE);
        iconImg.setVisibility(View.INVISIBLE);

        //Setup a listener we can use to swtich from the loading indicator to the Image once it's ready
        ImageLoadingListener listener = new ImageLoadingListener(){



            @Override
            public void onLoadingStarted(String arg0, View arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onLoadingCancelled(String arg0, View arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {
                indicator.setVisibility(View.INVISIBLE);
                iconImg.setVisibility(View.VISIBLE);
            }

            @Override
            public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {
                // TODO Auto-generated method stub

            }

        };

        //Load the image and use our options so caching is handled.
        imageLoader.displayImage(getItem(pos).geticonUrl(),iconImg,options,listener);

        //Set the relavent text in our TextViews
        appHeadline.setText(getItem(pos).getappHeadline());
        developer.setText(getItem(pos).getdeveloper());
        ratingpoints.setRating(Float.parseFloat(getItem(pos).getratingPoints()));


        return row;


    }

}

When I slide slower, everything works fine. What is wrong? :\


Solution

  • As your flip quickly from fragment A to B, A might be destroyed at any time and you have no control on that. Before the fragment destroying process finish, it will also be detached from the activity. Starting from this moment, any call go getActivity() may returns null.

    So your issue is that your fragment has not a reference to an activity, but the async task still is running in a background thread, leading to null pointer exceptions.

    I'd suggest you to do:

    Activity attached = getActivity();
    if (attached != null) {
        mAdapter = new AppAdapter(attached.getApplicationContext(), -1, XMLsourcePullParser.getmobAppModel(getActivity()));
        siteList.setAdapter(mAdapter);
    }
    

    More about this issue in:http://developer.android.com/guide/components/fragments.html#Lifecycle . I'd like to highlight the Caution session: if you need a Context object within your Fragment, you can call getActivity(). However, be careful to call getActivity() only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle, getActivity() will return null.