横竖屏切换引起的fragment恢复问题(InstantiationException)

横竖屏切换引起的fragment恢复问题

monkey测试时出现的问题log:

AndroidRuntime: FATAL EXCEPTION: main
AndroidRuntime: Process: com.android.settings, PID: 11889
AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.settings/com.android.settings.SubSettings}: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.unisoc.settings.smartcontrols.SmartPickUpAnimation: could not find Fragment constructor
AndroidRuntime: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3500)
AndroidRuntime: 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3678)
AndroidRuntime: 	at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5623)
AndroidRuntime: 	at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:5486)
AndroidRuntime: 	at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:69)
AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2106)
AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
AndroidRuntime: 	at android.os.Looper.loop(Looper.java:223)
AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:7943)
AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:603)
AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
AndroidRuntime: Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.unisoc.settings.smartcontrols.SmartPickUpAnimation: could not find Fragment constructor
AndroidRuntime: 	at androidx.fragment.app.Fragment.instantiate(Fragment.java:619)
AndroidRuntime: 	at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
AndroidRuntime: 	at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:455)
AndroidRuntime: 	at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:88)
AndroidRuntime: 	at androidx.fragment.app.FragmentManager.restoreSaveState(FragmentManager.java:2655)
AndroidRuntime: 	at androidx.fragment.app.FragmentController.restoreSaveState(FragmentController.java:198)
AndroidRuntime: 	at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:237)
AndroidRuntime: 	at com.android.settings.core.SettingsBaseActivity.onCreate(SettingsBaseActivity.java:65)
AndroidRuntime: 	at com.android.settings.SettingsActivity.onCreate(SettingsActivity.java:254)
AndroidRuntime: 	at android.app.Activity.performCreate(Activity.java:8079)
AndroidRuntime: 	at android.app.Activity.performCreate(Activity.java:8062)
AndroidRuntime: 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
AndroidRuntime: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3470)
AndroidRuntime: 	... 13 more
AndroidRuntime: Caused by: java.lang.NoSuchMethodException: com.unisoc.settings.smartcontrols.SmartPickUpAnimation.<init> []
AndroidRuntime: 	at java.lang.Class.getConstructor0(Class.java:2332)
AndroidRuntime: 	at java.lang.Class.getConstructor(Class.java:1728)
AndroidRuntime: 	at androidx.fragment.app.Fragment.instantiate(Fragment.java:604)
AndroidRuntime: 	... 25 more

想办法定位到Exception的原因(Unable to instantiate fragment com.unisoc.settings.smartcontrols.SmartPickUpAnimation: could not find Fragment constructor),找到以下方法:即对fragment重建时会调用无参构造方法,而源码中没有则会抛出异常

 public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
       try {
           Class<?> clazz = sClassMap.get(fname);
           if (clazz == null) {
               // Class not found in the cache, see if it's real, and try to add it
               clazz = context.getClassLoader().loadClass(fname);
               if (!Fragment.class.isAssignableFrom(clazz)) {
                   throw new InstantiationException("Trying to instantiate a class " + fname
                           + " that is not a Fragment", new ClassCastException());
               }
               sClassMap.put(fname, clazz);
           }
           Fragment f = (Fragment) clazz.getConstructor().newInstance();
           if (args != null) {
               args.setClassLoader(f.getClass().getClassLoader());
               f.setArguments(args);
           }
           return f;
       } catch (ClassNotFoundException e) {
           throw new InstantiationException("Unable to instantiate fragment " + fname
                   + ": make sure class name exists, is public, and has an"
                   + " empty constructor that is public", e);
       } catch (java.lang.InstantiationException e) {
           throw new InstantiationException("Unable to instantiate fragment " + fname
                   + ": make sure class name exists, is public, and has an"
                   + " empty constructor that is public", e);
       } catch (IllegalAccessException e) {
           throw new InstantiationException("Unable to instantiate fragment " + fname
                   + ": make sure class name exists, is public, and has an"
                   + " empty constructor that is public", e);
       } catch (NoSuchMethodException e) {
           throw new InstantiationException("Unable to instantiate fragment " + fname
                   + ": could not find Fragment constructor", e);
       } catch (InvocationTargetException e) {
           throw new InstantiationException("Unable to instantiate fragment " + fname
                   + ": calling Fragment constructor caused an exception", e);
       }
   }

定位到问题点后再看看代码中是如何写的:(0.0 根本没有无参构造函数,所以出现以上错误)

public class MuteIncomingCallsAnimation extends BaseAnimation {
    public static final String TAG = "MuteIncomingCallsAnimation";

    public MuteIncomingCallsAnimation(SmartSwitchPreference preference) {
        super(preference);
        mLayoutId = R.layout.mute_incoming_calls;
        mImageViewId = R.id.mute_incoming_calls_display;
        mAnimResId = R.drawable.mute_incoming_calls_anim;
        mDatabaseKey = MUTE_INCOMING_CALLS;
    }
}

进行修改:添加static的newInstance方法其中new一个无参的实例,并保存参数,然后返回实例,然后就能通过保存的参数进行恢复了

public class MuteIncomingCallsAnimation extends DialogFragment {
    public static final String TAG = "MuteIncomingCallsAnimation";
    private static SmartSwitchPreference mPreference;

    public static MuteIncomingCallsAnimation newInstance(SmartSwitchPreference preference) {
        final MuteIncomingCallsAnimation muteIncomingCallsDialog = new MuteIncomingCallsAnimation();
        mPreference = preference;
        return muteIncomingCallsDialog;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return Utils.createAnimationDialog(getActivity(), mPreference, R.layout.mute_incoming_calls,
                R.id.mute_incoming_calls_display, R.drawable.mute_incoming_calls_anim, MUTE_INCOMING_CALLS);
    }
}

Utils工具类,弹出一个dialog:

public static Dialog createAnimationDialog(final Context context, final SmartSwitchPreference preference,
        int layoutId, int imageViewId, int animationId, final String databaseKey) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        final View customView = inflater.inflate(layoutId, null);
        ImageView imageView = (ImageView) customView.findViewById(imageViewId);
        imageView.setImageResource(animationId);
        AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
        animationDrawable.start();
        AlertDialog.Builder animationDialog = new AlertDialog.Builder(context);
        animationDialog.setView(customView);
        animationDialog.setPositiveButton(R.string.smart_ok,
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        boolean turnOn = which == DialogInterface.BUTTON_POSITIVE;
                        if (preference != null) {
                            preference.setChecked(turnOn);
                            ContentResolver resolver = context.getContentResolver();
                            if (DOZE_PICK_UP_GESTURE.equals(databaseKey) || WAKE_GESTURE_ENABLED.equals(databaseKey)) {
                                Settings.Secure.putInt(resolver, databaseKey, turnOn ? 1 : 0);
                            } else if (SmartMotionFragment.isSmartMotionEnabled(context)) {
                                Settings.Global.putInt(resolver, databaseKey, turnOn ? 1 : 0);
                            }
                        }
                    }
                });
       return animationDialog.create();
    }
}

总结:
fragment进行横竖屏切换恢复的时候,会调用无参构造方法,所以需要定义或调用无参构造方法才不会出现异常,恢复所需的参数可以通过Bundle或者全局变量之类的进行保存,方便获取,从而到达恢复的目的.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值