美文网首页
汇总:记录线上崩溃的问题

汇总:记录线上崩溃的问题

作者: 巴黎没有摩天轮Li | 来源:发表于2020-10-22 17:57 被阅读0次

前言

记录线上崩溃问题,持续记录...

DigitsKeyListener导致7.x.x以下手机崩溃

设备列表

修复方式:

修复方式

Fragment 构造方法私有化导致崩溃

Fragment导致崩溃 修复方式

原因分析

FragmentActivity#onSaveInstanceState
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    markFragmentsCreated();
    mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
    // 进行Fragment状态保存 拿到Parcelable对象
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
         // 存入
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    // 省略部分保存Key Value
}
FragmentActivity#onCreate(@Nullable Bundle savedInstanceState)
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
    if (savedInstanceState != null) {
        Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
        // 恢复部分数据
        mFragments.restoreSaveState(p);
    }
    // 省略部分代码....
}
FragmentManagerImpl#
void restoreSaveState(Parcelable state) {
    for (FragmentState fs : fms.mActive) {
        if (fs != null) {
            // 创建一个新的Fragment对象
            Fragment f = fs.instantiate(mHost.getContext().getClassLoader(),
                    getFragmentFactory());
            f.mFragmentManager = this;
            if (DEBUG) Log.v(TAG, "restoreSaveState: active (" + f.mWho + "): " + f);
            mActive.put(f.mWho, f);
            // Now that the fragment is instantiated (or came from being
            // retained above), clear mInstance in case we end up re-restoring
            // from this FragmentState again.
            fs.mInstance = null;
        }
    }
}
FragmentState#instantiate()
public Fragment instantiate(@NonNull ClassLoader classLoader,
        @NonNull FragmentFactory factory) {
    if (mInstance == null) {
        if (mArguments != null) {
            mArguments.setClassLoader(classLoader);
        }
        // FragmentFactory 所谓的工厂进行创建对象
        mInstance = factory.instantiate(classLoader, mClassName);
        mInstance.setArguments(mArguments);
        if (mSavedFragmentState != null) {
            mSavedFragmentState.setClassLoader(classLoader);
            mInstance.mSavedFragmentState = mSavedFragmentState;
        } else {
            mInstance.mSavedFragmentState = new Bundle();
        }
    }
    return mInstance;
}
FragmentFactory创建对象
@NonNull
public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
    try {
        // 反射创建对象
        Class<? extends Fragment> cls = loadFragmentClass(classLoader, className);
        return cls.getConstructor().newInstance();
    } catch (java.lang.InstantiationException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (IllegalAccessException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (NoSuchMethodException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": could not find Fragment constructor", e);
    } catch (InvocationTargetException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": calling Fragment constructor caused an exception", e);
    }
}

因此在Fragment中是不允许存在私有构造方法的否则导致在恢复状态的时候Fragment创建失败。

部分机型导致TimeOut异常

java.util.concurrent.TimeoutException: android.content.res.AssetManager$AssetInputStream.finalize() timed out after 10 seconds
 at android.content.res.AssetManager$AssetInputStream.close(AssetManager.java:812)
 at android.content.res.AssetManager$AssetInputStream.finalize(AssetManager.java:845)
 at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:202)
 at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:185)
 at java.lang.Thread.run(Thread.java:833)

解决办法以及原理分析

private void fixTimeOutException() {
    if (BuildConfig.DEBUG || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        return; 
    }
    try {
        final Class<?> clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");
        final Method method = clazz.getSuperclass().getDeclaredMethod("stop");
        method.setAccessible(true);
        final Field field = clazz.getDeclaredField("INSTANCE");
        field.setAccessible(true);
        method.invoke(field.get(null));
        UmengUtils.UmEvent(mApplication.getApplicationContext(), "fix_oppo_success");
    } catch (Exception e1) {
        try {
            UmengUtils.UmEvent(mApplication.getApplicationContext(), "fix_oppo_failed");
        } catch (Exception e2) {
            Logger.e(TAG, e1.getMessage());
            Logger.e(TAG, e2.getMessage());
        }
    }
}

Android9.0 启动Activity时,导致的ActivityRecord not found异常。

java.lang.IllegalArgumentException: reportSizeConfigurations: ActivityRecord not found for: Token{dd2d7e2 ActivityRecord{b2548ad u0 com.ehai/cn.jpush.android.service.JNotifyActivity t-1 f}}
    at android.os.Parcel.createException(Parcel.java:1957)
    at android.os.Parcel.readException(Parcel.java:1921)
    at android.os.Parcel.readException(Parcel.java:1871)
    at android.app.IActivityManager$Stub$Proxy.reportSizeConfigurations(IActivityManager.java:8737)
    at android.app.ActivityThread.reportSizeConfigurations(ActivityThread.java:3670)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3625)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:86)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2199)
    at android.os.Handler.dispatchMessage(Handler.java:112)
    at android.os.Looper.loop(Looper.java:216)
    at android.app.ActivityThread.main(ActivityThread.java:7625)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
Caused by: android.os.RemoteException: Remote stack trace:
    at com.android.server.am.ActivityManagerService.reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V(libmapleservices.so:5919109)
    at android.app.IActivityManager$Stub.onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z(libmapleframework.so:4765897)
    at com.android.server.am.ActivityManagerService.onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z(libmapleservices.so:5931469)
    at com.android.server.am.HwActivityManagerService.onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z(libmaplehwServices.so:3387765)
    at android.os.Binder.execTransact(IJJI)Z(libmapleframework.so:6090741)
android.os.RemoteException: Remote stack trace:
    at com.android.server.am.ActivityManagerService.reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V(libmapleservices.so:5919109)
    at android.app.IActivityManager$Stub.onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z(libmapleframework.so:4765897)
    at com.android.server.am.ActivityManagerService.onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z(libmapleservices.so:5931469)
    at com.android.server.am.HwActivityManagerService.onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z(libmaplehwServices.so:3387765)
    at android.os.Binder.execTransact(IJJI)Z(libmapleframework.so:6090741)

究其原因

先找一下抛出异常的具体位置。我们知道启动Activity时,会通过IPC binder机制,通知AMS我要启动Activity了,最终会告知ActivityThread这个类进行回调Activity的各个生命周期的处理。

@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    //... 省略部分代码
    final Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        // 嗯嗯.... 这里就是问题入口拉
        reportSizeConfigurations(r);
        if (!r.activity.mFinished && pendingActions != null) {
            pendingActions.setOldState(r.state);
            pendingActions.setRestoreInstanceState(true);
            pendingActions.setCallOnPostCreate(true);
        }
    } else {
        // If there was an error, for any reason, tell the activity manager to stop us.
        try {
            ActivityTaskManager.getService()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
    return a;
}

看下reportSizeConfigurations()这个方法。

private void reportSizeConfigurations(ActivityClientRecord r) {
    // 这里通知了ActivityTaskManagerService去获取ActivityRecord
    try {
        ActivityTaskManager.getService().reportSizeConfigurations(r.token,
                horizontal.copyKeys(), vertical.copyKeys(), smallest.copyKeys());
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}
ActivityTaskManagerService#reportSizeConfigurations()
@Override
public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
        int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
            + horizontalSizeConfiguration + " " + verticalSizeConfigurations);
    synchronized (mGlobalLock) {
        ActivityRecord record = ActivityRecord.isInStackLocked(token);
        // 若ActivityRecord 为 null, 则throw出我们Bugly所记录的异常。
        if (record == null) {
            throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
                    + "found for: " + token);
        }
        record.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations,
                smallestSizeConfigurations);
    }
}

static ActivityRecord isInStackLocked(IBinder token) {
    // 根据Token来获取ActivityRecord对象 
    final ActivityRecord r = ActivityRecord.forTokenLocked(token);
    return (r != null) ? r.getActivityStack().isInStackLocked(r) : null;
}

private static @Nullable ActivityRecord tokenToActivityRecordLocked(Token token) {
    if (token == null) {
        return null;
    }
    // 从弱引用中获取ActivityRecord
    ActivityRecord r = token.weakActivity.get();
    if (r == null || r.getActivityStack() == null) {
        return null;
    }
    return r;
}

// Token 继承了 Stub  我们知道Stub 是跨进程通信的,并且实现了IBinder接口。
static class Token extends IApplicationToken.Stub {
    // 弱引用 ActivityRecord
    private final WeakReference<ActivityRecord> weakActivity;
    private final String name;
    Token(ActivityRecord activity, Intent intent) {
        weakActivity = new WeakReference<>(activity);
        name = intent.getComponent().flattenToShortString();
    }
    private static @Nullable ActivityRecord tokenToActivityRecordLocked(Token token) {
        if (token == null) {
            return null;
        }
        ActivityRecord r = token.weakActivity.get();
        if (r == null || r.getActivityStack() == null) {
            return null;
        }
        return r;
    }
}

因此产生这个问题的原因就是再执行Activity启动的时候,根据Token 去获取ActivityRecord对象,但是这个对象为空,所以会抛出该异常。暂时该问题还不知道源头怎么解决,所以我的处理方式就是直接将reportSizeConfigurations()这个方法通过动态代理进行异常捕捉。

解决办法

private void fixReportSizeConfigurationsException() {
    if (Build.VERSION.SDK_INT != Build.VERSION_CODES.P) {
        return;
    }
    try {
        // 反射拿到ActivityManager
        Field activityManager = ActivityManager.class.getDeclaredField("IActivityManagerSingleton");
        activityManager.setAccessible(true);
        Object iActivityManagerSingleton = activityManager.get(null);
        if (iActivityManagerSingleton == null) {
            return;
        }
        Class<?> singletonCls = iActivityManagerSingleton.getClass().getSuperclass();
        if (singletonCls == null){
            return;
        }
        Field instance = singletonCls.getDeclaredField("mInstance");
        instance.setAccessible(true);
        Object iActivityManager = instance.get(iActivityManagerSingleton);
        @SuppressLint("PrivateApi")
        Class<?> iActivityManagerCls = Class.forName("android.app.IActivityManager");
        Class<?>[] classes = {iActivityManagerCls};
        Object iActivityManageProxy = Proxy.newProxyInstance(
                iActivityManagerCls.getClassLoader(),
                classes,
                new IActivityManagerProxy(iActivityManager));
        instance.set(iActivityManagerSingleton, iActivityManageProxy);
    } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}
/**
 * 动态代理处理 try catch  ATMS #ActivityTaskManager#reportSizeConfigurations()方法
 */
private static class IActivityManagerProxy implements InvocationHandler {
    private Object mIActivityManager;
    public IActivityManagerProxy(Object iActivityManager) {
        mIActivityManager = iActivityManager;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("reportSizeConfigurations".equals(method.getName())) {
            try {
                Log.w(TAG, "reportSizeConfigurations invoke execute ");
                return method.invoke(mIActivityManager, args);
            } catch (Exception e) {
                Log.w(TAG, "reportSizeConfigurations exception: " + e.getMessage());
                return null;
            }
        }
        return method.invoke(mIActivityManager, args);
    }
}

相关文章

  • 汇总:记录线上崩溃的问题

    前言 记录线上崩溃问题,持续记录... DigitsKeyListener导致7.x.x以下手机崩溃 修复方式: ...

  • JSPath使用汇总

    业务需求,决定接入JSPath来方便处理线上bug问题。 再次记录下使用过程中的问题汇总,使用方法等资料。 一、具...

  • Spring+SpringMVC+MyBatis+easyUI整

    前文提要 承接前文《一次线上Mysql数据库崩溃事故的记录》,在文章中讲到了一次线上数据库崩溃的事件记录,建议两篇...

  • iOS崩溃问题汇总

    1.Xcode9打包,在iOS 9.0.2, iOS 9.2和iOS9.2.1上崩溃.而且用debug\relea...

  • crash疑难修复:Class not found when u

    崩溃堆栈 首先,崩溃上报的堆栈: 这是我们app升级androidx之后,第一次外灰时发现的线上问题。来自线上的偶...

  • iOS崩溃日志分析

    什么是崩溃日志 iOS的App在崩溃时,系统会记录下当前的每个线程的调用栈信息等等,并保存到设备中。这些信息汇总起...

  • 线上分析问题命令汇总

    前言 在生产环境中,逐出出现CPU飙升100%,内存溢出等,连接池等待等一些不容易定位的问题,在生产环境的也无法像...

  • 崩溃的线上

    孩子今年上小学三年级了,每次做作业的过程都是一个很痛苦的过程,也不知道从什么时候开始,孩子的教育已经成为我们家庭...

  • 线上问题记录

    1,在丽珠的时候发生一个线上问题,系统在调用数据库时,一直连接不上,然后使用工具连接MYSQL可以正常连接,但是在...

  • Android 学习问题汇总

    Android 学习问题汇总 开始android开发, 记录遇到的问题 1. Error:moudle not s...

网友评论

      本文标题:汇总:记录线上崩溃的问题

      本文链接:https://www.haomeiwen.com/subject/oxjpmktx.html