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
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);
}