• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Dagger Authors.
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 dagger.hilt.android.internal.managers;
18 
19 import androidx.lifecycle.ViewModel;
20 import androidx.lifecycle.ViewModelProvider;
21 import androidx.lifecycle.ViewModelStoreOwner;
22 import android.content.Context;
23 import androidx.annotation.NonNull;
24 import androidx.annotation.Nullable;
25 import androidx.activity.ComponentActivity;
26 import dagger.Binds;
27 import dagger.Module;
28 import dagger.hilt.EntryPoint;
29 import dagger.hilt.EntryPoints;
30 import dagger.hilt.InstallIn;
31 import dagger.hilt.android.ActivityRetainedLifecycle;
32 import dagger.hilt.android.components.ActivityRetainedComponent;
33 import dagger.hilt.android.internal.ThreadUtil;
34 import dagger.hilt.android.internal.builders.ActivityRetainedComponentBuilder;
35 import dagger.hilt.android.scopes.ActivityRetainedScoped;
36 import dagger.hilt.components.SingletonComponent;
37 import dagger.hilt.internal.GeneratedComponentManager;
38 import java.util.HashSet;
39 import java.util.Set;
40 import javax.inject.Inject;
41 
42 /** A manager for the creation of components that survives activity configuration changes. */
43 final class ActivityRetainedComponentManager
44     implements GeneratedComponentManager<ActivityRetainedComponent> {
45 
46   /** Entry point for {@link ActivityRetainedComponentBuilder}. */
47   @EntryPoint
48   @InstallIn(SingletonComponent.class)
49   public interface ActivityRetainedComponentBuilderEntryPoint {
retainedComponentBuilder()50     ActivityRetainedComponentBuilder retainedComponentBuilder();
51   }
52 
53   /** Entry point for {@link Lifecycle}. */
54   @EntryPoint
55   @InstallIn(ActivityRetainedComponent.class)
56   public interface ActivityRetainedLifecycleEntryPoint {
getActivityRetainedLifecycle()57     ActivityRetainedLifecycle getActivityRetainedLifecycle();
58   }
59 
60   static final class ActivityRetainedComponentViewModel extends ViewModel {
61     private final ActivityRetainedComponent component;
62 
ActivityRetainedComponentViewModel(ActivityRetainedComponent component)63     ActivityRetainedComponentViewModel(ActivityRetainedComponent component) {
64       this.component = component;
65     }
66 
getComponent()67     ActivityRetainedComponent getComponent() {
68       return component;
69     }
70 
71     @Override
onCleared()72     protected void onCleared() {
73       super.onCleared();
74       ActivityRetainedLifecycle lifecycle =
75           EntryPoints.get(component, ActivityRetainedLifecycleEntryPoint.class)
76               .getActivityRetainedLifecycle();
77       ((ActivityRetainedComponentManager.Lifecycle) lifecycle).dispatchOnCleared();
78     }
79   }
80 
81   private final ViewModelProvider viewModelProvider;
82 
83   @Nullable private volatile ActivityRetainedComponent component;
84   private final Object componentLock = new Object();
85 
ActivityRetainedComponentManager(ComponentActivity activity)86   ActivityRetainedComponentManager(ComponentActivity activity) {
87     this.viewModelProvider = getViewModelProvider(activity, activity.getApplication());
88   }
89 
getViewModelProvider( ViewModelStoreOwner owner, Context applicationContext)90   private ViewModelProvider getViewModelProvider(
91       ViewModelStoreOwner owner, Context applicationContext) {
92     return new ViewModelProvider(
93         owner,
94         new ViewModelProvider.Factory() {
95           @NonNull
96           @Override
97           @SuppressWarnings("unchecked")
98           public <T extends ViewModel> T create(@NonNull Class<T> aClass) {
99             ActivityRetainedComponent component =
100                 EntryPoints.get(
101                         applicationContext, ActivityRetainedComponentBuilderEntryPoint.class)
102                     .retainedComponentBuilder()
103                     .build();
104             return (T) new ActivityRetainedComponentViewModel(component);
105           }
106         });
107   }
108 
109   @Override
110   public ActivityRetainedComponent generatedComponent() {
111     if (component == null) {
112       synchronized (componentLock) {
113         if (component == null) {
114           component = createComponent();
115         }
116       }
117     }
118     return component;
119   }
120 
121   private ActivityRetainedComponent createComponent() {
122     return viewModelProvider.get(ActivityRetainedComponentViewModel.class).getComponent();
123   }
124 
125   /** The default implementation of {@link ActivityRetainedLifecycle}. */
126   @ActivityRetainedScoped
127   static final class Lifecycle implements ActivityRetainedLifecycle {
128 
129     private final Set<OnClearedListener> listeners = new HashSet<>();
130     private boolean onClearedDispatched = false;
131 
132     @Inject
133     Lifecycle() {}
134 
135     @Override
136     public void addOnClearedListener(@NonNull OnClearedListener listener) {
137       ThreadUtil.ensureMainThread();
138       throwIfOnClearedDispatched();
139       listeners.add(listener);
140     }
141 
142     @Override
143     public void removeOnClearedListener(@NonNull OnClearedListener listener) {
144       ThreadUtil.ensureMainThread();
145       throwIfOnClearedDispatched();
146       listeners.remove(listener);
147     }
148 
149     void dispatchOnCleared() {
150       ThreadUtil.ensureMainThread();
151       onClearedDispatched = true;
152       for (OnClearedListener listener : listeners) {
153         listener.onCleared();
154       }
155     }
156 
157     private void throwIfOnClearedDispatched() {
158       if (onClearedDispatched) {
159         throw new IllegalStateException(
160             "There was a race between the call to add/remove an OnClearedListener and onCleared(). "
161                 + "This can happen when posting to the Main thread from a background thread, "
162                 + "which is not supported.");
163       }
164     }
165   }
166 
167   @Module
168   @InstallIn(ActivityRetainedComponent.class)
169   abstract static class LifecycleModule {
170     @Binds
171     abstract ActivityRetainedLifecycle bind(Lifecycle impl);
172   }
173 }
174