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.
Related
I have no formal coding education so please let me know if something I did is considered bad coding
I am using a bottom navigation view for a train schedule application I am making. One of the options is a nearby functionality - when nearby is clicked on the bottom navigation drawer, the NearbyFragment is launched and my server gets a request via OKHttp automatically without additional user interaction
The problem is that if you switch from Nearby to another button on the bottom navigation view OR if you tapped the nearby button multiple times, the app would crash. Initially, this was because the runOnUiThread method wouldn't be able to find the UI thread since I moved on to a different fragment (and presumably the main thread no longer existed). I tried fixing this by creating a 1 second delay before the getLocation method was called in onViewCreated
// Delay before calling on location to prevent crashing when rapidly switching from Nearby Fragment
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (getActivity() != null) {
getLocation();
}
}
}, 1000);
I also wrapped the above, as well as OKHttp onSuccess and onFailure methods in an if statement (getActivity != null) to check that the UI thread still existed before proceeding
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = response.body().string();
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
processJSON(myResponse);
}
});
}
}
}
I would still get crashes associated with the onFailure method with NullPointerExceptions and cannot find views (such as the snackbar) IF I changed from the NearbyFragment to a different fragment after 1 second of loading but before the loading had finished, so I wrapped the onFailure in a try/catch to catch all exceptions and ignore them. This feels like a hack, but my app no longer crashes/acts as expected when switching from from the NearbyFragment to another fragment via bottom navigation.
#Override
public void onFailure(Call call, IOException e) {
try {
mSwipeToRefresh.setRefreshing(false);
Log.e("Main", e.toString());
mSnackbar = Snackbar.make(getActivity().findViewById(R.id.swipe_container_nearby), "ERROR!", Snackbar.LENGTH_INDEFINITE);
snackBarCoordinator(mSnackbar);
mSnackbar.show();
} catch (Exception j) {
Log.e("Failed Get Closest Stations ", j.toString());
}
}
Is there a cleaner was to stop the crashes without the combination of three failsafes i.e., handler.postDelayed, (getActivity() != null), and an empty try/catch block?
To know whether activity or fragment is on UI.
For Activity, you can call isFinishing()
For Fragment call isAttached().
i.e before show loader, snack bar or any widgets wrap with if(...) {}
if(isAttached()) { snackbar.show(); }
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.
I think I know why this happens.
When rotating the screen the Activity gets destroyed and recreated and the runOnUiThread() method is referencing the old(destroyed) Activity which isn't valid anymore and so android throws a runtimeexception.
To demonstrate this I have this code:
#ContentView(R.layout.activity_start)
public class StartActivity extends RoboSherlockFragmentActivity {
#Inject
private EventManager eventManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Fragment newFragment = new WelcomeFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, newFragment).commit();
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2000);
//Do some short initialization here like read shared prefs
//and then decide for example whether to login or skip the login
//If the rotation happens while sleeping the app will certainly crash
runOnUiThread(new Runnable() {
#Override
public void run() {
addFragmentToStack();
}
});
} catch (Exception e) {
// TODO: handle exception
}
}
}).start();
}
}
void addFragmentToStack() {
Fragment newFragment = new LoginOrRegisterFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
}
}
Do I have to use an asynctask for some easy task like that? If so how I handle the rotation then? Because the callback reference would be faulty either.
try this
new Handler(getMainLooper()).postDelayed(new Runnable(){
if(!isFinishing()){
addFragmentToStack();
}
},200);
instead of your thread code
isFinishing() is called when the activity is in the process of being destoryed
It seems that all you're trying to do is to make the task executed after 200 ms have passed.
there is no need to open a new thread for that, a handler will do
2ndly if you want to ensure that the code will be executed on the main thread
you create the handler calling for the main looper
Handler(getMainLooper()) and it will make this handler execute its task on the main thread
AsyncTask will generate the same crash. You can Simply override onPause() and find a way to notify the thread that Activity is about to be destroyed. Like checking for null value of a variable.
public void run() {
if(someVariable != null)
addFragmentToStack();
}
Or you can use a callback fragment (without view) to keep track of configuration change. This article is a great way to start. This method I prefer because it is a very clean approach.
I had a similar problem and solved it by using a retain fragment where I store a reference to the Activity on every OnCreate e.g. retainFragment.activity = this; . In runOnUiThread I check if the retained reference is the current Activity e.g. if (retainFragment.activity == MainActivity.this)..
I have about resuming multiple threads within the same Activity: I've three different threads in my app, that I call AThread, BThread and CThread.
If my app is closed and reopened, I need to reopen all preview Threads. How could I do that? I thought on returning a list of threads. Is that a good option? Something like:
#Override
public Object onRetainNonConfigurationInstance() {
return new ArrayList<Thread>(AThread, BThread, CThread);
}
And than, at the function onCreate, call a "for each" that verifies all Threads. Something like
#Override
public void onCreate(Bundle savedInstanceState) {
(...)
super.onCreate(savedInstanceState);
ArrayList<Thread> allThreads = (ArrayList<Thread>) getLastNonConfigurationInstance();
AThread = allThreads.get(0);
BThread = allThreads.get(1);
CThread = allThreads.get(2);
if (AThread != null && AThread.isAlive()) {
// TODO SOMETHING
}
if (BThread != null && BThread.isAlive()) {
// TODO SOMETHING
}
if (CThread != null && CThread.isAlive()) {
// TODO SOMETHING
}
}
Is that correct? Any better Idea?
Cheers =)
Use a Fragment to host your Threads (or more preferably an AsyncTask) and call setRetainInstance(true) in the Fragment's onCreate method. The Fragment will be retained across configuration changes and the Fragment (and its Threads) won't be destroyed along with the Activity. I believe this is preferred over using onRetainNonConfigurationInstance.
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.