Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.apis.app;
     18 
     19 import com.example.android.apis.R;
     20 
     21 import android.app.Activity;
     22 import android.app.Fragment;
     23 import android.app.FragmentManager;
     24 import android.os.Bundle;
     25 import android.view.LayoutInflater;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 import android.view.View.OnClickListener;
     29 import android.widget.Button;
     30 import android.widget.ProgressBar;
     31 
     32 /**
     33  * This example shows how you can use a Fragment to easily propagate state
     34  * (such as threads) across activity instances when an activity needs to be
     35  * restarted due to, for example, a configuration change.  This is a lot
     36  * easier than using the raw Activity.onRetainNonConfiguratinInstance() API.
     37  */
     38 public class FragmentRetainInstance extends Activity {
     39     @Override
     40     protected void onCreate(Bundle savedInstanceState) {
     41         super.onCreate(savedInstanceState);
     42 
     43         // First time init, create the UI.
     44         if (savedInstanceState == null) {
     45             getFragmentManager().beginTransaction().add(android.R.id.content,
     46                     new UiFragment()).commit();
     47         }
     48     }
     49 
     50     /**
     51      * This is a fragment showing UI that will be updated from work done
     52      * in the retained fragment.
     53      */
     54     public static class UiFragment extends Fragment {
     55         RetainedFragment mWorkFragment;
     56 
     57         @Override
     58         public View onCreateView(LayoutInflater inflater, ViewGroup container,
     59                 Bundle savedInstanceState) {
     60             View v = inflater.inflate(R.layout.fragment_retain_instance, container, false);
     61 
     62             // Watch for button clicks.
     63             Button button = (Button)v.findViewById(R.id.restart);
     64             button.setOnClickListener(new OnClickListener() {
     65                 public void onClick(View v) {
     66                     mWorkFragment.restart();
     67                 }
     68             });
     69 
     70             return v;
     71         }
     72 
     73         @Override
     74         public void onActivityCreated(Bundle savedInstanceState) {
     75             super.onActivityCreated(savedInstanceState);
     76 
     77             FragmentManager fm = getFragmentManager();
     78 
     79             // Check to see if we have retained the worker fragment.
     80             mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");
     81 
     82             // If not retained (or first time running), we need to create it.
     83             if (mWorkFragment == null) {
     84                 mWorkFragment = new RetainedFragment();
     85                 // Tell it who it is working with.
     86                 mWorkFragment.setTargetFragment(this, 0);
     87                 fm.beginTransaction().add(mWorkFragment, "work").commit();
     88             }
     89         }
     90 
     91     }
     92 
     93     /**
     94      * This is the Fragment implementation that will be retained across
     95      * activity instances.  It represents some ongoing work, here a thread
     96      * we have that sits around incrementing a progress indicator.
     97      */
     98     public static class RetainedFragment extends Fragment {
     99         ProgressBar mProgressBar;
    100         int mPosition;
    101         boolean mReady = false;
    102         boolean mQuiting = false;
    103 
    104         /**
    105          * This is the thread that will do our work.  It sits in a loop running
    106          * the progress up until it has reached the top, then stops and waits.
    107          */
    108         final Thread mThread = new Thread() {
    109             @Override
    110             public void run() {
    111                 // We'll figure the real value out later.
    112                 int max = 10000;
    113 
    114                 // This thread runs almost forever.
    115                 while (true) {
    116 
    117                     // Update our shared state with the UI.
    118                     synchronized (this) {
    119                         // Our thread is stopped if the UI is not ready
    120                         // or it has completed its work.
    121                         while (!mReady || mPosition >= max) {
    122                             if (mQuiting) {
    123                                 return;
    124                             }
    125                             try {
    126                                 wait();
    127                             } catch (InterruptedException e) {
    128                             }
    129                         }
    130 
    131                         // Now update the progress.  Note it is important that
    132                         // we touch the progress bar with the lock held, so it
    133                         // doesn't disappear on us.
    134                         mPosition++;
    135                         max = mProgressBar.getMax();
    136                         mProgressBar.setProgress(mPosition);
    137                     }
    138 
    139                     // Normally we would be doing some work, but put a kludge
    140                     // here to pretend like we are.
    141                     synchronized (this) {
    142                         try {
    143                             wait(50);
    144                         } catch (InterruptedException e) {
    145                         }
    146                     }
    147                 }
    148             }
    149         };
    150 
    151         /**
    152          * Fragment initialization.  We way we want to be retained and
    153          * start our thread.
    154          */
    155         @Override
    156         public void onCreate(Bundle savedInstanceState) {
    157             super.onCreate(savedInstanceState);
    158 
    159             // Tell the framework to try to keep this fragment around
    160             // during a configuration change.
    161             setRetainInstance(true);
    162 
    163             // Start up the worker thread.
    164             mThread.start();
    165         }
    166 
    167         /**
    168          * This is called when the Fragment's Activity is ready to go, after
    169          * its content view has been installed; it is called both after
    170          * the initial fragment creation and after the fragment is re-attached
    171          * to a new activity.
    172          */
    173         @Override
    174         public void onActivityCreated(Bundle savedInstanceState) {
    175             super.onActivityCreated(savedInstanceState);
    176 
    177             // Retrieve the progress bar from the target's view hierarchy.
    178             mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(
    179                     R.id.progress_horizontal);
    180 
    181             // We are ready for our thread to go.
    182             synchronized (mThread) {
    183                 mReady = true;
    184                 mThread.notify();
    185             }
    186         }
    187 
    188         /**
    189          * This is called when the fragment is going away.  It is NOT called
    190          * when the fragment is being propagated between activity instances.
    191          */
    192         @Override
    193         public void onDestroy() {
    194             // Make the thread go away.
    195             synchronized (mThread) {
    196                 mReady = false;
    197                 mQuiting = true;
    198                 mThread.notify();
    199             }
    200 
    201             super.onDestroy();
    202         }
    203 
    204         /**
    205          * This is called right before the fragment is detached from its
    206          * current activity instance.
    207          */
    208         @Override
    209         public void onDetach() {
    210             // This fragment is being detached from its activity.  We need
    211             // to make sure its thread is not going to touch any activity
    212             // state after returning from this function.
    213             synchronized (mThread) {
    214                 mProgressBar = null;
    215                 mReady = false;
    216                 mThread.notify();
    217             }
    218 
    219             super.onDetach();
    220         }
    221 
    222         /**
    223          * API for our UI to restart the progress thread.
    224          */
    225         public void restart() {
    226             synchronized (mThread) {
    227                 mPosition = 0;
    228                 mThread.notify();
    229             }
    230         }
    231     }
    232 }
    233