Import Android SDK Platform P [4386628]
/google/data/ro/projects/android/fetch_artifact \
--bid 4386628 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4386628.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: I9b8400ac92116cae4f033d173f7a5682b26ccba9
diff --git a/android/arch/lifecycle/ClassesInfoCache.java b/android/arch/lifecycle/ClassesInfoCache.java
new file mode 100644
index 0000000..f077dae
--- /dev/null
+++ b/android/arch/lifecycle/ClassesInfoCache.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.Nullable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Reflection is expensive, so we cache information about methods
+ * for {@link ReflectiveGenericLifecycleObserver}, so it can call them,
+ * and for {@link Lifecycling} to determine which observer adapter to use.
+ */
+class ClassesInfoCache {
+
+ static ClassesInfoCache sInstance = new ClassesInfoCache();
+
+ private static final int CALL_TYPE_NO_ARG = 0;
+ private static final int CALL_TYPE_PROVIDER = 1;
+ private static final int CALL_TYPE_PROVIDER_WITH_EVENT = 2;
+
+ private final Map<Class, CallbackInfo> mCallbackMap = new HashMap<>();
+ private final Map<Class, Boolean> mHasLifecycleMethods = new HashMap<>();
+
+ boolean hasLifecycleMethods(Class klass) {
+ if (mHasLifecycleMethods.containsKey(klass)) {
+ return mHasLifecycleMethods.get(klass);
+ }
+
+ Method[] methods = klass.getDeclaredMethods();
+ for (Method method : methods) {
+ OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
+ if (annotation != null) {
+ // Optimization for reflection, we know that this method is called
+ // when there is no generated adapter. But there are methods with @OnLifecycleEvent
+ // so we know that will use ReflectiveGenericLifecycleObserver,
+ // so we createInfo in advance.
+ // CreateInfo always initialize mHasLifecycleMethods for a class, so we don't do it
+ // here.
+ createInfo(klass, methods);
+ return true;
+ }
+ }
+ mHasLifecycleMethods.put(klass, false);
+ return false;
+ }
+
+ CallbackInfo getInfo(Class klass) {
+ CallbackInfo existing = mCallbackMap.get(klass);
+ if (existing != null) {
+ return existing;
+ }
+ existing = createInfo(klass, null);
+ return existing;
+ }
+
+ private void verifyAndPutHandler(Map<MethodReference, Lifecycle.Event> handlers,
+ MethodReference newHandler, Lifecycle.Event newEvent, Class klass) {
+ Lifecycle.Event event = handlers.get(newHandler);
+ if (event != null && newEvent != event) {
+ Method method = newHandler.mMethod;
+ throw new IllegalArgumentException(
+ "Method " + method.getName() + " in " + klass.getName()
+ + " already declared with different @OnLifecycleEvent value: previous"
+ + " value " + event + ", new value " + newEvent);
+ }
+ if (event == null) {
+ handlers.put(newHandler, newEvent);
+ }
+ }
+
+ private CallbackInfo createInfo(Class klass, @Nullable Method[] declaredMethods) {
+ Class superclass = klass.getSuperclass();
+ Map<MethodReference, Lifecycle.Event> handlerToEvent = new HashMap<>();
+ if (superclass != null) {
+ CallbackInfo superInfo = getInfo(superclass);
+ if (superInfo != null) {
+ handlerToEvent.putAll(superInfo.mHandlerToEvent);
+ }
+ }
+
+ Class[] interfaces = klass.getInterfaces();
+ for (Class intrfc : interfaces) {
+ for (Map.Entry<MethodReference, Lifecycle.Event> entry : getInfo(
+ intrfc).mHandlerToEvent.entrySet()) {
+ verifyAndPutHandler(handlerToEvent, entry.getKey(), entry.getValue(), klass);
+ }
+ }
+
+ Method[] methods = declaredMethods != null ? declaredMethods : klass.getDeclaredMethods();
+ boolean hasLifecycleMethods = false;
+ for (Method method : methods) {
+ OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
+ if (annotation == null) {
+ continue;
+ }
+ hasLifecycleMethods = true;
+ Class<?>[] params = method.getParameterTypes();
+ int callType = CALL_TYPE_NO_ARG;
+ if (params.length > 0) {
+ callType = CALL_TYPE_PROVIDER;
+ if (!params[0].isAssignableFrom(LifecycleOwner.class)) {
+ throw new IllegalArgumentException(
+ "invalid parameter type. Must be one and instanceof LifecycleOwner");
+ }
+ }
+ Lifecycle.Event event = annotation.value();
+
+ if (params.length > 1) {
+ callType = CALL_TYPE_PROVIDER_WITH_EVENT;
+ if (!params[1].isAssignableFrom(Lifecycle.Event.class)) {
+ throw new IllegalArgumentException(
+ "invalid parameter type. second arg must be an event");
+ }
+ if (event != Lifecycle.Event.ON_ANY) {
+ throw new IllegalArgumentException(
+ "Second arg is supported only for ON_ANY value");
+ }
+ }
+ if (params.length > 2) {
+ throw new IllegalArgumentException("cannot have more than 2 params");
+ }
+ MethodReference methodReference = new MethodReference(callType, method);
+ verifyAndPutHandler(handlerToEvent, methodReference, event, klass);
+ }
+ CallbackInfo info = new CallbackInfo(handlerToEvent);
+ mCallbackMap.put(klass, info);
+ mHasLifecycleMethods.put(klass, hasLifecycleMethods);
+ return info;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ static class CallbackInfo {
+ final Map<Lifecycle.Event, List<MethodReference>> mEventToHandlers;
+ final Map<MethodReference, Lifecycle.Event> mHandlerToEvent;
+
+ CallbackInfo(Map<MethodReference, Lifecycle.Event> handlerToEvent) {
+ mHandlerToEvent = handlerToEvent;
+ mEventToHandlers = new HashMap<>();
+ for (Map.Entry<MethodReference, Lifecycle.Event> entry : handlerToEvent.entrySet()) {
+ Lifecycle.Event event = entry.getValue();
+ List<MethodReference> methodReferences = mEventToHandlers.get(event);
+ if (methodReferences == null) {
+ methodReferences = new ArrayList<>();
+ mEventToHandlers.put(event, methodReferences);
+ }
+ methodReferences.add(entry.getKey());
+ }
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ void invokeCallbacks(LifecycleOwner source, Lifecycle.Event event, Object target) {
+ invokeMethodsForEvent(mEventToHandlers.get(event), source, event, target);
+ invokeMethodsForEvent(mEventToHandlers.get(Lifecycle.Event.ON_ANY), source, event,
+ target);
+ }
+
+ private static void invokeMethodsForEvent(List<MethodReference> handlers,
+ LifecycleOwner source, Lifecycle.Event event, Object mWrapped) {
+ if (handlers != null) {
+ for (int i = handlers.size() - 1; i >= 0; i--) {
+ handlers.get(i).invokeCallback(source, event, mWrapped);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ static class MethodReference {
+ final int mCallType;
+ final Method mMethod;
+
+ MethodReference(int callType, Method method) {
+ mCallType = callType;
+ mMethod = method;
+ mMethod.setAccessible(true);
+ }
+
+ void invokeCallback(LifecycleOwner source, Lifecycle.Event event, Object target) {
+ //noinspection TryWithIdenticalCatches
+ try {
+ switch (mCallType) {
+ case CALL_TYPE_NO_ARG:
+ mMethod.invoke(target);
+ break;
+ case CALL_TYPE_PROVIDER:
+ mMethod.invoke(target, source);
+ break;
+ case CALL_TYPE_PROVIDER_WITH_EVENT:
+ mMethod.invoke(target, source, event);
+ break;
+ }
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException("Failed to call observer method", e.getCause());
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ MethodReference that = (MethodReference) o;
+ return mCallType == that.mCallType && mMethod.getName().equals(that.mMethod.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * mCallType + mMethod.getName().hashCode();
+ }
+ }
+}
diff --git a/android/arch/lifecycle/CompositeGeneratedAdaptersObserver.java b/android/arch/lifecycle/CompositeGeneratedAdaptersObserver.java
new file mode 100644
index 0000000..e8cbe7c
--- /dev/null
+++ b/android/arch/lifecycle/CompositeGeneratedAdaptersObserver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+
+import android.support.annotation.RestrictTo;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class CompositeGeneratedAdaptersObserver implements GenericLifecycleObserver {
+
+ private final GeneratedAdapter[] mGeneratedAdapters;
+
+ CompositeGeneratedAdaptersObserver(GeneratedAdapter[] generatedAdapters) {
+ mGeneratedAdapters = generatedAdapters;
+ }
+
+ @Override
+ public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
+ MethodCallsLogger logger = new MethodCallsLogger();
+ for (GeneratedAdapter mGenerated: mGeneratedAdapters) {
+ mGenerated.callMethods(source, event, false, logger);
+ }
+ for (GeneratedAdapter mGenerated: mGeneratedAdapters) {
+ mGenerated.callMethods(source, event, true, logger);
+ }
+ }
+}
diff --git a/android/arch/lifecycle/ComputableLiveData.java b/android/arch/lifecycle/ComputableLiveData.java
index fe18243..f135244 100644
--- a/android/arch/lifecycle/ComputableLiveData.java
+++ b/android/arch/lifecycle/ComputableLiveData.java
@@ -1,136 +1,9 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+//ComputableLiveData interface for tests
package android.arch.lifecycle;
-
-import android.arch.core.executor.AppToolkitTaskExecutor;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.annotation.WorkerThread;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A LiveData class that can be invalidated & computed on demand.
- * <p>
- * This is an internal class for now, might be public if we see the necessity.
- *
- * @param <T> The type of the live data
- * @hide internal
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+import android.arch.lifecycle.LiveData;
public abstract class ComputableLiveData<T> {
-
- private final LiveData<T> mLiveData;
-
- private AtomicBoolean mInvalid = new AtomicBoolean(true);
- private AtomicBoolean mComputing = new AtomicBoolean(false);
-
- /**
- * Creates a computable live data which is computed when there are active observers.
- * <p>
- * It can also be invalidated via {@link #invalidate()} which will result in a call to
- * {@link #compute()} if there are active observers (or when they start observing)
- */
- @SuppressWarnings("WeakerAccess")
- public ComputableLiveData() {
- mLiveData = new LiveData<T>() {
- @Override
- protected void onActive() {
- // TODO if we make this class public, we should accept an executor
- AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
- }
- };
- }
-
- /**
- * Returns the LiveData managed by this class.
- *
- * @return A LiveData that is controlled by ComputableLiveData.
- */
- @SuppressWarnings("WeakerAccess")
- @NonNull
- public LiveData<T> getLiveData() {
- return mLiveData;
- }
-
- @VisibleForTesting
- final Runnable mRefreshRunnable = new Runnable() {
- @WorkerThread
- @Override
- public void run() {
- boolean computed;
- do {
- computed = false;
- // compute can happen only in 1 thread but no reason to lock others.
- if (mComputing.compareAndSet(false, true)) {
- // as long as it is invalid, keep computing.
- try {
- T value = null;
- while (mInvalid.compareAndSet(true, false)) {
- computed = true;
- value = compute();
- }
- if (computed) {
- mLiveData.postValue(value);
- }
- } finally {
- // release compute lock
- mComputing.set(false);
- }
- }
- // check invalid after releasing compute lock to avoid the following scenario.
- // Thread A runs compute()
- // Thread A checks invalid, it is false
- // Main thread sets invalid to true
- // Thread B runs, fails to acquire compute lock and skips
- // Thread A releases compute lock
- // We've left invalid in set state. The check below recovers.
- } while (computed && mInvalid.get());
- }
- };
-
- // invalidation check always happens on the main thread
- @VisibleForTesting
- final Runnable mInvalidationRunnable = new Runnable() {
- @MainThread
- @Override
- public void run() {
- boolean isActive = mLiveData.hasActiveObservers();
- if (mInvalid.compareAndSet(false, true)) {
- if (isActive) {
- // TODO if we make this class public, we should accept an executor.
- AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
- }
- }
- }
- };
-
- /**
- * Invalidates the LiveData.
- * <p>
- * When there are active observers, this will trigger a call to {@link #compute()}.
- */
- public void invalidate() {
- AppToolkitTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
- }
-
- @SuppressWarnings("WeakerAccess")
- @WorkerThread
- protected abstract T compute();
+ public ComputableLiveData(){}
+ abstract protected T compute();
+ public LiveData<T> getLiveData() {return null;}
+ public void invalidate() {}
}
diff --git a/android/arch/lifecycle/ComputableLiveDataTest.java b/android/arch/lifecycle/ComputableLiveDataTest.java
index 0a3fbed..eb89d8d 100644
--- a/android/arch/lifecycle/ComputableLiveDataTest.java
+++ b/android/arch/lifecycle/ComputableLiveDataTest.java
@@ -27,7 +27,7 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
-import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.ArchTaskExecutor;
import android.arch.core.executor.TaskExecutor;
import android.arch.core.executor.TaskExecutorWithFakeMainThread;
import android.arch.lifecycle.util.InstantTaskExecutor;
@@ -58,12 +58,12 @@
@Before
public void swapExecutorDelegate() {
mTaskExecutor = spy(new InstantTaskExecutor());
- AppToolkitTaskExecutor.getInstance().setDelegate(mTaskExecutor);
+ ArchTaskExecutor.getInstance().setDelegate(mTaskExecutor);
}
@After
public void removeExecutorDelegate() {
- AppToolkitTaskExecutor.getInstance().setDelegate(null);
+ ArchTaskExecutor.getInstance().setDelegate(null);
}
@Test
@@ -76,7 +76,7 @@
@Test
public void noConcurrentCompute() throws InterruptedException {
TaskExecutorWithFakeMainThread executor = new TaskExecutorWithFakeMainThread(2);
- AppToolkitTaskExecutor.getInstance().setDelegate(executor);
+ ArchTaskExecutor.getInstance().setDelegate(executor);
try {
// # of compute calls
final Semaphore computeCounter = new Semaphore(0);
@@ -121,7 +121,7 @@
// assert no other results arrive
verify(observer, never()).onChanged(anyInt());
} finally {
- AppToolkitTaskExecutor.getInstance().setDelegate(null);
+ ArchTaskExecutor.getInstance().setDelegate(null);
}
}
diff --git a/android/arch/lifecycle/DefaultLifecycleObserver.java b/android/arch/lifecycle/DefaultLifecycleObserver.java
new file mode 100644
index 0000000..b6f468c
--- /dev/null
+++ b/android/arch/lifecycle/DefaultLifecycleObserver.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.NonNull;
+
+/**
+ * Callback interface for listening to {@link LifecycleOwner} state changes.
+ * <p>
+ * If you use Java 8 language, <b>always</b> prefer it over annotations.
+ */
+@SuppressWarnings("unused")
+public interface DefaultLifecycleObserver extends FullLifecycleObserver {
+
+ /**
+ * Notifies that {@code ON_CREATE} event occurred.
+ * <p>
+ * This method will be called after the {@link LifecycleOwner}'s {@code onCreate}
+ * method returns.
+ *
+ * @param owner the component, whose state was changed
+ */
+ @Override
+ default void onCreate(@NonNull LifecycleOwner owner) {
+ }
+
+ /**
+ * Notifies that {@code ON_START} event occurred.
+ * <p>
+ * This method will be called after the {@link LifecycleOwner}'s {@code onStart} method returns.
+ *
+ * @param owner the component, whose state was changed
+ */
+ @Override
+ default void onStart(@NonNull LifecycleOwner owner) {
+ }
+
+ /**
+ * Notifies that {@code ON_RESUME} event occurred.
+ * <p>
+ * This method will be called after the {@link LifecycleOwner}'s {@code onResume}
+ * method returns.
+ *
+ * @param owner the component, whose state was changed
+ */
+ @Override
+ default void onResume(@NonNull LifecycleOwner owner) {
+ }
+
+ /**
+ * Notifies that {@code ON_PAUSE} event occurred.
+ * <p>
+ * This method will be called before the {@link LifecycleOwner}'s {@code onPause} method
+ * is called.
+ *
+ * @param owner the component, whose state was changed
+ */
+ @Override
+ default void onPause(@NonNull LifecycleOwner owner) {
+ }
+
+ /**
+ * Notifies that {@code ON_STOP} event occurred.
+ * <p>
+ * This method will be called before the {@link LifecycleOwner}'s {@code onStop} method
+ * is called.
+ *
+ * @param owner the component, whose state was changed
+ */
+ @Override
+ default void onStop(@NonNull LifecycleOwner owner) {
+ }
+
+ /**
+ * Notifies that {@code ON_DESTROY} event occurred.
+ * <p>
+ * This method will be called before the {@link LifecycleOwner}'s {@code onStop} method
+ * is called.
+ *
+ * @param owner the component, whose state was changed
+ */
+ @Override
+ default void onDestroy(@NonNull LifecycleOwner owner) {
+ }
+}
+
diff --git a/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java b/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java
index 3397f5f..f48f788 100644
--- a/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java
+++ b/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java
@@ -37,6 +37,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
@@ -58,20 +59,20 @@
final ArrayList<Event> collectedEvents = new ArrayList<>();
LifecycleObserver collectingObserver = new LifecycleObserver() {
@OnLifecycleEvent(Event.ON_ANY)
- void onAny(LifecycleOwner owner, Event event) {
+ void onAny(@SuppressWarnings("unused") LifecycleOwner owner, Event event) {
collectedEvents.add(event);
}
};
final FragmentActivity activity = activityTestRule.getActivity();
activityTestRule.runOnUiThread(() -> {
FragmentManager fm = activity.getSupportFragmentManager();
- LifecycleFragment fragment = new LifecycleFragment();
+ Fragment fragment = new Fragment();
fm.beginTransaction().add(R.id.fragment_container, fragment, "tag").addToBackStack(null)
.commit();
fm.executePendingTransactions();
fragment.getLifecycle().addObserver(collectingObserver);
- LifecycleFragment fragment2 = new LifecycleFragment();
+ Fragment fragment2 = new Fragment();
fm.beginTransaction().replace(R.id.fragment_container, fragment2).addToBackStack(null)
.commit();
fm.executePendingTransactions();
@@ -82,12 +83,13 @@
EmptyActivity newActivity = recreateActivity(activityTestRule.getActivity(),
activityTestRule);
+ //noinspection ArraysAsListWithZeroOrOneArgument
assertThat(collectedEvents, is(asList(ON_DESTROY)));
collectedEvents.clear();
EmptyActivity lastActivity = recreateActivity(newActivity, activityTestRule);
activityTestRule.runOnUiThread(() -> {
FragmentManager fm = lastActivity.getSupportFragmentManager();
- LifecycleFragment fragment = (LifecycleFragment) fm.findFragmentByTag("tag");
+ Fragment fragment = fm.findFragmentByTag("tag");
fragment.getLifecycle().addObserver(collectingObserver);
assertThat(collectedEvents, iterableWithSize(0));
fm.popBackStackImmediate();
diff --git a/android/arch/lifecycle/FragmentOperationsLifecycleTest.java b/android/arch/lifecycle/FragmentOperationsLifecycleTest.java
index be062cb..3e61277 100644
--- a/android/arch/lifecycle/FragmentOperationsLifecycleTest.java
+++ b/android/arch/lifecycle/FragmentOperationsLifecycleTest.java
@@ -34,6 +34,7 @@
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import org.junit.Rule;
@@ -55,7 +56,7 @@
@UiThreadTest
public void addRemoveFragment() {
EmptyActivity activity = mActivityTestRule.getActivity();
- LifecycleFragment fragment = new LifecycleFragment();
+ Fragment fragment = new Fragment();
FragmentManager fm = activity.getSupportFragmentManager();
fm.beginTransaction().add(fragment, "tag").commitNow();
CollectingObserver observer = observeAndCollectIn(fragment);
@@ -70,7 +71,7 @@
@UiThreadTest
public void fragmentInBackstack() {
EmptyActivity activity = mActivityTestRule.getActivity();
- LifecycleFragment fragment1 = new LifecycleFragment();
+ Fragment fragment1 = new Fragment();
FragmentManager fm = activity.getSupportFragmentManager();
fm.beginTransaction().add(R.id.fragment_container, fragment1, "tag").addToBackStack(null)
.commit();
@@ -78,7 +79,7 @@
CollectingObserver observer1 = observeAndCollectIn(fragment1);
assertThat(observer1.getEventsAndReset(), is(asList(ON_CREATE, ON_START, ON_RESUME)));
- LifecycleFragment fragment2 = new LifecycleFragment();
+ Fragment fragment2 = new Fragment();
fm.beginTransaction().replace(R.id.fragment_container, fragment2).addToBackStack(null)
.commit();
fm.executePendingTransactions();
@@ -95,7 +96,7 @@
assertThat(observer1.getEventsAndReset(), is(asList(ON_PAUSE, ON_STOP, ON_DESTROY)));
}
- private static CollectingObserver observeAndCollectIn(LifecycleFragment fragment) {
+ private static CollectingObserver observeAndCollectIn(Fragment fragment) {
CollectingObserver observer = new CollectingObserver();
fragment.getLifecycle().addObserver(observer);
return observer;
diff --git a/android/arch/lifecycle/FullLifecycleObserver.java b/android/arch/lifecycle/FullLifecycleObserver.java
new file mode 100644
index 0000000..f179274
--- /dev/null
+++ b/android/arch/lifecycle/FullLifecycleObserver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+interface FullLifecycleObserver extends LifecycleObserver {
+
+ void onCreate(LifecycleOwner owner);
+
+ void onStart(LifecycleOwner owner);
+
+ void onResume(LifecycleOwner owner);
+
+ void onPause(LifecycleOwner owner);
+
+ void onStop(LifecycleOwner owner);
+
+ void onDestroy(LifecycleOwner owner);
+}
diff --git a/android/arch/lifecycle/FullLifecycleObserverAdapter.java b/android/arch/lifecycle/FullLifecycleObserverAdapter.java
new file mode 100644
index 0000000..0a91a66
--- /dev/null
+++ b/android/arch/lifecycle/FullLifecycleObserverAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+class FullLifecycleObserverAdapter implements GenericLifecycleObserver {
+
+ private final FullLifecycleObserver mObserver;
+
+ FullLifecycleObserverAdapter(FullLifecycleObserver observer) {
+ mObserver = observer;
+ }
+
+ @Override
+ public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
+ switch (event) {
+ case ON_CREATE:
+ mObserver.onCreate(source);
+ break;
+ case ON_START:
+ mObserver.onStart(source);
+ break;
+ case ON_RESUME:
+ mObserver.onResume(source);
+ break;
+ case ON_PAUSE:
+ mObserver.onPause(source);
+ break;
+ case ON_STOP:
+ mObserver.onStop(source);
+ break;
+ case ON_DESTROY:
+ mObserver.onDestroy(source);
+ break;
+ case ON_ANY:
+ throw new IllegalArgumentException("ON_ANY must not been send by anybody");
+ }
+ }
+}
diff --git a/android/arch/lifecycle/FullLifecycleObserverTest.java b/android/arch/lifecycle/FullLifecycleObserverTest.java
new file mode 100644
index 0000000..def6755
--- /dev/null
+++ b/android/arch/lifecycle/FullLifecycleObserverTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.Lifecycle.State.CREATED;
+import static android.arch.lifecycle.Lifecycle.State.INITIALIZED;
+import static android.arch.lifecycle.Lifecycle.State.RESUMED;
+import static android.arch.lifecycle.Lifecycle.State.STARTED;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+
+@RunWith(JUnit4.class)
+public class FullLifecycleObserverTest {
+ private LifecycleOwner mOwner;
+ private Lifecycle mLifecycle;
+
+ @Before
+ public void initMocks() {
+ mOwner = mock(LifecycleOwner.class);
+ mLifecycle = mock(Lifecycle.class);
+ when(mOwner.getLifecycle()).thenReturn(mLifecycle);
+ }
+
+ @Test
+ public void eachEvent() {
+ FullLifecycleObserver obj = mock(FullLifecycleObserver.class);
+ FullLifecycleObserverAdapter observer = new FullLifecycleObserverAdapter(obj);
+ when(mLifecycle.getCurrentState()).thenReturn(CREATED);
+
+ observer.onStateChanged(mOwner, ON_CREATE);
+ InOrder inOrder = Mockito.inOrder(obj);
+ inOrder.verify(obj).onCreate(mOwner);
+ reset(obj);
+
+ when(mLifecycle.getCurrentState()).thenReturn(STARTED);
+ observer.onStateChanged(mOwner, ON_START);
+ inOrder.verify(obj).onStart(mOwner);
+ reset(obj);
+
+ when(mLifecycle.getCurrentState()).thenReturn(RESUMED);
+ observer.onStateChanged(mOwner, ON_RESUME);
+ inOrder.verify(obj).onResume(mOwner);
+ reset(obj);
+
+ when(mLifecycle.getCurrentState()).thenReturn(STARTED);
+ observer.onStateChanged(mOwner, ON_PAUSE);
+ inOrder.verify(obj).onPause(mOwner);
+ reset(obj);
+
+ when(mLifecycle.getCurrentState()).thenReturn(CREATED);
+ observer.onStateChanged(mOwner, ON_STOP);
+ inOrder.verify(obj).onStop(mOwner);
+ reset(obj);
+
+ when(mLifecycle.getCurrentState()).thenReturn(INITIALIZED);
+ observer.onStateChanged(mOwner, ON_DESTROY);
+ inOrder.verify(obj).onDestroy(mOwner);
+ reset(obj);
+ }
+}
diff --git a/android/arch/lifecycle/GeneratedAdapter.java b/android/arch/lifecycle/GeneratedAdapter.java
new file mode 100644
index 0000000..a8862da
--- /dev/null
+++ b/android/arch/lifecycle/GeneratedAdapter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.RestrictTo;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public interface GeneratedAdapter {
+
+ /**
+ * Called when a state transition event happens.
+ *
+ * @param source The source of the event
+ * @param event The event
+ * @param onAny approveCall onAny handlers
+ * @param logger if passed, used to track called methods and prevent calling the same method
+ * twice
+ */
+ void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
+ MethodCallsLogger logger);
+}
diff --git a/android/arch/lifecycle/GeneratedAdaptersTest.java b/android/arch/lifecycle/GeneratedAdaptersTest.java
new file mode 100644
index 0000000..2abb511
--- /dev/null
+++ b/android/arch/lifecycle/GeneratedAdaptersTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class GeneratedAdaptersTest {
+
+ private LifecycleOwner mOwner;
+ @SuppressWarnings("FieldCanBeLocal")
+ private Lifecycle mLifecycle;
+
+ @Before
+ public void initMocks() {
+ mOwner = mock(LifecycleOwner.class);
+ mLifecycle = mock(Lifecycle.class);
+ when(mOwner.getLifecycle()).thenReturn(mLifecycle);
+ }
+
+ static class SimpleObserver implements LifecycleObserver {
+ List<String> mLog;
+
+ SimpleObserver(List<String> log) {
+ mLog = log;
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ void onCreate() {
+ mLog.add("onCreate");
+ }
+ }
+
+ @Test
+ public void testSimpleSingleGeneratedAdapter() {
+ List<String> actual = new ArrayList<>();
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new SimpleObserver(actual));
+ callback.onStateChanged(mOwner, Lifecycle.Event.ON_CREATE);
+ assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
+ assertThat(actual, is(singletonList("onCreate")));
+ }
+
+ static class TestObserver implements LifecycleObserver {
+ List<String> mLog;
+
+ TestObserver(List<String> log) {
+ mLog = log;
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ void onCreate() {
+ mLog.add("onCreate");
+ }
+
+ @OnLifecycleEvent(ON_ANY)
+ void onAny() {
+ mLog.add("onAny");
+ }
+ }
+
+ @Test
+ public void testOnAny() {
+ List<String> actual = new ArrayList<>();
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new TestObserver(actual));
+ callback.onStateChanged(mOwner, Lifecycle.Event.ON_CREATE);
+ assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
+ assertThat(actual, is(asList("onCreate", "onAny")));
+ }
+
+ interface OnPauses extends LifecycleObserver {
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ void onPause();
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ void onPause(LifecycleOwner owner);
+ }
+
+ interface OnPauseResume extends LifecycleObserver {
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ void onPause();
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ void onResume();
+ }
+
+ class Impl1 implements OnPauses, OnPauseResume {
+
+ List<String> mLog;
+
+ Impl1(List<String> log) {
+ mLog = log;
+ }
+
+ @Override
+ public void onPause() {
+ mLog.add("onPause_0");
+ }
+
+ @Override
+ public void onResume() {
+ mLog.add("onResume");
+ }
+
+ @Override
+ public void onPause(LifecycleOwner owner) {
+ mLog.add("onPause_1");
+ }
+ }
+
+ @Test
+ public void testClashingInterfaces() {
+ List<String> actual = new ArrayList<>();
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new Impl1(actual));
+ callback.onStateChanged(mOwner, Lifecycle.Event.ON_PAUSE);
+ assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
+ assertThat(actual, is(asList("onPause_0", "onPause_1")));
+ actual.clear();
+ callback.onStateChanged(mOwner, Lifecycle.Event.ON_RESUME);
+ assertThat(actual, is(singletonList("onResume")));
+ }
+
+ class Base implements LifecycleObserver {
+
+ List<String> mLog;
+
+ Base(List<String> log) {
+ mLog = log;
+ }
+
+ @OnLifecycleEvent(ON_ANY)
+ void onAny() {
+ mLog.add("onAny_0");
+ }
+
+ @OnLifecycleEvent(ON_ANY)
+ void onAny(LifecycleOwner owner) {
+ mLog.add("onAny_1");
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ void onResume() {
+ mLog.add("onResume");
+ }
+ }
+
+ interface OnAny extends LifecycleObserver {
+ @OnLifecycleEvent(ON_ANY)
+ void onAny();
+
+ @OnLifecycleEvent(ON_ANY)
+ void onAny(LifecycleOwner owner, Lifecycle.Event event);
+ }
+
+ class Derived extends Base implements OnAny {
+ Derived(List<String> log) {
+ super(log);
+ }
+
+ @Override
+ public void onAny() {
+ super.onAny();
+ }
+
+ @Override
+ public void onAny(LifecycleOwner owner, Lifecycle.Event event) {
+ mLog.add("onAny_2");
+ assertThat(event, is(ON_RESUME));
+ }
+ }
+
+ @Test
+ public void testClashingClassAndInterface() {
+ List<String> actual = new ArrayList<>();
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new Derived(actual));
+ callback.onStateChanged(mOwner, Lifecycle.Event.ON_RESUME);
+ assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
+ assertThat(actual, is(asList("onResume", "onAny_0", "onAny_1", "onAny_2")));
+ }
+
+}
diff --git a/android/arch/lifecycle/Lifecycle.java b/android/arch/lifecycle/Lifecycle.java
index fcbd50a..02db5ff 100644
--- a/android/arch/lifecycle/Lifecycle.java
+++ b/android/arch/lifecycle/Lifecycle.java
@@ -34,7 +34,21 @@
* before {@link android.app.Activity#onStop onStop} is called.
* This gives you certain guarantees on which state the owner is in.
* <p>
- * Lifecycle events are observed using annotations.
+ * If you use <b>Java 8 Language</b>, then observe events with {@link DefaultLifecycleObserver}.
+ * To include it you should add {@code "android.arch.lifecycle:common-java8:<version>"} to your
+ * build.gradle file.
+ * <pre>
+ * class TestObserver implements DefaultLifecycleObserver {
+ * {@literal @}Override
+ * public void onCreate(LifecycleOwner owner) {
+ * // your code
+ * }
+ * }
+ * </pre>
+ * If you use <b>Java 7 Language</b>, Lifecycle events are observed using annotations.
+ * Once Java 8 Language becomes mainstream on Android, annotations will be deprecated, so between
+ * {@link DefaultLifecycleObserver} and annotations,
+ * you must always prefer {@code DefaultLifecycleObserver}.
* <pre>
* class TestObserver implements LifecycleObserver {
* {@literal @}OnLifecycleEvent(ON_STOP)
@@ -42,16 +56,6 @@
* }
* </pre>
* <p>
- * Multiple methods can observe the same event.
- * <pre>
- * class TestObserver implements LifecycleObserver {
- * {@literal @}OnLifecycleEvent(ON_STOP)
- * void onStoppedFirst() {}
- * {@literal @}OnLifecycleEvent(ON_STOP)
- * void onStoppedSecond() {}
- * }
- * </pre>
- * <p>
* Observer methods can receive zero or one argument.
* If used, the first argument must be of type {@link LifecycleOwner}.
* Methods annotated with {@link Event#ON_ANY} can receive the second argument, which must be
diff --git a/android/arch/lifecycle/Lifecycling.java b/android/arch/lifecycle/Lifecycling.java
index 3a5c0b9..7d6b37f 100644
--- a/android/arch/lifecycle/Lifecycling.java
+++ b/android/arch/lifecycle/Lifecycling.java
@@ -22,52 +22,61 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
* Internal class to handle lifecycle conversion etc.
+ *
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class Lifecycling {
- private static Constructor<? extends GenericLifecycleObserver> sREFLECTIVE;
+public class Lifecycling {
- static {
- try {
- sREFLECTIVE = ReflectiveGenericLifecycleObserver.class
- .getDeclaredConstructor(Object.class);
- } catch (NoSuchMethodException ignored) {
+ private static final int REFLECTIVE_CALLBACK = 1;
+ private static final int GENERATED_CALLBACK = 2;
- }
- }
-
- private static Map<Class, Constructor<? extends GenericLifecycleObserver>> sCallbackCache =
+ private static Map<Class, Integer> sCallbackCache = new HashMap<>();
+ private static Map<Class, List<Constructor<? extends GeneratedAdapter>>> sClassToAdapters =
new HashMap<>();
@NonNull
static GenericLifecycleObserver getCallback(Object object) {
+ if (object instanceof FullLifecycleObserver) {
+ return new FullLifecycleObserverAdapter((FullLifecycleObserver) object);
+ }
+
if (object instanceof GenericLifecycleObserver) {
return (GenericLifecycleObserver) object;
}
+
+ final Class<?> klass = object.getClass();
+ int type = getObserverConstructorType(klass);
+ if (type == GENERATED_CALLBACK) {
+ List<Constructor<? extends GeneratedAdapter>> constructors =
+ sClassToAdapters.get(klass);
+ if (constructors.size() == 1) {
+ GeneratedAdapter generatedAdapter = createGeneratedAdapter(
+ constructors.get(0), object);
+ return new SingleGeneratedAdapterObserver(generatedAdapter);
+ }
+ GeneratedAdapter[] adapters = new GeneratedAdapter[constructors.size()];
+ for (int i = 0; i < constructors.size(); i++) {
+ adapters[i] = createGeneratedAdapter(constructors.get(i), object);
+ }
+ return new CompositeGeneratedAdaptersObserver(adapters);
+ }
+ return new ReflectiveGenericLifecycleObserver(object);
+ }
+
+ private static GeneratedAdapter createGeneratedAdapter(
+ Constructor<? extends GeneratedAdapter> constructor, Object object) {
//noinspection TryWithIdenticalCatches
try {
- final Class<?> klass = object.getClass();
- Constructor<? extends GenericLifecycleObserver> cachedConstructor = sCallbackCache.get(
- klass);
- if (cachedConstructor != null) {
- return cachedConstructor.newInstance(object);
- }
- cachedConstructor = getGeneratedAdapterConstructor(klass);
- if (cachedConstructor != null) {
- if (!cachedConstructor.isAccessible()) {
- cachedConstructor.setAccessible(true);
- }
- } else {
- cachedConstructor = sREFLECTIVE;
- }
- sCallbackCache.put(klass, cachedConstructor);
- return cachedConstructor.newInstance(object);
+ return constructor.newInstance(object);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
@@ -78,37 +87,95 @@
}
@Nullable
- private static Constructor<? extends GenericLifecycleObserver> getGeneratedAdapterConstructor(
- Class<?> klass) {
- Package aPackage = klass.getPackage();
- final String fullPackage = aPackage != null ? aPackage.getName() : "";
-
- String name = klass.getCanonicalName();
- // anonymous class bug:35073837
- if (name == null) {
- return null;
- }
- final String adapterName = getAdapterName(fullPackage.isEmpty() ? name :
- name.substring(fullPackage.length() + 1));
+ private static Constructor<? extends GeneratedAdapter> generatedConstructor(Class<?> klass) {
try {
- @SuppressWarnings("unchecked")
- final Class<? extends GenericLifecycleObserver> aClass =
- (Class<? extends GenericLifecycleObserver>) Class.forName(
+ Package aPackage = klass.getPackage();
+ String name = klass.getCanonicalName();
+ final String fullPackage = aPackage != null ? aPackage.getName() : "";
+ final String adapterName = getAdapterName(fullPackage.isEmpty() ? name :
+ name.substring(fullPackage.length() + 1));
+
+ @SuppressWarnings("unchecked") final Class<? extends GeneratedAdapter> aClass =
+ (Class<? extends GeneratedAdapter>) Class.forName(
fullPackage.isEmpty() ? adapterName : fullPackage + "." + adapterName);
- return aClass.getDeclaredConstructor(klass);
- } catch (ClassNotFoundException e) {
- final Class<?> superclass = klass.getSuperclass();
- if (superclass != null) {
- return getGeneratedAdapterConstructor(superclass);
+ Constructor<? extends GeneratedAdapter> constructor =
+ aClass.getDeclaredConstructor(klass);
+ if (!constructor.isAccessible()) {
+ constructor.setAccessible(true);
}
+ return constructor;
+ } catch (ClassNotFoundException e) {
+ return null;
} catch (NoSuchMethodException e) {
// this should not happen
throw new RuntimeException(e);
}
- return null;
}
- static String getAdapterName(String className) {
+ private static int getObserverConstructorType(Class<?> klass) {
+ if (sCallbackCache.containsKey(klass)) {
+ return sCallbackCache.get(klass);
+ }
+ int type = resolveObserverCallbackType(klass);
+ sCallbackCache.put(klass, type);
+ return type;
+ }
+
+ private static int resolveObserverCallbackType(Class<?> klass) {
+ // anonymous class bug:35073837
+ if (klass.getCanonicalName() == null) {
+ return REFLECTIVE_CALLBACK;
+ }
+
+ Constructor<? extends GeneratedAdapter> constructor = generatedConstructor(klass);
+ if (constructor != null) {
+ sClassToAdapters.put(klass, Collections
+ .<Constructor<? extends GeneratedAdapter>>singletonList(constructor));
+ return GENERATED_CALLBACK;
+ }
+
+ boolean hasLifecycleMethods = ClassesInfoCache.sInstance.hasLifecycleMethods(klass);
+ if (hasLifecycleMethods) {
+ return REFLECTIVE_CALLBACK;
+ }
+
+ Class<?> superclass = klass.getSuperclass();
+ List<Constructor<? extends GeneratedAdapter>> adapterConstructors = null;
+ if (isLifecycleParent(superclass)) {
+ if (getObserverConstructorType(superclass) == REFLECTIVE_CALLBACK) {
+ return REFLECTIVE_CALLBACK;
+ }
+ adapterConstructors = new ArrayList<>(sClassToAdapters.get(superclass));
+ }
+
+ for (Class<?> intrface : klass.getInterfaces()) {
+ if (!isLifecycleParent(intrface)) {
+ continue;
+ }
+ if (getObserverConstructorType(intrface) == REFLECTIVE_CALLBACK) {
+ return REFLECTIVE_CALLBACK;
+ }
+ if (adapterConstructors == null) {
+ adapterConstructors = new ArrayList<>();
+ }
+ adapterConstructors.addAll(sClassToAdapters.get(intrface));
+ }
+ if (adapterConstructors != null) {
+ sClassToAdapters.put(klass, adapterConstructors);
+ return GENERATED_CALLBACK;
+ }
+
+ return REFLECTIVE_CALLBACK;
+ }
+
+ private static boolean isLifecycleParent(Class<?> klass) {
+ return klass != null && LifecycleObserver.class.isAssignableFrom(klass);
+ }
+
+ /**
+ * Create a name for an adapter class.
+ */
+ public static String getAdapterName(String className) {
return className.replace(".", "_") + "_LifecycleAdapter";
}
}
diff --git a/android/arch/lifecycle/LifecyclingTest.java b/android/arch/lifecycle/LifecyclingTest.java
new file mode 100644
index 0000000..70ce84c
--- /dev/null
+++ b/android/arch/lifecycle/LifecyclingTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.lifecycle.observers.DerivedSequence1;
+import android.arch.lifecycle.observers.DerivedSequence2;
+import android.arch.lifecycle.observers.DerivedWithNewMethods;
+import android.arch.lifecycle.observers.DerivedWithNoNewMethods;
+import android.arch.lifecycle.observers.DerivedWithOverridenMethodsWithLfAnnotation;
+import android.arch.lifecycle.observers.InterfaceImpl1;
+import android.arch.lifecycle.observers.InterfaceImpl2;
+import android.arch.lifecycle.observers.InterfaceImpl3;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class LifecyclingTest {
+
+ @Test
+ public void testDerivedWithNewLfMethodsNoGeneratedAdapter() {
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new DerivedWithNewMethods());
+ assertThat(callback, instanceOf(ReflectiveGenericLifecycleObserver.class));
+ }
+
+ @Test
+ public void testDerivedWithNoNewLfMethodsNoGeneratedAdapter() {
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new DerivedWithNoNewMethods());
+ assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
+ }
+
+ @Test
+ public void testDerivedWithOverridenMethodsNoGeneratedAdapter() {
+ GenericLifecycleObserver callback = Lifecycling.getCallback(
+ new DerivedWithOverridenMethodsWithLfAnnotation());
+ // that is not effective but...
+ assertThat(callback, instanceOf(ReflectiveGenericLifecycleObserver.class));
+ }
+
+ @Test
+ public void testInterfaceImpl1NoGeneratedAdapter() {
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new InterfaceImpl1());
+ assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
+ }
+
+ @Test
+ public void testInterfaceImpl2NoGeneratedAdapter() {
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new InterfaceImpl2());
+ assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
+ }
+
+ @Test
+ public void testInterfaceImpl3NoGeneratedAdapter() {
+ GenericLifecycleObserver callback = Lifecycling.getCallback(new InterfaceImpl3());
+ assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
+ }
+
+ @Test
+ public void testDerivedSequence() {
+ GenericLifecycleObserver callback2 = Lifecycling.getCallback(new DerivedSequence2());
+ assertThat(callback2, instanceOf(ReflectiveGenericLifecycleObserver.class));
+ GenericLifecycleObserver callback1 = Lifecycling.getCallback(new DerivedSequence1());
+ assertThat(callback1, instanceOf(SingleGeneratedAdapterObserver.class));
+ }
+}
diff --git a/android/arch/lifecycle/LiveData.java b/android/arch/lifecycle/LiveData.java
index 99d859c..3aea6ac 100644
--- a/android/arch/lifecycle/LiveData.java
+++ b/android/arch/lifecycle/LiveData.java
@@ -1,411 +1,4 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+//LiveData interface for tests
package android.arch.lifecycle;
-
-import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
-import static android.arch.lifecycle.Lifecycle.State.STARTED;
-
-import android.arch.core.executor.AppToolkitTaskExecutor;
-import android.arch.core.internal.SafeIterableMap;
-import android.arch.lifecycle.Lifecycle.State;
-import android.support.annotation.MainThread;
-import android.support.annotation.Nullable;
-
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * LiveData is a data holder class that can be observed within a given lifecycle.
- * This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and
- * this observer will be notified about modifications of the wrapped data only if the paired
- * LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is
- * {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via
- * {@link #observeForever(Observer)} is considered as always active and thus will be always notified
- * about modifications. For those observers, you should manually call
- * {@link #removeObserver(Observer)}.
- *
- * <p> An observer added with a Lifecycle will be automatically removed if the corresponding
- * Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for
- * activities and fragments where they can safely observe LiveData and not worry about leaks:
- * they will be instantly unsubscribed when they are destroyed.
- *
- * <p>
- * In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods
- * to get notified when number of active {@link Observer}s change between 0 and 1.
- * This allows LiveData to release any heavy resources when it does not have any Observers that
- * are actively observing.
- * <p>
- * This class is designed to hold individual data fields of {@link ViewModel},
- * but can also be used for sharing data between different modules in your application
- * in a decoupled fashion.
- *
- * @param <T> The type of data hold by this instance
- * @see ViewModel
- */
-@SuppressWarnings({"WeakerAccess", "unused"})
-// TODO: Thread checks are too strict right now, we may consider automatically moving them to main
-// thread.
-public abstract class LiveData<T> {
- private final Object mDataLock = new Object();
- static final int START_VERSION = -1;
- private static final Object NOT_SET = new Object();
-
- private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {
-
- private LifecycleRegistry mRegistry = init();
-
- private LifecycleRegistry init() {
- LifecycleRegistry registry = new LifecycleRegistry(this);
- registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
- registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
- registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
- return registry;
- }
-
- @Override
- public Lifecycle getLifecycle() {
- return mRegistry;
- }
- };
-
- private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
- new SafeIterableMap<>();
-
- // how many observers are in active state
- private int mActiveCount = 0;
- private volatile Object mData = NOT_SET;
- // when setData is called, we set the pending data and actual data swap happens on the main
- // thread
- private volatile Object mPendingData = NOT_SET;
- private int mVersion = START_VERSION;
-
- private boolean mDispatchingValue;
- @SuppressWarnings("FieldCanBeLocal")
- private boolean mDispatchInvalidated;
- private final Runnable mPostValueRunnable = new Runnable() {
- @Override
- public void run() {
- Object newValue;
- synchronized (mDataLock) {
- newValue = mPendingData;
- mPendingData = NOT_SET;
- }
- //noinspection unchecked
- setValue((T) newValue);
- }
- };
-
- private void considerNotify(LifecycleBoundObserver observer) {
- if (!observer.active) {
- return;
- }
- // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
- //
- // we still first check observer.active to keep it as the entrance for events. So even if
- // the observer moved to an active state, if we've not received that event, we better not
- // notify for a more predictable notification order.
- if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
- return;
- }
- if (observer.lastVersion >= mVersion) {
- return;
- }
- observer.lastVersion = mVersion;
- //noinspection unchecked
- observer.observer.onChanged((T) mData);
- }
-
- private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
- if (mDispatchingValue) {
- mDispatchInvalidated = true;
- return;
- }
- mDispatchingValue = true;
- do {
- mDispatchInvalidated = false;
- if (initiator != null) {
- considerNotify(initiator);
- initiator = null;
- } else {
- for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
- mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
- considerNotify(iterator.next().getValue());
- if (mDispatchInvalidated) {
- break;
- }
- }
- }
- } while (mDispatchInvalidated);
- mDispatchingValue = false;
- }
-
- /**
- * Adds the given observer to the observers list within the lifespan of the given
- * owner. The events are dispatched on the main thread. If LiveData already has data
- * set, it will be delivered to the observer.
- * <p>
- * The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
- * or {@link Lifecycle.State#RESUMED} state (active).
- * <p>
- * If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
- * automatically be removed.
- * <p>
- * When data changes while the {@code owner} is not active, it will not receive any updates.
- * If it becomes active again, it will receive the last available data automatically.
- * <p>
- * LiveData keeps a strong reference to the observer and the owner as long as the
- * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
- * the observer & the owner.
- * <p>
- * If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
- * ignores the call.
- * <p>
- * If the given owner, observer tuple is already in the list, the call is ignored.
- * If the observer is already in the list with another owner, LiveData throws an
- * {@link IllegalArgumentException}.
- *
- * @param owner The LifecycleOwner which controls the observer
- * @param observer The observer that will receive the events
- */
- @MainThread
- public void observe(LifecycleOwner owner, Observer<T> observer) {
- if (owner.getLifecycle().getCurrentState() == DESTROYED) {
- // ignore
- return;
- }
- LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
- LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
- if (existing != null && existing.owner != wrapper.owner) {
- throw new IllegalArgumentException("Cannot add the same observer"
- + " with different lifecycles");
- }
- if (existing != null) {
- return;
- }
- owner.getLifecycle().addObserver(wrapper);
- wrapper.activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
- }
-
- /**
- * Adds the given observer to the observers list. This call is similar to
- * {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which
- * is always active. This means that the given observer will receive all events and will never
- * be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop
- * observing this LiveData.
- * While LiveData has one of such observers, it will be considered
- * as active.
- * <p>
- * If the observer was already added with an owner to this LiveData, LiveData throws an
- * {@link IllegalArgumentException}.
- *
- * @param observer The observer that will receive the events
- */
- @MainThread
- public void observeForever(Observer<T> observer) {
- observe(ALWAYS_ON, observer);
- }
-
- /**
- * Removes the given observer from the observers list.
- *
- * @param observer The Observer to receive events.
- */
- @MainThread
- public void removeObserver(final Observer<T> observer) {
- assertMainThread("removeObserver");
- LifecycleBoundObserver removed = mObservers.remove(observer);
- if (removed == null) {
- return;
- }
- removed.owner.getLifecycle().removeObserver(removed);
- removed.activeStateChanged(false);
- }
-
- /**
- * Removes all observers that are tied to the given {@link LifecycleOwner}.
- *
- * @param owner The {@code LifecycleOwner} scope for the observers to be removed.
- */
- @MainThread
- public void removeObservers(final LifecycleOwner owner) {
- assertMainThread("removeObservers");
- for (Map.Entry<Observer<T>, LifecycleBoundObserver> entry : mObservers) {
- if (entry.getValue().owner == owner) {
- removeObserver(entry.getKey());
- }
- }
- }
-
- /**
- * Posts a task to a main thread to set the given value. So if you have a following code
- * executed in the main thread:
- * <pre class="prettyprint">
- * liveData.postValue("a");
- * liveData.setValue("b");
- * </pre>
- * The value "b" would be set at first and later the main thread would override it with
- * the value "a".
- * <p>
- * If you called this method multiple times before a main thread executed a posted task, only
- * the last value would be dispatched.
- *
- * @param value The new value
- */
- protected void postValue(T value) {
- boolean postTask;
- synchronized (mDataLock) {
- postTask = mPendingData == NOT_SET;
- mPendingData = value;
- }
- if (!postTask) {
- return;
- }
- AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
- }
-
- /**
- * Sets the value. If there are active observers, the value will be dispatched to them.
- * <p>
- * This method must be called from the main thread. If you need set a value from a background
- * thread, you can use {@link #postValue(Object)}
- *
- * @param value The new value
- */
- @MainThread
- protected void setValue(T value) {
- assertMainThread("setValue");
- mVersion++;
- mData = value;
- dispatchingValue(null);
- }
-
- /**
- * Returns the current value.
- * Note that calling this method on a background thread does not guarantee that the latest
- * value set will be received.
- *
- * @return the current value
- */
- @Nullable
- public T getValue() {
- Object data = mData;
- if (data != NOT_SET) {
- //noinspection unchecked
- return (T) data;
- }
- return null;
- }
-
- int getVersion() {
- return mVersion;
- }
-
- /**
- * Called when the number of active observers change to 1 from 0.
- * <p>
- * This callback can be used to know that this LiveData is being used thus should be kept
- * up to date.
- */
- protected void onActive() {
-
- }
-
- /**
- * Called when the number of active observers change from 1 to 0.
- * <p>
- * This does not mean that there are no observers left, there may still be observers but their
- * lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}
- * (like an Activity in the back stack).
- * <p>
- * You can check if there are observers via {@link #hasObservers()}.
- */
- protected void onInactive() {
-
- }
-
- /**
- * Returns true if this LiveData has observers.
- *
- * @return true if this LiveData has observers
- */
- public boolean hasObservers() {
- return mObservers.size() > 0;
- }
-
- /**
- * Returns true if this LiveData has active observers.
- *
- * @return true if this LiveData has active observers
- */
- public boolean hasActiveObservers() {
- return mActiveCount > 0;
- }
-
- class LifecycleBoundObserver implements LifecycleObserver {
- public final LifecycleOwner owner;
- public final Observer<T> observer;
- public boolean active;
- public int lastVersion = START_VERSION;
-
- LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
- this.owner = owner;
- this.observer = observer;
- }
-
- @SuppressWarnings("unused")
- @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
- void onStateChange() {
- if (owner.getLifecycle().getCurrentState() == DESTROYED) {
- removeObserver(observer);
- return;
- }
- // immediately set active state, so we'd never dispatch anything to inactive
- // owner
- activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
-
- }
-
- void activeStateChanged(boolean newActive) {
- if (newActive == active) {
- return;
- }
- active = newActive;
- boolean wasInactive = LiveData.this.mActiveCount == 0;
- LiveData.this.mActiveCount += active ? 1 : -1;
- if (wasInactive && active) {
- onActive();
- }
- if (LiveData.this.mActiveCount == 0 && !active) {
- onInactive();
- }
- if (active) {
- dispatchingValue(this);
- }
- }
- }
-
- static boolean isActiveState(State state) {
- return state.isAtLeast(STARTED);
- }
-
- private void assertMainThread(String methodName) {
- if (!AppToolkitTaskExecutor.getInstance().isMainThread()) {
- throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
- + " thread");
- }
- }
+public class LiveData<T> {
}
diff --git a/android/arch/lifecycle/LiveDataReactiveStreams.java b/android/arch/lifecycle/LiveDataReactiveStreams.java
index 0be0149..2b25bc9 100644
--- a/android/arch/lifecycle/LiveDataReactiveStreams.java
+++ b/android/arch/lifecycle/LiveDataReactiveStreams.java
@@ -16,7 +16,8 @@
package android.arch.lifecycle;
-import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.ArchTaskExecutor;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.reactivestreams.Publisher;
@@ -85,7 +86,7 @@
if (n < 0 || mCanceled) {
return;
}
- AppToolkitTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
+ ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
if (mCanceled) {
@@ -110,7 +111,7 @@
if (mCanceled) {
return;
}
- AppToolkitTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
+ ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
if (mCanceled) {
@@ -133,40 +134,101 @@
/**
* Creates an Observable {@link LiveData} stream from a ReactiveStreams publisher.
+ *
+ * <p>
+ * When the LiveData becomes active, it subscribes to the emissions from the Publisher.
+ *
+ * <p>
+ * When the LiveData becomes inactive, the subscription is cleared.
+ * LiveData holds the last value emitted by the Publisher when the LiveData was active.
+ * <p>
+ * Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
+ * added, it will automatically notify with the last value held in LiveData,
+ * which might not be the last value emitted by the Publisher.
+ *
+ * @param <T> The type of data hold by this instance.
*/
public static <T> LiveData<T> fromPublisher(final Publisher<T> publisher) {
- MutableLiveData<T> liveData = new MutableLiveData<>();
- // Since we don't have a way to directly observe cancels, weakly hold the live data.
- final WeakReference<MutableLiveData<T>> liveDataRef = new WeakReference<>(liveData);
-
- publisher.subscribe(new Subscriber<T>() {
- @Override
- public void onSubscribe(Subscription s) {
- // Don't worry about backpressure. If the stream is too noisy then backpressure can
- // be handled upstream.
- s.request(Long.MAX_VALUE);
- }
-
- @Override
- public void onNext(final T t) {
- final LiveData<T> liveData = liveDataRef.get();
- if (liveData != null) {
- liveData.postValue(t);
- }
- }
-
- @Override
- public void onError(Throwable t) {
- // Errors should be handled upstream, so propagate as a crash.
- throw new RuntimeException(t);
- }
-
- @Override
- public void onComplete() {
- }
- });
-
- return liveData;
+ return new PublisherLiveData<>(publisher);
}
+ /**
+ * Defines a {@link LiveData} object that wraps a {@link Publisher}.
+ *
+ * <p>
+ * When the LiveData becomes active, it subscribes to the emissions from the Publisher.
+ *
+ * <p>
+ * When the LiveData becomes inactive, the subscription is cleared.
+ * LiveData holds the last value emitted by the Publisher when the LiveData was active.
+ * <p>
+ * Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
+ * added, it will automatically notify with the last value held in LiveData,
+ * which might not be the last value emitted by the Publisher.
+ *
+ * @param <T> The type of data hold by this instance.
+ */
+ private static class PublisherLiveData<T> extends LiveData<T> {
+ private WeakReference<Subscription> mSubscriptionRef;
+ private final Publisher mPublisher;
+ private final Object mLock = new Object();
+
+ PublisherLiveData(@NonNull final Publisher publisher) {
+ mPublisher = publisher;
+ }
+
+ @Override
+ protected void onActive() {
+ super.onActive();
+
+ mPublisher.subscribe(new Subscriber<T>() {
+ @Override
+ public void onSubscribe(Subscription s) {
+ // Don't worry about backpressure. If the stream is too noisy then
+ // backpressure can be handled upstream.
+ synchronized (mLock) {
+ s.request(Long.MAX_VALUE);
+ mSubscriptionRef = new WeakReference<>(s);
+ }
+ }
+
+ @Override
+ public void onNext(final T t) {
+ postValue(t);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ synchronized (mLock) {
+ mSubscriptionRef = null;
+ }
+ // Errors should be handled upstream, so propagate as a crash.
+ throw new RuntimeException(t);
+ }
+
+ @Override
+ public void onComplete() {
+ synchronized (mLock) {
+ mSubscriptionRef = null;
+ }
+ }
+ });
+
+ }
+
+ @Override
+ protected void onInactive() {
+ super.onInactive();
+ synchronized (mLock) {
+ WeakReference<Subscription> subscriptionRef = mSubscriptionRef;
+ if (subscriptionRef != null) {
+ Subscription subscription = subscriptionRef.get();
+ if (subscription != null) {
+ subscription.cancel();
+ }
+ mSubscriptionRef = null;
+ }
+ }
+ }
+ }
}
diff --git a/android/arch/lifecycle/LiveDataReactiveStreamsTest.java b/android/arch/lifecycle/LiveDataReactiveStreamsTest.java
index 87fba27..7278847 100644
--- a/android/arch/lifecycle/LiveDataReactiveStreamsTest.java
+++ b/android/arch/lifecycle/LiveDataReactiveStreamsTest.java
@@ -16,12 +16,10 @@
package android.arch.lifecycle;
-import static android.arch.lifecycle.Lifecycle.State.RESUMED;
-
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.ArchTaskExecutor;
import android.arch.core.executor.TaskExecutor;
import android.support.annotation.Nullable;
import android.support.test.filters.SmallTest;
@@ -47,28 +45,7 @@
@SmallTest
public class LiveDataReactiveStreamsTest {
- private static final Lifecycle sLifecycle = new Lifecycle() {
- @Override
- public void addObserver(LifecycleObserver observer) {
- }
-
- @Override
- public void removeObserver(LifecycleObserver observer) {
- }
-
- @Override
- public State getCurrentState() {
- return RESUMED;
- }
- };
- private static final LifecycleOwner S_LIFECYCLE_OWNER = new LifecycleOwner() {
-
- @Override
- public Lifecycle getLifecycle() {
- return sLifecycle;
- }
-
- };
+ private LifecycleOwner mLifecycleOwner;
private final List<String> mLiveDataOutput = new ArrayList<>();
private final Observer<String> mObserver = new Observer<String>() {
@@ -85,8 +62,19 @@
@Before
public void init() {
+ mLifecycleOwner = new LifecycleOwner() {
+ LifecycleRegistry mRegistry = new LifecycleRegistry(this);
+ {
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+ }
+
+ @Override
+ public Lifecycle getLifecycle() {
+ return mRegistry;
+ }
+ };
mTestThread = Thread.currentThread();
- AppToolkitTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
+ ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
@Override
public void executeOnDiskIO(Runnable runnable) {
@@ -109,7 +97,7 @@
@After
public void removeExecutorDelegate() {
- AppToolkitTaskExecutor.getInstance().setDelegate(null);
+ ArchTaskExecutor.getInstance().setDelegate(null);
}
@Test
@@ -117,7 +105,7 @@
PublishProcessor<String> processor = PublishProcessor.create();
LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
- liveData.observe(S_LIFECYCLE_OWNER, mObserver);
+ liveData.observe(mLifecycleOwner, mObserver);
processor.onNext("foo");
processor.onNext("bar");
@@ -132,13 +120,13 @@
PublishProcessor<String> processor = PublishProcessor.create();
LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
- liveData.observe(S_LIFECYCLE_OWNER, mObserver);
+ liveData.observe(mLifecycleOwner, mObserver);
processor.onNext("foo");
processor.onNext("bar");
// The second mObserver should only get the newest value and any later values.
- liveData.observe(S_LIFECYCLE_OWNER, new Observer<String>() {
+ liveData.observe(mLifecycleOwner, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
output2.add(s);
@@ -152,12 +140,44 @@
}
@Test
+ public void convertsFromPublisherAfterInactive() {
+ PublishProcessor<String> processor = PublishProcessor.create();
+ LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
+
+ liveData.observe(mLifecycleOwner, mObserver);
+ processor.onNext("foo");
+ liveData.removeObserver(mObserver);
+ processor.onNext("bar");
+
+ liveData.observe(mLifecycleOwner, mObserver);
+ processor.onNext("baz");
+
+ assertThat(mLiveDataOutput, is(Arrays.asList("foo", "foo", "baz")));
+ }
+
+ @Test
+ public void convertsFromPublisherManagesSubcriptions() {
+ PublishProcessor<String> processor = PublishProcessor.create();
+ LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
+
+ assertThat(processor.hasSubscribers(), is(false));
+ liveData.observe(mLifecycleOwner, mObserver);
+
+ // once the live data is active, there's a subscriber
+ assertThat(processor.hasSubscribers(), is(true));
+
+ liveData.removeObserver(mObserver);
+ // once the live data is inactive, the subscriber is removed
+ assertThat(processor.hasSubscribers(), is(false));
+ }
+
+ @Test
public void convertsFromAsyncPublisher() {
Flowable<String> input = Flowable.just("foo")
.concatWith(Flowable.just("bar", "baz").observeOn(sBackgroundScheduler));
LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(input);
- liveData.observe(S_LIFECYCLE_OWNER, mObserver);
+ liveData.observe(mLifecycleOwner, mObserver);
assertThat(mLiveDataOutput, is(Collections.singletonList("foo")));
sBackgroundScheduler.triggerActions();
@@ -170,7 +190,7 @@
liveData.setValue("foo");
assertThat(liveData.getValue(), is("foo"));
- Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+ Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(mLifecycleOwner, liveData))
.subscribe(mOutputProcessor);
liveData.setValue("bar");
@@ -188,7 +208,7 @@
assertThat(liveData.getValue(), is("foo"));
Disposable disposable = Flowable
- .fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+ .fromPublisher(LiveDataReactiveStreams.toPublisher(mLifecycleOwner, liveData))
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
@@ -216,7 +236,7 @@
final AsyncSubject<Subscription> subscriptionSubject = AsyncSubject.create();
- Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+ Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(mLifecycleOwner, liveData))
.subscribe(new Subscriber<String>() {
@Override
public void onSubscribe(Subscription s) {
@@ -275,7 +295,7 @@
public void convertsToPublisherWithAsyncData() {
MutableLiveData<String> liveData = new MutableLiveData<>();
- Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(S_LIFECYCLE_OWNER, liveData))
+ Flowable.fromPublisher(LiveDataReactiveStreams.toPublisher(mLifecycleOwner, liveData))
.observeOn(sBackgroundScheduler)
.subscribe(mOutputProcessor);
diff --git a/android/arch/lifecycle/LiveDataTest.java b/android/arch/lifecycle/LiveDataTest.java
index ed2a35d..9f0b425 100644
--- a/android/arch/lifecycle/LiveDataTest.java
+++ b/android/arch/lifecycle/LiveDataTest.java
@@ -36,16 +36,20 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.ArchTaskExecutor;
import android.arch.lifecycle.util.InstantTaskExecutor;
import android.support.annotation.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
import org.mockito.Mockito;
@SuppressWarnings({"unchecked"})
+@RunWith(JUnit4.class)
public class LiveDataTest {
private PublicLiveData<String> mLiveData;
private LifecycleOwner mOwner;
@@ -66,12 +70,12 @@
@Before
public void swapExecutorDelegate() {
- AppToolkitTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
+ ArchTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
}
@After
public void removeExecutorDelegate() {
- AppToolkitTaskExecutor.getInstance().setDelegate(null);
+ ArchTaskExecutor.getInstance().setDelegate(null);
}
@Test
@@ -418,6 +422,40 @@
verify(mActiveObserversChanged, never()).onCall(anyBoolean());
}
+ @Test
+ public void testRemoveDuringAddition() {
+ mRegistry.handleLifecycleEvent(ON_START);
+ mLiveData.setValue("bla");
+ mLiveData.observeForever(new Observer<String>() {
+ @Override
+ public void onChanged(@Nullable String s) {
+ mLiveData.removeObserver(this);
+ }
+ });
+ assertThat(mLiveData.hasActiveObservers(), is(false));
+ InOrder inOrder = Mockito.inOrder(mActiveObserversChanged);
+ inOrder.verify(mActiveObserversChanged).onCall(true);
+ inOrder.verify(mActiveObserversChanged).onCall(false);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveDuringBringingUpToState() {
+ mLiveData.setValue("bla");
+ mLiveData.observeForever(new Observer<String>() {
+ @Override
+ public void onChanged(@Nullable String s) {
+ mLiveData.removeObserver(this);
+ }
+ });
+ mRegistry.handleLifecycleEvent(ON_RESUME);
+ assertThat(mLiveData.hasActiveObservers(), is(false));
+ InOrder inOrder = Mockito.inOrder(mActiveObserversChanged);
+ inOrder.verify(mActiveObserversChanged).onCall(true);
+ inOrder.verify(mActiveObserversChanged).onCall(false);
+ inOrder.verifyNoMoreInteractions();
+ }
+
@SuppressWarnings("WeakerAccess")
static class PublicLiveData<T> extends LiveData<T> {
// cannot spy due to internal calls
diff --git a/android/arch/lifecycle/MediatorLiveDataTest.java b/android/arch/lifecycle/MediatorLiveDataTest.java
index 3de3eee..e2eadbe 100644
--- a/android/arch/lifecycle/MediatorLiveDataTest.java
+++ b/android/arch/lifecycle/MediatorLiveDataTest.java
@@ -25,7 +25,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.ArchTaskExecutor;
import android.arch.lifecycle.util.InstantTaskExecutor;
import android.support.annotation.Nullable;
@@ -69,7 +69,7 @@
@Before
public void swapExecutorDelegate() {
- AppToolkitTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
+ ArchTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
}
@Test
diff --git a/android/arch/lifecycle/MethodCallsLogger.java b/android/arch/lifecycle/MethodCallsLogger.java
new file mode 100644
index 0000000..031e43e
--- /dev/null
+++ b/android/arch/lifecycle/MethodCallsLogger.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.RestrictTo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class MethodCallsLogger {
+ private Map<String, Integer> mCalledMethods = new HashMap<>();
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public boolean approveCall(String name, int type) {
+ Integer nullableMask = mCalledMethods.get(name);
+ int mask = nullableMask != null ? nullableMask : 0;
+ boolean wasCalled = (mask & type) != 0;
+ mCalledMethods.put(name, mask | type);
+ return !wasCalled;
+ }
+}
diff --git a/android/arch/lifecycle/ProcessOwnerTest.java b/android/arch/lifecycle/ProcessOwnerTest.java
index e80e11c..37bdcdb 100644
--- a/android/arch/lifecycle/ProcessOwnerTest.java
+++ b/android/arch/lifecycle/ProcessOwnerTest.java
@@ -37,6 +37,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.FragmentActivity;
import org.junit.After;
import org.junit.Rule;
@@ -78,7 +79,7 @@
@Test
public void testNavigation() throws Throwable {
- LifecycleActivity firstActivity = setupObserverOnResume();
+ FragmentActivity firstActivity = setupObserverOnResume();
Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
NavigationTestActivitySecond.class.getCanonicalName(), null, false);
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
@@ -88,15 +89,15 @@
firstActivity.finish();
firstActivity.startActivity(intent);
- LifecycleActivity secondActivity = (LifecycleActivity) monitor.waitForActivity();
+ FragmentActivity secondActivity = (FragmentActivity) monitor.waitForActivity();
assertThat("Failed to navigate", secondActivity, notNullValue());
checkProcessObserverSilent(secondActivity);
}
@Test
public void testRecreation() throws Throwable {
- LifecycleActivity activity = setupObserverOnResume();
- LifecycleActivity recreated = TestUtils.recreateActivity(activity, activityTestRule);
+ FragmentActivity activity = setupObserverOnResume();
+ FragmentActivity recreated = TestUtils.recreateActivity(activity, activityTestRule);
assertThat("Failed to recreate", recreated, notNullValue());
checkProcessObserverSilent(recreated);
}
@@ -112,14 +113,15 @@
NavigationTestActivityFirst activity = activityTestRule.getActivity();
activity.startActivity(new Intent(activity, NavigationDialogActivity.class));
- LifecycleActivity dialogActivity = (LifecycleActivity) monitor.waitForActivity();
+ FragmentActivity dialogActivity = (FragmentActivity) monitor.waitForActivity();
checkProcessObserverSilent(dialogActivity);
List<Event> events = Collections.synchronizedList(new ArrayList<>());
LifecycleObserver collectingObserver = new LifecycleObserver() {
@OnLifecycleEvent(Event.ON_ANY)
- public void onStateChanged(LifecycleOwner provider, Event event) {
+ public void onStateChanged(@SuppressWarnings("unused") LifecycleOwner provider,
+ Event event) {
events.add(event);
}
};
@@ -138,8 +140,8 @@
dialogActivity.finish();
}
- private LifecycleActivity setupObserverOnResume() throws Throwable {
- LifecycleActivity firstActivity = activityTestRule.getActivity();
+ private FragmentActivity setupObserverOnResume() throws Throwable {
+ FragmentActivity firstActivity = activityTestRule.getActivity();
waitTillResumed(firstActivity, activityTestRule);
addProcessObserver(mObserver);
mObserver.mChangedState = false;
@@ -156,7 +158,7 @@
ProcessLifecycleOwner.get().getLifecycle().removeObserver(observer));
}
- private void checkProcessObserverSilent(LifecycleActivity activity) throws Throwable {
+ private void checkProcessObserverSilent(FragmentActivity activity) throws Throwable {
waitTillResumed(activity, activityTestRule);
assertThat(mObserver.mChangedState, is(false));
activityTestRule.runOnUiThread(() ->
diff --git a/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java b/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java
index 44815e6..f010ed8 100644
--- a/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java
+++ b/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java
@@ -16,204 +16,23 @@
package android.arch.lifecycle;
+import android.arch.lifecycle.ClassesInfoCache.CallbackInfo;
import android.arch.lifecycle.Lifecycle.Event;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
/**
* An internal implementation of {@link GenericLifecycleObserver} that relies on reflection.
*/
class ReflectiveGenericLifecycleObserver implements GenericLifecycleObserver {
private final Object mWrapped;
private final CallbackInfo mInfo;
- @SuppressWarnings("WeakerAccess")
- static final Map<Class, CallbackInfo> sInfoCache = new HashMap<>();
ReflectiveGenericLifecycleObserver(Object wrapped) {
mWrapped = wrapped;
- mInfo = getInfo(mWrapped.getClass());
+ mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass());
}
@Override
public void onStateChanged(LifecycleOwner source, Event event) {
- invokeCallbacks(mInfo, source, event);
+ mInfo.invokeCallbacks(source, event, mWrapped);
}
-
- private void invokeMethodsForEvent(List<MethodReference> handlers, LifecycleOwner source,
- Event event) {
- if (handlers != null) {
- for (int i = handlers.size() - 1; i >= 0; i--) {
- MethodReference reference = handlers.get(i);
- invokeCallback(reference, source, event);
- }
- }
- }
-
- @SuppressWarnings("ConstantConditions")
- private void invokeCallbacks(CallbackInfo info, LifecycleOwner source, Event event) {
- invokeMethodsForEvent(info.mEventToHandlers.get(event), source, event);
- invokeMethodsForEvent(info.mEventToHandlers.get(Event.ON_ANY), source, event);
- }
-
- private void invokeCallback(MethodReference reference, LifecycleOwner source, Event event) {
- //noinspection TryWithIdenticalCatches
- try {
- switch (reference.mCallType) {
- case CALL_TYPE_NO_ARG:
- reference.mMethod.invoke(mWrapped);
- break;
- case CALL_TYPE_PROVIDER:
- reference.mMethod.invoke(mWrapped, source);
- break;
- case CALL_TYPE_PROVIDER_WITH_EVENT:
- reference.mMethod.invoke(mWrapped, source, event);
- break;
- }
- } catch (InvocationTargetException e) {
- throw new RuntimeException("Failed to call observer method", e.getCause());
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static CallbackInfo getInfo(Class klass) {
- CallbackInfo existing = sInfoCache.get(klass);
- if (existing != null) {
- return existing;
- }
- existing = createInfo(klass);
- return existing;
- }
-
- private static void verifyAndPutHandler(Map<MethodReference, Event> handlers,
- MethodReference newHandler, Event newEvent, Class klass) {
- Event event = handlers.get(newHandler);
- if (event != null && newEvent != event) {
- Method method = newHandler.mMethod;
- throw new IllegalArgumentException(
- "Method " + method.getName() + " in " + klass.getName()
- + " already declared with different @OnLifecycleEvent value: previous"
- + " value " + event + ", new value " + newEvent);
- }
- if (event == null) {
- handlers.put(newHandler, newEvent);
- }
- }
-
- private static CallbackInfo createInfo(Class klass) {
- Class superclass = klass.getSuperclass();
- Map<MethodReference, Event> handlerToEvent = new HashMap<>();
- if (superclass != null) {
- CallbackInfo superInfo = getInfo(superclass);
- if (superInfo != null) {
- handlerToEvent.putAll(superInfo.mHandlerToEvent);
- }
- }
-
- Method[] methods = klass.getDeclaredMethods();
-
- Class[] interfaces = klass.getInterfaces();
- for (Class intrfc : interfaces) {
- for (Entry<MethodReference, Event> entry : getInfo(intrfc).mHandlerToEvent.entrySet()) {
- verifyAndPutHandler(handlerToEvent, entry.getKey(), entry.getValue(), klass);
- }
- }
-
- for (Method method : methods) {
- OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
- if (annotation == null) {
- continue;
- }
- Class<?>[] params = method.getParameterTypes();
- int callType = CALL_TYPE_NO_ARG;
- if (params.length > 0) {
- callType = CALL_TYPE_PROVIDER;
- if (!params[0].isAssignableFrom(LifecycleOwner.class)) {
- throw new IllegalArgumentException(
- "invalid parameter type. Must be one and instanceof LifecycleOwner");
- }
- }
- Event event = annotation.value();
-
- if (params.length > 1) {
- callType = CALL_TYPE_PROVIDER_WITH_EVENT;
- if (!params[1].isAssignableFrom(Event.class)) {
- throw new IllegalArgumentException(
- "invalid parameter type. second arg must be an event");
- }
- if (event != Event.ON_ANY) {
- throw new IllegalArgumentException(
- "Second arg is supported only for ON_ANY value");
- }
- }
- if (params.length > 2) {
- throw new IllegalArgumentException("cannot have more than 2 params");
- }
- MethodReference methodReference = new MethodReference(callType, method);
- verifyAndPutHandler(handlerToEvent, methodReference, event, klass);
- }
- CallbackInfo info = new CallbackInfo(handlerToEvent);
- sInfoCache.put(klass, info);
- return info;
- }
-
- @SuppressWarnings("WeakerAccess")
- static class CallbackInfo {
- final Map<Event, List<MethodReference>> mEventToHandlers;
- final Map<MethodReference, Event> mHandlerToEvent;
-
- CallbackInfo(Map<MethodReference, Event> handlerToEvent) {
- mHandlerToEvent = handlerToEvent;
- mEventToHandlers = new HashMap<>();
- for (Entry<MethodReference, Event> entry : handlerToEvent.entrySet()) {
- Event event = entry.getValue();
- List<MethodReference> methodReferences = mEventToHandlers.get(event);
- if (methodReferences == null) {
- methodReferences = new ArrayList<>();
- mEventToHandlers.put(event, methodReferences);
- }
- methodReferences.add(entry.getKey());
- }
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- static class MethodReference {
- final int mCallType;
- final Method mMethod;
-
- MethodReference(int callType, Method method) {
- mCallType = callType;
- mMethod = method;
- mMethod.setAccessible(true);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- MethodReference that = (MethodReference) o;
- return mCallType == that.mCallType && mMethod.getName().equals(that.mMethod.getName());
- }
-
- @Override
- public int hashCode() {
- return 31 * mCallType + mMethod.getName().hashCode();
- }
- }
-
- private static final int CALL_TYPE_NO_ARG = 0;
- private static final int CALL_TYPE_PROVIDER = 1;
- private static final int CALL_TYPE_PROVIDER_WITH_EVENT = 2;
}
diff --git a/android/arch/lifecycle/SingleGeneratedAdapterObserver.java b/android/arch/lifecycle/SingleGeneratedAdapterObserver.java
new file mode 100644
index 0000000..d176a3a
--- /dev/null
+++ b/android/arch/lifecycle/SingleGeneratedAdapterObserver.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import android.support.annotation.RestrictTo;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SingleGeneratedAdapterObserver implements GenericLifecycleObserver {
+
+ private final GeneratedAdapter mGeneratedAdapter;
+
+ SingleGeneratedAdapterObserver(GeneratedAdapter generatedAdapter) {
+ mGeneratedAdapter = generatedAdapter;
+ }
+
+ @Override
+ public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
+ mGeneratedAdapter.callMethods(source, event, false, null);
+ mGeneratedAdapter.callMethods(source, event, true, null);
+ }
+}
diff --git a/android/arch/lifecycle/TestUtils.java b/android/arch/lifecycle/TestUtils.java
index c5a520f..f0214bf 100644
--- a/android/arch/lifecycle/TestUtils.java
+++ b/android/arch/lifecycle/TestUtils.java
@@ -23,15 +23,16 @@
import android.app.Instrumentation.ActivityMonitor;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
+import android.support.v4.app.FragmentActivity;
import java.util.concurrent.CountDownLatch;
-public class TestUtils {
+class TestUtils {
private static final long TIMEOUT_MS = 2000;
@SuppressWarnings("unchecked")
- public static <T extends Activity> T recreateActivity(final T activity, ActivityTestRule rule)
+ static <T extends Activity> T recreateActivity(final T activity, ActivityTestRule rule)
throws Throwable {
ActivityMonitor monitor = new ActivityMonitor(
activity.getClass().getCanonicalName(), null, false);
@@ -60,7 +61,7 @@
return result;
}
- static void waitTillResumed(final LifecycleActivity a, ActivityTestRule<?> activityRule)
+ static void waitTillResumed(final FragmentActivity a, ActivityTestRule<?> activityRule)
throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
activityRule.runOnUiThread(() -> {
diff --git a/android/arch/lifecycle/Transformations.java b/android/arch/lifecycle/Transformations.java
index c316563..9ce9cbb 100644
--- a/android/arch/lifecycle/Transformations.java
+++ b/android/arch/lifecycle/Transformations.java
@@ -22,6 +22,12 @@
/**
* Transformations for a {@link LiveData} class.
+ * <p>
+ * You can use transformation methods to carry information across the observer's lifecycle. The
+ * transformations aren't calculated unless an observer is observing the returned LiveData object.
+ * <p>
+ * Because the transformations are calculated lazily, lifecycle-related behavior is implicitly
+ * passed down without requiring additional explicit calls or dependencies.
*/
@SuppressWarnings("WeakerAccess")
public class Transformations {
@@ -34,6 +40,18 @@
* LiveData and returns LiveData, which emits resulting values.
* <p>
* The given function {@code func} will be executed on the main thread.
+ * <p>
+ * Suppose that you have a LiveData, named {@code userLiveData}, that contains user data and you
+ * need to display the user name, created by concatenating the first and the last
+ * name of the user. You can define a function that handles the name creation, that will be
+ * applied to every value emitted by {@code useLiveData}.
+ *
+ * <pre>
+ * LiveData<User> userLiveData = ...;
+ * LiveData<String> userName = Transformations.map(userLiveData, user -> {
+ * return user.firstName + " " + user.lastName
+ * });
+ * </pre>
*
* @param source a {@code LiveData} to listen to
* @param func a function to apply
@@ -63,9 +81,39 @@
* <p>
* If the given function returns null, then {@code swLiveData} is not "backed" by any other
* LiveData.
+ *
* <p>
* The given function {@code func} will be executed on the main thread.
*
+ * <p>
+ * Consider the case where you have a LiveData containing a user id. Every time there's a new
+ * user id emitted, you want to trigger a request to get the user object corresponding to that
+ * id, from a repository that also returns a LiveData.
+ * <p>
+ * The {@code userIdLiveData} is the trigger and the LiveData returned by the {@code
+ * repository.getUserById} is the "backing" LiveData.
+ * <p>
+ * In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the
+ * userIdLiveData value is set to "1", the {@code switchMap} will call {@code getUser(1)},
+ * that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData
+ * will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"),
+ * the {@code userLiveData} gets automatically notified and will emit User(1, "Sarah").
+ * <p>
+ * When the {@code setUserId} method is called with userId = "2", the value of the {@code
+ * userIdLiveData} changes and automatically triggers a request for getting the user with id
+ * "2" from the repository. So, the {@code userLiveData} emits User(2, "John"). The LiveData
+ * returned by {@code repository.getUserById(1)} is removed as a source.
+ *
+ * <pre>
+ * MutableLiveData<String> userIdLiveData = ...;
+ * LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, id ->
+ * repository.getUserById(id));
+ *
+ * void setUserId(String userId) {
+ * this.userIdLiveData.setValue(userId);
+ * }
+ * </pre>
+ *
* @param trigger a {@code LiveData} to listen to
* @param func a function which creates "backing" LiveData
* @param <X> a type of {@code source} LiveData
diff --git a/android/arch/lifecycle/TransformationsTest.java b/android/arch/lifecycle/TransformationsTest.java
index e92ecca..940a3e8 100644
--- a/android/arch/lifecycle/TransformationsTest.java
+++ b/android/arch/lifecycle/TransformationsTest.java
@@ -25,7 +25,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.executor.ArchTaskExecutor;
import android.arch.core.util.Function;
import android.arch.lifecycle.util.InstantTaskExecutor;
@@ -42,7 +42,7 @@
@Before
public void swapExecutorDelegate() {
- AppToolkitTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
+ ArchTaskExecutor.getInstance().setDelegate(new InstantTaskExecutor());
}
@Before
diff --git a/android/arch/lifecycle/ViewModelProviderTest.java b/android/arch/lifecycle/ViewModelProviderTest.java
index 61760fc..8877357 100644
--- a/android/arch/lifecycle/ViewModelProviderTest.java
+++ b/android/arch/lifecycle/ViewModelProviderTest.java
@@ -21,6 +21,8 @@
import static org.hamcrest.MatcherAssert.assertThat;
import android.arch.lifecycle.ViewModelProvider.NewInstanceFactory;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
import org.junit.Assert;
import org.junit.Before;
@@ -82,6 +84,18 @@
assertThat(viewModel, is(provider.get(ViewModel1.class)));
}
+ @Test(expected = IllegalStateException.class)
+ public void testNotAttachedActivity() {
+ // This is similar to call ViewModelProviders.of in Activity's constructor
+ ViewModelProviders.of(new FragmentActivity());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testNotAttachedFragment() {
+ // This is similar to call ViewModelProviders.of in Activity's constructor
+ ViewModelProviders.of(new Fragment());
+ }
+
public static class ViewModel1 extends ViewModel {
boolean mCleared;
diff --git a/android/arch/lifecycle/ViewModelProviders.java b/android/arch/lifecycle/ViewModelProviders.java
index f64365b..746162a 100644
--- a/android/arch/lifecycle/ViewModelProviders.java
+++ b/android/arch/lifecycle/ViewModelProviders.java
@@ -17,6 +17,7 @@
package android.arch.lifecycle;
import android.annotation.SuppressLint;
+import android.app.Activity;
import android.app.Application;
import android.arch.lifecycle.ViewModelProvider.Factory;
import android.support.annotation.MainThread;
@@ -40,6 +41,23 @@
}
}
+ private static Application checkApplication(Activity activity) {
+ Application application = activity.getApplication();
+ if (application == null) {
+ throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ + "Application. You can't request ViewModel before onCreate call.");
+ }
+ return application;
+ }
+
+ private static Activity checkActivity(Fragment fragment) {
+ Activity activity = fragment.getActivity();
+ if (activity == null) {
+ throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
+ }
+ return activity;
+ }
+
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
* {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
@@ -51,12 +69,7 @@
*/
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
- FragmentActivity activity = fragment.getActivity();
- if (activity == null) {
- throw new IllegalArgumentException(
- "Can't create ViewModelProvider for detached fragment");
- }
- initializeFactoryIfNeeded(activity.getApplication());
+ initializeFactoryIfNeeded(checkApplication(checkActivity(fragment)));
return new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory);
}
@@ -71,7 +84,7 @@
*/
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
- initializeFactoryIfNeeded(activity.getApplication());
+ initializeFactoryIfNeeded(checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}
@@ -87,6 +100,7 @@
*/
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
+ checkApplication(checkActivity(fragment));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@@ -103,6 +117,7 @@
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
+ checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
diff --git a/android/arch/lifecycle/ViewModelTest.java b/android/arch/lifecycle/ViewModelTest.java
index 98ce027..03ebdf3 100644
--- a/android/arch/lifecycle/ViewModelTest.java
+++ b/android/arch/lifecycle/ViewModelTest.java
@@ -32,6 +32,7 @@
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
@@ -120,7 +121,7 @@
void onResume() {
try {
final FragmentManager manager = activity.getSupportFragmentManager();
- LifecycleFragment fragment = new LifecycleFragment();
+ Fragment fragment = new Fragment();
manager.beginTransaction().add(fragment, "temp").commitNow();
ViewModel1 vm = ViewModelProviders.of(fragment).get(ViewModel1.class);
assertThat(vm.mCleared, is(false));
diff --git a/android/arch/lifecycle/activity/EmptyActivity.java b/android/arch/lifecycle/activity/EmptyActivity.java
index 017fff4..c32c898 100644
--- a/android/arch/lifecycle/activity/EmptyActivity.java
+++ b/android/arch/lifecycle/activity/EmptyActivity.java
@@ -16,13 +16,12 @@
package android.arch.lifecycle.activity;
+import android.arch.lifecycle.extensions.test.R;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
-import android.arch.lifecycle.LifecycleActivity;
-import android.arch.lifecycle.extensions.test.R;
-
-public class EmptyActivity extends LifecycleActivity {
+public class EmptyActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/android/arch/lifecycle/activity/FragmentLifecycleActivity.java b/android/arch/lifecycle/activity/FragmentLifecycleActivity.java
index f4485e8..2eb1cc2 100644
--- a/android/arch/lifecycle/activity/FragmentLifecycleActivity.java
+++ b/android/arch/lifecycle/activity/FragmentLifecycleActivity.java
@@ -17,7 +17,6 @@
package android.arch.lifecycle.activity;
import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleFragment;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.OnLifecycleEvent;
@@ -26,6 +25,7 @@
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
@@ -70,9 +70,9 @@
mLoggedEvents.clear();
}
- public static class MainFragment extends LifecycleFragment {
+ public static class MainFragment extends Fragment {
@Nullable
- LifecycleFragment mNestedFragment;
+ Fragment mNestedFragment;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -85,7 +85,7 @@
}
}
- public static class NestedFragment extends LifecycleFragment {
+ public static class NestedFragment extends Fragment {
}
public static Intent intentFor(Context context, boolean nested) {
@@ -98,7 +98,8 @@
mObservedOwner = provider;
provider.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
- public void anyEvent(LifecycleOwner owner, Lifecycle.Event event) {
+ public void anyEvent(@SuppressWarnings("unused") LifecycleOwner owner,
+ Lifecycle.Event event) {
mLoggedEvents.add(event);
}
});
diff --git a/android/arch/lifecycle/observers/Base.java b/android/arch/lifecycle/observers/Base.java
new file mode 100644
index 0000000..08919d4
--- /dev/null
+++ b/android/arch/lifecycle/observers/Base.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class Base implements LifecycleObserver {
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ public void onCreate() {
+ }
+}
diff --git a/android/arch/lifecycle/observers/Base_LifecycleAdapter.java b/android/arch/lifecycle/observers/Base_LifecycleAdapter.java
new file mode 100644
index 0000000..4218b96
--- /dev/null
+++ b/android/arch/lifecycle/observers/Base_LifecycleAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.GeneratedAdapter;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.MethodCallsLogger;
+
+public class Base_LifecycleAdapter implements GeneratedAdapter {
+
+ public Base_LifecycleAdapter(Base base) {
+ }
+
+ @Override
+ public void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
+ MethodCallsLogger logger) {
+
+ }
+}
diff --git a/android/arch/lifecycle/observers/DerivedSequence1.java b/android/arch/lifecycle/observers/DerivedSequence1.java
new file mode 100644
index 0000000..9db37f1
--- /dev/null
+++ b/android/arch/lifecycle/observers/DerivedSequence1.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+public class DerivedSequence1 extends Base {
+
+ public void something() {
+ }
+}
diff --git a/android/arch/lifecycle/observers/DerivedSequence2.java b/android/arch/lifecycle/observers/DerivedSequence2.java
new file mode 100644
index 0000000..f2ef943
--- /dev/null
+++ b/android/arch/lifecycle/observers/DerivedSequence2.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class DerivedSequence2 extends DerivedSequence1 {
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+ void onStop() {
+
+ }
+}
diff --git a/android/arch/lifecycle/observers/DerivedWithNewMethods.java b/android/arch/lifecycle/observers/DerivedWithNewMethods.java
new file mode 100644
index 0000000..b1eaef0
--- /dev/null
+++ b/android/arch/lifecycle/observers/DerivedWithNewMethods.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class DerivedWithNewMethods extends Base {
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+ void onStop() {
+
+ }
+}
diff --git a/android/arch/lifecycle/observers/DerivedWithNoNewMethods.java b/android/arch/lifecycle/observers/DerivedWithNoNewMethods.java
new file mode 100644
index 0000000..cb1afb8
--- /dev/null
+++ b/android/arch/lifecycle/observers/DerivedWithNoNewMethods.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+public class DerivedWithNoNewMethods extends Base {
+}
diff --git a/android/arch/lifecycle/observers/DerivedWithOverridenMethodsWithLfAnnotation.java b/android/arch/lifecycle/observers/DerivedWithOverridenMethodsWithLfAnnotation.java
new file mode 100644
index 0000000..40c7c9a
--- /dev/null
+++ b/android/arch/lifecycle/observers/DerivedWithOverridenMethodsWithLfAnnotation.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class DerivedWithOverridenMethodsWithLfAnnotation extends Base {
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+}
diff --git a/android/arch/lifecycle/observers/Interface1.java b/android/arch/lifecycle/observers/Interface1.java
new file mode 100644
index 0000000..e193de9
--- /dev/null
+++ b/android/arch/lifecycle/observers/Interface1.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public interface Interface1 extends LifecycleObserver {
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ void onCreate();
+}
diff --git a/android/arch/lifecycle/observers/Interface1_LifecycleAdapter.java b/android/arch/lifecycle/observers/Interface1_LifecycleAdapter.java
new file mode 100644
index 0000000..c597b1c
--- /dev/null
+++ b/android/arch/lifecycle/observers/Interface1_LifecycleAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.GeneratedAdapter;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.MethodCallsLogger;
+
+public class Interface1_LifecycleAdapter implements GeneratedAdapter {
+
+ public Interface1_LifecycleAdapter(Interface1 base) {
+ }
+
+ @Override
+ public void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
+ MethodCallsLogger logger) {
+
+ }
+}
diff --git a/android/arch/lifecycle/observers/Interface2.java b/android/arch/lifecycle/observers/Interface2.java
new file mode 100644
index 0000000..1056fcb
--- /dev/null
+++ b/android/arch/lifecycle/observers/Interface2.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public interface Interface2 extends LifecycleObserver {
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ void onCreate();
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ void onDestroy();
+}
diff --git a/android/arch/lifecycle/observers/Interface2_LifecycleAdapter.java b/android/arch/lifecycle/observers/Interface2_LifecycleAdapter.java
new file mode 100644
index 0000000..b05b41a
--- /dev/null
+++ b/android/arch/lifecycle/observers/Interface2_LifecycleAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+import android.arch.lifecycle.GeneratedAdapter;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.MethodCallsLogger;
+
+public class Interface2_LifecycleAdapter implements GeneratedAdapter {
+
+ public Interface2_LifecycleAdapter(Interface2 base) {
+ }
+
+ @Override
+ public void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
+ MethodCallsLogger logger) {
+
+ }
+}
diff --git a/android/arch/lifecycle/observers/InterfaceImpl1.java b/android/arch/lifecycle/observers/InterfaceImpl1.java
new file mode 100644
index 0000000..2f03393
--- /dev/null
+++ b/android/arch/lifecycle/observers/InterfaceImpl1.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+public class InterfaceImpl1 implements Interface1 {
+ @Override
+ public void onCreate() {
+ }
+}
diff --git a/android/arch/lifecycle/observers/InterfaceImpl2.java b/android/arch/lifecycle/observers/InterfaceImpl2.java
new file mode 100644
index 0000000..eef8ce4
--- /dev/null
+++ b/android/arch/lifecycle/observers/InterfaceImpl2.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+public class InterfaceImpl2 implements Interface1, Interface2 {
+ @Override
+ public void onCreate() {
+ }
+
+ @Override
+ public void onDestroy() {
+
+ }
+}
diff --git a/android/arch/lifecycle/observers/InterfaceImpl3.java b/android/arch/lifecycle/observers/InterfaceImpl3.java
new file mode 100644
index 0000000..8f31808
--- /dev/null
+++ b/android/arch/lifecycle/observers/InterfaceImpl3.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.observers;
+
+public class InterfaceImpl3 extends Base implements Interface1 {
+ @Override
+ public void onCreate() {
+ }
+}
diff --git a/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java b/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
index 5972b16..5f33c28 100644
--- a/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
+++ b/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
@@ -19,8 +19,8 @@
import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleActivity;
import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
import android.util.Pair;
import java.util.ArrayList;
@@ -31,7 +31,7 @@
/**
* Activity for testing full lifecycle
*/
-public class FullLifecycleTestActivity extends LifecycleActivity implements CollectingActivity {
+public class FullLifecycleTestActivity extends FragmentActivity implements CollectingActivity {
private List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents = new ArrayList<>();
private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
diff --git a/android/arch/lifecycle/testapp/LifecycleTestActivity.java b/android/arch/lifecycle/testapp/LifecycleTestActivity.java
index 093ec7f..cf07aee 100644
--- a/android/arch/lifecycle/testapp/LifecycleTestActivity.java
+++ b/android/arch/lifecycle/testapp/LifecycleTestActivity.java
@@ -16,13 +16,13 @@
package android.arch.lifecycle.testapp;
-import android.arch.lifecycle.LifecycleActivity;
import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
/**
* Activity for testing events by themselves
*/
-public class LifecycleTestActivity extends LifecycleActivity {
+public class LifecycleTestActivity extends FragmentActivity {
/**
* identifies that
diff --git a/android/arch/lifecycle/testapp/NavigationDialogActivity.java b/android/arch/lifecycle/testapp/NavigationDialogActivity.java
index 709bd8d..0ae9403 100644
--- a/android/arch/lifecycle/testapp/NavigationDialogActivity.java
+++ b/android/arch/lifecycle/testapp/NavigationDialogActivity.java
@@ -16,10 +16,10 @@
package android.arch.lifecycle.testapp;
-import android.arch.lifecycle.LifecycleActivity;
+import android.support.v4.app.FragmentActivity;
/**
* an activity with Dialog theme.
*/
-public class NavigationDialogActivity extends LifecycleActivity {
+public class NavigationDialogActivity extends FragmentActivity {
}
diff --git a/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java b/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java
index f1847c9..69fd478 100644
--- a/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java
+++ b/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java
@@ -16,10 +16,10 @@
package android.arch.lifecycle.testapp;
-import android.arch.lifecycle.LifecycleActivity;
+import android.support.v4.app.FragmentActivity;
/**
* Activity for ProcessOwnerTest
*/
-public class NavigationTestActivityFirst extends LifecycleActivity {
+public class NavigationTestActivityFirst extends FragmentActivity {
}
diff --git a/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java b/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java
index 221e927..0f9a4c9 100644
--- a/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java
+++ b/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java
@@ -16,10 +16,10 @@
package android.arch.lifecycle.testapp;
-import android.arch.lifecycle.LifecycleActivity;
+import android.support.v4.app.FragmentActivity;
/**
* Activity for ProcessOwnerTest
*/
-public class NavigationTestActivitySecond extends LifecycleActivity {
+public class NavigationTestActivitySecond extends FragmentActivity {
}
diff --git a/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java b/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java
index 6d61c5e..77bd99f 100644
--- a/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java
+++ b/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java
@@ -17,12 +17,12 @@
package android.arch.lifecycle.testapp;
import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleActivity;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.OnLifecycleEvent;
import android.arch.lifecycle.ProcessLifecycleOwner;
import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
import android.util.Pair;
import java.util.ArrayList;
@@ -33,7 +33,7 @@
/**
* Activity for SimpleAppFullLifecycleTest
*/
-public class SimpleAppLifecycleTestActivity extends LifecycleActivity {
+public class SimpleAppLifecycleTestActivity extends FragmentActivity {
public enum TestEventType {
PROCESS_EVENT,
diff --git a/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java b/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java
index 5ef9f16..1f9f100 100644
--- a/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java
+++ b/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java
@@ -16,15 +16,14 @@
package android.arch.lifecycle.viewmodeltest;
+import android.arch.lifecycle.ViewModelProviders;
import android.arch.lifecycle.extensions.test.R;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
-import android.arch.lifecycle.LifecycleActivity;
-import android.arch.lifecycle.LifecycleFragment;
-import android.arch.lifecycle.ViewModelProviders;
-
-public class ViewModelActivity extends LifecycleActivity {
+public class ViewModelActivity extends FragmentActivity {
public static final String KEY_FRAGMENT_MODEL = "fragment-model";
public static final String KEY_ACTIVITY_MODEL = "activity-model";
public static final String FRAGMENT_TAG_1 = "f1";
@@ -47,7 +46,7 @@
defaultActivityModel = ViewModelProviders.of(this).get(TestViewModel.class);
}
- public static class ViewModelFragment extends LifecycleFragment {
+ public static class ViewModelFragment extends Fragment {
public TestViewModel fragmentModel;
public TestViewModel activityModel;
public TestViewModel defaultActivityModel;