前言
記錄線上崩潰問(wèn)題硬猫,持續(xù)記錄...
-
DigitsKeyListener導(dǎo)致7.x.x以下手機(jī)崩潰
修復(fù)方式:
-
Fragment 構(gòu)造方法私有化導(dǎo)致崩潰
原因分析
FragmentActivity#onSaveInstanceState
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
markFragmentsCreated();
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
// 進(jìn)行Fragment狀態(tài)保存 拿到Parcelable對(duì)象
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);
// 恢復(fù)部分?jǐn)?shù)據(jù)
mFragments.restoreSaveState(p);
}
// 省略部分代碼....
}
FragmentManagerImpl#
void restoreSaveState(Parcelable state) {
for (FragmentState fs : fms.mActive) {
if (fs != null) {
// 創(chuàng)建一個(gè)新的Fragment對(duì)象
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 所謂的工廠進(jìn)行創(chuàng)建對(duì)象
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創(chuàng)建對(duì)象
@NonNull
public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
try {
// 反射創(chuàng)建對(duì)象
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中是不允許存在私有構(gòu)造方法的否則導(dǎo)致在恢復(fù)狀態(tài)的時(shí)候Fragment創(chuàng)建失敗顽馋。
部分機(jī)型導(dǎo)致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());
}
}
}
-
Android7.0 - 9.0 啟動(dòng)Activity時(shí),導(dǎo)致的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)
究其原因
先找一下拋出異常的具體位置挎扰。我們知道啟動(dòng)Activity時(shí)翠订,會(huì)通過(guò)IPC binder機(jī)制巢音,通知AMS我要啟動(dòng)Activity了,最終會(huì)告知ActivityThread這個(gè)類進(jìn)行回調(diào)Activity的各個(gè)生命周期的處理尽超。
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
//... 省略部分代碼
final Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
// 嗯嗯.... 這里就是問(wèn)題入口拉
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()這個(gè)方法官撼。
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) {
// 根據(jù)Token來(lái)獲取ActivityRecord對(duì)象
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 是跨進(jìn)程通信的似谁,并且實(shí)現(xiàn)了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;
}
}
因此產(chǎn)生這個(gè)問(wèn)題的原因就是再執(zhí)行Activity啟動(dòng)的時(shí)候,根據(jù)Token 去獲取ActivityRecord對(duì)象巩踏,但是這個(gè)對(duì)象為空秃诵,所以會(huì)拋出該異常。暫時(shí)該問(wèn)題還不知道源頭怎么解決塞琼,所以我的處理方式就是直接將reportSizeConfigurations()這個(gè)方法通過(guò)動(dòng)態(tài)代理進(jìn)行異常捕捉菠净。
解決辦法
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();
}
}
/**
* 動(dòng)態(tài)代理處理 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);
}
}
-
AbstractMethodError 抽象方法錯(cuò)誤
這個(gè)錯(cuò)誤問(wèn)題相對(duì)來(lái)講遇到的不多,此次制造這個(gè)原因的問(wèn)題解決了,原因是我們公司的項(xiàng)目集成了Mob的ShareSDK毅往,在2020年11月18日15時(shí)他們服務(wù)端進(jìn)行了錯(cuò)誤的配置項(xiàng)導(dǎo)致Android端ShareSDK拋出該問(wèn)題牵咙。雖然問(wèn)題不是我們的,但為了防范該問(wèn)題的產(chǎn)生攀唯,還是有必要了解一下洁桌。
AbstractMethodError
首先AbstractMethodError,顧名思義是應(yīng)用在調(diào)用抽象方法的時(shí)候會(huì)拋出該異常侯嘀,并且這個(gè)錯(cuò)誤只會(huì)在代碼運(yùn)行的時(shí)候進(jìn)行觸發(fā)的另凌,原因在于某些類在實(shí)現(xiàn)父類的抽象方法的時(shí)候,在最近一次編譯之后該父類的抽象方法又發(fā)生了改變戒幔。所以途茫,出現(xiàn)這種情況的原因就是代碼版本不兼容導(dǎo)致的問(wèn)題。
針對(duì)Android開發(fā)溪食,最容易產(chǎn)生該錯(cuò)誤的問(wèn)題原因則跟混淆有關(guān)囊卜。在編譯期代碼被混淆過(guò)后,有些方法不應(yīng)該被混淆错沃,在運(yùn)行時(shí)栅组,找不到該方法,所以導(dǎo)致拋出AbstractMethodError枢析。
-
OkHttp - Unexpected TLS version: NONE
公司項(xiàng)目代碼OkHttp版本升級(jí)之前是3.6.0玉掸,由于提出對(duì)請(qǐng)求埋點(diǎn)的需要,需要記錄一次請(qǐng)求各個(gè)環(huán)節(jié)所消耗的時(shí)間醒叁。OkHttp 對(duì)外提供了EventListener接口司浪,不過(guò)在3.6.0版本沒(méi)有該api,所以升級(jí)到3.9.0. 在3.9.0版本把沼,發(fā)現(xiàn)有時(shí)會(huì)崩潰啊易,異常日志。該異常在于成功從連接緩存池中找到一個(gè)健康的連接通路后饮睬,進(jìn)行TLS連接時(shí)拋出的異常租谈,對(duì)于OkHttp的Tls握手連接細(xì)節(jié)請(qǐng)點(diǎn)擊這里。
解決辦法升級(jí)到3.14.9捆愁。日志請(qǐng)點(diǎn)擊這里割去。
-
OkHttp 網(wǎng)絡(luò)請(qǐng)求加密導(dǎo)致得Unexpected Char問(wèn)題
公司得項(xiàng)目最近在更改加解密方式,所以使用Okhttp攔截器對(duì)請(qǐng)求參數(shù)昼丑,請(qǐng)求body進(jìn)行新一輪加解密方式呻逆。
老加密方式為,將body與url中得參數(shù)進(jìn)行aes加密菩帝,由于aes是對(duì)稱加密咖城,根據(jù)key與iv就能進(jìn)行加密解密憔足,因此相對(duì)于RSA非對(duì)稱加密來(lái)說(shuō)并不安全。這里簡(jiǎn)單說(shuō)下酒繁。我們?cè)谑褂胊es加密得過(guò)程中滓彰,加密后又對(duì)其值進(jìn)行了base64編碼,這時(shí)候問(wèn)題就來(lái)了州袒。
String s =Base64.encodeToString("abc".getBytes(StandardCharsets.UTF_8),Base64.DEFAULT);
Base64.DEFAULT的屬性生成的最終編碼會(huì)帶上換行揭绑,只不過(guò)當(dāng)字符串長(zhǎng)度大于76會(huì)加上換行符,這時(shí)候比如編碼的是Json字符串郎哭,則會(huì)改變Json結(jié)構(gòu)他匪。由于我這邊將Json先用aes加密之后base64編碼有換行符,然后放到請(qǐng)求頭中導(dǎo)致后臺(tái)拿不到header對(duì)應(yīng)的value值所以爆出的問(wèn)題夸研。解決辦法:使用NO_WARP屬性邦蜜。
String s =Base64.encodeToString("abc".getBytes(StandardCharsets.UTF_8),Base64.NO_WRAP);
-
共用RecyclerPool導(dǎo)致viewHolder views must not be attached when created.錯(cuò)誤
protected RecyclerView orderListRecycler;
private final RecyclerView.RecycledViewPool mCachePool = new RecycledViewPool();
{
orderListRecycler.setRecycledViewPool(cachePool);
}
錯(cuò)誤日志
2021-02-01 15:47:03.691 7636-7636/com.xxx E/EHiError: java.lang.IllegalStateException: ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7080)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6235)
at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:288)
at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:345)
at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:361)
at androidx.recyclerview.widget.GapWorker.prefetch(GapWorker.java:368)
at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:399)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7560)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
解決辦法BaseQuickAdapter
@Override
protected BaseViewHolder onCreateDefViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_VIEW_TYPE_LOAD_MORE) {
// 原因在于這里邊 回調(diào)用多次LoadMoreView 但是這個(gè)LoadMoreView 實(shí)例只有一個(gè),之前添加過(guò)了亥至,若再次創(chuàng)建ViewHolder就會(huì)報(bào)錯(cuò)
BaseViewHolder holder = createBaseViewHolder(mLoadMoreView.getLoadMoreView());
// 禁用回收機(jī)制
holder.setIsRecyclable(false);
return holder;
}
final SelfOrderItemWidget widget = new SelfOrderItemWidget(mContext);
widget.setOrderItemBtnClick(mOrderItemBtnClick);
widget.setEntranceType(mEntranceType);
return createBaseViewHolder(widget);
}