You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

三星设备拍照后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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 09:17:08