android程序崩溃_Android上意外的应用程序崩溃以及如何处理它们

android程序崩溃

If there’s anything common among all the developers out there, be it front-end, back-end, or even game developers, it is that we hate production bugs. Especially when these bugs result in apps crashing. It is an unpleasant experience to monitor these increasing crashes in your application when you have recently rolled out to production.

如果前端,后端甚至游戏开发人员之间存在共同点,那就是我们讨厌生产错误。 尤其是当这些错误导致应用崩溃时。 最近刚投入生产时,监视应用程序中这些增加的崩溃是一种令人不愉快的体验。

Some crashes are encountered because of the system/platform it runs on irrespective of the business logic of the app. In Android, you might have encountered a crash when you resume the app from the background state. Such crashes are unexpected and difficult to understand or process by just looking at the crash logs.

无论应用程序的业务逻辑如何,都会由于运行的系统/平台而导致某些崩溃。 在Android中,从后台状态恢复应用程序时可能会遇到崩溃。 这样的崩溃是意外的,仅通过查看崩溃日志就很难理解或处理。

This article talks about such issues and ways to solve them.

本文讨论了此类问题及其解决方法。

问题 (The Problem)

While monitoring crash logs in production, I noticed some issues that were increasing day by day. The app seemed to work fine in normal testing conditions and the crash log was not replicable. This was until the application was brought back from the background tasks.

在监视生产中的崩溃日志时,我注意到一些问题每天都在增加。 该应用在正常测试条件下似乎运行良好,并且崩溃日志不可复制。 直到应用程序从后台任务中撤回。

Every android app runs in its own process and this process has been allocated some memory by the Operating System. When your app is put in the background while the user interacts with other applications, the OS can kill your app process if there’s not enough memory available for your application. This generally occurs when another app is being run in the foreground which demands greater phone memory (RAM).

每个android应用程序都在其自己的进程中运行,并且操作系统已为该进程分配了一些内存。 当用户与其他应用程序交互时将应用程序置于后台时,如果没有足够的内存可用于应用程序,则操作系统会终止您的应用程序进程。 当另一个应用程序在前台运行且需要更大的电话内存(RAM)时,通常会发生这种情况。

When the app process is killed, all the singleton objects and temporary data are also lost. Now, when you return back to your app, the system will create a new process and your app will resume from the activity that was at the top of your stack.

当应用程序进程终止时,所有单例对象和临时数据也会丢失。 现在,当您返回到应用程序时,系统将创建一个新流程,并且您的应用程序将从堆栈顶部的活动中恢复。

Since all your singleton objects were lost at this point, when the activity tries to access the same objects, the app crashes resulting in a NullPointerException.

由于此时您所有的单例对象都丢失了,因此当活动尝试访问相同的对象时,应用程序崩溃导致NullPointerException。

This is a problem and before we move on to the solution, let us replicate this scenario.

这是一个问题,在我们继续解决方案之前,让我们复制这种情况。

复制崩溃 (Replicating the Crash)

  1. Go ahead and run any of your applications from Android Studio in an emulator or an actual device connected by a USB cable.

    继续并在仿真器或通过USB电缆连接的实际设备上从Android Studio运行任何应用程序。
  2. Navigate to a random screen and press the ‘home’ button

    导航到随机屏幕,然后按“主页”按钮
  3. Open terminal and type the following command to get the process ID (PID) of your application

    打开终端,然后键入以下命令以获取您的应用程序的进程ID(PID)
adb shell pidof com.darktheme.example

The syntax for this command is ‘adb shell pidof *APP_BUNDLE_ID*’

该命令的语法为“ adb shell pidof * APP_BUNDLE_ID *”

Note the PID that you see on the terminal window. (This can be used to verify whether the existing app process was killed and a new process was started when we resume the app)

注意您在终端窗口上看到的PID。 (这可以用来验证现有的应用程序进程是否被终止,并且在我们恢复应用程序时是否启动了新的进程)

4. Type the following terminal command to kill your app process

4.键入以下终端命令以终止您的应用程序进程

adb shell am kill com.darktheme.example

At this point, your terminal windows should look like this:

此时,您的终端窗口应如下所示:

Image for post
Process kill commands entered in terminal
在终端中输入处理终止命令

Now open your app from the background tasks and check whether the app crashes. If yes, don’t worry we will discuss how to deal with this issue in the next section. If not, give yourself a pat on the back because you deserve it :)

现在,从后台任务中打开您的应用程序,并检查该应用程序是否崩溃。 如果是,请不要担心,我们将在下一部分中讨论如何处理此问题。 如果没有,请拍一下自己的背,因为你应该得到它:)

Note: After opening the app from the background, check the PID of the new process. If the PID that you noted down in Step 3 and the new PID are equal, the process was never killed.

注意:从后台打开应用后,请检查新进程的PID。 如果您在第3步中记下的PID与新的PID相等,则该过程永远不会终止。

拟议的解决方案 (Proposed Solution)

There are two ways of dealing with this issue. Depending upon the situation that you are in, you can decide which one to go forward with:

有两种方法可以解决此问题。 根据您所处的情况,您可以决定前进哪一个:

解决方案1: (Solution 1:)

An easy and convenient solution would be to check whether our existing app process was killed and recreated when the user resumes the app from the background. If yes, you can navigate back to the launch screen, so that it appears like a fresh app launch scenario.

一个简单方便的解决方案是,当用户从后台恢复应用程序时,检查我们现有的应用程序进程是否被杀死并重新创建。 如果是,则可以导航回到启动屏幕,使其看起来像是全新的应用程序启动方案。

You can place this following code in your BaseActivity:

您可以将以下代码放在BaseActivity中:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState != null) {
            // Get current PID
            val currentPID = android.os.Process.myPid().toString()
            
            // Check current PID with old PID
            if (currentPID != savedInstanceState.getString(PID)) {
                
                // If current PID and old PID are not equal, new process was created, restart the app from SplashActivity
                val intent = Intent(applicationContext, SplashActivity::class.java)
                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                startActivity(intent)
                finish()
            }
        }
    }


    override fun onSaveInstanceState(bundle: Bundle) {
        super.onSaveInstanceState(bundle)
        bundle.putString(PID, android.os.Process.myPid().toString())
    }
  • Save your PID in a bundle by overriding ‘onSaveInstanceState’ function.

    通过覆盖“ onSaveInstanceState”功能将您的PID保存在捆绑中。
  • In the ‘onCreate’ method, compare the current PID and the PID from the bundle.

    在“ onCreate”方法中,比较当前PID和捆绑软件中的PID。
  • Redirect to the Splash Activity, if the process was recreated.

    如果重新创建了流程,则重定向到Splash Activity。

When the user navigates back to the app from the background, if the app process was killed, the app would restart from the SplashActivity as if it is a fresh app launch.

当用户从后台导航回该应用程序时,如果该应用程序进程被终止,则该应用程序将从SplashActivity重新启动,就好像它是一个全新的应用程序启动一样。

This will prevent the app from accessing any resources that might have been lost during the process recreation and hence prevent the app from crashing.

这将防止应用程序访问在重新创建过程中可能丢失的任何资源,从而防止应用程序崩溃。

While this solution would prevent a crash, this approach restarts the app rather than resuming the app from where it was left off. If you are facing this issue in a production app and are desperately looking for a quick fix, this solution should work fine for you.

尽管此解决方案可以防止崩溃,但是这种方法可以重新启动应用程序,而不是从停止的位置恢复应用程序。 如果您在生产应用程序中遇到此问题,并且急切地希望快速解决问题,则此解决方案应该对您来说很好。

However, if you have recently started development from scratch, solution 2 would be ideal for you since it will resume the app from where it was left off

但是,如果您最近刚从头开始开发,则解决方案2将是您的理想选择,因为它将从中断的位置恢复应用程序

解决方案2: (Solution 2:)

By now, you must have noticed that you can save and access data from ‘Bundle’ objects. Save all the necessary information in each Activity/Fragment similar to how we have done in the previous example.

到目前为止,您必须已经注意到可以保存和访问“捆绑”对象中的数据。 与前面的示例类似,将所有必要的信息保存在每个活动/片段中。

Since we are accessing data that was saved in a bundle, the app crash should be prevented and the app should resume from where it was left off. All the other Activities/Fragments would also be recreated.

由于我们正在访问保存在捆绑软件中的数据,因此应避免应用程序崩溃,并且该应用程序应从中断处恢复。 所有其他活动/片段也将重新创建。

For a RecyclerView in a Fragment, it would look something like this:

对于片段中的RecyclerView,它看起来像这样:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val recyclerView = view.findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(context)
        users = savedInstanceState?.getParcelableArrayList("userList") ?: viewModel.getUsers()
        rv.adapter = DataAdapter(users, this)
    }


    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putParcelableArrayList("userList", users as ArrayList)
    }
  • Save the required information in a Bundle object by overriding the ‘onSaveInstanceState’ function.

    通过覆盖“ onSaveInstanceState”功能将所需信息保存在Bundle对象中。
  • Check whether data from the bundle is available in ‘onViewCreated’ function, otherwise, get data from source through ViewModel.

    检查“ onViewCreated”函数中捆绑包中的数据是否可用,否则,通过ViewModel从源获取数据。

结论 (Conclusion)

App Crashes due to process-kill are very common on the Android platform. With newer Android versions, it is observed that background apps are killed aggressively to save on phone battery.

在Android平台上,常见的由于处理失败而导致的应用崩溃。 使用较新的Android版本,可以观察到后台应用程序被大量杀害以节省手机电量。

Solution 1 can work as a quick fix to your existing production crashes.

解决方案1可以快速解决您现有的生产崩溃问题。

However, I would suggest Solution 2 if you are developing an app from scratch since it ensures that the app is resumed from where it was previously left off. Hence resulting in a better user experience.

但是,如果您要从头开始开发应用程序,我建议使用解决方案2,因为它可以确保从先前停止的位置恢复该应用程序。 因此导致更好的用户体验。

Investing the root cause of such crashes could become difficult so I hope this article has helped you in any way possible. Let me know what you guys think about the solutions that we discussed.

研究此类崩溃的根本原因可能会变得困难,因此我希望本文能以任何可能的方式对您有所帮助。 让我知道你们对我们讨论的解决方案有何看法。

That’s all for this article. Happy Coding!

这就是本文的全部内容。 编码愉快!

翻译自: https://levelup.gitconnected.com/unexpected-app-crashes-on-android-and-how-to-deal-with-them-c5d07512d99f

android程序崩溃

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值