0

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

Zack
  • 377
  • 6
  • 16
  • why multiple overrides to onAttach() ? – CSmith Feb 13 '17 at 19:54
  • @CSmith If I remember right, it was for different build versions as some method was deprecated. I will try to take one of them out and see if it affects anything. – Zack Feb 13 '17 at 19:56
  • you certainly can't call getActivity() from onAttach() and expect it to work. Get rid of both onAttach() overrides and onDetach(), they aren't needed. If you need access to a context, use getActivity(). Follow the guidance from @Blattman – CSmith Feb 13 '17 at 20:02
  • 1
    We've already covered that [you don't really need a Fragment here](http://stackoverflow.com/questions/41844116/android-studio-error-of-java-lang-nullpointerexception-when-the-headlessfragmen) – OneCricketeer Feb 13 '17 at 20:09

1 Answers1

0

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.

Jeffrey Blattman
  • 22,176
  • 9
  • 79
  • 134
  • I think I got most of what you're trying to say. So, in my MainActivity I should change this>>if (mFragment != null) { mNumber = mFragment.loadIMEI(MainActivity.this); mDeviceCode.setText(Html.fromHtml("IMEI: " + mNumber)); }<< to this>> if (mFragment != null && mFragment.isAdded()) { mNumber = mFragment.loadIMEI(MainActivity.this); mDeviceCode.setText(Html.fromHtml("IMEI: " + mNumber)); }< – Zack Feb 13 '17 at 20:01
  • Or should I call the loadIMEI inside onResume() instead of from within the Activity? – Zack Feb 13 '17 at 20:01
  • Your activity should not call any method on your fragment class. Your fragment should call `loadIMEI()` as part of its lifecycle. Consider my comments above re: Do you really need a fragment? – Jeffrey Blattman Feb 13 '17 at 20:03
  • Looks like OP is trying to make a headless fragment. – OneCricketeer Feb 13 '17 at 20:07
  • 1
    Headless fragment == `Object`. – Jeffrey Blattman Feb 13 '17 at 20:07
  • I don't think that I really need a fragment. I used a headfragment for multiple reasons; so I don't have to worry about screen rotation and such, and for writing a manageable code and to request runtime permissions from outside of my MainActivity (I already have runtime permission methods there and use it for different group of perm.). – Zack Feb 13 '17 at 20:09
  • @Zack A singleton object can also "handle rotation" because it is loaded in memory exactly once. Using fragments for handling that is a bad design because you have nothing in your IMEI loading that would benefit from handling rotation. You request permissions, and they are accepted or denied. – OneCricketeer Feb 13 '17 at 20:12
  • @cricket_007 I think you're right. I'm rewriting my code right now. I might have a couple follow up questions as I remember I had some difficulty implementing this last time you suggested it and I would really appreciate some pointers. I wish I listened earlier, I used the same design for a lot of things, I'm in too deep... – Zack Feb 13 '17 at 20:20