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