更新后Android应用程序崩溃,Google Play上打开

时间:2016-07-02 10:32:01

标签: android race-condition onresume

我在Google Play上有一个应用程序,如果应用程序更新后再由用户重新打开,则应用程序会在后台崩溃。崩溃有时只会发生,这暗示某个地方会出现竞争。我很难调试这个并且自己只能重现这几次,尽管我有一些用户报告的堆栈跟踪。堆栈跟踪永远不会指向代码中的同一行,但堆栈跟踪始终通过Android中的performResumeActivity()输入我的代码,因此在我的代码中输入onResume()。请注意,这一点从未发生过,或者用户在任何其他上下文中报告的情况都比更新应用程序时要多(据我所知)。这并不意味着它只在更新应用程序时才会发生,但这是我看到崩溃发生的唯一方法。

(有时)重现的步骤:

  1. 安装旧的应用版本,例如adb install app-release-previous-version.apk
  2. 打开应用程序,稍微玩一下,然后最小化应用程序。
  3. 打开Goog​​le Play并将应用更新到最新版本。
  4. 从Google Play打开应用程序(我们可以在更新后从那里打开它)。
  5. 该应用不时在performResumeActivity() / onResume()崩溃。
  6. 我将添加发生崩溃的Activity的代码。不会粘贴此崩溃的非重要代码,但请告诉我是否应添加更多代码。我在问题的最后粘贴了各种堆栈痕迹。

    MyActivity(仅限重要部分)

    public class MyActivity extends AppCompatActivity
    {
        private MyMainFragment          myMainFragment          = null;
        private FilteredRecipes         myFilteredRecipes       = null;
        private MyShoppingList          myShoppingList          = null;
        private boolean mShouldReadAutomaticSave = false;
    
        // <snip> other members
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            mShouldReadAutomaticSave = savedInstanceState == null;
            setContentView(R.layout.my_activity);
    
            // Setup Toolbar / ActionBar.
            Toolbar toolbar = (Toolbar) findViewById(R.id.my_main_toolbar);
            setSupportActionBar(toolbar);
    
            NavigationView navigationView = (NavigationView) findViewById(R.id.my_navigationview);
            if (navigationView != null)
            {
                // <snip> Setup navigationView
            }
    
            DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.my_drawer_layout);
            if (drawerLayout != null)
            {
               // <snip> Setup DrawerLayout
            }
    
            if (myShoppingList == null)     { myShoppingList    =   new MyShoppingList(this);       }
            if (myFilteredRecipes == null)  { myFilteredRecipes =   new MyFilteredRecipes(this);    }
            if (myMainFragment == null)     { myMainFragment    =   new MyMainFragment();           }
    
            if (savedInstanceState == null)
            {
                // Runs a thread to read recipes from an SQLite database. This is not the culprit.
                filterAllRecipes(null);
            }
            else
            {
                restoreState(savedInstanceState); // See method below
            }
    
            getSupportFragmentManager().beginTransaction()
                .setCustomAnimations(R.anim.fade_in, R.anim.none)
                .replace(R.id.my_content_frame, myMainFragment, "main_fragment")
                .commit();
        }
    
        @Override
        protected void onSaveInstanceState(Bundle outState)
        {
            super.onSaveInstanceState(outState);
    
            if (myFilteredRecipes != null)
            {
                myFilteredRecipes.onSaveInstanceState(outState); // Restores from Bundle
            }
            if (myShoppingList != null)
            {
                myShoppingList.onSaveInstanceState(outState); // Restores from Bundle
            }
            if (myMainFragment != null)
            {
                getSupportFragmentManager().putFragment(outState, "myFragment", myMainFragment); // Restores from Bundle
            }
        }
    
        @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState)
        {
            super.onRestoreInstanceState(savedInstanceState);
        }
    
        private void restoreState(Bundle savedInstanceState)
        {
            myShoppingList.onRestoreInstanceState(savedInstanceState);
            myFilteredRecipes.onRestoreInstanceState(savedInstanceState);
            myMainFragment = (MyMainFragment)
                getSupportFragmentManager().getFragment(savedInstanceState, "myFragment");
        }
    
        @Override
        public void onResume()
        {
            super.onResume();
    
            // These tests were added as an attempt to cure the crash.
            // Probably fixed some of the crashes (stack traces below), but not the entire issue.
            if (myShoppingList == null) { myShoppingList = new MyShoppingList(this); }
            if (myFilteredRecipes == null) { myFilteredRecipes = new MyFilteredRecipes(this); }
            if (myMainFragment == null)
            {
                myMainFragment = new MyMainFragment();
                getSupportFragmentManager().beginTransaction()
                    .setCustomAnimations(R.anim.fade_in, R.anim.none)
                    .replace(R.id.my_content_frame, myMainFragment, "main_fragment")
                    .commit();
            }
    
            if (mShouldReadAutomaticSave) // Set in onCreate() when applicable
            {
                mShouldReadAutomaticSave = false;
                final MyActivity mThis = this;
                Thread loadAutomaticSaveThread = new Thread()
                {
                    @Override
                    public void run()
                    {
                        // Load automatic save if any. Must be done last to avoid race condition in LoadShoppingListTaks.
                        JSONObject automaticSave = ShoppingListFileHandler.readAutomaticSavedShoppingList(mThis);
                        if (automaticSave != null)
                        {
                            // This task takes the JSON data from the file read above, reads data from an SQLite database and sets up the myShoppingList data.
                            // Calls back to MyActivity.onShoppingListLoaded() when done.
                            // NOTE: There has been a crash in here when myShoppingList was null. This was before the "== null" tests above was added.
                            new ShoppingListFileHandler.LoadShoppingListTask(mThis, myShoppingList, automaticSave, false).execute();
                        }
    
                    }
                };
                loadAutomaticSaveThread.setName("LoadAutomaticSaveThread");
                loadAutomaticSaveThread.start();
            }
        }
    
        // This is called from the LoadShoppingListTask when done.
        @Override
        public synchronized void onShoppingListLoaded()
        {
            if (myShoppingList != null)
            {
                // MyShoppingList.notifyDataSetChanged will call notifyDataSetChanged on a
                // RecyclerView adapter in the main Fragment. There is a stack trace for this below.
                myShoppingList.notifyDataSetChanged();
            }
        }
    }
    

    这就是发生崩溃的活动。以下是我得到的堆栈跟踪,其中一些是部分混淆的,因为我当时并不了解它的作用。

    丢失片段的堆栈跟踪

    注意:似乎我们得到的片段为null,即尝试重新启动Activity时可能为myMainFragment为空。

    java.lang.RuntimeException: Unable to resume activity {com.my.app/com.my.app.activities.MyActivity}: java.lang.IllegalArgumentException: No view found for id 0x7f0f0089 (com.my.app/my_content_frame) for fragment a{215e6dc #0 id=0x7f0f0089 main_fragment}
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3403)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3434)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2772)
        at android.app.ActivityThread.access$900(ActivityThread.java:177)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1449)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:145)
        at android.app.ActivityThread.main(ActivityThread.java:5951)
        at java.lang.reflect.Method.invoke(Method.java)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
    Caused by: java.lang.IllegalArgumentException: No view found for id 0x7f0f0089 (com.my.app/my_content_frame) for fragment a{215e6dc #0 id=0x7f0f0089 main_fragment}
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1098)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1286)
        at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:758)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1671)
        at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:388)
        at android.support.v4.app.FragmentActivity.onPostResume(FragmentActivity.java:512)
        at android.support.v7.app.AppCompatActivity.onPostResume(AppCompatActivity.java:178)
        at android.app.Activity.performResume(Activity.java:6430)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3392)
        ... 11 more
    

    购物清单的堆栈跟踪== null

    注意:LoadShoppingListTask崩溃,因为myShoppingList成员在任务中为空。这是在onResume()开头添加测试之前发生的。

    java.lang.RuntimeException: An error occured while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:304)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
        at java.util.concurrent.FutureTask.run(FutureTask.java:242)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:818)
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] com.my.app.shoppinglist.j.a(android.content.Context, java.util.ArrayList, java.lang.String, android.util.SparseIntArray, org.json.JSONArray, java.util.ArrayList, boolean)' on a null object reference
        at com.my.app.filehandling.ShoppingListFileHandler$LoadShoppingListTask.doInBackground(ShoppingListFileHandler.java:882)
        at com.my.app.filehandling.ShoppingListFileHandler$LoadShoppingListTask.doInBackground(ShoppingListFileHandler.java:770)
        at android.os.AsyncTask$2.call(AsyncTask.java:292)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        ... 4 more
    

    来自主要片段中的RecyclerView的堆栈跟踪

    注意:绑定数据时,RecyclerView中的不同位置有多个。这个特定的RecyclerView存在于Activity的主要片段中。这些崩溃是我遇到问题的唯一一次。 RecyclerView从活动中的myShoppingList成员读取其数据。似乎还没有创建视图。

    java.lang.NullPointerException: Attempt to read from field 'android.widget.TextView com.my.app.gui.fragments.shoppinglist.k.a' on a null object reference
        at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.bindRecipeName(ShoppingListAdapter.java:401)
        at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.onBindViewHolder(ShoppingListAdapter.java:249)
        at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.onBindViewHolder(ShoppingListAdapter.java:31)
        at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.access$000(ShoppingListAdapter.java:31)
        at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5471)
        at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5504)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4741)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4617)
        at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1994)
        at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1390)
        at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353)
    <snip> Long stack trace inside Android classes...
    

    使用Fragment的堆栈跟踪== null

    注意:这是我得到的第一个堆栈跟踪。不幸的是,我没有意识到我必须在此时添加deobscufation文件,因此堆栈跟踪不是那么有用。但很明显,成员myMainFragment在崩溃时为空。

    java.lang.RuntimeException: Unable to resume activity {com.my.app/com.my.app.activities.MyActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.my.app.gui.fragments.c.a.L()' on a null object reference
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3403)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3434)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2772)
        at android.app.ActivityThread.access$900(ActivityThread.java:177)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1449)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:145)
        at android.app.ActivityThread.main(ActivityThread.java:5951)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.my.app.gui.fragments.c.a.L()' on a null object reference
        at com.my.app.activities.MyActivity.t(Unknown Source)
        at com.my.app.activities.MyActivity.onResume(Unknown Source)
        at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1255)
        at android.app.Activity.performResume(Activity.java:6412)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3392)
        ... 11 more
    

    我看不出我走错了路。任何提示和评论都表示赞赏。

0 个答案:

没有答案