三星设备拍照后App崩溃问题排查求助
我的App允许用户调用系统默认相机拍照,代码如下:
private void takePhoto(Uri outputUri) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); startActivityForResult(intent, TAKE_PHOTO_REQUEST_CODE); }
拍照后App恢复时频繁崩溃,抛出异常及堆栈信息如下:
Fatal Exception: android.view.ViewRootImpl$CalledFromWrongThreadException Only the original thread that created a view hierarchy can touch its views. android.view.ViewRootImpl.checkThread (ViewRootImpl.java:11379) android.view.ViewRootImpl.requestLayout (ViewRootImpl.java:2562) android.view.ViewRootImpl.updateConfiguration (ViewRootImpl.java:6324) android.app.ActivityThread.handleActivityConfigurationChanged (ActivityThread.java:6925) android.app.servertransaction.ActivityConfigurationChangeItem.execute (ActivityConfigurationChangeItem.java:53) android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45) android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:135) android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:95) android.app.ActivityThread$H.handleMessage (ActivityThread.java:2571) android.os.Handler.dispatchMessage (Handler.java:106) android.os.Looper.loopOnce (Looper.java:226) android.os.Looper.loop (Looper.java:313) android.app.ActivityThread.main (ActivityThread.java:8741) java.lang.reflect.Method.invoke (Method.java) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571) com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)
堆栈存在多种变体,但均涉及配置变更,推测是用户拍照时旋转设备,App切回前台时因方向变化触发崩溃。
我的App的Activity在清单文件中设置了android:configChanges="orientation|screenSize"(因包含WebView),但未实现onConfigurationChanged逻辑。排查时添加日志发现,崩溃前onConfigurationChanged在主线程调用(与onCreate同线程),但onStart和onResume未触发。
该崩溃仅出现在多款三星手机/平板机型上,未找到同类问题报告,恳请提供排查思路或解决方案!
补充:更多堆栈变体
Fatal Exception: android.view.ViewRootImpl$CalledFromWrongThreadException Only the original thread that created a view hierarchy can touch its views. android.view.ViewRootImpl.checkThread (ViewRootImpl.java:11586) android.view.ViewRootImpl.requestLayout (ViewRootImpl.java:2648) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.widget.TextView.onConfigurationChanged (TextView.java:4706) android.view.View.dispatchConfigurationChanged (View.java:16145) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewRootImpl.updateConfiguration (ViewRootImpl.java:6502) android.app.ActivityThread.handleActivityConfigurationChanged (ActivityThread.java:6941) android.app.servertransaction.ActivityConfigurationChangeItem.execute (ActivityConfigurationChangeItem.java:53) android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45) android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:135) android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:95) android.app.ActivityThread$H.handleMessage (ActivityThread.java:2574) android.os.Handler.dispatchMessage (Handler.java:106) android.os.Looper.loopOnce (Looper.java:226) android.os.Looper.loop (Looper.java:313) android.app.ActivityThread.main (ActivityThread.java:8757) java.lang.reflect.Method.invoke (Method.java) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571) com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)
Fatal Exception: android.view.ViewRootImpl$CalledFromWrongThreadException Only the original thread that created a view hierarchy can touch its views. android.view.ViewRootImpl.checkThread (ViewRootImpl.java:11586) android.view.ViewRootImpl.requestLayout (ViewRootImpl.java:2648) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.view.View.requestLayout (View.java:27612) android.widget.TextView.onConfigurationChanged (TextView.java:4706) android.view.View.dispatchConfigurationChanged (View.java:16145) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewGroup.dispatchConfigurationChanged (ViewGroup.java:1654) android.view.ViewRootImpl.updateConfiguration (ViewRootImpl.java:6502) android.app.ActivityThread.handleActivityConfigurationChanged (ActivityThread.java:6941) android.app.ActivityThread$ActivityClientRecord$1.onConfigurationChanged (ActivityThread.java:797) android.view.ViewRootImpl.performConfigurationChange (ViewRootImpl.java:6462) android.view.ViewRootImpl.handleResized (ViewRootImpl.java:2424) android.view.ViewRootImpl.-$$Nest$mhandleResized android.view.ViewRootImpl$ViewRootHandler.handleMessageImpl (ViewRootImpl.java:6728) android.view.ViewRootImpl$ViewRootHandler.handleMessage (ViewRootImpl.java:6697) android.os.Handler.dispatchMessage (Handler.java:106) android.os.Looper.loopOnce (Looper.java:226) android.os.Looper.loop (Looper.java:313) android.app.ActivityThread.main (ActivityThread.java:8757) java.lang.reflect.Method.invoke (Method.java) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571) com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)
1. 补全onConfigurationChanged实现
虽然官方文档说明设置configChanges后不重写该方法系统会默认处理布局,但三星定制ROM可能存在特殊逻辑导致异常。补写方法并确保视图操作在主线程执行:
@Override public void onConfigurationChanged(Configuration newConfig) { // 强制在主线程执行配置变更处理 if (Looper.myLooper() != Looper.getMainLooper()) { runOnUiThread(() -> super.onConfigurationChanged(newConfig)); return; } super.onConfigurationChanged(newConfig); // 针对WebView添加适配逻辑(如果有) if (mWebView != null) { mWebView.destroy(); initWebView(); // 重新初始化WebView适配新配置 } }
2. 拍照时锁定Activity方向
拍照过程中锁定当前方向,避免配置变更触发异常,拍照完成后再解锁:
private void takePhoto(Uri outputUri) { // 锁定当前屏幕方向 int currentOrientation = getResources().getConfiguration().orientation; setRequestedOrientation(currentOrientation); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); startActivityForResult(intent, TAKE_PHOTO_REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == TAKE_PHOTO_REQUEST_CODE) { // 恢复自动方向适配 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); // 处理拍照结果逻辑 } }
3. 强制主线程调度视图更新
从堆栈看,ViewRootImpl.updateConfiguration触发requestLayout时线程校验失败,推测三星系统可能将部分视图操作调度到非主线程。可以在onConfigurationChanged中延迟或强制主线程执行视图刷新:
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 延迟执行视图更新,规避线程异常 post(() -> { // 刷新关键视图,比如TextView、RecyclerView等 if (mTextView != null) { mTextView.requestLayout(); } }); }
4. 升级WebView相关依赖
如果项目使用了自定义WebView或第三方WebView库,检查版本兼容性,三星设备的WebView可能存在特殊适配逻辑,升级到最新稳定版尝试解决问题。
5. 排查三星ROM定制行为
在三星设备上打印详细线程日志,对比onConfigurationChanged与onCreate的线程ID,确认ViewRootImpl的创建线程是否存在系统层面的调度异常。若确认是三星ROM问题,可向三星开发者平台反馈,同时保留兼容处理逻辑。
内容的提问来源于stack exchange,提问作者Tako

