On some device running Android 5.0 and above, my app will crash when it's opened from home screen with the following message:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at com.imincode.menitiplus.MainActivity.loadSelectedIntent(MainActivity.java:229)
at com.imincode.menitiplus.MainActivity.onNewIntent(MainActivity.java:213)
at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1233)
at android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2462)
at android.app.ActivityThread.performNewIntents(ActivityThread.java:2475)
at android.app.ActivityThread.handleNewIntent(ActivityThread.java:2484)
at android.app.ActivityThread.access$1600(ActivityThread.java:148)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1364)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5312)
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:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)
and here's the code that caused the error.
private void loadSelectedIntent(Intent intent){
if ((intent != null) && (intent.getExtras() != null)){
Fragment fragment = null;
FragmentManager fManager = this.getSupportFragmentManager();
FragmentTransaction transaction = fManager.beginTransaction();
Bundle bundle;
if (intent.getStringExtra("intentCaller").equals("expensesRecurring")){
fragment = new MenuNotifications();
bundle = new Bundle();
bundle.putString("tab", "0");
fragment.setArguments(bundle);
transaction.replace(R.id.container, fragment, "fragmentMenuNotifications");
transaction.addToBackStack(null);
transaction.commit();
}else if (intent.getStringExtra("intentCaller").equals("creditCard")){
fragment = new MenuNotifications();
bundle = new Bundle();
bundle.putString("tab", "1");
fragment.setArguments(bundle);
transaction.replace(R.id.container, fragment, "fragmentMenuNotifications");
transaction.addToBackStack(null);
transaction.commit();
}else if (intent.getStringExtra("intentCaller").equals("locationAlert")){
fragment = new MenuNotifications();
bundle = new Bundle();
bundle.putString("tab", "2");
fragment.setArguments(bundle);
transaction.replace(R.id.container, fragment, "fragmentLocationAlert");
transaction.addToBackStack(null);
transaction.commit();
}
}
}
Specifically, the line that throws the error is
if (intent.getStringExtra("intentCaller").equals("expensesRecurring"))
loadSelectedIntent would be called from 2 places in MainActivity.
The first one:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
loadSelectedIntent(intent);
}
And the second one:
#Override
protected void onResume() {
super.onResume();
Intent isThereAnyIntentHere = getIntent();
if ((isThereAnyIntentHere != null) && (isThereAnyIntentHere.getExtras() != null)){
loadSelectedIntent(isThereAnyIntentHere);
}
}
Take note that this error would only appear when the user tries to launch the app by clicking on the app's icon on the home screen. The app can be lauched without any problem if the user clicks on 'Open App' from Play Store.
Why do this error happens only on selected devices, and only when launched from the home screen?
And I thought by putting the code
if ((intent != null) && (intent.getExtras() != null))
it surely would have made sure intent.getStringExtra isn't null?
The line intent.getExtras() != null only guarantees there is some kind of extra in your intent, but that is not limited to only the extras you've set. The launch intent may contain anything.
The problem is that intent.getStringExtra() can return null if it doesn't find the extra name you were trying to find. One quick and dirt solution is to invert your if statement as follows:
if ("expensesRecurring".equals(intent.getStringExtra("intentCaller")))
You can also check for the nullity of your extra and then consume it as in:
if (intent.getStringExtra("intentCaller") != null && intent.getStringExtra("intentCaller").equals("expensesRecurring"))
And I thought by putting the code
if ((intent != null) && (intent.getExtras() != null))
it surely would have made sure intent.getStringExtra isn't null?
nope, you know that the extras bundle isn't null but you haven't established that the specific keys exist. getStringExtra documentation
So from getExtras documentation we know that it will return null if there aren't any extras.
Related
I have found a specific case when FragmentManager.findFragmentByTag("tag") returns null.
I have a gut feeling it has to do with timing?
I have a networking library with the following callbacks:
onStart()
{
Utils.ShowLoadingDialog("loading");
}
onFinnish()
{
Utils.DismissLoadingDialog("loading");
}
Then in my Utils class I have the following code:
public void showLoadingDialog(String title, String message, String tag) {
DialogFragment loadingDialogFragment = new LoadingDialogFragment();
Bundle args = new Bundle();
args.putString(CommonBundleAttributes.CONNECTING_ACTIVITY_DIALOG_TITLE, title);
args.putString(CommonBundleAttributes.CONNECTING_ACTIVITY_DIALOG_MESSAGE, message);
loadingDialogFragment.setArguments(args);
FragmentTransaction transaction = fragManager.beginTransaction();
loadingDialogFragment.show(transaction, tag);
}
public void dismissLoadingDialog(String tag) {
DialogFragment dg = (DialogFragment) fragManager.findFragmentByTag(tag);
if (dg != null) {
// this reference isn't null so the dialog is available
dg.dismissAllowingStateLoss();
}
}
Now this generally works fine. However in cases when the network layer detects there is no internet. It will throw an error and then immediately call onFinnish(). In this case the Utils.DismissDialog(tag) does nto find the fragment and therefore does not dismiss it?
You can use executePendingTransactions() to wait for the fragment transaction to come through.
public void dismissLoadingDialog(String tag) {
fragManager.executePendingTransactions();
DialogFragment dg = (DialogFragment) fragManager.findFragmentByTag(tag);
if (dg != null) {
// this reference isn't null so the dialog is available
dg.dismissAllowingStateLoss();
}
}
Use TRY-CATCH or even IF statement to check current internet-connection.
This can be case with the committing the transaction.
Just check if in your show method if you have commit the transaction for Dialog fragment or not.
transaction.commit();
Until that your fragment manager does not have fragment to be added in that.
Also you need to make sure you are finding fragment from same Activity's fragment manager to which you committed fragment.
I've an activity which receives a bundle from one activity via intent and it works fine. But the same activity is needed to be called by another activity which does not deliver a bundle to it, and hence the app crashes throwing a null pointer exception expecting a bundle on every intent. So how to code the activity to looks for a bundle only when called from a particular activity. Or is there another way of passing data which is sender specific?
Just check if the Bundle is not null before accessing it:
Bundle bundle = getIntent().getExtras();
if(bundle != null) {
// do whatever you wanted to do here
}
Further more if in future you want any other Activity also to send some other value or data type to the same Activity you can do one more check inside the null check:
if (bundle != null) {
if(bundle.getString("key_specific_to_previous_activity") != null){
// do something for that specific activity
}
}
I'm experimenting with receiving Share intents, but I've come across a situation that I can't wrap my head around. Currently my app shows up as a Share option in other apps, and my Activity receives such these intents just fine. However, I'm having a problem with passing off this intent to a Fragment that I want to handle it. Namely, the Fragment isn't being loaded in time for my Activity to pass it off.
Here's what I have so far in my Activity's onCreate():
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_screen);
if (savedInstanceState == null)
{
Log.i(TAG, "Loading new fragment");
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment(), "tag")
.commit();
}
PlaceholderFragment frag = (PlaceholderFragment) getFragmentManager().findFragmentByTag("tag");
Log.i(TAG, "Fragment Exists: " + (frag != null));
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if(Intent.ACTION_SEND.equals(action) && type != null)
{
if("text/plain".equals(type))
{
//handleIntent(intent); // Doesn't do anything right now
}
}
}
And here's the log output from when I hit Share in another app and tell it to send the text to my app:
I/ActivityStartScreen﹕ Loading new fragment
I/ActivityStartScreen﹕ Fragment Exists: false
I/ActivityStartScreen﹕ Handling Intent
I/PlaceholderFragment﹕ Loading Fragment View
As you can see, the Activity wants to hand off the intent before the Fragment is ready, even though I've added a Fragment to the layout before handling the intent. Is part of the problem the fact that the FragmentTransaction needs time to commit? That's the only reason I can imagine the null-check would fail...
What do I do in this situation so that I can pass off the intent to the Fragment once it's ready to go?
I want to transfer the user ID from the main Activity to a fragment.
So in the main activity, I do:
Fragment fragment = new Fragment();
final Bundle bundle = new Bundle();
bundle.putString("id_User", id);
Log.i("BUNDLE", bundle.toString());
fragment.setArguments(bundle);
And in the log I can see
BUNDLE : Bundle[{id_User=1}]
In the fragment, I test it in onCreate
Bundle arguments = getArguments();
if (arguments != null)
{
Log.i("BUNDLE != null","NO NULL");
} else {
Log.i("BUNDLE == null","NULL");
}
And I have
BUNDLE == null: NULL
So the transfer is successful, but how can I receive the data in fragment, please?
You can use:
Bundle args = getArguments();
if (args != null && args.containsKey("id_User"))
String userId = args.getString("id_User");
Just use:
String User = getArguments().getString("id_User", "Default Value");
The default value you supply will be returned if the key you request does not exist.
I know this is a very asked question but i have done my research on it and still didn't manage to come up with a valid solution.
Problem is i try to send data from fragment like this:
Intent intent = new Intent(getActivity(),EditReceiptActivity.class);
System.out.println("RECEIPT IN SETTINGS: "+ receipt);
intent.putExtra("receipt_to_be_edited", receipt);
startActivity(intent);
this is done in a fragment context.
and then when i try to retrive this data in an activity context:
#Override
protected void onResume() {
Bundle extras = getIntent().getExtras();
if (extras != null) {
Receipt value = (Receipt) extras.getSerializable("receipt_to_be_edited");
System.out.println("RECEIPT ON RESUME: "+ value);
FolderDataSource f = new FolderDataSource(getApplicationContext());
f.open();
String name = f.getFolderName(value.getId());
f.close();
folder_title.setText(name);
details_txt.setText(value.getDetails());
value_txt.setText(value.getValue());
category_picked_txt.setText(value.getCategory());
folder_picked_txt.setText(name);
currency_picked_txt.setText(value.getCurrency());
location_picked_txt.setText(value.getLocation());
setImageView();
}
super.onResume();
}
I get a null pointer exception... even though the prints are there... the null pointer exception is on the line:
String name = f.getFolderName(value.getId());
EDIT
The problem was my method getFolderName(), that was returning null, so there is nothing wrong with my passing information code!
So this could turn out to be yet another example of how to pass information from fragment to activity via Intent!
Thanks!