NullPointerException while selecting image from the storage for the Second time onwards, No NullPointerException while selecting First time

Tags: , , , ,



I have set up a button that is meant to check for READ_EXTERNAL_STORAGE permissions, and then open the users phone storage display images. I’ve done this successfully via the following code:

All of this works perfectly on the first try. The user sees the permission dialog box, presses agree and the image selector opens displaying images on their phone! However, the second time we try to use the same button the app crashes. So basically after the permissions are granted the app crashes every time the button is pressed.

The following error is displayed:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.memory.pod.debug, PID: 26684
    java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.PackageManager android.content.Context.getPackageManager()' on a null object reference
        at android.content.ContextWrapper.getPackageManager(ContextWrapper.java:98)
        at com.memory.pod.camerax.ui.home.HomeActivity.selectImage(HomeActivity.java:144)
        at com.memory.pod.camerax.ui.home.HomeActivity.openFileChooser(HomeActivity.java:137)
        at com.memory.pod.view.BottomTabView$4.onClick(BottomTabView.java:84)
        at android.view.View.performClick(View.java:7259)
        at android.view.View.performClickInternal(View.java:7236)
        at android.view.View.access$3600(View.java:801)
        at android.view.View$PerformClick.run(View.java:27892)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

BottomTabView.java

    public class BottomTabView extends FrameLayout implements ViewPager.OnPageChangeListener {

      // Initialize content
      private ImageView mCenterImageView;
      private ImageView mBottomImageView;
      private LinearLayout mStartImageView;
      private LinearLayout mEndImageView;
      private View mIndicator;

      private ArgbEvaluator mArgbEvaluator;

      private int mCenterColor;
      private int mSideColor;

      private int mEndViewsTranslationX;
      private int mIndicatorTranslationX;
      private int mCenterTranslationY;

      public BottomTabView(@NonNull Context context) {
        this(context, null);
      }

      public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
      }

      public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init();
      }

      public void setUpWithViewPager(ViewPager viewPager) {
        viewPager.addOnPageChangeListener(this);

        mStartImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() != 0)
              viewPager.setCurrentItem(0);
          }
        });

        mCenterImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() != 1)
              viewPager.setCurrentItem(1);
          }
        });

        mEndImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() != 2)
              viewPager.setCurrentItem(2);
          }
        });
        mBottomImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() == 1) {
              HomeActivity.openFileChooser(getContext());
            }
          }
        });
      }

      private void init() {
        LayoutInflater.from(getContext()).inflate(R.layout.view_bottom_tabs, this, true);

        // Initialize bottom tab view content
        mCenterImageView = findViewById(R.id.view_bottom_tabs_camera_iv);
        mStartImageView = findViewById(R.id.view_bottom_tabs_start_linear_layout);
        mEndImageView = findViewById(R.id.view_bottom_tabs_end_linear_layout);
        mBottomImageView = findViewById(R.id.view_bottom_tabs_gallery_iv);

        // Initialize bottom tab view indicator, meant to indicate the current tab in view
        mIndicator = findViewById(R.id.view_bottom_tabs_indicator);

        // Set start color and end color for tab view icons
        mCenterColor = ContextCompat.getColor(getContext(), R.color.white);
        mSideColor = ContextCompat.getColor(getContext(), R.color.black);

        // Evaluate the difference between colors
        mArgbEvaluator = new ArgbEvaluator();

        // Change the padding of tab icons based on view
        // This is the min padding accepted (0)
        mIndicatorTranslationX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 140, getResources().getDisplayMetrics());

        // Wait till GalleryImageView has been drawn, once GalleryImageView is drawn then proceed with relative padding calculation
        mBottomImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
          @Override
          public void onGlobalLayout() {

            // Change padding relative to other icons
            mEndViewsTranslationX = (int)((mBottomImageView.getX() - mStartImageView.getX()) - mIndicatorTranslationX);
            mBottomImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // Change the position of the center image
            mCenterTranslationY = getHeight() - mBottomImageView.getBottom();
          }
        });
      }

      @Override
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (position == 0) {

          // Change color of icons based on view
          setColor(1 - positionOffset);

          // Move the start and end images
          moveViews(1 - positionOffset);

          // Change indicator position
          mIndicator.setTranslationX((positionOffset - 1) * mIndicatorTranslationX);

          // Change scale of center image
          moveAndScaleCenter(1 - positionOffset);
        } else if (position == 1) {

          // Change color of icons based on view
          setColor(positionOffset);

          // Move the start and end images
          moveViews(positionOffset);

          // Change indicator position
          mIndicator.setTranslationX(positionOffset * mIndicatorTranslationX);

          // Change scale of center image
          moveAndScaleCenter(positionOffset);
        }
      }

      @Override
      public void onPageSelected(int position) {

      }

      @Override
      public void onPageScrollStateChanged(int state) {

      }

      private void moveAndScaleCenter(float fractionFromCenter) {
        float scale = .7 f + ((1 - fractionFromCenter) * .3 f);

        // Change scale of center image
        mCenterImageView.setScaleX(scale);
        mCenterImageView.setScaleY(scale);

        int translation = (int)(fractionFromCenter * mCenterTranslationY * 2);

        // Move center image down
        mCenterImageView.setTranslationY(translation);
        mBottomImageView.setTranslationY(translation);

        // Fadeout bottom image
        mBottomImageView.setAlpha(1 - fractionFromCenter);
      }

      private void moveViews(float fractionFromCenter) {
        mStartImageView.setTranslationX(fractionFromCenter * mEndViewsTranslationX);
        mEndImageView.setTranslationX(-fractionFromCenter * mEndViewsTranslationX);

        mIndicator.setAlpha(fractionFromCenter);
        mIndicator.setScaleX(fractionFromCenter);
      }

      private void setColor(float fractionFromCenter) {
        int color = (int) mArgbEvaluator.evaluate(fractionFromCenter, mCenterColor, mSideColor);

        /* mCenterImageView.setColorFilter(color);
        mStartImageView.setColorFilter(color);
        mEndImageView.setColorFilter(color);
        mBottomImageView.setColorFilter(color); */
      }

    }

I did a log.d to identify where it fails, please refer to previous code to see log.d.

How can I overcome this issue?

EDIT:

HomeActivity:

    public class HomeActivity extends AppCompatActivity {

      // Initialize image picker
      private static final int PICK_IMAGE_REQUEST = 1;
      private static final int REQUEST_CODE_STORAGE_PERMISSION = 1;
      private static final int REQUEST_CODE_SELECT_IMAGE = 2;

      // TODO: FIREBASE TEMPORARY LOGOUT
      private static FirebaseAuth mAuth;

      private static final String TAG = "HomeActivity";

      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        // Initialize views
        View background = findViewById(R.id.background_view);
        ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);

        // Initialize adapter
        HomePagerAdapter adapter = new HomePagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);

        // Initialize BottomTabView
        BottomTabView bottomTabView = findViewById(R.id.bottom_tabs);
        bottomTabView.setUpWithViewPager(viewPager);

        // Initialize TopSearchView
        TopSearchView topSearchView = findViewById(R.id.top_search);
        topSearchView.setUpWithViewPager(viewPager);

        // Initialize TopProfilePictureView
        TopProfilePictureView topProfilePictureView = findViewById(R.id.top_profile_picture);
        topProfilePictureView.setUpWithViewPager(viewPager);

        // Initialize TopVariableFunctionalityButtonView
        TopVariableFunctionalityButtonView topVariableFunctionalityButtonView = findViewById(R.id.top_variable_functionality_button);
        topVariableFunctionalityButtonView.setUpWithViewPager(viewPager);

        // Initialize color for fade effect
        final int windowBackgroundColor = ContextCompat.getColor(this, R.color.windowBackground);
        final int blackBackgroundColor = ContextCompat.getColor(this, R.color.black);

        // Set the default position of the viewPager
        viewPager.setCurrentItem(1);

        // TODO: Initialize Firebase (TEMPORARY)
        //Initialize Firebase Auth
        mAuth = FirebaseAuth.getInstance();


        bottomTabView.setOnBottomTabViewClickListener(new BottomTabViewClickListener() {
          @Override
          public void onClick(View v) {
            // call your openFileChooser from here,
            //REMOVE THE STATIC
            openFileChooser(HomeActivity.this);
          }
        });

        // Callback each time the fragment is changed
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

          @Override
          public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if (position == 0) {
              background.setBackgroundColor(windowBackgroundColor);
              background.setAlpha(1 - positionOffset);

            } else if (position == 1) {
              background.setBackgroundColor(blackBackgroundColor);
              background.setAlpha(positionOffset);

            }
          }

          @Override
          public void onPageSelected(int position) {

          }

          @Override
          public void onPageScrollStateChanged(int state) {

          }
        });
      }

      // Choose file extended from BottomTabView, opens all images on device
      // Check for permissions before hand
      public void openFileChooser(Context context) {
        Log.d("HomeActivity", "This is the last place a log is observed");
        if (ContextCompat.checkSelfPermission(getInstance().getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) !=
          PackageManager.PERMISSION_GRANTED) {

          ActivityCompat.requestPermissions(
            ((Activity) context),
            new String[] {
              Manifest.permission.READ_EXTERNAL_STORAGE
            },
            REQUEST_CODE_STORAGE_PERMISSION
          );
        } else {
          selectImage();
        }
      }

      // Open image selector via phone storage
      private void selectImage() {
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        if (intent.resolveActivity(getPackageManager()) != null) {
          startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE);
        }
      }

      // Request permission results
      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == REQUEST_CODE_STORAGE_PERMISSION && grantResults.length > 0) {
          if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            selectImage();
          } else {
            Toast.makeText(this, "Permission Denied!", Toast.LENGTH_SHORT).show();
          }
        }
      }

      // TODO: Is it better to use bitmap or URI
      // Check if user has selected file and describe next step
      @Override
      protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_SELECT_IMAGE && resultCode == RESULT_OK) {
          if (data != null) {

            // Get image URI
            Uri selectedImageUri = data.getData();

            if (selectedImageUri != null) {
              try {

                InputStream inputStream = getContentResolver().openInputStream(selectedImageUri);
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                // Do something with image

                // Identify the selected image file, pass onto firebase
                File selectedImageFile = new File(getPathFormUri(selectedImageUri));

              } catch (Exception exception) {
                Toast.makeText(this, exception.getMessage(), Toast.LENGTH_SHORT).show();
              }
            }
          }
        }
      }

      private String getPathFormUri(Uri contentUri) {
        String filePath;
        Cursor cursor = getContentResolver()
          .query(contentUri, null, null, null, null);
        if (cursor == null) {
          filePath = contentUri.getPath();
        } else {
          cursor.moveToFirst();
          int index = cursor.getColumnIndex("_data");
          filePath = cursor.getString(index);
          cursor.close();
        }
        return filePath;
      }

      // TODO: TEMPORARY LOGOUT
      public static void logoutUser(Context context) {
        mAuth.signOut();
      }
    }



BottomTabActivity:

    public class BottomTabView extends FrameLayout implements ViewPager.OnPageChangeListener {

      // Initialize content
      BottomTabViewClickListener bottomTabViewClickListener;
      private ImageView mCenterImageView;
      private ImageView mBottomImageView;
      private LinearLayout mStartImageView;
      private LinearLayout mEndImageView;
      private View mIndicator;

      private ArgbEvaluator mArgbEvaluator;

      private int mCenterColor;
      private int mSideColor;

      private int mEndViewsTranslationX;
      private int mIndicatorTranslationX;
      private int mCenterTranslationY;

      //add this function somewhere
      public void setOnBottomTabViewClickListener(BottomTabViewClickListener bottomTabViewClickListener) {
        this.bottomTabViewClickListener = bottomTabViewClickListener;
      }

      public BottomTabView(@NonNull Context context) {
        this(context, null);
      }

      public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
      }

      public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init();
      }

      public void setUpWithViewPager(ViewPager viewPager) {
        viewPager.addOnPageChangeListener(this);

        mStartImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() != 0)
              viewPager.setCurrentItem(0);
          }
        });

        mCenterImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() != 1)
              viewPager.setCurrentItem(1);
          }
        });

        mEndImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() != 2)
              viewPager.setCurrentItem(2);
          }
        });
        mBottomImageView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View view) {
            if (viewPager.getCurrentItem() == 1) {

            }
          }
        });
      }

      private void init() {
        LayoutInflater.from(getContext()).inflate(R.layout.view_bottom_tabs, this, true);

        // Initialize bottom tab view content
        mCenterImageView = findViewById(R.id.view_bottom_tabs_camera_iv);
        mStartImageView = findViewById(R.id.view_bottom_tabs_start_linear_layout);
        mEndImageView = findViewById(R.id.view_bottom_tabs_end_linear_layout);
        mBottomImageView = findViewById(R.id.view_bottom_tabs_gallery_iv);

        // Initialize bottom tab view indicator, meant to indicate the current tab in view
        mIndicator = findViewById(R.id.view_bottom_tabs_indicator);

        // Set start color and end color for tab view icons
        mCenterColor = ContextCompat.getColor(getContext(), R.color.white);
        mSideColor = ContextCompat.getColor(getContext(), R.color.black);

        // Evaluate the difference between colors
        mArgbEvaluator = new ArgbEvaluator();

        // Change the padding of tab icons based on view
        // This is the min padding accepted (0)
        mIndicatorTranslationX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 140, getResources().getDisplayMetrics());

        // Wait till GalleryImageView has been drawn, once GalleryImageView is drawn then proceed with relative padding calculation
        mBottomImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
          @Override
          public void onGlobalLayout() {

            // Change padding relative to other icons
            mEndViewsTranslationX = (int)((mBottomImageView.getX() - mStartImageView.getX()) - mIndicatorTranslationX);
            mBottomImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // Change the position of the center image
            mCenterTranslationY = getHeight() - mBottomImageView.getBottom();
          }
        });
      }

      @Override
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (position == 0) {

          // Change color of icons based on view
          setColor(1 - positionOffset);

          // Move the start and end images
          moveViews(1 - positionOffset);

          // Change indicator position
          mIndicator.setTranslationX((positionOffset - 1) * mIndicatorTranslationX);

          // Change scale of center image
          moveAndScaleCenter(1 - positionOffset);
        } else if (position == 1) {

          // Change color of icons based on view
          setColor(positionOffset);

          // Move the start and end images
          moveViews(positionOffset);

          // Change indicator position
          mIndicator.setTranslationX(positionOffset * mIndicatorTranslationX);

          // Change scale of center image
          moveAndScaleCenter(positionOffset);
        }
      }

      @Override
      public void onPageSelected(int position) {

      }

      @Override
      public void onPageScrollStateChanged(int state) {

      }

      private void moveAndScaleCenter(float fractionFromCenter) {
        float scale = .7 f + ((1 - fractionFromCenter) * .3 f);

        // Change scale of center image
        mCenterImageView.setScaleX(scale);
        mCenterImageView.setScaleY(scale);

        int translation = (int)(fractionFromCenter * mCenterTranslationY * 2);

        // Move center image down
        mCenterImageView.setTranslationY(translation);
        mBottomImageView.setTranslationY(translation);

        // Fadeout bottom image
        mBottomImageView.setAlpha(1 - fractionFromCenter);
      }

      private void moveViews(float fractionFromCenter) {
        mStartImageView.setTranslationX(fractionFromCenter * mEndViewsTranslationX);
        mEndImageView.setTranslationX(-fractionFromCenter * mEndViewsTranslationX);

        mIndicator.setAlpha(fractionFromCenter);
        mIndicator.setScaleX(fractionFromCenter);
      }

      private void setColor(float fractionFromCenter) {
        int color = (int) mArgbEvaluator.evaluate(fractionFromCenter, mCenterColor, mSideColor);

        /* mCenterImageView.setColorFilter(color);
        mStartImageView.setColorFilter(color);
        mEndImageView.setColorFilter(color);
        mBottomImageView.setColorFilter(color); */
      }
    }

    interface BottomTabViewClickListener {
      public void onClick();
    }

Answer

Your else statement is currently like this, which means you are trying to create a new instance of HomeActivity and call selectimage(), and it does not work that way.

else {
        new HomeActivity().selectImage();
    }

Please, change it to

else{
       selectImage();
    }

Please, try this, this should solve your problem.

EDIT: Use of interface and listener pattern

In your BottomTabView add the following

public class BottomTabView extends FrameLayout implements ViewPager.OnPageChangeListener {

   // Initialize content 
   // add this at top level.
   BottomTabViewClickListener bottomTabViewClickListener;

   //add this function somewhere
   public void setOnBottomTabViewClickListener(BottomTabViewClickListener bottomTabViewClickListener){
        this.bottomTabViewClickListener = bottomTabViewClickListener;
    }

    // replace HomeActivity.openFileChooser(getContext());
    mBottomImageView.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        if (viewPager.getCurrentItem() == 1) {
          bottomTabViewClickListener.onClick();
        }
      }
    });
}
//end of BottomTab View

//add this after your BottomTabView not inside
interface BottomTabViewClickListener{
    public void onClick();
}

Add the following in your HomeActivity

 // Initialize BottomTabView
        BottomTabView bottomTabView = findViewById(R.id.bottom_tabs);
        bottomTabView.setUpWithViewPager(viewPager);
        bottomTabView.setOnBottomTabViewClickListener(new BottomTabViewClickListener() {
            @Override
            public void onClick() {
                // call your openFileChooser from here, 
                //REMOVE THE STATIC
                 openFileChooser(HomeActivity.this)
            }
        });


Source: stackoverflow