{"id":19372936,"url":"https://github.com/android-notes/cockroach","last_synced_at":"2025-05-15T03:06:23.902Z","repository":{"id":41381607,"uuid":"81901842","full_name":"android-notes/Cockroach","owner":"android-notes","description":"降低Android非必要crash","archived":false,"fork":false,"pushed_at":"2023-05-28T16:36:02.000Z","size":6579,"stargazers_count":3256,"open_issues_count":7,"forks_count":453,"subscribers_count":84,"default_branch":"X","last_synced_at":"2025-04-14T12:58:26.385Z","etag":null,"topics":["android","crash"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/android-notes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-14T03:43:51.000Z","updated_at":"2025-04-14T03:13:11.000Z","dependencies_parsed_at":"2024-11-10T08:25:54.626Z","dependency_job_id":"aa1e1013-dfae-465d-9ced-6a91fdaf2caf","html_url":"https://github.com/android-notes/Cockroach","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/android-notes%2FCockroach","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/android-notes%2FCockroach/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/android-notes%2FCockroach/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/android-notes%2FCockroach/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/android-notes","download_url":"https://codeload.github.com/android-notes/Cockroach/tar.gz/refs/heads/X","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254264765,"owners_count":22041793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","crash"],"created_at":"2024-11-10T08:25:47.509Z","updated_at":"2025-05-15T03:06:18.878Z","avatar_url":"https://github.com/android-notes.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":" \n\n## Cockroach 2.0\n\n\n\n## 为什么开发这个库\n很多时候由于一些微不足道的bug导致app崩溃很可惜，android默认的异常杀进程机制简单粗暴，但很多时候让app崩溃其实也并不能解决问题。\n\n有些bug可能是系统bug，对于这些难以预料的系统bug我们不好绕过，还有一些bug是我们自己编码造成的，对于有些bug来说直接忽略掉的话可能只是导致部分不重要的功能没法使用而已，又或者对用户来说完全没有影响，这种情况总比每次都崩溃要好很多。\n\n下面介绍几个真实案例来说明这个库的优势：\n\n* 有一款特殊的手机，每次开启某个Activity时都报错，提示没有在清单中声明，但其他几百万机型都没问题，这种情况很可能就是系统bug了，由于是在onclick回调里直接使用startActivity来开启Activity，onclick里没有其他逻辑，对于这种情况的话直接忽略掉是最好的选择，因为onclick回调是在一个单独的message中的，执行完了该message就接着执行下一个message，该message执行不完也不会影响下一个message的执行，调用startactivity后会同步等待ams返回的错误码，结果这款特殊的机型返回了没有声明这个Activity，所以对于这种情况可以直接忽略掉，唯一的影响就是这个Activity不会显示，就跟没有调用onClick一样\n\n* 我们在app中集成了个三方的数据统计库，这个库是在Application的onCreate的最后初始化的，但上线后执行初始化时却崩溃了，对于这种情况直接忽略掉也是最好的选择。根据app的启动流程来分析，Application的创建以及onCreate方法的调用都是在同一个message中执行的，该message执行的最后调用了Application的onCreate方法，又由于这个数据统计库是在onCreate的最后才初始化的，所以直接忽略的话也没有影响，就跟没有初始化过一样\n\n* 我们做了个检查app是否需要升级的功能，若需要升级，则使用context开启一个dialog风格的Activity提示是否需要升级，测试阶段没有任何问题，但一上线就崩溃了，提示没有设置FLAG_ACTIVITY_NEW_TASK,由于启动Activity的context是Application，但在高版本android中，可以使用Application启动Activity并且不设置这个FLAG，但在低版本中必须要设置这个FLAG，对于这种问题也可以直接忽略\n\n  API28 ContextImpl startActivity源码\n ```java\n  public void startActivity(Intent intent, Bundle options) {\n        warnIfCallingFromSystemProcess();\n\n        // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is\n        // generally not allowed, except if the caller specifies the task id the activity should\n        // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We\n        // maintain this for backwards compatibility.\n        final int targetSdkVersion = getApplicationInfo().targetSdkVersion;\n\n        if ((intent.getFlags() \u0026 Intent.FLAG_ACTIVITY_NEW_TASK) == 0\n                \u0026\u0026 (targetSdkVersion \u003c Build.VERSION_CODES.N\n                        || targetSdkVersion \u003e= Build.VERSION_CODES.P)\n                \u0026\u0026 (options == null\n                        || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {\n            throw new AndroidRuntimeException(\n                    \"Calling startActivity() from outside of an Activity \"\n                            + \" context requires the FLAG_ACTIVITY_NEW_TASK flag.\"\n                            + \" Is this really what you want?\");\n        }\n        mMainThread.getInstrumentation().execStartActivity(\n                getOuterContext(), mMainThread.getApplicationThread(), null,\n                (Activity) null, intent, -1, options);\n    }\n ```\n\n* 还有各种执行onclick时触发的异常，这些很多时候都是可以直接忽略掉的\n\n### 更新日志\n* 修复Android P反射限制导致的Activity生命周期异常无法finish Activity问题 \n\n[cockroach1.0版在这](https://github.com/android-notes/Cockroach/tree/master)\n\n### Cockroach 2.0新特性\n* Cockroach 2.0减少了Cockroach 1.0版本中Activity生命周期中抛出异常黑屏的问题。\n* Cockroach 1.0未雨绸缪，提前做好准备，等待异常到来。Cockroach 2.0马后炮，只有当抛出异常时才去拯救。\n* Cockroach 2.0试图在APP即将崩溃时尽量去挽救，不至于情况更糟糕。\n\n\n用一张图片来形容就是\n\n![img](https://github.com/android-notes/Cockroach/blob/X/wanjiu.jpeg?raw=true)\n\n\n\u003e特别注意： 当view的measure,layout,draw，以及recyclerview的bindviewholder 方法抛出异常时会导致\nviewrootimpl挂掉，此时会回调 onMayBeBlackScreen 方法，建议直接杀死app。目前可以拦截到抛出异常的ViewRootImpl，具体参考这https://github.com/android-notes/SwissArmyKnife/blob/master/saklib/src/main/java/com/wanjian/sak/system/traversals/ViewTraversalsCompact.java\n\n## 使用姿势\n\n* 必须要在Application初始化时装载\n\n例如：\n\n```java\n  \n    package com.wanjian.demo;\n    \n    import android.app.Application;\n    import android.os.Handler;\n    import android.os.Looper;\n    import android.util.Log;\n    import android.widget.Toast;\n    \n    import com.wanjian.cockroach.Cockroach;\n    \n    /**\n     * Created by wanjian on 2018/5/19.\n     */\n    \n    public class App extends Application {\n    \n        @Override\n        public void onCreate() {\n            super.onCreate();\n            install();\n        }\n    \n    \n        private void install() {\n            Cockroach.install(new ExceptionHandler() {\n                       @Override\n                       protected void onUncaughtExceptionHappened(Thread thread, Throwable throwable) {\n                           Log.e(\"AndroidRuntime\", \"---\u003eonUncaughtExceptionHappened:\" + thread + \"\u003c---\", throwable);\n                           new Handler(Looper.getMainLooper()).post(new Runnable() {\n                               @Override\n                               public void run() {\n                                   toast.setText(R.string.safe_mode_excep_tips);\n                                   toast.show();\n                               }\n                           });\n                       }\n           \n                       @Override\n                       protected void onBandageExceptionHappened(Throwable throwable) {\n                           throwable.printStackTrace();//打印警告级别log，该throwable可能是最开始的bug导致的，无需关心\n                           toast.setText(\"Cockroach Worked\");\n                           toast.show();\n                       }\n           \n                       @Override\n                       protected void onEnterSafeMode() {\n                           int tips = R.string.safe_mode_tips;\n                           Toast.makeText(App.this, getResources().getString(tips), Toast.LENGTH_LONG).show();\n                       \n                       }\n           \n                       @Override\n                       protected void onMayBeBlackScreen(Throwable e) {\n                           Thread thread = Looper.getMainLooper().getThread();\n                           Log.e(\"AndroidRuntime\", \"---\u003eonUncaughtExceptionHappened:\" + thread + \"\u003c---\", e);\n                           //黑屏时建议直接杀死app\n                           sysExcepHandler.uncaughtException(thread, new RuntimeException(\"black screen\"));\n                       }\n           \n                   });\n    \n        }\n    }\n    \n\n```\n\n\n## 原理分析\n\ncockroach2.0通过替换`ActivityThread.mH.mCallback`，实现拦截Activity生命周期，\n通过调用ActivityManager的`finishActivity`结束掉生命周期抛出异常的Activity\n\n\n\n\n\n相关视频 \n[https://github.com/android-notes/Cockroach/blob/master/cockroach.mp4?raw=true](https://github.com/android-notes/Cockroach/blob/master/cockroach.mp4?raw=true)\n\n \n\n \n[相关原理分析](https://github.com/android-notes/Cockroach/blob/master/%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)\n\n[相关连接](https://github.com/android-notes/Cockroach/tree/master)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandroid-notes%2Fcockroach","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandroid-notes%2Fcockroach","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandroid-notes%2Fcockroach/lists"}