Using RunOnUIThread causing null pointer Exception After Switching to another Fragment - android

public class ViewPagerTask extends TimerTask {
#Override
public void run() {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (viewPager.getCurrentItem() == 0){
viewPager.setCurrentItem(1);
} else if (viewPager.getCurrentItem() == 1){
viewPager.setCurrentItem(2);
} else viewPager.setCurrentItem(0);
}
});
}
}
I'm using this code to change images in a view. But the problem is I'm using it in fragment and when I change the fragment and app runs for few seconds and then suddenly pop's null pointer error. Now What I understand the reason is that it tries to change the image but doesn't find the view and create this error I have no clue what to do. Please Help :)
Error
E/AndroidRuntime: FATAL EXCEPTION: Timer-0
Process: com.example.android.indianmetro, PID: 5150
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.support.v4.app.FragmentActivity.runOnUiThread(java.lang.Runnable)'
on a null object reference
at com.example.android.indianmetro.HomeFragment$ViewPagerTask.run(HomeFragment.java:258)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
E/AbstractTracker: Can't create handler inside thread that has not called
Looper.prepare()

The first, you should understand about threads in Android. In Android, we have a thread called MainUIThread. I'm not sure that's the main thread or not. However, It has an instance of Looper class.
Your fragment source basically runs on MainUIThread by default. When you create the sub-class of TimerTask, you are creating a new Runnable for run your source in another thread, is that right?
Your error message java.lang.NullPointerException means that the return value of getActivity() method are null. A good place to start is the JavaDocs. They have this covered:
Thrown when an application attempts to use null in a case where an object is required. These include:
Calling the instance method of a null object.
Accessing or modifying the field of a null object.
Taking the length of null as if it were an array.
Accessing or modifying the slots of null as if it were an array.
Throwing null as if it were a Throwable value.
Applications should throw instances of this class to indicate other illegal uses of the null object.
The solution for you:
Let check and make sure you are not finishing the Activity. (Another case that's you detach this Fragment of called Activity. Because you are using ViewPager, I guess that you are swipe two times to left or right. It automatically detach your fragment by default) Try to abandon it.
If you have to finish your Activity which contains your Fragment or has to detach your fragment. The easy way is checked null before call runOnUiThread() method.
public class ViewPagerTask extends TimerTask {
#Override
public void run() {
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
if (viewPager.getCurrentItem() == 0) {
viewPager.setCurrentItem(1);
} else if (viewPager.getCurrentItem() == 1) {
viewPager.setCurrentItem(2);
} else {
viewPager.setCurrentItem(0);
}
}
});
}
}
}

Your TimerTask is fired after the fragment is removed. In that case, getActivity() will return null and hence the exception.
I can suggest two choices:
assign getActivity() output to a local var when TimeTask is fired, check and use if not null.
cancel your TimerTask in onDetach(). This is a recommended approach.

Related

App randomly crashes when starting background thread of a fragment

I have this code for my fragment:
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
txtAngle = view.findViewById(R.id.textView_angle);
updateTextThread = new Thread(new Runnable() {
#Override
public void run() {
while (threadRunning) {
txtAngle.setText("1");
}
}
});
threadRunning = true;
updateTextThread.start();
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
threadRunning = false;
}
The app works fine the first time I navigate to this fragment, but if I return to the main page and navigate to this fragment again, the app has a 30% chance to crash, throwing java.lang.NullPointerException: Attempt to invoke virtual method 'int android.text.Layout.getWidth()' on a null object reference for the setText line of the thread. I tried to use Thread.interrupt() to stop the thread but it didn't work.
So what caused the crash and how can I solve it?
You should take care ot two things here :
sharing a variable between two threads
updating UI out of the render thread / main thread
What you should do :
use thread safe variables like AtomicBoolean or a volatile boolean for the threadRunning var... and a double checked locking or a Lock for verifying the value of the var
without this you have no guarantee that you update thread loop is not before the setText method when changing the threadRunning var value...
also, you'd better call super.onDestroyView() at the end of the onDestroyView method.
What you could do :
Dispatch the TextView update from the update thread to the main thread using one of the following possibility (not exhaustive)
use a Handler associated with the main Looper
use a coroutine or rxJava to dispatch the work to the right thread

getActivity null exception warning?

I have a fragment where I'll do the following:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
}
});
Here I get for runOnUiThread a warning may produce NullPointerException.
The code works without problems.Android Studio suggests I change the code like this:
Objects.requireNonNull(getActivity()).runOnUiThread(new Runnable() {
#Override
public void run() {
is that sensible ? is there any other/better way ?
It depends on what your objective is :
1) You want the method caller to know he made a mistake by calling this method at a wrong state where getActivity()
private void myMethod() {
if(null == getActivity()) {
throw new IllegalStateException("Trying to call getActivity() at a state where the Activity equals null"); // Or new NullPointerException("")
}
// Do your stuff with getActivity()
}
2) You know that getActivity() will not throw a NullPointerException in your case :
private void myMethod() {
assert getActivity() != null;
// Do your stuff with getActivity()
}
3) You know that getActivity() may be null, you don't want the app to suddenly stop :
private void myMethod() {
if(null == getActivity()) {
return;
}
// Do your stuff with getActivity()
}
Using Objects.requireNonNull() also requires api level 19 (4.4 (KITKAT))
You also have tons of information right here
You could just add a null check, which may be more understandable.
if(getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
}
});
}
But also, you can use the method #requireActivity()
This works as a getActivity but if the activity is null (if the fragment is detached) it will throw an IllegalStateException
https://developer.android.com/reference/android/support/v4/app/Fragment#requireActivity()
(1) Use Flag isAdded(fragment is added to host activity) before getActivity(). This helps to avoid null pointer exception if the fragment is detached from the host activity.
if (isAdded() && null != getActivity()) {
// your logic
}
(2) requireNonNull(T obj) Checks that the specified object reference is not null. This method is designed primarily for doing parameter validation in methods and constructors.
Throws NullPointerException - if obj is null(application execution may terminate at this point).
Summary: As per your current context requireNonNull(T obj) is not suitable. You must handle null pointer gracefully.

Toast crash during main thread execution

I've got a method test() that runs every 3 seconds in main thread using the following Runnable and Handler decleration:
private Runnable _checker = new Runnable() {
#Override
public void run() {
try {
test();
} finally {
if (!_stopTest) {
_testHandler.postDelayed(_checker, 3000);
}
}
}
};
This toast message runs on main thread during test() method:
Toast.makeText(getActivity(), "Recalculating...", Toast.LENGTH_SHORT).show();
ends up with exception:
04-02 09:56:44.229 31232-31232/com.app E/UncaughtException: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
at android.widget.Toast.<init>(Toast.java:105)
at android.widget.Toast.makeText(Toast.java:262)
at com.BaseSupportFragment.test(BaseSupportFragment.java:916)
The exception occurs during onBackPressed, which I override to remove the fragment container (which contains the fragment that should show the toast message that fails).
Does anybody know what happens here?
The exception occurs during onBackPressed.
Next time getActivity() is called it returns null, because your fragment currently is not attached to any activity. Thus crash happens.
In onBackPress() you have to cancel scheduled runnables:
_testHandler.removeCallbacks(_checker);

Attempt to invoke virtual method 'boolean android.os.Handler.postDelayed(java.lang.Runnable, long)' on a null object reference

I was following this page Adding items to Endless Scroll RecyclerView with ProgressBar at bottom and I think I did everything right, but for some reason I get back on this line onLoadMoreListener.onLoadMore();
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.os.Handler.postDelayed(java.lang.Runnable, long)' on a null object reference
I know that there are a lot of posts regarding the null pointer error, but that line is in a if statement and it shouldn't be executed if it's null.
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
Why is this happening ? What code should I post to help figuring out the problem ?
mAdapter.setOnLoadMoreListener(new MyAdapter.OnLoadMoreListener() {
#Override
public void onLoadMore() {
//add progress item
myDataset.add(null);
mAdapter.notifyItemInserted(myDataset.size() - 1);
handler.postDelayed(new Runnable() {
#Override
public void run() {
//remove progress item
myDataset.remove(myDataset.size() - 1);
mAdapter.notifyItemRemoved(myDataset.size());
MyRecyclerItems gridItem= new MyRecyclerItems();
gridItem.setAbout("Test");
gridItem.setEmail("tesssst#gmail.com");
myDataset.add(0,gridItem);
mAdapter.notifyItemInserted(myDataset.size());
mAdapter.setLoaded();
}
}, 2000);
System.out.println("load");
}
});
This error means handler is null.
Just initialize handler object before calling post method like this:
handler=new Handler();
As you pointed out the onLoadMoreListener field is not null based on the if statement. What you didn't include however is the code being executed inside the onLoadMore() method.
From the partial stack trace you are trying the call postDelayed(java.lang.Runnable, long) on a Handler inside the onLoadMore() method and said Handler has not been initialized (is null).

WeakReferences and Handler in Android

I was just trying to figure out if I could get a NULL pointer exception with the following code. The cause could be this: The check is done at time X. But, I post the runnable to the handler, he will execute at X+5. He should have a strong reference, therefore preventing the Runnable being gc-ed in between.
Am I correct? (that what I call easy reputation, a YES is enough. A no, you have to explain :-)
public class WeakRunnableUiList
{
private ArrayList<WeakReference<Runnable>> _items = new ArrayList<WeakReference<Runnable>>();
private Handler _handler = new Handler(Looper.getMainLooper());
public void Add(Runnable r)
{
_items.add(new WeakReference<Runnable>(r));
}
public void Execute()
{
ArrayList<WeakReference<Runnable>> remove = new ArrayList<WeakReference<Runnable>>();
for (WeakReference<Runnable> item : _items)
{
if (item.get() == null)
{
remove.add(item);
}
else
{
_handler.post(item.get());
}
}
_items.removeAll(remove);
}
}
He should have a strong reference, therefore preventing the Runnable
being gc-ed in between
No.
Put this into your code just before _handler.post(...:
...
byte[] b=new byte[1024*32]; // this can occur in an other thread!
if (item.get() == null) {
Log.e("Item is NULL now!", "Item is NULL now!");
}
_handler.post(item.get());
...
And then in the main program:
for(int i=0;i<100;i++) {
weakRunnableUiList.Add(new X());
}
weakRunnableUiList.Execute();
Will give you 12-03 21:56:01.521: E/Item is NULL now!(1071): Item is NULL now!
So the runnable can get NULL after your first check!
Do it like this:
Runnable r=item.get();
if (r==null) ...
But you can post nulls to handlers: _handler.post(null);, and it will not throw a nullpointer actually.

Categories

Resources