I have a main Activity and a headless Fragment.
The headless Fragment is supposed to get the IMEI number of the phone to be recorded and returned to the main Activity.
I had this bug for a few hours now and I can't seem to shake it off.
Here's the logcat:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.androidproject.example, PID: 5418
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.androidproject.example/com.androidproject.example.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.app.Activity.getApplicationContext()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.app.Activity.getApplicationContext()' on a null object reference
at com.androidproject.example.HeadlessFragment.loadIMEI(HeadlessFragment.java:110)
at com.androidproject.example.MainActivity.onCreate(MainActivity.java:40)
at android.app.Activity.performCreate(Activity.java:6237)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Application terminated.
And here's the relevant part of the code in the MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDeviceCode = (TextView)findViewById(R.id.device_code);
// Initializing headless fragment
mFragment =
(HeadlessFragment) getFragmentManager()
.findFragmentByTag("IMEILoader");
if(mFragment == null) {
mFragment = new HeadlessFragment();
getFragmentManager().beginTransaction()
.add(mFragment, "IMEILoader").commit();
}
if(mFragment != null){
mNumber = mFragment.loadIMEI(); //Here's the error
mDeviceCode.setText(Html.fromHtml("<b>IMEI</b>: " + mFragment.loadIMEI()));
}
And here's the HeadlessFragment code:
//Called when the 'loadIMEI' function is triggered.
public String loadIMEI() {
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// READ_PHONE_STATE permission has not been granted.
requestPermissions();
} else {
// READ_PHONE_STATE permission is already been granted.
RecordedIMEI = permissionGrantedActions();
}
if(RecordedIMEI != null) {
Log.i("loadIMEIService", "IMEI number returned!");
}
return RecordedIMEI;
}
public String permissionGrantedActions() {
//Get IMEI Number of Phone
TelephonyManager tm =(TelephonyManager)getActivity().getSystemService(Context.TELEPHONY_SERVICE);
String IMEINumber = tm.getDeviceId();
//RecordedIMEI = IMEINumber;
if(IMEINumber != null) {
Log.i("IMEI Loader", "IMEI number recorded!");
}
return IMEINumber;
}
}
I tried different things but no luck. I think getActivity().getApplicationContext() is pointing no where, which means this is being called before headless fragment is attached to mainactivity?
I've been stuck on this for quite some time and need some help.
I think you're going about this "headless Fragment" idea wrong since...
A Fragment represents a behavior or a portion of user interface in an Activity.
You seem to want a static utility class that holds a Context and can call some permission things. Take away the extends Fragment from the code and other answer(s), and this is basically what you are left with.
Turn it into a singleton, and you don't need the Context parameter everywhere.
(code untested)
May not completely work. For example, not sure how the ActivityCompat.OnRequestPermissionsResultCallback works...
But it exposes the functionality you need without working around a Fragment lifecycle.
public final class IMEILoader {
public static IMEILoader mInstance;
private Context mContext;
private IMEILoader() {}
private IMEILoader(Context c) {
this.mContext = c;
}
// Singleton pattern
public static IMEILoader getInstance(Context c) {
if (!(c instanceof ActivityCompat.OnRequestPermissionsResultCallback)) {
throw new Exception("Passed context not implementing permission callbacks");
}
if (mInstance == null) mInstance = new IMEILoader(c);
return mInstance;
}
public String load() {
String recordedIMEI = null;
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// READ_PHONE_STATE permission has not been granted.
requestPermissions();
} else {
// READ_PHONE_STATE permission is already been granted.
recordedIMEI = permissionGrantedActions();
}
if(recordedIMEI != null) {
Log.i("loadIMEIService", "IMEI number returned!");
}
return recordedIMEI;
}
public String permissionGrantedActions() {
return null;
}
}
And you can use that in the Activity like
class FooActivity extends AppCompatActivity impelements ActivityCompat.OnRequestPermissionsResultCallback {
private IMEILoader loader;
...
public void onCreate(Bundle b) {
...
loader = IMEILoader.getInstance(MainActivity.this);
String blah = loader.load();
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
...
}
Change This Line
mNumber = mFragment.loadIMEI();
To
mNumber = mFragment.loadIMEI(MainActivity.this);
And your function would be like this.
public String loadIMEI(Context context) {
//Context context = getActivity().getApplicationContext();
//Activity activity = context instanceof Activity ? (Activity) context : null;
//mActivity = activity;
// Check if the READ_PHONE_STATE permission is already available.
//this.context = getActivity().getApplicationContext();
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// READ_PHONE_STATE permission has not been granted.
requestPermissions();
} else {
// READ_PHONE_STATE permission is already been granted.
RecordedIMEI = permissionGrantedActions();
}
if(RecordedIMEI != null) {
Log.i("loadIMEIService", "IMEI number returned!");
}
return RecordedIMEI;
}
Related
I'm trying to publish my app in google play console.
But it tells me that it crashes on Google Pixel 2 (virtuel) (works on the 9 others) with 2 errors (very similar)
Google Pixel 2 (virtuel) 1080x1920 Android 12 (SDK 31) - x86_64 en_US
Error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.activity.result.ActivityResultLauncher.launch(java.lang.Object)' on a null object reference
Detail:
FATAL EXCEPTION: Thread-2
Process: xxxxxxxx, PID: 8724
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.activity.result.ActivityResultLauncher.launch(java.lang.Object)' on a null object reference
at xxxxxxxx.models.StockInputDialog.lambda$init$9$xxxxxxxx-models-StockInputDialog(StockInputDialog.java:417)
at xxxxxxxx.models.StockInputDialog$$ExternalSyntheticLambda9.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7441)
at android.view.View.performClickInternal(View.java:7418)
at android.view.View.access$3700(View.java:835)
at android.view.View$PerformClick.run(View.java:28676)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at androidx.test.espresso.base.Interrogator.loopAndInterrogate(Interrogator.java:10)
at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:7)
at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:1)
at androidx.test.espresso.base.UiControllerImpl.injectMotionEvent(UiControllerImpl.java:5)
at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:6)
at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:1)
at androidx.test.espresso.action.Tap.sendSingleTap(Tap.java:5)
at androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap(Unknown Source:0)
at androidx.test.espresso.action.Tap$1.sendTap(Tap.java:1)
at androidx.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:4)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:2)
at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:21)
at androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform(Unknown Source:0)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:6)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
My code is explained (and the reason why) startActivityForResult migration, call registerForActivityResult outside activity, and simplified to the maximum:
public class StockActivity extends AppCompatActivity implements DialogCloseListener {
private ActivityResultLauncher<Intent> stockGalleryActivityResultLauncher;
private ActivityResultLauncher<Intent> stockCameraActivityResultLauncher;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
stockCameraActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
// code
});
stockGalleryActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
// code
});
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
if (id == R.id.action_add) {
mStockInputDialog = new StockInputDialog(this, stockCameraActivityResultLauncher, stockGalleryActivityResultLauncher,);
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
}
public class StockInputDialog {
private final Context mContext;
private AlertDialog mInputDialog;
private ActivityResultLauncher<Intent> stockCameraActivityResultLauncher;
private ActivityResultLauncher<Intent> stockGalleryActivityResultLauncher;
public StockInputDialog(Context context, ActivityResultLauncher<Intent> pStockCameraActivityResultLaunchera, ActivityResultLauncher<Intent> pStockGalleryActivityResultLauncher) {
mContext = context;
stockCameraActivityResultLauncher = pStockCameraActivityResultLaunchera;
stockGalleryActivityResultLauncher = pStockGalleryActivityResultLauncher;
LayoutInflater li = LayoutInflater.from(this.mContext);
mPromptsView = li.inflate(R.layout.text_input_stock, null);
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this.mContext);
alertDialogBuilder.setView(mPromptsView);
final ImageButton imgButtonCam = mPromptsView.findViewById(R.id.addCam);
final ImageButton imgButtonGal = mPromptsView.findViewById(R.id.addGal);
imgButtonCam.setOnClickListener(view -> {
Uri uri = FileProvider.getUriForFile(mContext, "fr.foo.bar.provider",
new Product(Consts.TEMP_NUM).getIMGPathCacheFile(mContext));
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
stockCameraActivityResultLauncher.launch(intent); <= ERROR
});
imgButtonGal.setOnClickListener(view -> {
stockGalleryActivityResultLauncher.launch(new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)) <= ERROR
});
}
}
The 2 errors correspond to the 2 launch()
What I don't understand is that I'm using this same principle elsewhere in the application without an error being raised.
No more crashes when testing the "nullity" of the launchers (#Abdo21 2nd answer).
imgButtonCam.setOnClickListener(view -> {
Uri uri = FileProvider.getUriForFile(mContext, "fr.novazur.donteat.provider",
new Product(Consts.TEMP_NUM).getIMGPathCacheFile(mContext));
if (stockCameraActivityResultLauncher != null)
stockCameraActivityResultLauncher.launch(uri);
else
Toast.makeText(mContext, R.string.featureAbsent, Toast.LENGTH_LONG).show();
});
imgButtonGal.setOnClickListener(view -> {
if (stockGalleryActivityResultLauncher != null)
stockGalleryActivityResultLauncher.launch("image/*");
else
Toast.makeText(mContext, R.string.featureAbsent, Toast.LENGTH_LONG).show();
});
That's what I should have done from the start. I'm a little sorry for not having thought of it, it's so obvious. I guess it's related to the creation of the AlertDialog which should not allow immediate access to the launchers. What I don't understand in this case is why only the Pixel 2 crashes, but problem seems to be solved for the moment. Thanks all.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
I am trying to implement a GoogleMap object into my code. When I use getMapASync() and assign the map created in the onMapReady() call back to the GoogleMap object I want to use, it works within that method, but once I refer to it outside of that method it says that it is still null. Why?
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
GoogleMap mMap;
private static final int ERROR_DIALOG_REQUEST = 9001;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(servicesOK()) {
setContentView(R.layout.activity_map);
if (initMap()) {
Toast.makeText(this, "Ready to map", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Map not connected", Toast.LENGTH_SHORT).show();
}
}
else{
setContentView(R.layout.activity_main);
}
String mapType2 = Integer.toString(mMap.getMapType());
Toast.makeText(this, mapType2 + " mMap", Toast.LENGTH_SHORT).show();
//This is for debugging purposes
}
public boolean servicesOK(){
int isAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if(isAvailable == ConnectionResult.SUCCESS){
return true;
}
else if (GooglePlayServicesUtil.isUserRecoverableError(isAvailable)){
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(isAvailable,this,ERROR_DIALOG_REQUEST);
dialog.show();
}
else{
Toast.makeText(this, "Can't connect to mapping service", Toast.LENGTH_SHORT).show();
}
return false;
}
private boolean initMap(){
if(mMap == null){
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
return (mMap != null);
}
#Override
public void onMapReady(GoogleMap map) {
this.mMap = map;
}
}
The error message I get is this:
FATAL EXCEPTION: main
Process: com.ramaya947yahoo.mymaps, PID: 1037
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ramaya947yahoo.mymaps/com.ramaya947yahoo.mymaps.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.google.android.gms.maps.GoogleMap.getMapType()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2452)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2535)
at android.app.ActivityThread.access$900(ActivityThread.java:155)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1380)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:152)
at android.app.ActivityThread.main(ActivityThread.java:5497)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.google.android.gms.maps.GoogleMap.getMapType()' on a null object reference
at com.ramaya947yahoo.mymaps.MainActivity.onCreate(MainActivity.java:34)
at android.app.Activity.performCreate(Activity.java:6289)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2405)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2535)
at android.app.ActivityThread.access$900(ActivityThread.java:155)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1380)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:152)
at android.app.ActivityThread.main(ActivityThread.java:5497)
at java.lang.reflect.Method.invoke(Native Method)
Its because you are ivoking
String mapType2 = Integer.toString(mMap.getMapType());
in onCreate method when map is not yet initializer nor assigned.
All map initialization related code should be placed in
#Override
public void onMapReady(GoogleMap map) {
this.mMap = map;
//here
}
As this is called some time after activity is already created.
So I'm working on an app and I had this part working for days, and out of no where it just stopped working for no reason...
I also had the same error when I was trying to use another headless fragment in my MainActivity, but ended up replacing the fragment with inner methods inside of the MainActivity and everything went back to working properly.
However, I can't rewrite every bit of code I have just to avoid using fragments. The fragment code is below.
public class IMEIFragment extends Fragment implements ActivityCompat.OnRequestPermissionsResultCallback{
public static final String TAG_IMEI = "IMEILoader";
protected Activity mActivity;
private String RecordedIMEI;
//public static final String CHECK_INTERNET = "network_connection";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return null; //Do we need this at all?
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity activity = context instanceof Activity ? (Activity) context : null;
mActivity = activity;
}
//Is this needed?
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
activity = getActivity();
if (isAdded() && activity != null) {
super.onAttach(activity);
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mActivity = activity;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onDetach() {
super.onDetach();
mActivity = null;
}
public String loadIMEI(Context context) {
if (Build.VERSION.SDK_INT >= 23) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// READ_PHONE_STATE permission has not been granted.
requestPermissions(context);
} else {
// READ_PHONE_STATE permission is already been granted.
RecordedIMEI = permissionGrantedActions(context);
}
if (RecordedIMEI != null) {
Log.i("loadIMEIService", "IMEI number returned!");
}
} else {
// READ_PHONE_STATE permission is already been granted.
RecordedIMEI = permissionGrantedActions(context);
}
if (RecordedIMEI != null) {
Log.i("loadIMEIService", "IMEI number returned!");
}
return RecordedIMEI;
}
private void requestPermissions(Context context) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
Log.i("loadIMEIService", "READ_PHONE_STATE permission not granted, asking for it...");
// TODO create proper notification content
PermissionHelper.requestPermissions(((PriceActivity) getActivity()),
new String[]{Manifest.permission.READ_PHONE_STATE},
Constants.PERM_REQUEST_PHONE_STATE,
getString(R.string.notify_perm_title),
getString(R.string.notify_perm_body),
R.drawable.ic_security);
}
}
// Callback received when a permissions request has been completed.
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
boolean isGranted = false;
for (int i = 0; i < grantResults.length; i++)
if (permissions[i].equals(Manifest.permission.READ_PHONE_STATE) && (grantResults[i] == PackageManager.PERMISSION_GRANTED))
isGranted = true;
if (isGranted) {
Context context = getActivity().getApplicationContext();
permissionGrantedActions(context);
}
else
Log.w("loadIMEIService", "READ_PHONE_STATE permission not granted. loadIMEI will not be available.");
}
public String permissionGrantedActions(Context context) {
//Have an object of TelephonyManager
TelephonyManager tm =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
//Get IMEI Number of Phone
String IMEINumber = tm.getDeviceId();
if(IMEINumber != null) {
Log.i("loadIMEIService", "IMEI number recorded!");
}
return IMEINumber;
}
}
Error is below:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.project1, PID: 5498
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.project1/com.android.project1.main.MainActivity}: java.lang.IllegalStateException: Fragment IMEIFragment{3e80da7 IMEILoader} not attached to Activity
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.IllegalStateException: Fragment IMEIFragment{3e80da7 IMEILoader} not attached to Activity
at android.app.Fragment.getResources(Fragment.java:805)
at android.app.Fragment.getString(Fragment.java:827)
at com.android.project1.fragments.IMEIFragment.requestPermissions(IMEIFragment.java:107)
at com.android.project1.fragments.IMEIFragment.loadIMEI(IMEIFragment.java:80)
at com.android.project1.main.MainActivity.onCreate(MainActivity.java:108)
at android.app.Activity.performCreate(Activity.java:6237)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
And here's the relevant part of my MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDeviceCode = (TextView) findViewById(R.id.device_code);
// Initializing headless fragment
mFragment =
(IMEIFragment) getFragmentManager()
.findFragmentByTag("IMEILoader");
if (mFragment == null) {
mFragment = new IMEIFragment();
getFragmentManager().beginTransaction()
.add(mFragment, "IMEILoader").commit();
}
if (mFragment != null) {
mNumber = mFragment.loadIMEI(MainActivity.this);
mDeviceCode.setText(Html.fromHtml("<b>IMEI</b>: " + mNumber));
}
I literally had the exact same code working for over a week. Anyone knows what could be the problem?
Edit 1: The error is pointing to requestPermissions inside my fragment
Fragments should be self contained as much as possible. You are calling directly into your IMEIFragment from the activity,
Caused by: java.lang.IllegalStateException: Fragment IMEIFragment{3e80da7 IMEILoader} not attached to Activity
at android.app.Fragment.getResources(Fragment.java:805)
at android.app.Fragment.getString(Fragment.java:827)
at com.android.project1.fragments.IMEIFragment.requestPermissions(IMEIFragment.java:107)
at com.android.project1.fragments.IMEIFragment.loadIMEI(IMEIFragment.java:80)
at com.android.project1.main.MainActivity.onCreate(MainActivity.java:108)
You can't do that. Adding the fragment via a transaction from the activity is an asynchronous operation. E.g., when the commit() method completes, the fragment is not initialized. Moreover, you have no way of knowing when it's initialized. That's why it should be self contained. The fragment decides when to call loadIMEI(), not the activity.
If you really need it to be initiated by the activity, you can add a callback from the fragment to the activity like,
void onFragmentReady(Fragment f);
Or something.
And yes, onCreateView() should return something. If your fragment really doesn't have any UI at all, you don't need it to be a fragment.
Trying to migrate from Parse to OneSignal and I am stuck on how to start a new Activity after the user clicks on a push notification. My handler is working, the log shows the text, the issue seems to be how to gain access to the application context from within my push opened handler. The example code is vague, getApplicationContext() does not work without first doing something else.
One post I came upon, unrelated to OneSignal, suggests extending the Application class to gain access to the application context. This did not produce any syntax errors but my app crashes.
Code:
package com.linkedresponder.onesignal;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
import com.onesignal.OSNotificationAction;
import com.onesignal.OSNotificationOpenResult;
import com.onesignal.OneSignal;
import org.json.JSONObject;
class NotificationOpenHandler extends Application implements OneSignal.NotificationOpenedHandler {
// This fires when a notification is opened by tapping on it.
private Context mContext;
#Override
public void onCreate() {
this.mContext = getApplicationContext();
}
#Override
public void notificationOpened(OSNotificationOpenResult result) {
OSNotificationAction.ActionType actionType = result.action.type;
JSONObject data = result.notification.payload.additionalData;
String customKey;
if (data != null) {
customKey = data.optString("customkey", null);
if (customKey != null) {
Log.i("OneSignalExample", "customkey set with value: " + customKey);
} else {
Log.i("OneSignalExample", "No data");
}
}
if (actionType == OSNotificationAction.ActionType.ActionTaken)
Log.i("OneSignalExample", "Button pressed with id: " + result.action.actionID);
Intent intent = new Intent(mContext, PushClicked.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
Error:
er.onesignal E/AndroidRuntime: FATAL EXCEPTION: main Process: com.linkedresponder.onesignal, PID: 5680
java.lang.RuntimeException: Unable to start receiver com.onesignal.NotificationOpenedReceiver: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3018)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1544)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.content.ComponentName.<init>(ComponentName.java:128)
at android.content.Intent.<init>(Intent.java:4868)
at com.linkedresponder.onesignal.NotificationOpenHandler.notificationOpened(NotificationOpenHandler.java:41)
at com.onesignal.OneSignal.fireNotificationOpenedHandler(OneSignal.java:1009)
at com.onesignal.OneSignal.runNotificationOpenedCallback(OneSignal.java:954)
at com.onesignal.OneSignal.handleNotificationOpen(OneSignal.java:1041)
at com.onesignal.NotificationOpenedProcessor.processIntent(NotificationOpenedProcessor.java:101)
at com.onesignal.NotificationOpenedProcessor.processFromActivity(NotificationOpenedProcessor.java:57)
at com.onesignal.NotificationOpenedReceiver.onReceive(NotificationOpenedReceiver.java:11)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3011)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1544)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Here's how I made it work:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
OneSignal.startInit(this)
.setNotificationOpenedHandler(new MyNotificationOpenedHandler(this))
.init();
}
}
The NotificationOpenedHandler custom class
public class MyNotificationOpenedHandler implements OneSignal.NotificationOpenedHandler {
private Application application;
public MyNotificationOpenedHandler(Application application) {
this.application = application;
}
#Override
public void notificationOpened(OSNotificationOpenResult result) {
// Get custom datas from notification
JSONObject data = result.notification.payload.additionalData;
if (data != null) {
String myCustomData = data.optString("key", null);
}
// React to button pressed
OSNotificationAction.ActionType actionType = result.action.type;
if (actionType == OSNotificationAction.ActionType.ActionTaken)
Log.i("OneSignalExample", "Button pressed with id: " + result.action.actionID);
// Launch new activity using Application object
startApp();
}
private void startApp() {
Intent intent = new Intent(application, MyActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
application.startActivity(intent);
}
}
Don't forget to add this to your manifest:
<application ...>
<meta-data android:name="com.onesignal.NotificationOpened.DEFAULT" android:value="DISABLE" />
</application>
The short answer to this issue is to include your handler for the push notification open within the same class where you initialize OneSignal:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Init OneSignal
OneSignal.startInit(this).setNotificationOpenedHandler(new NotificationOpenHandler()).init();
Toolbar mToolbar = (Toolbar) findViewById(R.id.app_bar);
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayUseLogoEnabled(true);
getSupportActionBar().setLogo(R.drawable.ic_launcher);
getSupportActionBar().setDisplayShowTitleEnabled(false);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new RecordingsFragment())
.commit();
}
}
class NotificationOpenHandler implements OneSignal.NotificationOpenedHandler {
// This fires when a notification is opened by tapping on it.
#Override
public void notificationOpened(OSNotificationOpenResult result) {
OSNotificationAction.ActionType actionType = result.action.type;
JSONObject data = result.notification.payload.additionalData;
String stationName = data.optString("stationName");
String timestamp = data.optString("timestamp");
String filename = data.optString("filename");
String url = getString(R.string.callResourceUrl) + filename;
Intent intent = new Intent(getApplicationContext(), CallActivity.class);
intent.putExtra("stationName", stationName);
intent.putExtra("time", timestamp);
intent.putExtra("url", url);
// intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
Updated code in 2022
OneSignal.NotificationOpenedHandler replaced by OneSignal.OSNotificationOpenedHandler
and result.notification.payload.additionalData replaced by result.notification.additionalData
Full code
class OneSignalNotificationOpenHandler(private val context : Context) : OneSignal.OSNotificationOpenedHandler {
override fun notificationOpened(result: OSNotificationOpenedResult?) {
if (result == null) return
val type = result.action.type
val data = result.notification.additionalData
val name = data.optString("name")
val intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_NEW_TASK
intent.putExtra("name", name)
context.startActivity(intent)
}
}
Add it to OneSignel after OneSignal.initWithContext(this)
OneSignal.setNotificationOpenedHandler(OneSignalNotificationOpenHandler(applicationContext))
Don't forget to Add the following to your AndroidManifest.xml to prevent the launching of your main Activity
<application ...>
<meta-data android:name="com.onesignal.NotificationOpened.DEFAULT" android:value="DISABLE" />
</application>
I want to make it clear that this question will look very similar to one I asked earlier, but that I'm not asking exactly the same thing.
In my previous question, I got a RuntimeException/IllegalStateException, which told me my Activity got destroyed upon adding a new fragment.
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.tim.timapp
/com.example.tim.timapp.MainActivity}: java.lang.IllegalStateException:
Activity has been destroyed
In that case, it turned out it had to do with me creating new instances of MainActivityin an invalid way:
MainActivity ma = new MainActivity();
(PSA: Don't do the above, use MainActivity ma = (MainActivity) getActivity(); instead.)
I have now corrected this in my entire project, and am getting almost exactly the same error. Let me be clear: I (think I) know the original error was fixed, because I got a different error in between these two RE's, which I was able to fix myself.
To reiterate on my gibberish: Got the first RE, fixed it with the answer on my question, got a different error, fixed that myself, got almost exactly the same RE.
I have searched through my entire project to see if I had anything similar to the error I made before, but I can't find anything, so here I am. So basically, the answer I got on my previous question fixed my issue, temporarily. That answer however, does not help me with this new error I'm getting, that's why I'm asking this question.
TL;DR: Answer on Q1 fixed my issue at first(which makes it a working answer), but it does not fix the issue I'm having right now, which is almost the same.
The actual question
So, now we've got that bit out of the way, let's get on with my issue. So, I'm getting a RuntimeException/IllegalStateExcetion:
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.tim.timapp/com.example.tim.timapp.MainActivity}:
java.lang.IllegalStateException: Activity has been destroyed
(PS. It's only a RE because I have my app navigate to the GeneralSettings fragment on startup, for debugging ease.)
I've read up on this kind of error, but nothing I could find that applies on my project.
So, what is causing this RuntimeException/IllegalStateException?
Full log
04-05 14:17:53.140 23411-23411/? I/art: Not late-enabling -Xcheck:jni (already on)
04-05 14:17:53.190 23411-23411/com.example.tim.timapp W/System: ClassLoader referenced unknown path: /data/app/com.example.tim.timapp-1/lib/x86_64
04-05 14:17:53.210 23411-23411/com.example.tim.timapp D/TEST DBHandler: sInstance == null
04-05 14:17:53.370 23411-23411/com.example.tim.timapp D/AndroidRuntime: Shutting down VM
04-05 14:17:53.370 23411-23411/com.example.tim.timapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.tim.timapp, PID: 23411
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.tim.timapp/com.example.tim.timapp.MainActivity}: java.lang.IllegalStateException: Activity has been destroyed
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.IllegalStateException: Activity has been destroyed
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1433)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:687)
at android.app.BackStackRecord.commit(BackStackRecord.java:663)
at com.example.tim.timapp.MainActivity.DrawVariableFragments(MainActivity.java:276)
at com.example.fragments.Settings.GeneralSettingsFragment.onCreateView(GeneralSettingsFragment.java:58)
at android.app.Fragment.performCreateView(Fragment.java:2220)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:973)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1148)
at android.app.BackStackRecord.run(BackStackRecord.java:793)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1535)
at android.app.FragmentController.execPendingActions(FragmentController.java:325)
at android.app.Activity.performStart(Activity.java:6252)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2379)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
MainActivity (Snippet)
package com.example.tim.timapp;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private static boolean isMainShown = false;
private static boolean isSettingsShown = false;
private static boolean doSavePopup = false;
private static String backTitle = "";
private String tag = "TEST MA";
DBHandler dbHandler;
Menu menu;
public void DrawVariableFragments(String base,String token){
// FragmentManager fm = getFragmentManager();
ArrayList<String> Data;
dbHandler = DBHandler.getInstance(this);
int AmountOfEntries;
int SettingsContainer;
String SettingsTag;
Fragment SettingsVariableFragment;
Fragment SettingsEmptyFragment;
if (base.equalsIgnoreCase("StuffManager")) {
Data = new ArrayList<String>() {{add("StuffManager"); add("name"); add("tag"); }};
SettingsContainer = R.id.FragmentContainer2;
SettingsTag = getString(R.string.navdrawer_stuffmanager);
SettingsVariableFragment = new StuffManagerVariableFragment();
SettingsEmptyFragment = new StuffManagerEmptyFragment();
} else if (base.equalsIgnoreCase("GeneralSettings")) {
Data = new ArrayList<String>() {{add("GeneralSettings"); add("name"); add("ip"); add("port"); add("username"); add("pass"); }};
SettingsContainer = R.id.FragmentContainerGeneralSettings;
SettingsTag = getString(R.string.navdrawer_generalsettings);
SettingsVariableFragment = new GeneralSettingsVariableFragment();
SettingsEmptyFragment = new GeneralSettingsEmptyFragment();
} else {
Log.e(tag, "String Base not recognised");
return;
}
AmountOfEntries = dbHandler.returnArray(base, Data.get(1)).size();
FragmentManager fm = getFragmentManager().findFragmentByTag(SettingsTag).getChildFragmentManager();
if ((dbHandler.returnArray(base, Data.get(1))).size() == 0 ) {
// Log.d(tag, "SettingsContainer1: " + String.valueOf(SettingsContainer) + "; SettingsEmtpyFragment1: " + SettingsEmptyFragment + "; Base1: " + base);
fm.beginTransaction().add(SettingsContainer, SettingsEmptyFragment, (base + "EmptyFragment")).commit();
fm.executePendingTransactions();
return;
}
if (AmountOfEntries > 0) {
String EmptyFragName = (base + "EmptyFragment");
if ((fm.findFragmentByTag(EmptyFragName)) != null) {
fm.beginTransaction().remove(fm.findFragmentByTag(EmptyFragName)).commit();
fm.executePendingTransactions();
}
for (int i = 0; i < AmountOfEntries; i++) {
ArrayList<String> fragmentData = new ArrayList<>();
for (int k=1; k < Data.size(); k++) {
int j=k-1;
fragmentData.set(j, (dbHandler.returnArray(base, Data.get(k)).get(j)));
}
if (token.equalsIgnoreCase("edit")) {
LinearLayout linearLayout = (LinearLayout) findViewById(SettingsContainer);
linearLayout.removeAllViews();
DrawVariableFragments(base ,"draw");
} else if (token.equalsIgnoreCase("add")) {
if (fm.findFragmentByTag(fragmentData.get(i)) == null) {
fm.beginTransaction().add(SettingsContainer, SettingsVariableFragment, fragmentData.get(0)).commit();
fm.executePendingTransactions();
if (base.equalsIgnoreCase("StuffManager")) {
((StuffManagerVariableFragment) fm
.findFragmentByTag(fragmentData.get(i)))
.setText(fragmentData.get(0), fragmentData.get(1));
} else if (base.equalsIgnoreCase("GeneralSettings")) {
((GeneralSettingsVariableFragment) fm
.findFragmentByTag(fragmentData.get(i)))
.setText(fragmentData.get(0), fragmentData.get(1), fragmentData.get(2), fragmentData.get(3));
}
}
} else if (token.equalsIgnoreCase("draw")) {
fm.beginTransaction().add(SettingsContainer, SettingsVariableFragment, fragmentData.get(0)).commit();
fm.executePendingTransactions();
if (base.equalsIgnoreCase("StuffManager")) {
((StuffManagerVariableFragment) fm
.findFragmentByTag(fragmentData.get(i)))
.setText(fragmentData.get(0), fragmentData.get(1));
} else if (base.equalsIgnoreCase("GeneralSettings")) {
((GeneralSettingsVariableFragment) fm
.findFragmentByTag(fragmentData.get(i)))
.setText(fragmentData.get(0), fragmentData.get(1), fragmentData.get(2), fragmentData.get(3));
}
}
}
} else {
Log.d("TEST", "WTF, nameArray.size != 0 && !> 0");
}
}
}
GeneralSettingsFragment (Snippet)
package com.example.fragments.Settings;
public class GeneralSettingsFragment extends Fragment {
MainActivity ma;
DBHandler dbHandler;
private static Menu optionsMenu;
public static boolean hideDeleteAllButton = false;
LinearLayout linearLayout;
View rootView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_generalsettings, container, false);
ma = (MainActivity) getActivity();
linearLayout = (LinearLayout) rootView.findViewById(R.id.FragmentContainerGeneralSettings);
if (linearLayout == null) {
Log.e("GMF", "Layout is null");
} else if (linearLayout.getChildCount() == 0) {
GeneralSettingsInitialInputDialog GSIID = new GeneralSettingsInitialInputDialog();
GSIID.show(getFragmentManager(), "dialog");
hideDeleteAllButton = true;
} else {
hideDeleteAllButton = false;
}
ma.DrawVariableFragments("GeneralSettings", "draw");
return rootView;
}
}
You are still doing things in an unsupported way. In MainActivity.DrawVariableFragments() you are creating a new GeneralSettingsVariableFragment() and then call getChildFragmentManager() on it and attempt to commit a fragment.
The GeneralSettingsFragment has not yet been attached to an Activity so it does not have a host. This throws the IllegalStateException("Activity has been destroyed") exception you are seeing when you try to commit the FragmentTransaction.
It is unclear why you are creating a new GeneralSettingsVariableFragmentwhen you are already inside a new instance of one.
To properly lookup an existing fragment use getFragmentManager().findFragmentByTag(...) or getFragmentManager().findFragmentById(...).