-1

I'm pulling a html page from the web. The page contains images and I load the images with UILImageGetter.

The problem is that if the page just begins to load and I rotate the device multiple times the app crashes with an NPE.

UILImageGetter

public class UILImageGetter implements Html.ImageGetter{
    Context c;
    TextView container;
    UrlImageDownloader urlDrawable;


    public UILImageGetter(View textView, Context context) {
        this.c = context;
        this.container = (TextView) textView;
    }

    @Override
    public Drawable getDrawable(String source) {
        urlDrawable = new UrlImageDownloader(c.getResources(), source);
        if (Build.VERSION.SDK_INT >= 21) {
        urlDrawable.mDrawable = c.getResources().getDrawable(R.drawable.default_thumb,null);
        } else {
            urlDrawable.mDrawable = c.getResources().getDrawable(R.drawable.default_thumb);
        }
        ImageLoader.getInstance().loadImage(source, new SimpleListener(urlDrawable));
        return urlDrawable;
    }

    private class SimpleListener extends SimpleImageLoadingListener {
        UrlImageDownloader mUrlImageDownloader;

        public SimpleListener(UrlImageDownloader downloader) {
            super();
            mUrlImageDownloader= downloader;
        }

        @Override
        public void onLoadingStarted(String imageUri, View view) {
            Log.d("DEBUG", "onLoadingStarted called");
            //spinner.setVisibility(View.VISIBLE);

        }

        @Override
        public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
            //spinner.setVisibility(View.GONE);

            int width = loadedImage.getWidth();
            int height = loadedImage.getHeight();

            int newWidth = width;
            int newHeight = height;

            if (width > container.getWidth()) {
                newWidth = container.getWidth();
                newHeight = (newWidth * height) / width;
            }

            if (view != null) {
                container.getLayoutParams().width = newWidth;
                container.getLayoutParams().height = newHeight;
            }

            Drawable result = new BitmapDrawable(c.getResources(), loadedImage);
            result.setBounds(0, 0, newWidth, newHeight);

            mUrlImageDownloader.setBounds(1, 1, newWidth, newHeight);
            mUrlImageDownloader.mDrawable = result;
            container.invalidate();

            container.setText(container.getText());

        }

        @Override
        public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
            String message = null;
            switch (failReason.getType()) {
                case IO_ERROR:
                    message = "Input/Output error";
                    break;
                case DECODING_ERROR:
                    message = "Image can't be decoded";
                    break;
                case NETWORK_DENIED:
                    message = "Downloads are denied";
                    break;
                case OUT_OF_MEMORY:
                    message = "Out Of Memory error";
                    break;
                case UNKNOWN:
                    message = "Unknown error";
                    break;
            }
            Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
            //spinner.setVisibility(View.GONE);
        }
    }

    private class UrlImageDownloader extends BitmapDrawable {
        public  Drawable mDrawable;

        public UrlImageDownloader(Resources resources, String filepath) {
            super(resources, filepath);
            mDrawable = new BitmapDrawable(resources, filepath);
        }

        @Override
        public void draw(Canvas canvas) {
            if (mDrawable != null) {
                mDrawable.draw(canvas);
            }
        }
    }
}

DetailsFragment

public class DetailsFragment extends Fragment implements ObservableScrollViewCallbacks{


    private AlertDialog internetDialog;
    private AlertDialog sthWrongAlert;
    private String url;

    private String birdData;

    private final String TAG = "DetailsFragment";
    protected com.nostra13.universalimageloader.core.ImageLoader mImageLoader;


    TextView birdContent;
    public DetailsFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        url = getArguments().getString("bird_link");

        // Inflate the layout for this fragment
        view =  inflater.inflate(R.layout.fragment_details, container, false);

        showDialog();
        sthWrongDialog();

        gadContent = (TextView) view.findViewById(R.id.gad_content);


        DisplayImageOptions defaultoptions = new DisplayImageOptions.Builder()
                .cacheInMemory(true)
                .cacheOnDisk(true)
                .build();
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getActivity())
                .defaultDisplayImageOptions(defaultoptions)
                .writeDebugLogs()
                .build();


        mImageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance();
        mImageLoader.init(config);





        if (savedInstanceState != null) {
            birdData = savedInstanceState.getString("birdData");
            if (birdData != null) {
                parseHtml(birdData);
            } 
            } else if (NetworkCheck.isAvailableAndConnected(getActivity())){
                    loadBird();
                } 
            }
        } else {
            if (NetworkCheck.isAvailableAndConnected(getActivity())) {
                    loadGad();
                }
            } else {
                internetDialog.show();
            }
        }
        return view;
    }


    public interface OnLinkClickedListener {
        public void OnLinkClicked (String link);
    }

    private static OnLinkClickedListener sLinkCallbacks = new OnLinkClickedListener() {
        @Override
        public void OnLinkClicked(String link) {

        }
    };


    private void showDialog() {
        internetDialog = new AlertDialog.Builder(getActivity())
                // The usuals
                .create();
    }

    private void sthWrongDialog() {
        sthWrongAlert = new AlertDialog.Builder(getActivity())
                // The usuals
                .create();
    }

    private void loadBird() {
        Log.d(TAG, "loadBird called");

        final ProgressBar progressBar;
        progressBar = (ProgressBar) view.findViewById(R.id.progress_circle);
        progressBar.setVisibility(View.VISIBLE);


//        String news_id = getIntent().getStringExtra("BirdId");
        Log.d(TAG, "You clicked bird id " + url);

        StringRequest stringRequest = new StringRequest(url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        //Log.d("Debug", response.toString());
                        if (progressBar != null) {
                            progressBar.setVisibility(View.GONE);
                        }
                        parseHtml(response);
                        birdData = response;


                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        VolleyLog.d(TAG, "Error: " + error.getMessage());

                        if (progressBar != null) {
                            progressBar.setVisibility(View.GONE);
                        }
                        if (sthWrongAlert != null) {
                            sthWrongAlert.show();
                        }


                    }
                });

        //Creating requestqueue
        RequestQueue requestQueue = Volley.newRequestQueue(getActivity());

        //Adding request queue
        requestQueue.add(stringRequest);
    }

    private void parseHtml(String response) {
        Log.d(TAG, "parsinghtml");
        Document document = Jsoup.parse(response);
        bird_content = document.select("div.entry-content").first().html();
        setTextViewHTML(birdContent, bird_content);
    protected void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span)
    {
        int start = strBuilder.getSpanStart(span);
        int end = strBuilder.getSpanEnd(span);
        int flags = strBuilder.getSpanFlags(span);
        ClickableSpan clickable = new ClickableSpan() {
            public void onClick(View view) {
                Log.e(TAG, "on click" + span.getURL());
            }
        };
        strBuilder.setSpan(clickable, start, end, flags);
        strBuilder.removeSpan(span);
    }

    protected void setTextViewHTML(TextView text, String html) {
        CharSequence sequence = Html.fromHtml(html, new UILImageGetter(birdContent, getActivity()), null);
        SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
        URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
        for(URLSpan span : urls) {
            makeLinkClickable(strBuilder, span);
        }
        text.setText(strBuilder);
        text.setMovementMethod(LinkMovementMethod.getInstance());
    }

       @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (birdData != null) {
            outState.putString("birdData", birdData);
        }

        if (internetDialog != null) {
            outState.putBundle("internetDialog", internetDialog.onSaveInstanceState());
        }
        if (sthWrongAlert != null) {
            outState.putBundle("sthWrongAlert", sthWrongAlert.onSaveInstanceState());
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (savedInstanceState != null) {
            if (internetDialog != null) {
                internetDialog.onRestoreInstanceState(savedInstanceState.getBundle("internetDialog"));
            }
            if (sthWrongAlert != null) {
                sthWrongAlert.onRestoreInstanceState(savedInstanceState.getBundle("sthWrongAlert"));

            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy called");
        if (mImageLoader.isInited()) {
            mImageLoader.destroy();
        }


        if (internetDialog != null){
            internetDialog.dismiss();
            internetDialog = null;
        }
        if (sthWrongAlert != null) {
            sthWrongAlert.dismiss();
            sthWrongAlert = null;
        }
    }

}

StackTrace

05-19 13:37:14.936 12213-12213/com.pexample.birds E/AndroidRuntime: FATAL EXCEPTION: main
                                                                     Process: com.pexample.birds, PID: 12213
                                                                     java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
                                                                         at com.pexample.birds.UILImageGetter.getDrawable(UILImageGetter.java:39)
                                                                         at android.text.HtmlToSpannedConverter.startImg(Html.java:634)
                                                                         at android.text.HtmlToSpannedConverter.handleStartTag(Html.java:520)
                                                                         at android.text.HtmlToSpannedConverter.startElement(Html.java:761)
                                                                         at org.ccil.cowan.tagsoup.Parser.push(Parser.java:794)
                                                                         at org.ccil.cowan.tagsoup.Parser.rectify(Parser.java:1061)
                                                                         at org.ccil.cowan.tagsoup.Parser.stagc(Parser.java:1016)
                                                                         at org.ccil.cowan.tagsoup.HTMLScanner.scan(HTMLScanner.java:624)
                                                                         at org.ccil.cowan.tagsoup.Parser.parse(Parser.java:449)
                                                                         at android.text.HtmlToSpannedConverter.convert(Html.java:442)
                                                                         at android.text.Html.fromHtml(Html.java:136)
                                                                         at com.pexample.birds.DetailsFragment.setTextViewHTML(DetailsFragment.java:400)
                                                                         at com.pexample.birds.DetailsFragment.parseHtml(DetailsFragment.java:350)
                                                                         at com.pexample.birds.DetailsFragment.access$500(DetailsFragment.java:48)
                                                                         at com.pexample.birds.DetailsFragment$6.onResponse(DetailsFragment.java:291)
                                                                         at com.pexample.birds.DetailsFragment$6.onResponse(DetailsFragment.java:284)
                                                                         at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:60)
                                                                         at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:30)
                                                                         at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
                                                                         at android.os.Handler.handleCallback(Handler.java:739)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                         at android.os.Looper.loop(Looper.java:135)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:5910)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at java.lang.reflect.Method.invoke(Method.java:372)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1405)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1200)

From the stacktrace, line 39 is urlDrawable = new UrlImageDownloader(c.getResources(), source); in UILImageGetter and line 400 corrsponds to line CharSequence sequence = Html.fromHtml(html, new UILImageGetter(birdContent, getActivity()), null); in DetailsFragment.

Please, how do I fix this?

Please this not a duplicate of this question. I know what a NPE and what causes it and maybe I am wrong but I also know that I will have to check if the resource is null before line urlDrawable = new UrlImageDownloader(c.getResources(), source); but I just don't know how to do that. I am lost here, please help me.


EDIT

onCreate of hosting Activity

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post);


        getWindow().getDecorView().setBackgroundColor(Color.WHITE);

        String bird_id = getIntent().getStringExtra("BirdId");
        Log.d(TAG,  bird_id);

        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        if (savedInstanceState == null) {
            Bundle bundle = new Bundle();
            Fragment fragment;

            fragment = new DetailsFragment();
            bundle.putString("bird_link", bird_id);
            fragment.setArguments(bundle);

            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.setCustomAnimations(android.R.anim.slide_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right, android.R.anim.slide_in_left);
            ft.add(R.id.post_frame, fragment);
            ft.commit();
        }

        if (savedInstanceState != null) {
            mContent = getSupportFragmentManager().getFragment(savedInstanceState, "mContent");
        }



    }
Community
  • 1
  • 1
X09
  • 3,827
  • 10
  • 47
  • 92
  • The problem is that your context is itself null. What are you passing as context? – Ragesh Ramesh May 19 '16 at 13:30
  • @RageshRamesh, context in where, the fragment or util class? – X09 May 19 '16 at 13:37
  • The problem is that your `DetailsFragment.getActivity()` is returning `null`. This could be for a number of different reasons, so your best bet will be to post the code where you attach your fragment to your activity. – nagyben May 19 '16 at 13:42
  • @exantas I doubt that. Even in an Activity, I still get this NPE when I rotate multiple times before the data is fully loaded. – X09 May 19 '16 at 13:48
  • But in case, I do my fragment transaction in the `onCreate` of the activity. – X09 May 19 '16 at 13:51
  • Put a breakpoint at line 39 and check if `c` is `null` before it is executed. `CharSequence sequence = Html.fromHtml(html, new UILImageGetter(birdContent, getActivity()), null);` <- in this line, `getActivity()` is returning `null` and this is your problem. Fragment.getActivity() can return null for a whole heap of reasons, which is why we need to see the bit of code where you commit your fragment – nagyben May 19 '16 at 13:57
  • @exantas I tried it but I the app crashed and I didn't see any value for `c` in line 39. Please note that I don't see this problem unless I rotate multiple times. – X09 May 19 '16 at 14:17
  • @exantas I have added my fragment transaction to the question. – X09 May 19 '16 at 14:18
  • Please what's the reason for the down-vote? – X09 May 19 '16 at 14:39

3 Answers3

0

When you are transfer from portrait to landscape then your activity will restart. When you first time start then your context not null but when you are change the screen then your context will null and your app will crash.I suggest that just set screen orientations Portrait in your manifest file. like this

<activity
        android:name="..."
        android:label="@string/app_name"
        android:screenOrientation="portrait"/>
Farmer
  • 4,093
  • 3
  • 23
  • 47
0

As your context gets null on some event, So you need to renew context in onAttach(mcontext) of fragment & there are also other ways to get rid of this issue:

1) Retain instance of fragment using Fragment.setRetainInstance(true)

2) Use this flag in manifest against activity to get rid of it android:configChanges="orientation|screenSize|keyboardHidden"

Community
  • 1
  • 1
Muhammad Farhan Habib
  • 1,859
  • 20
  • 23
0

I have create new Class for checking that network Connection is available or not and also use BroadcastReceiver.

ConnectivityStatus.java

 public class ConnectivityStatus extends ContextWrapper {
public ConnectivityStatus(Context base) {
    super(base);
}

//checking for network connection
public static boolean isNetworkAvailable(Context context) {
    try {
        ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    /*
            if connected or connecting in network then return true
     */
        return networkInfo != null && networkInfo.isConnectedOrConnecting();
    }
   catch (NullPointerException e)
   {
       return false;
   }
}}

DealerLocator.java

public class DealerLocator extends Fragment {
FragmentDealerLocatorBinding fragmentDealerLocatorBinding;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    getContext().registerReceiver(broadcastReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); //for checking network connection

    // Inflate the layout for this fragment
    fragmentDealerLocatorBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_dealer_locator, container, false);
    return fragmentDealerLocatorBinding.getRoot();
}

private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (!ConnectivityStatus.isNetworkAvailable(getContext())) {
            Toast.makeText(context,"No Connection is available", Toast.LENGTH_SHORT).show();
        } else {
                //if connection available then write your code here
        }
    }
};
}

DealerLocator is Class that extends Fragment and I have check condition in this class for network connection. Generally in Toast first argument is getContext() but for Class that extends Fragment, I have change that argument to context, which is accessed from public void onReceive(Context context, Intent intent) method. So using this code my application is run perfectly.

I have add three permissions in Manifest file for checking network connection

 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
Khyati Vara
  • 1,042
  • 13
  • 22