[go: nahoru, domu]

blob: 4c4368a9199a7ae5e9189cc84ccdf771c95d2a86 [file] [log] [blame]
Simon Schillere154ee02021-02-16 10:29:29 -08001/*
2 * Copyright 2021 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
17package androidx.fragment.app.strictmode;
18
19import android.annotation.SuppressLint;
Simon Schiller39b483d2021-03-02 17:08:35 +000020import android.os.Handler;
21import android.os.Looper;
Simon Schillere154ee02021-02-16 10:29:29 -080022import android.util.Log;
23
24import androidx.annotation.NonNull;
25import androidx.annotation.Nullable;
26import androidx.annotation.RestrictTo;
27import androidx.annotation.VisibleForTesting;
28import androidx.fragment.app.Fragment;
Simon Schillerdf2d4fd2021-03-29 14:48:42 -070029import androidx.fragment.app.FragmentContainerView;
Simon Schillere154ee02021-02-16 10:29:29 -080030import androidx.fragment.app.FragmentManager;
31
Sanura N'Jaka1c683822021-05-13 01:31:39 +000032import java.util.HashMap;
Simon Schillere154ee02021-02-16 10:29:29 -080033import java.util.HashSet;
Sanura N'Jaka1c683822021-05-13 01:31:39 +000034import java.util.Map;
Simon Schillere154ee02021-02-16 10:29:29 -080035import java.util.Set;
36
37/**
38 * FragmentStrictMode is a tool which detects things you might be doing by accident and brings
39 * them to your attention so you can fix them. Basically, it's a version of
40 * {@link android.os.StrictMode} specifically for fragment-related issues.
41 *
42 * <p>You can decide what should happen when a violation is detected. For example, using {@link
43 * Policy.Builder#penaltyLog} you can watch the output of <code>adb logcat</code> while you
44 * use your application to see the violations as they happen.
45 */
46@SuppressLint("SyntheticAccessor")
Simon Schillere154ee02021-02-16 10:29:29 -080047public final class FragmentStrictMode {
48 private static final String TAG = "FragmentStrictMode";
49 private static Policy defaultPolicy = Policy.LAX;
50
51 private enum Flag {
52 PENALTY_LOG,
Simon Schillercb3b96b2021-03-03 23:36:49 -080053 PENALTY_DEATH,
54
Simon Schiller97198852021-03-23 17:04:29 +010055 DETECT_FRAGMENT_REUSE,
Simon Schiller57ca2212021-03-22 16:09:30 -070056 DETECT_FRAGMENT_TAG_USAGE,
Simon Schiller909ebda2021-03-16 12:52:26 -070057 DETECT_RETAIN_INSTANCE_USAGE,
Simon Schillerf7967822021-03-22 01:26:40 -070058 DETECT_SET_USER_VISIBLE_HINT,
59 DETECT_TARGET_FRAGMENT_USAGE,
Simon Schillerdf2d4fd2021-03-29 14:48:42 -070060 DETECT_WRONG_FRAGMENT_CONTAINER,
Simon Schillere154ee02021-02-16 10:29:29 -080061 }
62
63 private FragmentStrictMode() {}
64
65 /**
66 * When #{@link Policy.Builder#penaltyListener} is enabled, the listener is called when a
67 * violation occurs.
68 */
Simon Schillere154ee02021-02-16 10:29:29 -080069 public interface OnViolationListener {
70
Simon Schiller5e9ec572021-04-12 11:42:39 -070071 /** Called on a policy violation. */
Simon Schillere154ee02021-02-16 10:29:29 -080072 void onViolation(@NonNull Violation violation);
73 }
74
75 /**
76 * {@link FragmentStrictMode} policy applied to a certain {@link FragmentManager} (or globally).
77 *
78 * <p>This policy can either be enabled globally using {@link #setDefaultPolicy} or for a
79 * specific {@link FragmentManager} using {@link FragmentManager#setStrictModePolicy(Policy)}.
80 * The current policy can be retrieved using {@link #getDefaultPolicy} and
81 * {@link FragmentManager#getStrictModePolicy} respectively.
82 *
83 * <p>Note that multiple penalties may be provided and they're run in order from least to most
84 * severe (logging before process death, for example). There's currently no mechanism to choose
85 * different penalties for different detected actions.
86 */
Simon Schillere154ee02021-02-16 10:29:29 -080087 public static final class Policy {
Sanura N'Jaka1c683822021-05-13 01:31:39 +000088 private final Set<Flag> mFlags;
89 private final OnViolationListener mListener;
90 private final Map<Class<? extends Fragment>,
91 Set<Class<? extends Violation>>> mAllowedViolations;
Simon Schillere154ee02021-02-16 10:29:29 -080092
93 /** The default, lax policy which doesn't catch anything. */
94 @NonNull
Sanura N'Jaka1c683822021-05-13 01:31:39 +000095 public static final Policy LAX = new Policy(new HashSet<>(), null, new HashMap<>());
Simon Schillere154ee02021-02-16 10:29:29 -080096
Sanura N'Jaka1c683822021-05-13 01:31:39 +000097 private Policy(
98 @NonNull Set<Flag> flags,
99 @Nullable OnViolationListener listener,
100 @NonNull Map<Class<? extends Fragment>,
101 Set<Class<? extends Violation>>> allowedViolations) {
102 this.mFlags = new HashSet<>(flags);
103 this.mListener = listener;
104
105 Map<Class<? extends Fragment>, Set<Class<? extends Violation>>>
106 newAllowedViolationsMap = new HashMap<>();
107 for (Map.Entry<Class<? extends Fragment>,
108 Set<Class<? extends Violation>>> entry : allowedViolations.entrySet()) {
109 newAllowedViolationsMap.put(entry.getKey(), new HashSet<>(entry.getValue()));
110 }
111 this.mAllowedViolations = newAllowedViolationsMap;
Simon Schillere154ee02021-02-16 10:29:29 -0800112 }
113
114 /**
115 * Creates {@link Policy} instances. Methods whose names start with {@code detect} specify
116 * what problems we should look for. Methods whose names start with {@code penalty} specify
117 * what we should do when we detect a problem.
118 *
119 * <p>You can call as many {@code detect} and {@code penalty} methods as you like. Currently
120 * order is insignificant: all penalties apply to all detected problems.
121 */
Simon Schillere154ee02021-02-16 10:29:29 -0800122 public static final class Builder {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000123 private final Set<Flag> mFlags;
124 private OnViolationListener mListener;
125 private final Map<Class<? extends Fragment>,
126 Set<Class<? extends Violation>>> mAllowedViolations;
Simon Schillere154ee02021-02-16 10:29:29 -0800127
128 /** Create a Builder that detects nothing and has no violations. */
129 public Builder() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000130 mFlags = new HashSet<>();
131 mAllowedViolations = new HashMap<>();
Simon Schillere154ee02021-02-16 10:29:29 -0800132 }
133
134 /** Log detected violations to the system log. */
135 @NonNull
136 @SuppressLint("BuilderSetStyle")
137 public Builder penaltyLog() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000138 mFlags.add(Flag.PENALTY_LOG);
Simon Schillere154ee02021-02-16 10:29:29 -0800139 return this;
140 }
141
142 /**
143 * Throws an exception on violation. This penalty runs at the end of all enabled
144 * penalties so you'll still get to see logging or other violations before the exception
145 * is thrown.
146 */
147 @NonNull
148 @SuppressLint("BuilderSetStyle")
149 public Builder penaltyDeath() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000150 mFlags.add(Flag.PENALTY_DEATH);
Simon Schillere154ee02021-02-16 10:29:29 -0800151 return this;
152 }
153
154 /**
155 * Call #{@link OnViolationListener#onViolation} for every violation. The listener will
Simon Schiller39b483d2021-03-02 17:08:35 +0000156 * be called on the main thread of the fragment host.
Simon Schillere154ee02021-02-16 10:29:29 -0800157 */
158 @NonNull
159 @SuppressLint("BuilderSetStyle")
160 public Builder penaltyListener(@NonNull OnViolationListener listener) {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000161 this.mListener = listener;
Simon Schillere154ee02021-02-16 10:29:29 -0800162 return this;
163 }
164
Simon Schiller97198852021-03-23 17:04:29 +0100165 /**
166 * Detects cases, where a #{@link Fragment} instance is reused, after it was previously
167 * removed from a #{@link FragmentManager}.
168 */
169 @NonNull
170 @SuppressLint("BuilderSetStyle")
171 public Builder detectFragmentReuse() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000172 mFlags.add(Flag.DETECT_FRAGMENT_REUSE);
Simon Schiller97198852021-03-23 17:04:29 +0100173 return this;
174 }
175
Simon Schiller57ca2212021-03-22 16:09:30 -0700176 /** Detects usage of the &lt;fragment&gt; tag inside XML layouts. */
177 @NonNull
178 @SuppressLint("BuilderSetStyle")
179 public Builder detectFragmentTagUsage() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000180 mFlags.add(Flag.DETECT_FRAGMENT_TAG_USAGE);
Simon Schiller57ca2212021-03-22 16:09:30 -0700181 return this;
182 }
183
Simon Schiller909ebda2021-03-16 12:52:26 -0700184 /**
185 * Detects calls to #{@link Fragment#setRetainInstance} and
186 * #{@link Fragment#getRetainInstance()}.
187 */
188 @NonNull
189 @SuppressLint("BuilderSetStyle")
190 public Builder detectRetainInstanceUsage() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000191 mFlags.add(Flag.DETECT_RETAIN_INSTANCE_USAGE);
Simon Schiller909ebda2021-03-16 12:52:26 -0700192 return this;
193 }
194
Simon Schillercb3b96b2021-03-03 23:36:49 -0800195 /** Detects calls to #{@link Fragment#setUserVisibleHint}. */
196 @NonNull
197 @SuppressLint("BuilderSetStyle")
198 public Builder detectSetUserVisibleHint() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000199 mFlags.add(Flag.DETECT_SET_USER_VISIBLE_HINT);
Simon Schillercb3b96b2021-03-03 23:36:49 -0800200 return this;
201 }
202
Simon Schillere154ee02021-02-16 10:29:29 -0800203 /**
Simon Schillerf7967822021-03-22 01:26:40 -0700204 * Detects calls to #{@link Fragment#setTargetFragment},
205 * #{@link Fragment#getTargetFragment()} and #{@link Fragment#getTargetRequestCode()}.
206 */
207 @NonNull
208 @SuppressLint("BuilderSetStyle")
209 public Builder detectTargetFragmentUsage() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000210 mFlags.add(Flag.DETECT_TARGET_FRAGMENT_USAGE);
Simon Schillerf7967822021-03-22 01:26:40 -0700211 return this;
212 }
213
214 /**
Simon Schillerdf2d4fd2021-03-29 14:48:42 -0700215 * Detects cases where a #{@link Fragment} is added to a container other than a
216 * #{@link FragmentContainerView}.
217 */
218 @NonNull
219 @SuppressLint("BuilderSetStyle")
220 public Builder detectWrongFragmentContainer() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000221 mFlags.add(Flag.DETECT_WRONG_FRAGMENT_CONTAINER);
222 return this;
223 }
224
225 /**
226 * Allow the specified {@link Fragment} class to bypass penalties for the
227 * specified {@link Violation}, if detected.
228 *
229 * By default, all {@link Fragment} classes will incur penalties for any
230 * detected {@link Violation}.
231 */
232 @NonNull
233 @SuppressLint("BuilderSetStyle")
234 public Builder allowViolation(
235 @NonNull Class<? extends Fragment> fragmentClass,
236 @NonNull Class<? extends Violation> violationClass) {
237 Set<Class<? extends Violation>> violationsToBypass =
238 mAllowedViolations.get(fragmentClass);
239 if (violationsToBypass == null) {
240 violationsToBypass = new HashSet<>();
241 }
242 violationsToBypass.add(violationClass);
243 mAllowedViolations.put(fragmentClass, violationsToBypass);
Simon Schillerdf2d4fd2021-03-29 14:48:42 -0700244 return this;
245 }
246
247 /**
Simon Schillere154ee02021-02-16 10:29:29 -0800248 * Construct the Policy instance.
249 *
250 * <p>Note: if no penalties are enabled before calling <code>build</code>, {@link
251 * #penaltyLog} is implicitly set.
252 */
253 @NonNull
254 public Policy build() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000255 if (mListener == null && !mFlags.contains(Flag.PENALTY_DEATH)) {
Simon Schillere154ee02021-02-16 10:29:29 -0800256 penaltyLog();
257 }
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000258 return new Policy(mFlags, mListener, mAllowedViolations);
Simon Schillere154ee02021-02-16 10:29:29 -0800259 }
260 }
261 }
262
263 /** Returns the current default policy. */
264 @NonNull
265 public static Policy getDefaultPolicy() {
266 return defaultPolicy;
267 }
268
269 /**
270 * Sets the policy for what actions should be detected, as well as the penalty if such actions
271 * occur.
272 *
273 * @param policy the policy to put into place
274 */
275 public static void setDefaultPolicy(@NonNull Policy policy) {
276 defaultPolicy = policy;
277 }
278
279 private static Policy getNearestPolicy(@Nullable Fragment fragment) {
280 while (fragment != null) {
281 if (fragment.isAdded()) {
282 FragmentManager fragmentManager = fragment.getParentFragmentManager();
283 if (fragmentManager.getStrictModePolicy() != null) {
284 return fragmentManager.getStrictModePolicy();
285 }
286 }
287 fragment = fragment.getParentFragment();
288 }
289 return defaultPolicy;
290 }
291
Simon Schillercb3b96b2021-03-03 23:36:49 -0800292 @RestrictTo(RestrictTo.Scope.LIBRARY)
Simon Schiller97198852021-03-23 17:04:29 +0100293 public static void onFragmentReuse(@NonNull Fragment fragment) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000294 Violation violation = new FragmentReuseViolation();
295 logIfDebuggingEnabled(fragment.getClass().getName(), violation);
296
Simon Schiller97198852021-03-23 17:04:29 +0100297 Policy policy = getNearestPolicy(fragment);
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000298 if (policy.mFlags.contains(Flag.DETECT_FRAGMENT_REUSE)
299 && shouldHandlePolicyViolation(
300 fragment.getClass(), policy, violation.getClass())) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000301 handlePolicyViolation(fragment, policy, violation);
Simon Schiller97198852021-03-23 17:04:29 +0100302 }
303 }
304
305 @RestrictTo(RestrictTo.Scope.LIBRARY)
Simon Schiller57ca2212021-03-22 16:09:30 -0700306 public static void onFragmentTagUsage(@NonNull Fragment fragment) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000307 Violation violation = new FragmentTagUsageViolation();
308 logIfDebuggingEnabled(fragment.getClass().getName(), violation);
309
Simon Schiller57ca2212021-03-22 16:09:30 -0700310 Policy policy = getNearestPolicy(fragment);
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000311 if (policy.mFlags.contains(Flag.DETECT_FRAGMENT_TAG_USAGE)
312 && shouldHandlePolicyViolation(
313 fragment.getClass(), policy, violation.getClass())) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000314 handlePolicyViolation(fragment, policy, violation);
Simon Schiller57ca2212021-03-22 16:09:30 -0700315 }
316 }
317
318 @RestrictTo(RestrictTo.Scope.LIBRARY)
Simon Schiller909ebda2021-03-16 12:52:26 -0700319 public static void onRetainInstanceUsage(@NonNull Fragment fragment) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000320 Violation violation = new RetainInstanceUsageViolation();
321 logIfDebuggingEnabled(fragment.getClass().getName(), violation);
322
Simon Schiller909ebda2021-03-16 12:52:26 -0700323 Policy policy = getNearestPolicy(fragment);
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000324 if (policy.mFlags.contains(Flag.DETECT_RETAIN_INSTANCE_USAGE)
325 && shouldHandlePolicyViolation(
326 fragment.getClass(), policy, violation.getClass())) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000327 handlePolicyViolation(fragment, policy, violation);
Simon Schiller909ebda2021-03-16 12:52:26 -0700328 }
329 }
330
331 @RestrictTo(RestrictTo.Scope.LIBRARY)
Simon Schillercb3b96b2021-03-03 23:36:49 -0800332 public static void onSetUserVisibleHint(@NonNull Fragment fragment) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000333 Violation violation = new SetUserVisibleHintViolation();
334 logIfDebuggingEnabled(fragment.getClass().getName(), violation);
335
Simon Schillercb3b96b2021-03-03 23:36:49 -0800336 Policy policy = getNearestPolicy(fragment);
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000337 if (policy.mFlags.contains(Flag.DETECT_SET_USER_VISIBLE_HINT)
338 && shouldHandlePolicyViolation(
339 fragment.getClass(), policy, violation.getClass())) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000340 handlePolicyViolation(fragment, policy, violation);
Simon Schillercb3b96b2021-03-03 23:36:49 -0800341 }
342 }
343
Simon Schillerf7967822021-03-22 01:26:40 -0700344 @RestrictTo(RestrictTo.Scope.LIBRARY)
345 public static void onTargetFragmentUsage(@NonNull Fragment fragment) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000346 Violation violation = new TargetFragmentUsageViolation();
347 logIfDebuggingEnabled(fragment.getClass().getName(), violation);
348
Simon Schillerf7967822021-03-22 01:26:40 -0700349 Policy policy = getNearestPolicy(fragment);
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000350 if (policy.mFlags.contains(Flag.DETECT_TARGET_FRAGMENT_USAGE)
351 && shouldHandlePolicyViolation(
352 fragment.getClass(), policy, violation.getClass())) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000353 handlePolicyViolation(fragment, policy, violation);
Simon Schillerf7967822021-03-22 01:26:40 -0700354 }
355 }
356
Simon Schillerdf2d4fd2021-03-29 14:48:42 -0700357 @RestrictTo(RestrictTo.Scope.LIBRARY)
358 public static void onWrongFragmentContainer(@NonNull Fragment fragment) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000359 Violation violation = new WrongFragmentContainerViolation();
360 logIfDebuggingEnabled(fragment.getClass().getName(), violation);
361
Simon Schillerdf2d4fd2021-03-29 14:48:42 -0700362 Policy policy = getNearestPolicy(fragment);
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000363 if (policy.mFlags.contains(Flag.DETECT_WRONG_FRAGMENT_CONTAINER)
364 && shouldHandlePolicyViolation(
365 fragment.getClass(), policy, violation.getClass())) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000366 handlePolicyViolation(fragment, policy, violation);
Simon Schillerdf2d4fd2021-03-29 14:48:42 -0700367 }
368 }
369
Simon Schillere154ee02021-02-16 10:29:29 -0800370 @VisibleForTesting
Simon Schillercb3b96b2021-03-03 23:36:49 -0800371 static void onPolicyViolation(@NonNull Fragment fragment, @NonNull Violation violation) {
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000372 logIfDebuggingEnabled(fragment.getClass().getName(), violation);
373
Simon Schillercb3b96b2021-03-03 23:36:49 -0800374 Policy policy = getNearestPolicy(fragment);
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000375 if (shouldHandlePolicyViolation(fragment.getClass(), policy, violation.getClass())) {
376 handlePolicyViolation(fragment, policy, violation);
377 }
Simon Schillercb3b96b2021-03-03 23:36:49 -0800378 }
379
Sanura N'Jaka74dde5c2021-05-13 16:10:55 +0000380 private static void logIfDebuggingEnabled(
381 @NonNull String fragmentName,
382 @NonNull final Violation violation
383 ) {
384 if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
385 Log.d(FragmentManager.TAG, "StrictMode violation in " + fragmentName,
386 violation);
387 }
388 }
389
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000390 private static boolean shouldHandlePolicyViolation(
391 @NonNull Class<? extends Fragment> fragmentClass,
392 @NonNull final Policy policy,
393 @NonNull Class<? extends Violation> violationClass) {
394 Set<Class<? extends Violation>> violationsToBypass =
395 policy.mAllowedViolations.get(fragmentClass);
396 return violationsToBypass == null || !violationsToBypass.contains(violationClass);
397 }
398
Simon Schillercb3b96b2021-03-03 23:36:49 -0800399 private static void handlePolicyViolation(
400 @NonNull Fragment fragment,
401 @NonNull final Policy policy,
402 @NonNull final Violation violation
403 ) {
Simon Schiller39b483d2021-03-02 17:08:35 +0000404 final String fragmentName = fragment.getClass().getName();
Simon Schillere154ee02021-02-16 10:29:29 -0800405
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000406 if (policy.mFlags.contains(Flag.PENALTY_LOG)) {
Simon Schillere154ee02021-02-16 10:29:29 -0800407 Log.d(TAG, "Policy violation in " + fragmentName, violation);
408 }
409
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000410 if (policy.mListener != null) {
Simon Schiller39b483d2021-03-02 17:08:35 +0000411 runOnHostThread(fragment, new Runnable() {
412 @Override
413 public void run() {
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000414 policy.mListener.onViolation(violation);
Simon Schiller39b483d2021-03-02 17:08:35 +0000415 }
416 });
Simon Schillere154ee02021-02-16 10:29:29 -0800417 }
418
Sanura N'Jaka1c683822021-05-13 01:31:39 +0000419 if (policy.mFlags.contains(Flag.PENALTY_DEATH)) {
Simon Schiller39b483d2021-03-02 17:08:35 +0000420 runOnHostThread(fragment, new Runnable() {
421 @Override
422 public void run() {
423 Log.e(TAG, "Policy violation with PENALTY_DEATH in " + fragmentName, violation);
424 throw violation;
425 }
426 });
427 }
428 }
429
430 private static void runOnHostThread(@NonNull Fragment fragment, @NonNull Runnable runnable) {
431 if (fragment.isAdded()) {
432 Handler handler = fragment.getParentFragmentManager().getHost().getHandler();
433 if (handler.getLooper() == Looper.myLooper()) {
434 runnable.run(); // Already on correct thread -> run synchronously
435 } else {
436 handler.post(runnable); // Switch to correct thread
437 }
438 } else {
439 runnable.run(); // Fragment is not attached to any host -> run synchronously
Simon Schillere154ee02021-02-16 10:29:29 -0800440 }
441 }
442}