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");
}
}