繁体   English   中英

有没有办法处理在暂停状态下被杀死的 Android 应用程序,以使系统自动重新启动活动?

[英]Is there a way to handle an Android app getting killed in the Paused state to make the system restart the activity automatically?

我有一个 Ionic/Cordova 应用程序,它大量使用了相机插件,并且在 Android 上运行时出现问题。

Android Activity Lifecycle docs docs 和Cordova's Lifecycle Docs for the Android Platform 中所述,当相机进入前台时,应用程序的主要 Activity 在后台进入pausedstopped状态。

虽然主要活动处于这两种状态中的任何一种,但如果它需要释放内存,它很容易被系统杀死,这种情况在这个应用程序中经常发生,足以让用户抱怨。

在测试中,我看到有时应用程序会在关闭相机时自行重新启动,而有时活动似乎已经消失在相机后面,因此当用户关闭它时,应用程序似乎崩溃了。

阅读上述文档后,我向 Cordova 生成的MainActivty android 代码添加了一些日志记录,以便我可以在使用相机时观察状态变化,并且还向onCreate添加了一个未捕获的异常处理程序:

public class MainActivity extends CordovaActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.e("com.xxx.test", "onCreate");
        super.onCreate(savedInstanceState);

        // enable Cordova apps to be started in the background
        Bundle extras = getIntent().getExtras();

        if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
            moveTaskToBack(true);
        }

        // Set by <content src="index.html" /> in config.xml
        loadUrl(launchUrl);


        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
                Log.e("com.xxx.test", "onCreate, UNCAUGHT EXCEPTION!!!");
                System.exit(2);
            }
        });
    }

    @Override
    public void onStart() {
        Log.e("com.xxx.test", "onStart");
        super.onStart();

    }

    @Override
    public void onResume() {
        Log.e("com.xxx.test", "onResume");
        super.onResume();

    }

    @Override
    public void onPause() {
        Log.e("com.xxx.test", "onPause");
        super.onPause();
    }

    @Override
    public void onStop() {
        Log.e("com.xxx.test", "onStop");
        super.onStop();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("com.xxx.test", "onDestroy");

    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        Log.e("com.xxx.test", "onSaveInstanceState");
        super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.e("com.xxx.test", "onRestoreInstanceState");
        super.onRestoreInstanceState(savedInstanceState);
    }
}

我运行应用程序并过滤包含“com.xxx.test”的行的logcat ,结果如下:

正常的相机操作

//camera button tapped
onPause
onSaveInstanceState
onStop

//user takes photo and dismisses camera
onResume

应用重启

到现在为止还挺好。 现在,如果应用程序在关闭相机时自行重新启动,我会得到:

//camera button tapped
onPause
onSaveInstanceState
onStop

//user takes photo and dismisses camera, app restarts itself here
onCreate
onStart
onRestoreInstanceState
onResume

当应用程序在后台终止活动时,这就是我希望看到的 - 当用户导航回它时,系统会重新创建活动并加载任何保存的状态等。这很好,我将能够处理中断.

碰撞

但是,有时日志会这样做:

//camera button tapped
onPause

//sometimes it stops here, sometimes I also get
onSaveInstanceState

//boom! - the app seems to have crashed and the user is taken back to the home screen when dismissing the camera

因此,我怀疑正在发生的是系统在活动进入stopped状态之前杀死了活动,并且没有保存重新启动它所需的任何参考。 然后,当我们尝试返回该活动时,系统不知道要加载什么,因此只退出应用程序。

不过,我不能 100% 确定情况确实如此。 也许应用程序在其他地方崩溃了,我只是不知道。

所以我的问题是,如果系统在 Android 上终止了处于paused状态的活动,我们是否应该能够根据需要再次导航回它,还是应用程序刚刚退出而用户必须重新启动它的标准行为? 如果是这样,是否有任何解决方法?

如果活动应该在不退出应用程序的情况下返回,我如何找出导致崩溃的原因? 我的异常处理程序永远不会被调用,我已经浏览了logcat所有行,但看不到任何可以让我了解问题所在的任何线索。

我已经使用分析器运行了该应用程序,尽管内存使用量高达 350 mb 左右,但似乎没有任何泄漏将其推得越来越高,因为拍摄的照片越来越多。

此外,似乎没有任何关于需要拍摄多少张照片来重现问题的常规模式,有时它会一遍又一遍地重新启动,有时它会一遍又一遍地退出,有时我会得到混合。

关于如何阻止这种类似行为的崩溃的任何想法都非常感谢。

编辑

我一直在浏览日志,试图拼凑出正在发生的事情的时间表:

所以用户点击应用程序中的相机按钮,主活动进入paused状态, onSaveInstanceState()被调用......

16:23:10.244 505-505/com.xxx.test I/chromium: [INFO:CONSOLE(24978)] "photo button tapped", source: file:///android_asset/www/build/main.js (24978)
16:23:10.315 505-505/com.xxx.test E/com.xxx.test: onPause
16:23:10.315 505-505/com.xxx.test D/CordovaActivity: Paused the activity.
16:23:10.322 505-505/com.xxx.test E/com.xxx.test: onSaveInstanceState

在此之后有一大堆相机设置日志,但其中主要活动死亡,大概是被系统杀死以释放资源......

16:23:11.415 3696-6128/? I/WindowManager: WIN DEATH: Window{2ae98e1d0 u0 com.xxx.test/com.xxx.test.MainActivity}
16:23:11.415 3696-4312/? I/ActivityManager: Process com.xxx.test (pid 505) has died(377,151)

尽管onSaveInstanceState()被调用,系统声称没有保存状态......

16:23:11.419 3696-4312/? W/ActivityManager: Force removing ActivityRecord{c76b13bd0 u0 com.xxx.test/.MainActivity t2117}: app died, no saved state

然后大约半秒后,系统为我的包名称启动一个新进程,而相机仍然可见......

16:23:12.047 3696-9816/? D/MountService: getExternalStorageMountMode : 3
    getExternalStorageMountMode : 3
    getExternalStorageMountMode : final mountMode=3, uid : 10298, packageName : com.xxx.test
16:23:12.061 3696-9816/? I/ActivityManager: Start proc 6426:com.xxx.test/u0a298 for content provider com.xxx.test/android.support.v4.content.FileProvider

尽管进程6426没有记录太多其他内容......

16:23:12.062 6426-6426/? E/Zygote: v2
16:23:12.062 6426-6426/? I/libpersona: KNOX_SDCARD checking this for 10298
16:23:12.063 6426-6426/? I/libpersona: KNOX_SDCARD not a persona
16:23:12.063 6426-6426/? E/Zygote: accessInfo : 0
16:23:12.064 6426-6426/? W/SELinux: SELinux selinux_android_compute_policy_index : Policy Index[2],  Con:u:r:zygote:s0 RAM:SEPF_SECMOBILE_7.0_0007, [-1 -1 -1 -1 0 1]
16:23:12.064 6426-6426/? I/SELinux: SELinux: seapp_context_lookup: seinfo=untrusted, level=s0:c512,c768, pkgname=com.xxx.test
16:23:12.069 6426-6426/? I/art: Late-enabling -Xcheck:jni
16:23:12.098 6426-6426/? D/TimaKeyStoreProvider: TimaKeyStore is not enabled: cannot add TimaSignature Service and generateKeyPair Service

就是这样。 不过很快, ActivityManager尝试将重新创建的ActivityManager移到前面......

16:23:12.212 3696-5435/? D/ActivityManager: moveToFront() : reason=startedActivity setFocusedActivity isAttached=true TaskRecord{d952617d0 #2117 A=com.xxx.test U=0 StackId=1 sz=2}
16:23:12.213 3696-5435/? D/InputDispatcher: Focused application set to: xxxx

然后相机暂停,即使它仍然对用户可见......

16:23:12.239 30891-30891/? V/Camera6: onPause

ActivityManager再次提到了我的包名...

16:23:12.558 3696-6838/? D/ActivityManager: resumeTopActivityInnerLocked() : #1 prevTask=TaskRecord{d952617d0 #2117 A=com.xxx.test U=0 StackId=1 sz=2} next=ActivityRecord{bc76899d0 u0 com.sec.android.app.camera/.AttachActivity t2117} mFocusedStack=ActivityStack{33a525ad0 stackId=1, 2 tasks}

不久之后, MultiScreenManagerService抱怨任务有多个活动, ActivityManager开始做更多的事情......

16:23:14.529 3696-13852/? W/MultiScreenManagerService: moveTaskBackToDisplayIfNeeded(): The task has more than one activity
16:23:14.530 3696-13852/? D/ActivityManager: moveToFront() : reason=finishActivity adjustFocus setFocusedActivity isAttached=true TaskRecord{d952617d0 #2117 A=com.xxx.test U=0 StackId=1 sz=2}
16:23:14.535 3696-13852/? D/InputDispatcher: Focused application set to: xxxx
16:23:14.536 3696-13852/? D/ActivityTrigger: ActivityTrigger activityPauseTrigger
16:23:14.544 30891-30891/? V/AttachActivity: onPause
16:23:14.545 3696-5485/? D/ActivityManager: setAppIconInfo(), x : 0, y : 0, width : 0, height : 0, isHomeItem : false
    resumeTopActivityInnerLocked() : #1 prevTask=TaskRecord{d952617d0 #2117 A=com.xxx.test U=0 StackId=1 sz=2} next=ActivityRecord{1da4823d0 u0 com.sec.android.app.camera/.Camera t2117} mFocusedStack=ActivityStack{33a525ad0 stackId=1, 2 tasks}

最终,相机被用户关闭...

16:23:14.634 30891-30891/? V/Camera6: finish

此时我们从MultiScreenManagerService收到更多投诉,现在ActivityManager想要将com.sec.android.app.launcher移到前面,我猜这是主屏幕......

16:23:14.637 3696-9814/? W/MultiScreenManagerService: moveTaskBackToDisplayIfNeeded(): root is not base activity
16:23:14.642 3696-9814/? D/ActivityManager: moveToFront() : reason=finishActivity adjustFocus setFocusedActivity isAttached=true TaskRecord{3965df5d0 #2072 A=com.sec.android.app.launcher U=0 StackId=0 sz=1}
16:23:14.642 3696-9814/? W/MultiScreenManagerService: moveTaskBackToDisplayIfNeeded(): root activity or app is null

ActivityManager还有另一个日志讨论了对相机的重复完成请求...

16:23:14.663 3696-6002/? W/ActivityManager: Duplicate finish request for ActivityRecord{1da4823d0 u0 com.sec.android.app.camera/.Camera t2117 f}

在这一点上,我假设正在显示主屏幕,但ActivityManager有另一个与我的包一起使用的...

16:23:14.715 3696-5436/? D/ActivityManager: resumeTopActivityInnerLocked() : #0 prevTask=TaskRecord{d952617d0 #2117 A=com.xxx.test U=0 StackId=1 sz=2} next=ActivityRecord{eed8739d0 u0 com.sec.android.app.launcher/.activities.LauncherActivity t2072} mFocusedStack=ActivityStack{514789d0 stackId=0, 2 tasks}
    applyOptionsLocked(), pendingOptions : null

但无济于事,然后对我的活动的唯一引用是偶尔的日志组,例如......

16:23:14.844 3696-3707/? D/PackageManager: getComponentMetadataForIconTray : com.xxx.test.MainActivity does not exist in mServices
    getComponentMetadataForIconTray : com.xxx.test.MainActivity does not exist in mProviders
    getComponentMetadataForIconTray : com.xxx.test.MainActivity does not exist in mReceivers
16:23:14.846 4439-4439/? I/ApplicationPackageManager: load=com.xxx.test, bg=192-192, dr=192-192, forDefault=true
    reset dr=192,192, bg=192,192

所以,问题就在那里。 系统似乎在摄像头仍然可见时尝试重新启动我的活动,这把事情搞砸了,但我仍然不确定发生了什么或我能做些什么。

有人有任何想法吗?

对于@mushishi78 和其他任何可能遇到此问题的人,我最终确实找出了我的问题所在。

在我的应用程序中,我使用了一个名为konva.js的绘图库,它允许用户用手指在 HTML 画布上创建绘图和图表。

我使用这个库制作的组件具有绘图的各个方面的属性,即主阶段、一些层、节点等,这些属性在初始化时被实例化。

经过大量测试后,我意识到相机的问题仅在用户与此图表组件交互后才发生,因此开始查看那里。

我注意到我没有明确地释放持有层引用的属性等等,因为我只是期望垃圾收集将其整理出来。

因此,对于每个属性,我都确保在ionViewWillLeave()时删除了可能存在的所有引用:

@Component({
  templateUrl: 'chamber-sketch-page.html'
})
    
export class ChamberSketchPage {

  // properties declared here

  @ViewChild('konvaStage') stageElementRef: ElementRef;

    stage: any;
    backgroundLayer: any;

  ...
}

...

ngAfterViewInit() {

  // properties instantiated here and listeners set up

  this.stage = new Konva.Stage({
    container: "konvaStage",
    width: window.innerWidth,
    height: window.innerWidth
  });

    this.backgroundLayer = new Konva.Layer({
        name: "background"
    });

  this.platform.ready().then(() => {
        window.addEventListener('native.keyboardshow', this.keyboardShowHandler);
        window.addEventListener('native.keyboardhide', this.keyboardHideHandler);
    });
}

...

ionViewWillLeave() : void {

  // clean up everything possible here - listeners, properties etc

  window.removeEventListener('native.keyboardshow', this.keyboardShowHandler);
  window.removeEventListener('native.keyboardhide', this.keyboardHideHandler);

  this.stage.destroy()
  this.stage = null

  this.backgroundLayer.destroy()
  this.backgroundLayer = null

  this.stageElementRef.nativeElement.remove();
}

我不知道究竟是哪个项目导致了问题,但是一旦我完成并确保在开始时创建的所有内容都在最后被删除,我的应用程序开始正常工作。

我的猜测是,在组件被解散后,存在一些不应该存在的引用,这在系统某处导致了一些奇怪的内存伏都教,也许是通过一个插件,这就是导致崩溃的原因。

我们永远不会知道,但我对遇到此问题的任何人的建议是确保在不再需要该组件时清理您实例化的任何对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM