Search code examples
androidandroid-studioandroid-fragmentsandroid-fragmentactivitygoogle-console-developer

Onboarding activity as FragmentActivity failing on certain devices per Pre-launch report


Android newb here. Any help is appreciated. So I posted my app to the Google Play Console. I had a few issues when I did a pre-launch report. The app fails on 4 devices running Android 6 and 7. The issue is with the onboarding activity. I've shown the OnBoardActivity class and one of the OnboardingFragments (they are all the same except with different pictures in each perspective layouts (R.id.onboardingscreen_1, R.id.onboardingscreen_2, etc).

I see the fatal crash is a memory issue "Caused by: java.lang.OutOfMemoryError: Failed to allocate a 132710412 byte allocation with 33554432 free bytes and 120MB until OOM." The line that caught my eye in the logcat that makes me think this is an OnBoardActivity issue:

" at Util.OnboardingFragment2.onCreateView(OnboardingFragment2.java:21)"

where 3 of the devices fail.

The Galaxy J7 fails with this difference in the logcat:

"java.lang.IllegalStateException: Fragment already added: OnboardingFragment3{fe12a94 #2 id=0x7f0a0099}"

I have no idea why my app would try and allocate 132 MB??? I cannot recreate either of these with an emulator. When I look at the video of the app crashing it's right at first time start up for these 4 devices. It either fails while trying to pull up OnBoardActivity or by moving to the next or previous onboarding fragment.

I'm not sure what to do! I need some sort of quick instruction for the first run. I'm confident I'm starting the OnBoardActivity appropriately (from MainActivity):

        prefs = getSharedPreferences(Util.APP_NAME, MODE_PRIVATE);

    if(!prefs.getBoolean("onboarding_complete", false)){
        Intent intent = new Intent(this, OnBoardActivity.class);
        startActivity(intent);

        finish();
        return;
    }

The log that Google provides for these 4 failures is:

<!-- language: lang-none -->
Caused by: android.view.InflateException: Binary XML file line #26: Error inflating class <unknown>
Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Constructor.newInstance0(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
    at android.view.LayoutInflater.createView(LayoutInflater.java:645)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:787)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:861)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
    at Util.OnboardingFragment2.onCreateView(OnboardingFragment2.java:21)
    at android.support.v4.app.Fragment.performCreateView(Fragment.java:2346)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1428)
    at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1827)
    at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:797)
    at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2596)
    at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2383)
    at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2338)
    at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2215)
    at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:649)
    at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:167)
    at android.support.v4.view.ViewPager.populate(ViewPager.java:1238)
    at android.support.v4.view.ViewPager.populate(ViewPager.java:1086)
    at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1616)
    at android.view.View.measure(View.java:19834)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
    at android.view.View.measure(View.java:19834)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6164)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.view.View.measure(View.java:19834)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6164)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:758)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:640)
    at android.view.View.measure(View.java:19834)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6164)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at com.android.internal.policy.DecorView.onMeasure(DecorView.java:692)
    at android.view.View.measure(View.java:19834)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2358)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1430)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1679)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1306)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6579)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
    at android.view.Choreographer.doCallbacks(Choreographer.java:683)
    at android.view.Choreographer.doFrame(Choreographer.java:619)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6316)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:872)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:762)
Caused by: java.lang.OutOfMemoryError: Failed to allocate a 132710412 byte allocation with 33554432 free bytes and 120MB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:620)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:455)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:1152)
    at android.content.res.ResourcesImpl.loadDrawableForCookie(ResourcesImpl.java:859)
    at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:710)
    at android.content.res.Resources.getDrawable(Resources.java:776)
    at android.content.Context.getDrawable(Context.java:530)
    at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:358)
    at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:198)
    at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:186)
    at android.support.v7.content.res.AppCompatResources.getDrawable(AppCompatResources.java:100)
    at android.support.v7.widget.AppCompatImageHelper.loadFromAttributes(AppCompatImageHelper.java:58)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:77)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:67)
    ... 58 more

Here's the code:

public class OnBoardActivity extends FragmentActivity {

private ViewPager pager;
private SmartTabLayout indicatior;
private ButtonFlat skip;
private ButtonFlat next;

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

    pager = findViewById(R.id.pager);
    indicatior = findViewById(R.id.indicator);
    skip = findViewById(R.id.skip);
    next = findViewById(R.id.next);

    FragmentStatePagerAdapter adapter = new FragmentStatePagerAdapter(getSupportFragmentManager()) {
        @Override
        public Fragment getItem(int position) {

            List<Fragment> f = getSupportFragmentManager().getFragments();

            for(int i=0; i<f.size(); i++){
                Log.d("Fragment Get", "Fragment: " + Integer.toString(i) + " "
                + f.get(i).getClass().getName());
            }

            Log.d("Fragment Pos", "Position: " + Integer.toString(position));

            switch (position) {
                case 0 :
                    Log.d("Fragment Ret", "Returning new OnboardingFragment1\n");
                    return new OnboardingFragment1();
                case 1 :
                    Log.d("Fragment Ret", "Returning new OnboardingFragment2\n");
                    return new OnboardingFragment2();
                case 2 :
                    Log.d("Fragment Ret", "Returning new OnboardingFragment3\n");
                    return new OnboardingFragment3();
                default: return null;
            }

        }


        @Override
        public int getCount() {
            return 3;
        }

        @Override
        public Parcelable saveState(){
            return null;
        }
    };

    pager.setAdapter(adapter);

    indicatior.setViewPager(pager);

    indicatior.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){
        @Override
        public void onPageSelected(int position) {
            if(position == 2){
                skip.setVisibility(View.GONE);
                next.setText("Done");
            } else {
                skip.setVisibility(View.VISIBLE);
                next.setText("Next");
            }
        }
    });

    skip.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            finishOnboarding();
        }
    });

    next.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(pager.getCurrentItem() == 2){
                finishOnboarding();
            } else {
                pager.setCurrentItem(pager.getCurrentItem() + 1, true);
            }
        }
    });

}

private void finishOnboarding() {

    SharedPreferences preferences =
            getSharedPreferences(Util.APP_NAME, MODE_PRIVATE);

    preferences.edit()
            .putBoolean("onboarding_complete",true).apply();

    Intent intent = new Intent(this, MainActivity.class);
    startActivity(intent);
    finish();
}

}

public class OnboardingFragment1 extends Fragment {

@Override
public View onCreateView(@Nullable LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    setRetainInstance(true);

    return inflater != null ? inflater.inflate(
            R.layout.onboarding_screen1,
            container,
            false
    ) : null;
}

}

Hopefully someone can help! Thanks for reading! -Matt


Solution

  • Per my comment I found the solution after much research regarding Fragments. Sometimes the FragmentStatePageAdapter will try and inflate views that it thinks have been removed, but actually haven't yet. It works great now by changing OnboardingFragment to:

    public class OnboardingFragment1 extends Fragment {
        @Override
        public View onCreateView(@Nullable LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    
            return getView() !=null ? getView(): inflater.inflate(R.layout.onboarding_screen1, container, false);
    }