捍卫您的应用

It all started from a crash reported in Firebase Crashlytics console. Its been happening over and over again and giving very different logs on each occurrence. As an android developer on that project, I had to think very carefully on what might be the cause of this crash.

这一切都是从Firebase Crashlytics控制台中报告的崩溃开始的。 它一遍又一遍地发生,并且在每次发生时给出非常不同的日志。 作为该项目的android开发人员,我不得不非常仔细地考虑一下导致崩溃的原因。

But before looking for the cause of the crash, reproducing the crash itself was important. I had to reproduce it somehow. Looking at the logs, I realized that there’s a globally static variable in the main Application class, which is initialized at the launch time of the app. And now this is null.

但是在寻找崩溃原因之前,重现崩溃本身很重要。 我不得不以某种方式重现它。 查看日志,我发现主Application类中有一个全局静态变量,该变量在Application启动时进行了初始化。 现在这是空的。

How is it possible? This is initialized the first thing in the app when it starts. How can that be null then?

这怎么可能? 启动时,这是应用程序中的第一件事。 那怎么能为空呢?

The answer lies in a simple word: Process Lifecycle. You can read more about it here.

答案在于一个简单的词: Process Lifecycle 。 您可以在此处了解更多信息。

Sometimes users press Home button to switch from your app to some other task they intend to do. Now your app is in background. Suddenly Android OS needs some memory and it looks in the inactive apps of the system. It sees that your app is in background for a while (say more than 30 minutes or so) and you have this big data stored as static variable in the memory. So, Android OS simply goes ahead and kills your app to reclaim that precious memory. And uses it for some other app in need.

有时,用户可以按“主页”按钮将您的应用程序切换到他们打算执行的其他任务。 现在,您的应用程序处于后台。 突然,Android OS需要一些内存,并且它会在系统的非活动应用程序中查找。 它会看到您的应用在后台运行了一段时间(例如超过30分钟左右),并且您已将这些大数据作为静态变量存储在内存中。 所以, Android OS只会继续前进,并杀死您的应用程序以回收宝贵的内存。 并将其用于其他需要的应用程序。

Now, user comes back to your app by opening it from the Recent Apps option, and boom 💣. Your current Activity is using static data and expects it as a non-null data. You get a NullPointerException . Firebase gets these crazy crashes with different logs each time because you never know at what screen user pressed home for some other app.

现在,用户可以通过从“最近使用的应用程序”选项中打开您的应用程序,然后重新打开boom。 您当前的活动正在使用静态数据,并且希望它是非空数据。 您得到一个NullPointerException 。 Firebase每次都会使用不同的日志来获取这些疯狂的崩溃,因为您永远不知道用户在什么屏幕上按下了其他应用程序。

You need to be able to simulate this scenario and test how your app behaves. And fix any issues/crashes. Because this is a very common thing users do on Android phones.

您需要能够模拟这种情况并测试您的应用程序的行为。 并解决所有问题/崩溃。 因为这是用户在Android手机上所做的非常普遍的事情。

但是,如何模拟这个? 您如何才能像Android OS那样手动杀死某些应用程序? (But, how can you simulate this? How can you kill some app like Android OS does manually?)

Android Studio comes with a simple button “Terminate Application” in the Logcat panel which kills your running debug app on emulator/device.

Android Studio在Logcat面板中带有一个简单的按钮“ Terminate Application” ,可终止您在模拟器/设备上正在运行的调试应用程序。

Image for post
Logcat panel in Android Studio with Terminate Application button
Android Studio中的Logcat面板,带有“终止应用程序”按钮

Usually, this helps in other cases but unfortunately not in this case. Pressing this button will completely kill your application process. And when you come back to the app from Recent Apps screen, the app will restart from the main launcher screen instead of the screen where user left it.

通常,这在其他情况下有帮助,但不幸的是在这种情况下没有帮助。 按下此按钮将完全终止您的申请过程。 当您从“最近使用的应用程序”屏幕返回到应用程序时,该应用程序将从主启动器屏幕而不是用户离开它的屏幕重新启动。

Simply go to the your Android SDK directory, and look for the platform-tools directory. And open the terminal there and type this command.

只需转到您的Android SDK目录,然后查找platform-tools目录。 并在那里打开终端并输入此命令。

adb shell am kill <PACKAGE_NAME>

It must be noted that your app should be in background while running this command. If its in focus and foreground state, nothing would happen.

必须注意,在运行此命令时,您的应用程序应处于后台。 如果它处于焦点和前景状态,则不会发生任何事情。

Please note that this is different than the command adb shell kill . That command kills the whole process of your app like the Terminate App button while adb shell am kill only kills processes that are safe to kill to reallocate resources such as memory, CPU etc.

请注意,这与命令不同 adb shell kill 该命令会杀死您的应用程序的整个过程,例如“终止应用程序”按钮,而 adb shell am kill 仅杀死可安全杀死以重新分配资源(例如内存,CPU等)的进程。

If this is implemented and handled by developers carefully, this could be the very useful functionality from the user’s perspective. Because user would gain trust to your app and its resume supported capability with confidence that no matter how much times user presses Home, your app will remain intact and bold.

如果开发人员仔细实施和处理,从用户的角度来看这可能是非常有用的功能。 因为用户将有信心获得对您的应用及其恢复支持功能的信任,所以无论用户按Home键有多少次,您的应用都将保持完整和粗体。

Here’s a simpler form of how can you simulate this case for debugging:

这是如何模拟这种情况以进行调试的简单形式:

Image for post
The summary of “adb shell kill” and “adb shell am kill” commands — Image Credits: https://twitter.com/WajahatKarim/status/1240675083003334656
“ adb shell kill”和“ adb shell am kill”命令的摘要—图片来源: https : //twitter.com/WajahatKarim/status/1240675083003334656

开发人员的关键要点和操作 (Key Takeaways & Actions for Developers)

Taking from the Process Lifecycle guide from Android Developers website:

摘自Android Developers网站上的Process Lifecycle指南

An unusual and fundamental feature of Android is that an application process’s lifetime is not directly controlled by the application itself. Instead, it is determined by the system through a combination of the parts of the application that the system knows are running, how important these things are to the user, and how much overall memory is available in the system.

Android的一个不寻常的基本特征是应用程序进程的生命周期不受应用程序本身的直接控制。 而是由系统通过系统知道正在运行的应用程序各部分的组合,这些事情对用户的重要性以及系统中可用的总内存量的组合来确定。

It is important that application developers understand how different application components (in particular Activity, Service, and BroadcastReceiver) impact the lifetime of the application’s process.

重要的是 应用程序开发人员了解不同的应用程序组件(尤其是 活动 服务 广播接收器 )影响应用程序流程的生命周期。

As an android developer, you need to make sure that your app doesn’t crash or behave unexpectedly when Android OS reclaims some resources it while in the background or inactive state.

作为一名Android开发人员,您需要确保当Android OS在后台或非活动状态下回收其某些资源时,您的应用不会崩溃或出现异常行为。

Here are some suggestions on how you can handle this case.

以下是一些有关如何处理这种情况的建议。

  1. Use the onSaveInstanceState() and onRestoreInstanceState() methods to save the important data of your app before getting destroyed and loading it when user has brought the app back in focus.** This is the most recommended method to handle this case.** You can also store data in SharedPreferences , or local SQLite database if the data can't be stored in a simple Bundle object. Usually, making your data Parcelable can help you save/restore it without much persistent storage hassle.

    使用 onSaveInstanceState() onRestoreInstanceState() 一种方法,可以在销毁应用程序之前保存应用程序的重要数据,并在用户重新关注应用程序时加载它们。 **这是处理这种情况的最推荐方法。**如果不能将数据存储在简单的Bundle对象中,也可以将数据存储在SharedPreferences或本地SQLite数据库中。 通常,使数据可Parcelable可帮助您保存/恢复数据,而无需太多持久的存储麻烦。

  2. You can also refresh data from the source such as network call to make sure that your data is valid and not null or corrupted. This depends on the app itself and its requirements, but this maybe a heavy option from the CPU or network perspective.

    您还可以从诸如网络呼叫之类的源刷新数据,以确保您的数据有效且不为null或损坏。 这取决于应用程序本身及其要求,但是从CPU或网络角度来看,这可能是一个沉重的选择。
  3. Or you simply restart the whole app from launcher screen. This won’t look good from the user experience as this would be unexpected to user and will make the purpose of putting your app in Recent Apps useless.

    或者您只是从启动器屏幕重新启动整个应用程序。 从用户体验来看,这看起来不太好,因为这对用户而言是意外的 并使将您的应用程序放入“最新应用程序”的目的毫无用处。

Have fun and use your code for good. 🙏

玩得开心,永远使用您的代码。 🙏

At the end, please Subscribe to my newsletter DroidUp to get more tutorials and tips on Android development directly in your inbox.

最后,请订阅我的时事通讯DroidUp ,直接在收件箱中获取有关Android开发的更多教程和技巧。

If you liked this article, you can read my new articles below:

如果您喜欢这篇文章,可以在下面阅读我的新文章:

Wajahat Karim is a graduate from NUST, Islamabad, an experienced mobile developer, an active open source contributor, and co-author of two books Learning Android Intents and Mastering Android Game Development with Unity. In his spare time, he likes to spend time with his family, do experiments on coding, loves to write about lots of things (mostly on blog and medium) and is passionate contributor to open source. In June 2018, one of his library became #1 on Github Trending. His libraries have about 2000 stars on Github and are being used in various apps by the developers all around the globe. Follow him on Twitter and Medium to get more updates about his work in Writing, Android and Open Source.

Wajahat Karim毕业于NUST伊斯兰堡 ,是一位经验丰富的移动开发人员,一名积极的开源贡献者,并且还是两本书的合著者之一,这两本书《 学习Android意图与Unity掌握Android游戏开发》 。 在业余时间里,他喜欢与家人共度时光,进行编码实验,喜欢写很多东西(主要是在博客和媒体上),并且是开源的热情贡献者。 2018年6月,他的图书馆之一成为Github Trending的第一名。 他的图书馆在Github上拥有大约2000颗星星,并且被全球的开发人员用于各种应用程序中。 在TwitterMedium上关注他,以获取有关他在Writing,Android和Open Source中的工作的更多更新。

Also, if you have any questions you’d like him to answer, contact him through his website at wajahatkarim.com with DEAR WAJAHAT in the subject line.

另外,如果您有任何问题要他回答,请通过他的网站wajahatkarim.com与他联系,主题行为DEAR WAJAHAT。

Originally published at https://wajahatkarim.com on March 19, 2020.

最初于 2020年3月19日 发布在 https://wajahatkarim.com 上。

翻译自: https://android.jlelse.eu/defending-your-app-310428698cfe

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值