进一步理解 setRetainInstance(true) [英] Further understanding setRetainInstance(true)

查看:35
本文介绍了进一步理解 setRetainInstance(true)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当您在 Fragment 上调用 setRetainInstance(true) 时,究竟会发生什么?文档几乎不存在,这似乎是一个非常重要的功能.具体来说,我想知道这个序列(我编造的)中有多少是真实的:

<块引用>

  1. 用户旋转设备.
  2. 片段与 Activity 分离并调用 Fragment.onDetach().
  3. 活动被销毁;Activity.onDestroy() 被调用.
  4. Activity java 对象被删除(如果可能,由 GC).
  5. 一个新的Activity java 对象被创建;它的构造函数和 onCreate() 被调用.
  6. Activity.onCreate() 中,我们要么使用 setContentView(...) 设置包含片段的布局,要么使用 FragmentTransaction>添加一个片段.
  7. 我真的不确定这一点,但我认为 android 足够聪明,可以找到旧片段,并调用 Fragment.onAttach()将其重新附加到新的 Activity
  8. 接下来(或之前?谁知道?)Activity.onResume() 被调用.

那是对的吗?即使我第一次明确使用 FragmentTransaction.add(new MyFragment(), ...) ,Android 是否足够智能以找到旧片段?如果是这样,我如何避免在 onCreate() 中添加 另一个 片段?我需要做这样的事情吗?:

 if (getSupportFragmentManager().findFragmentByTag("foo") == null){FragmentTransaction ft = getSupportFragmentManager().beginTransaction();ft.add(new FooFragment(), "foo").commit();}

解决方案

好吧,也许我对 Android 文档有点过于苛刻,因为它确实有一些有用的信息,但遗憾的是它们都没有从 setRetainInstance 链接().来自关于片段的页面

<块引用>

注意:每个片段都需要一个唯一的标识符,系统可以用于在活动重新启动时恢复片段(以及哪些您可以使用捕获片段来执行事务,例如去掉它).可以通过三种方式为片段提供 ID:

  • 提供具有唯一 ID 的 android:id 属性.
  • 提供具有唯一字符串的 android:tag 属性.
  • 如果前两个都不提供,系统将使用容器视图的 ID.

这强烈暗示,如果您在 Activity.onCreated() 中执行 setContentView(R.layout.whatever) 并且该布局包含带有 setRetainInstance(true) ,那么当活动被重新创建时,它将使用其 id 或标签再次搜索.

其次,对于无 UI 的片段,它声明

<块引用>

要添加没有 UI 的片段,请从 Activity 添加片段使用 add(Fragment, String)(为片段,而不是视图 ID).这添加了片段,但是,因为它不与活动布局中的视图相关联,它不接收对 onCreateView() 的调用.所以你不需要实现那个方法.

文档链接到一个非常好的示例 - FragmentRetainInstance.java,为了您的方便,我在下面复制了该示例.它完全符合我所推测的问题的答案(if (...findFragmentByTag() == null) { ...).

最后,我创建了自己的测试活动来准确查看调用了哪些函数.当您以纵向开始并旋转到横向时,它会输出此内容.代码如下.

(为了更容易阅读,这里做了一些编辑.)

TestActivity@415a4a30: this()TestActivity@415a4a30: onCreate()TestActivity@415a4a30:找不到现有片段.测试片段{41583008}:这个()测试片段{41583008}测试片段{41583008}:onAttach(TestActivity@415a4a30)测试片段{41583008}:onCreate()测试片段{41583008}:onCreateView()测试片段{41583008}:onActivityCreated()TestActivity@415a4a30: onStart()测试片段{41583008}:onStart()TestActivity@415a4a30: onResume()测试片段{41583008}:onResume()<旋转设备>测试片段{41583008}:onPause()TestActivity@415a4a30: onPause()测试片段{41583008}:onStop()TestActivity@415a4a30: onStop()测试片段{41583008}:onDestroyView()测试片段{41583008}:onDetach()TestActivity@415a4a30: onDestroy()TestActivity@415a3380: this()测试片段{41583008}:onAttach(TestActivity@415a3380)TestActivity@415a3380: onCreate()TestActivity@415a3380:找到现有片段.测试片段{41583008}:onCreateView()测试片段{41583008}:onActivityCreated()TestActivity@415a3380: onStart()测试片段{41583008}:onStart()TestActivity@415a3380: onResume()测试片段{41583008}:onResume()

请注意,Android 文档是错误的:无 UI 片段确实收到对 onCreateView() 的调用,但可以自由返回 null.

TestActivity/TestFragment的源代码

import android.app.Activity;导入 android.app.Fragment;导入 android.app.FragmentTransaction;导入 android.os.Bundle;导入 android.util.Log;导入 android.view.LayoutInflater;导入 android.view.View;导入 android.view.ViewGroup;导入 android.widget.TextView;进口 com.concentriclivers.ss.R;//一个用于理解 Android 生命周期事件的活动.公共类 TestActivity 扩展了 Activity{私有静态最终字符串标记 = TestActivity.class.getSimpleName();公共测试活动(){极好的();Log.d(TAG, this + ": this()");}protected void finalize() 抛出 Throwable{super.finalize();Log.d(TAG, this + ": finalize()");}@覆盖public void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);Log.d(TAG, this + ": onCreate()");TextView tv = new TextView(this);tv.setText("你好世界");设置内容视图(电视);if (getFragmentManager().findFragmentByTag("test_fragment") == null){Log.d(TAG, this + ": Existing fragment not found.");FragmentTransaction ft = getFragmentManager().beginTransaction();ft.add(new TestFragment(), "test_fragment").commit();}别的{Log.d(TAG, this + ": Existing fragment found.");}}@覆盖公共无效 onStart(){super.onStart();Log.d(TAG, this + ": onStart()");}@覆盖公共无效 onResume(){super.onResume();Log.d(TAG, this + ": onResume()");}@覆盖公共无效 onPause(){super.onPause();Log.d(TAG, this + ": onPause()");}@覆盖公共无效 onStop(){super.onStop();Log.d(TAG, this + ": onStop()");}@覆盖公共无效 onDestroy(){super.onDestroy();Log.d(TAG, this + ": onDestroy()");}公共静态类 TestFragment 扩展 Fragment{私有静态最终字符串标记 = TestFragment.class.getSimpleName();公共测试片段(){极好的();Log.d(TAG, this + ": this() " + this);}@覆盖public void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);Log.d(TAG, this + ": onCreate()");setRetainInstance(true);}@覆盖public void onAttach(最终活动活动){super.onAttach(活动);Log.d(TAG, this + ": onAttach(" + activity + ")");}@覆盖public void onActivityCreated(Bundle savedInstanceState){super.onActivityCreated(savedInstanceState);Log.d(TAG, this + ": onActivityCreated()");}@覆盖公共视图 onCreateView(LayoutInflater inflater,ViewGroup 容器,Bundle savedInstanceState){Log.d(TAG, this + ": onCreateView()");返回空;}@覆盖public void onViewCreated(View view, Bundle savedInstanceState){super.onViewCreated(view,savedInstanceState);Log.d(TAG, this + ": onViewCreated()");}@覆盖公共无效 onDestroyView(){super.onDestroyView();Log.d(TAG, this + ": onDestroyView()");}@覆盖公共无效 onDetach(){super.onDetach();Log.d(TAG, this + ": onDetach()");}@覆盖公共无效 onStart(){super.onStart();Log.d(TAG, this + ": onStart()");}@覆盖公共无效 onResume(){super.onResume();Log.d(TAG, this + ": onResume()");}@覆盖公共无效 onPause(){super.onPause();Log.d(TAG, this + ": onPause()");}@覆盖公共无效 onStop(){super.onStop();Log.d(TAG, this + ": onStop()");}@覆盖公共无效 onDestroy(){super.onDestroy();Log.d(TAG, this + ": onDestroy()");}}}

FragmentRetainInstance.java 的源代码(从 API 16 开始):

/** 版权所有 (C) 2010 安卓开源项目** 根据 Apache 许可,版本 2.0(许可")获得许可;* 除非遵守许可,否则您不得使用此文件.* 您可以在以下网址获得许可证副本** http://www.apache.org/licenses/LICENSE-2.0** 除非适用法律要求或书面同意,否则软件* 根据许可证分发是按原样"分发的,* 没有任何形式的明示或暗示的保证或条件.* 请参阅许可证以了解管理权限的特定语言和* 许可下的限制.*/包 com.example.android.apis.app;导入 com.example.android.apis.R;导入 android.app.Activity;导入 android.app.Fragment;导入 android.app.FragmentManager;导入 android.os.Bundle;导入 android.view.LayoutInflater;导入 android.view.View;导入 android.view.ViewGroup;导入 android.view.View.OnClickListener;导入 android.widget.Button;导入 android.widget.ProgressBar;/*** 此示例展示了如何使用 Fragment 轻松传播状态*(如线程)跨活动实例,当一个活动需要* 例如,由于配置更改而重新启动.这是很多* 比使用原始 Activity.onRetainNonConfiguratinInstance() API 更容易.*/公共类 FragmentRetainInstance 扩展活动 {@覆盖protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//第一次初始化,创建 UI.如果(savedInstanceState == null){getFragmentManager().beginTransaction().add(android.R.id.content,新的 UiFragment()).commit();}}/*** 这是一个片段,显示将从完成的工作中更新的 UI* 在保留的片段中.*/公共静态类 UiFragment 扩展 Fragment {RetainedFragment mWorkFragment;@覆盖公共视图 onCreateView(LayoutInflater inflater, ViewGroup 容器,捆绑savedInstanceState) {查看 v = inflater.inflate(R.layout.fragment_retain_instance, container, false);//注意按钮点击.Button button = (Button)v.findViewById(R.id.restart);button.setOnClickListener(new OnClickListener() {public void onClick(View v) {mWorkFragment.restart();}});返回 v;}@覆盖public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);FragmentManager fm = getFragmentManager();//检查我们是否保留了工作片段.mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");//如果没有保留(或第一次运行),我们需要创建它.如果(mWorkFragment == null){mWorkFragment = new RetainedFragment();//告诉它与谁一起工作.mWorkFragment.setTargetFragment(this, 0);fm.beginTransaction().add(mWorkFragment, "work").commit();}}}/*** 这是将在整个过程中保留的 Fragment 实现* 活动实例.它代表了一些正在进行的工作,这里是一个线程* 我们有关于增加进度指示器的内容.*/公共静态类 RetainedFragment 扩展 Fragment {ProgressBar mProgressBar;int mPosition;布尔 mReady = 假;布尔 mQuiting = 假;/*** 这是将完成我们工作的线程.它坐在一个循环中运行* 前进直到到达顶部,然后停止并等待.*/最终线程 mThread = 新线程(){@覆盖公共无效运行(){//我们稍后会计算出真正的价值.整数最大值 = 10000;//这个线程几乎永远运行.而(真){//用 UI 更新我们的共享状态.同步(这个){//如果 UI 没有准备好,我们的线程就会停止//或者它已经完成了它的工作.while (!mReady || mPosition >= max) {如果(m退出){返回;}尝试 {等待();} catch (InterruptedException e) {}}//现在更新进度.请注意,重要的是//我们在持有锁的情况下触摸进度条,所以它//不会消失在我们身上.位置++;max = mProgressBar.getMax();mProgressBar.setProgress(mPosition);}//通常我们会做一些工作,但要加把劲//在这里假装我们是这样的.同步(这个){尝试 {等待(50);} catch (InterruptedException e) {}}}}};/*** 片段初始化.我们希望被保留和* 开始我们的线程.*/@覆盖public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//告诉框架尽量保留这个片段//在配置更改期间.setRetainInstance(true);//启动工作线程.mThread.start();}/*** 当 Fragment 的 Activity 准备就绪时调用,之后* 其内容视图已安装;它被称为之后* 初始片段创建和片段重新附加后* 到一项新活动.*/@覆盖public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);//从目标的视图层次结构中检索进度条.mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(R.id.progress_horizo​​ntal);//我们准备好让我们的线程运行了.同步(mThread){mReady = 真;mThread.notify();}}/*** 当片段消失时调用.它不被称为* 当片段在活动实例之间传播时.*/@覆盖公共无效 onDestroy() {//使线程消失.同步(mThread){mReady = 假;mQuiting = 真;mThread.notify();}super.onDestroy();}/*** 在片段与其分离之前调用* 当前活动实例.*/@覆盖公共无效 onDetach() {//此片段正在与其活动分离.我们需要//确保它的线程不会接触任何活动//从这个函数返回后的状态.同步(mThread){mProgressBar = null;mReady = 假;mThread.notify();}super.onDetach();}/*** 用于我们 UI 的 API,用于重新启动进度线程.*/公共无效重启(){同步(mThread){位置 = 0;mThread.notify();}}}}

What exactly happens when you call setRetainInstance(true) on a Fragment? The documentation is virtually non-existent and this seems like a very important function. Specifically I want to know how much of this sequence (that I made up) is true:

  1. The user rotates the device.
  2. The fragment is detached from the Activity and Fragment.onDetach() is called.
  3. The activity is destroyed; Activity.onDestroy() is called.
  4. The Activity java object is deleted (when possible, by the GC).
  5. A new Activity java object is created; its constructor, and onCreate() are called.
  6. In Activity.onCreate() we either have setContentView(...) which sets a layout containing a fragment, or we use FragmentTransaction to add a fragment.
  7. I'm really not sure about this, but I assume that android is smart enough to find the old fragment, and call Fragment.onAttach() to reattach it to the new Activity
  8. Next (or before? who knows?) Activity.onResume() is called.

So is that correct? Is Android smart enough to find the old fragment, even if I explicitly use FragmentTransaction.add(new MyFragment(), ...) the first time? And if so, how do I avoid adding another fragment in onCreate()? Do I need to do something like this?:

    if (getSupportFragmentManager().findFragmentByTag("foo") == null)
    {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.add(new FooFragment(), "foo").commit();
    }

解决方案

Ok, perhaps I was slightly too harsh on the Android documentation, because it does have some useful information, but sadly none of it is linked from setRetainInstance(). From the page about fragments

Note: Each fragment requires a unique identifier that the system can use to restore the fragment if the activity is restarted (and which you can use to capture the fragment to perform transactions, such as remove it). There are three ways to provide an ID for a fragment:

  • Supply the android:id attribute with a unique ID.
  • Supply the android:tag attribute with a unique string.
  • If you provide neither of the previous two, the system uses the ID of the container view.

This strongly implies that if you do setContentView(R.layout.whatever) in Activity.onCreated() and that layout contains a fragment with setRetainInstance(true), then when the activity is recreated it will be searched for again using its id or tag.

Secondly, for UI-less fragments, it states

To add a fragment without a UI, add the fragment from the activity using add(Fragment, String) (supplying a unique string "tag" for the fragment, rather than a view ID). This adds the fragment, but, because it's not associated with a view in the activity layout, it does not receive a call to onCreateView(). So you don't need to implement that method.

And the docs link to a very good example - FragmentRetainInstance.java which I have reproduced below for your convenience. It does exactly what I speculated was the answer in my question (if (...findFragmentByTag() == null) { ...).

Finally, I created my own test activity to see exactly what functions are called. It outputs this, when you start in portrait and rotate to landscape. The code is below.

(This is edited a bit to make it easier to read.)

TestActivity@415a4a30: this()
TestActivity@415a4a30: onCreate()
TestActivity@415a4a30: Existing fragment not found.
TestFragment{41583008}: this() TestFragment{41583008}
TestFragment{41583008}: onAttach(TestActivity@415a4a30)
TestFragment{41583008}: onCreate()
TestFragment{41583008}: onCreateView()
TestFragment{41583008}: onActivityCreated()
TestActivity@415a4a30: onStart()
TestFragment{41583008}: onStart()
TestActivity@415a4a30: onResume()
TestFragment{41583008}: onResume()

<rotate device>

TestFragment{41583008}: onPause()
TestActivity@415a4a30: onPause()
TestFragment{41583008}: onStop()
TestActivity@415a4a30: onStop()
TestFragment{41583008}: onDestroyView()
TestFragment{41583008}: onDetach()
TestActivity@415a4a30: onDestroy()
TestActivity@415a3380: this()
TestFragment{41583008}: onAttach(TestActivity@415a3380)
TestActivity@415a3380: onCreate()
TestActivity@415a3380: Existing fragment found.
TestFragment{41583008}: onCreateView()
TestFragment{41583008}: onActivityCreated()
TestActivity@415a3380: onStart()
TestFragment{41583008}: onStart()
TestActivity@415a3380: onResume()
TestFragment{41583008}: onResume()

Note that the Android documentation is wrong: the UI-less fragment does receive a call to onCreateView() but it is free to return null.

Source code for TestActivity/TestFragment

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.concentriclivers.ss.R;

// An activity for understanding Android lifecycle events.
public class TestActivity extends Activity
{
    private static final String TAG = TestActivity.class.getSimpleName();

    public TestActivity()
    {
        super();
        Log.d(TAG, this + ": this()");
    }

    protected void finalize() throws Throwable
    {
        super.finalize();
        Log.d(TAG, this + ": finalize()");
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        Log.d(TAG, this + ": onCreate()");


        TextView tv = new TextView(this);
        tv.setText("Hello world");
        setContentView(tv);

        if (getFragmentManager().findFragmentByTag("test_fragment") == null)
        {
            Log.d(TAG, this + ": Existing fragment not found.");
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(new TestFragment(), "test_fragment").commit();
        }
        else
        {
            Log.d(TAG, this + ": Existing fragment found.");
        }
    }

    @Override
    public void onStart()
    {
        super.onStart();
        Log.d(TAG, this + ": onStart()");
    }

    @Override
    public void onResume()
    {
        super.onResume();
        Log.d(TAG, this + ": onResume()");
    }

    @Override
    public void onPause()
    {
        super.onPause();
        Log.d(TAG, this + ": onPause()");
    }

    @Override
    public void onStop()
    {
        super.onStop();
        Log.d(TAG, this + ": onStop()");
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        Log.d(TAG, this + ": onDestroy()");
    }




    public static class TestFragment extends Fragment
    {
        private static final String TAG = TestFragment.class.getSimpleName();

        public TestFragment()
        {
            super();
            Log.d(TAG,  this + ": this() " + this);
        }

        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            Log.d(TAG, this + ": onCreate()");
            setRetainInstance(true);
        }

        @Override
        public void onAttach(final Activity activity)
        {
            super.onAttach(activity);
            Log.d(TAG, this + ": onAttach(" + activity + ")");
        }

        @Override
        public void onActivityCreated(Bundle savedInstanceState)
        {
            super.onActivityCreated(savedInstanceState);
            Log.d(TAG, this + ": onActivityCreated()");
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            Log.d(TAG, this + ": onCreateView()");
            return null;
        }

        @Override
        public void onViewCreated(View view, Bundle savedInstanceState)
        {
            super.onViewCreated(view, savedInstanceState);
            Log.d(TAG, this + ": onViewCreated()");
        }

        @Override
        public void onDestroyView()
        {
            super.onDestroyView();
            Log.d(TAG, this + ": onDestroyView()");
        }

        @Override
        public void onDetach()
        {
            super.onDetach();
            Log.d(TAG, this + ": onDetach()");
        }

        @Override
        public void onStart()
        {
            super.onStart();
            Log.d(TAG, this + ": onStart()");
        }

        @Override
        public void onResume()
        {
            super.onResume();
            Log.d(TAG, this + ": onResume()");
        }

        @Override
        public void onPause()
        {
            super.onPause();
            Log.d(TAG, this + ": onPause()");
        }

        @Override
        public void onStop()
        {
            super.onStop();
            Log.d(TAG, this + ": onStop()");
        }

        @Override
        public void onDestroy()
        {
            super.onDestroy();
            Log.d(TAG, this + ": onDestroy()");
        }
    }

}

Source code for FragmentRetainInstance.java (as of API 16):

/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.app;

import com.example.android.apis.R;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;

/**
 * This example shows how you can use a Fragment to easily propagate state
 * (such as threads) across activity instances when an activity needs to be
 * restarted due to, for example, a configuration change.  This is a lot
 * easier than using the raw Activity.onRetainNonConfiguratinInstance() API.
 */
public class FragmentRetainInstance extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // First time init, create the UI.
        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction().add(android.R.id.content,
                    new UiFragment()).commit();
        }
    }

    /**
     * This is a fragment showing UI that will be updated from work done
     * in the retained fragment.
     */
    public static class UiFragment extends Fragment {
        RetainedFragment mWorkFragment;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.fragment_retain_instance, container, false);

            // Watch for button clicks.
            Button button = (Button)v.findViewById(R.id.restart);
            button.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    mWorkFragment.restart();
                }
            });

            return v;
        }

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);

            FragmentManager fm = getFragmentManager();

            // Check to see if we have retained the worker fragment.
            mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");

            // If not retained (or first time running), we need to create it.
            if (mWorkFragment == null) {
                mWorkFragment = new RetainedFragment();
                // Tell it who it is working with.
                mWorkFragment.setTargetFragment(this, 0);
                fm.beginTransaction().add(mWorkFragment, "work").commit();
            }
        }

    }

    /**
     * This is the Fragment implementation that will be retained across
     * activity instances.  It represents some ongoing work, here a thread
     * we have that sits around incrementing a progress indicator.
     */
    public static class RetainedFragment extends Fragment {
        ProgressBar mProgressBar;
        int mPosition;
        boolean mReady = false;
        boolean mQuiting = false;

        /**
         * This is the thread that will do our work.  It sits in a loop running
         * the progress up until it has reached the top, then stops and waits.
         */
        final Thread mThread = new Thread() {
            @Override
            public void run() {
                // We'll figure the real value out later.
                int max = 10000;

                // This thread runs almost forever.
                while (true) {

                    // Update our shared state with the UI.
                    synchronized (this) {
                        // Our thread is stopped if the UI is not ready
                        // or it has completed its work.
                        while (!mReady || mPosition >= max) {
                            if (mQuiting) {
                                return;
                            }
                            try {
                                wait();
                            } catch (InterruptedException e) {
                            }
                        }

                        // Now update the progress.  Note it is important that
                        // we touch the progress bar with the lock held, so it
                        // doesn't disappear on us.
                        mPosition++;
                        max = mProgressBar.getMax();
                        mProgressBar.setProgress(mPosition);
                    }

                    // Normally we would be doing some work, but put a kludge
                    // here to pretend like we are.
                    synchronized (this) {
                        try {
                            wait(50);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
        };

        /**
         * Fragment initialization.  We way we want to be retained and
         * start our thread.
         */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Tell the framework to try to keep this fragment around
            // during a configuration change.
            setRetainInstance(true);

            // Start up the worker thread.
            mThread.start();
        }

        /**
         * This is called when the Fragment's Activity is ready to go, after
         * its content view has been installed; it is called both after
         * the initial fragment creation and after the fragment is re-attached
         * to a new activity.
         */
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);

            // Retrieve the progress bar from the target's view hierarchy.
            mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(
                    R.id.progress_horizontal);

            // We are ready for our thread to go.
            synchronized (mThread) {
                mReady = true;
                mThread.notify();
            }
        }

        /**
         * This is called when the fragment is going away.  It is NOT called
         * when the fragment is being propagated between activity instances.
         */
        @Override
        public void onDestroy() {
            // Make the thread go away.
            synchronized (mThread) {
                mReady = false;
                mQuiting = true;
                mThread.notify();
            }

            super.onDestroy();
        }

        /**
         * This is called right before the fragment is detached from its
         * current activity instance.
         */
        @Override
        public void onDetach() {
            // This fragment is being detached from its activity.  We need
            // to make sure its thread is not going to touch any activity
            // state after returning from this function.
            synchronized (mThread) {
                mProgressBar = null;
                mReady = false;
                mThread.notify();
            }

            super.onDetach();
        }

        /**
         * API for our UI to restart the progress thread.
         */
        public void restart() {
            synchronized (mThread) {
                mPosition = 0;
                mThread.notify();
            }
        }
    }
}

这篇关于进一步理解 setRetainInstance(true)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆