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 onCreate(Bundle savedInstanceState)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 onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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 onActivityCreated(Bundle savedInstanceState)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 onCreate(Bundle savedInstanceState)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 onActivityCreated(Bundle savedInstanceState)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 onDestroy()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 onDetach()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 */ restart()225 public void restart() { 226 synchronized (mThread) { 227 mPosition = 0; 228 mThread.notify(); 229 } 230 } 231 } 232 } 233