-1

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.

Zack
  • 377
  • 6
  • 16
  • http://stackoverflow.com/questions/36615896/attempt-to-invoke-virtual-method-android-content-context-android-support-v4-app – IntelliJ Amiya Jan 25 '17 at 05:46
  • block this line `mActivity = null;` from `onDetach()` – IntelliJ Amiya Jan 25 '17 at 05:47
  • @IntelliJAmiya It's still giving me the same error. – Zack Jan 25 '17 at 05:51
  • What did you try? The answer there says "The solution is before getActivity(), check isAdded() is true or not, if not true, that means the fragment is already detached, call to getActivity() will return null"... You have a lot of calls to `getActivity()`, which just does not work for a detached Fragment, for good reason – OneCricketeer Jan 25 '17 at 05:53
  • What even is a "headless fragment"? Fragments are designed to be re-usable UI elements, so you are essentially just wanting static utility class that needs a `Context`? – OneCricketeer Jan 25 '17 at 05:55
  • @cricket_007 headless fragments are fragments without UI that are used for background operations. One advantage is that they don't get destroyed on screen rotation, etc. – Zack Jan 25 '17 at 06:01
  • @cricket_007 If I add the isAdded() it would just prevent the error from showing but the mrthod would not be executed. – Zack Jan 25 '17 at 06:02
  • "background operations" like asynctasks, yes, I am aware of that. Not for this permission checking thing you're doing, though – OneCricketeer Jan 25 '17 at 06:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/133941/discussion-between-zack-and-cricket-007). – Zack Jan 25 '17 at 06:10
  • @cricket_007 I have to ask for user permission to perform the above task and that's why I included it. Is there a way around that? – Zack Jan 25 '17 at 06:10
  • I think my answer addresses that. You just need to pass around the `Context` just like the answer you accepted does. – OneCricketeer Jan 25 '17 at 06:12

2 Answers2

1

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) {
        ...

    }
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • Testing this now. Why is using headless fragment incorrect here? Just for my reference. – Zack Jan 25 '17 at 06:12
  • 1
    I wrote it to be a separate class, but you could nest it. Anyways, I have never used headless fragment, personally. Both designs are ways to do the same thing, as far as I can tell. You can use this class from all Activities, though, and it's a Java pattern that I've learned separate from Android. http://luboganev.github.io/blog/headless-fragments/ – OneCricketeer Jan 25 '17 at 06:16
  • The user permission is kind of an issue. Is there a way to do that using the above method? – Zack Jan 25 '17 at 06:19
  • Not sure what you mean. `ActivityCompat.checkSelfPermission` is a static method that accepts a Context. That is all I'm showing. – OneCricketeer Jan 25 '17 at 06:22
  • I think it should work just fine. I'm having one error though. @Override error "Method does not override method from its super class" for this method public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults){....}. Any feedback? – Zack Jan 25 '17 at 07:02
  • Like I said, I don't know how you want to handle the interface you implemented. You seem to have added that interface back to the code – OneCricketeer Jan 25 '17 at 07:04
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/133949/discussion-between-zack-and-cricket-007). – Zack Jan 25 '17 at 07:06
  • Can I use/call this method in a Fragment or is it Activity-Specific? – Zack Mar 23 '17 at 23:56
  • I don't really know, but there are no Fragments in my answer, however `impelements ActivityCompat.OnRequestPermissionsResultCallback` has the word "Activity" in it. I've not tried this myself though ;) – OneCricketeer Mar 24 '17 at 02:57
0

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;
    }
Amy
  • 4,034
  • 1
  • 20
  • 34
  • This is how I had it earlier. I tried it again to be safe, but it's giving me the same error. – Zack Jan 25 '17 at 05:36
  • Code updated. I left the commented -out parts which are things that I've tried to get this to work, I thought it might be helpful. – Zack Jan 25 '17 at 05:45
  • `requestPermissions` and `permissionGrantedActions` also use `getActivity()`, by the way – OneCricketeer Jan 25 '17 at 05:52
  • @cricket_007 but he is calling this method from Activity class :) – Amy Jan 25 '17 at 05:54
  • No, I'm saying that the exception will still happen for your answer since you have those methods I mentioned – OneCricketeer Jan 25 '17 at 05:56
  • 1
    I think this solved the above error! However, it gave me a new error pointing to this line " RecordedIMEI = permissionGrantedActions(); ", and this one inside permissionGrantedActions() "TelephonyManager tm =(TelephonyManager)getActivity().getSystemService(Context.TELEPHONY_SERVICE);" – Zack Jan 25 '17 at 05:56
  • I think I should do the same as you did with the previous error? permissionGrantedActions(Context context) & TelephonyManager tm =(TelephonyManager).getSystemService(context.TELEPHONY_SERVICE); – Zack Jan 25 '17 at 05:58
  • Dear @Zack, You should accept this question first and ask another question for new query. Anyways you need to add READ_PHONE_STATE in the manifest to get it done. – Amy Jan 25 '17 at 05:59