ARouter works with Fragment: "Fetch fragment instance error" to handle exceptions. arouterfragment
In the current project, ARouter is used to handle redirection and data transfer requirements in componentized development. It has been used smoothly. Today, an uncommon exception is encountered. Record it:
Exception Information
E/ARouter::: Fetch fragment instance error, at java.lang.reflect.Constructor.newInstance0(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:430) at com.alibaba.android.arouter.launcher._ARouter._navigation(_ARouter.java:380) at com.alibaba.android.arouter.launcher._ARouter.navigation(_ARouter.java:329) at com.alibaba.android.arouter.launcher.ARouter.navigation(ARouter.java:183) at com.alibaba.android.arouter.facade.Postcard.navigation(Postcard.java:149) at com.alibaba.android.arouter.facade.Postcard.navigation(Postcard.java:140) at com.fec.yunmall.MainActivity$MainAdapter.
(MainActivity.java:181) at com.fec.yunmall.MainActivity.initTab(MainActivity.java:274) at com.fec.yunmall.MainActivity.initView(MainActivity.java:91) at com.fec.fecCommon.ui.activity.FecBaseActivity.onCreate(FecBaseActivity.java:45) at android.app.Activity.performCreate(Activity.java:6679) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) [ ]
To facilitate the searching of children's shoes with similar problems, the exception information is posted, which is a little long. an error occurred while obtaining Fragment. in MainActivity, I can use ARouter to directly obtain the Fragment instance function and get the Fragment in the business component. there has been no problem before. suddenly this exception occurs. an error occurred while obtaining the page. Switching in MainActivity causes the App to crash.
Problem Analysis
View the ARouter Exception Code:
case FRAGMENT: Class fragmentMeta = postcard.getDestination(); try { Object instance = fragmentMeta.getConstructor().newInstance(); if (instance instanceof Fragment) { ((Fragment) instance).setArguments(postcard.getExtras()); } else if (instance instanceof android.support.v4.app.Fragment) { ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras()); } return instance; } catch (Exception ex) { logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace())); }
The implementation here is implemented through reflection. observe the Fragment List in MainActivity through the breakpoint and find that the problematic part is Fragment 3, which is implemented by Kotlin. I thought it was the problem here, but there was no problem with the implementation of Kotlin on the 2nd. there have been no such problems before, and there is no deeper exception information. since the compileSdkVersion and buildToolsVersion of the App were upgraded to 27 +, it was slightly suspected that the Sdk version was a problem, but it was unlikely that the SdkVersion test would be reduced when other methods were fruitless. observe the Fragment on the 3rd to find the possible causes. this is the reason after testing.
Handling process
The following code is initially available in Fragment 3:
@ Route (path = "/memberCenter") class MemberCenterFragment: BaseMvcFragment () {val loginCallBack = object: NavCallback () {// construct a public callBack to jump to the logon page when the page in the Unlogged status is blocked. override fun onInterrupt (postcard: Postcard ?) {Member_center_ TV _name.post {initView2Start ()} ARouter. getInstance (). build ("/memberCenter/selectLoginActivity "). navigation (activity, FecBaseFragment. REQUEST_REFRESH_KEY)} override fun onArrival (postcard: Postcard ?) {}} ''' // Decompile java: public final class MemberCenterFragment extends BaseMvcFragment {@ NotNull private final NavCallback loginCallBack = (NavCallback) (new NavCallback () {public void onInterrupt (@ Nullable Postcard postcard) {(TextView) MemberCenterFragment. this. _ $ _ findCachedViewById (id. member_center_ TV _name )). post (Runnable) (new Runnable () {public final void run () {MemberCenterFragme Nt. this. initView2Start () ;}}); ARouter. getInstance (). build ("/memberCenter/selectLoginActivity "). navigation (Activity) MemberCenterFragment. this. getActivity (),'? ');} Public void onArrival (@ Nullable Postcard postcard ){}});
Later, because similar code is used in other places, a common callBack is extracted and the variable is assigned as follows:
@ Route (path = "/memberCenter") class MemberCenterFragment: BaseMvcFragment () {private val loginCallBack = LoginNVCallBack (activity, FecBaseFragment. REQUEST_REFRESH_KEY) {member_center_ TV _name.post {initView2Start ()} ''' // Decompile java when you use activity (actually call the getActivity () method: public final class MemberCenterFragment extends BaseMvcFragment {private final LoginNVCallBack loginCallBac K = new LoginNVCallBack (Activity) this. getActivity (),'? ', (Function0) (new Function0 () {// $ FF: synthetic method // $ FF: bridge method public Object invoke () {this. invoke (); return Unit. INSTANCE;} public final void invoke () {(TextView) MemberCenterFragment. this. _ $ _ findCachedViewById (id. member_center_ TV _name )). post (Runnable) (new Runnable () {public final void run () {MemberCenterFragment. this. initView2Start () ;}}); ''' // use mActivity. Decompile java: public final class MemberCenterFragment extends BaseMvcFragment {@ NotNull private final LoginNVCallBack loginCallBack; // Note: initialization is put into the constructor public MemberCenterFragment () {this. loginCallBack = new LoginNVCallBack (this. mActivity ,'? ', (Function0) (new Function0 () {// $ FF: synthetic method // $ FF: bridge method public Object invoke () {this. invoke (); return Unit. INSTANCE;} public final void invoke () {(TextView) MemberCenterFragment. this. _ $ _ findCachedViewById (id. member_center_ TV _name )). post (Runnable) (new Runnable () {public final void run () {MemberCenterFragment. this. initView2Start () ;}}) ;}''' class LoginNVCal LBack (val context: Context ?, Val requestCode: Int = FecBaseActivity. REQUEST_REFRESH_KEY, private var callback: ()-> Unit )? = Null): NavCallback () {constructor (activity: Activity ?, RequestCode: Int = FecBaseActivity. REQUEST_REFRESH_KEY, callback: ()-> Unit )? = Null): this (activity as Context, requestCode, callback) override fun onArrival (postcard: Postcard ?) {} Override fun onInterrupt (postcard: Postcard ?) {Callback ?. Invoke () if (context is Activity) {ARouter. getInstance (). build ("/memberCenter/selectLoginActivity "). navigation (context, requestCode)} else {ARouter. getInstance (). build ("/memberCenter/selectLoginActivity "). navigation (context )}}}
It seems that there is no big gap with the previous code, but this change causes reflection instantiation of this Fragment to fail. Why? Is the activity referenced when loginCallBack is created (actually calling the getActivity () method) not assigned a value? It is very likely that even if mActivity is used, the results are the same. Or is it because the function used becomes an anonymous class after compilation and references the Fragment outside? Check through the following java code. It seems that the reason is that the activity is empty. Whether it is a direct value assignment or a constructor, getActivity () and mActivity are both null. but it is not empty in onCreate.
Modify the following to solve the problem:
@ Route (path = "/memberCenter") class MemberCenterFragment: BaseMvcFragment () {lateinit var loginCallBack: LoginNVCallBack override fun init (savedInstanceState: Bundle ?) {// When this method is called, mActivity has assigned loginCallBack = LoginNVCallBack (mActivity, FecBaseFragment. REQUEST_REFRESH_KEY) {response {initView2Start ()}}}''''''
The specific cause still needs to be studied. When studying this problem, we found a small detail:
LoginCallBack is directly assigned a value for initialization when using activity (actually calling the getActivity () method). When using mActivity (custom fields are in onCreate () when the value is getActivity (), the initialization is put into the constructor.
Postscript
Finally, find the cause. The problem beyond comparison lies in LoginNVCallBack:
Class LoginNVCallBack (var context: Context ?, Val requestCode: Int = FecBaseActivity. REQUEST_REFRESH_KEY, private var callback: ()-> Unit )? = Null): NavCallback () {constructor (activity: Activity ?, RequestCode: Int = FecBaseActivity. REQUEST_REFRESH_KEY, callback: ()-> Unit )? = Null): this (activity as Context ?, // Here, we strongly convert it to non-empty requestCode, callback) override fun onArrival (postcard: Postcard ?) {} Override fun onInterrupt (postcard: Postcard ?) {Callback ?. Invoke () if (context is Activity) {ARouter. getInstance (). build ("/memberCenter/selectLoginActivity "). navigation (context as Activity, requestCode)} else {ARouter. getInstance (). build ("/memberCenter/selectLoginActivity "). navigation (context )}}}
This is because no matter whether getActivity () or mActivity in MemberCenterFragment is null, the activity of the LoginNVCallBack constructor is: Activity? It is also null, but it is strongly converted to a non-null type when calling the main constructor, so an exception occurs. the error log is not output due to mixed encoding. this has caused the above problems. Pay attention later.