Skip to content
This repository was archived by the owner on Aug 16, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,120 @@ Please follow along the codelab steps [here](https://codelabs.developers.google.
# Filing issues

If you find errors in the codelab steps or the code, please file them [here](https://github.com/googlecodelabs/android-lifecycles/issues/new)

## Notes

1. An activity is a poor choice to manage app data.
Activities and fragments are short-lived objects which are created and destroyed frequently as a user interacts with an app.

2. ViewModel can retain data across the entire lifecycle of an activity or a fragment.
A ViewModel is also better suited to managing tasks related to network communication, as well as data manipulation and persistence.
To avoid memory leaks, the ViewModel doesn't include references to the activity.

3. Instead of modifying views directly from the ViewModel, you configure an activity or fragment to observe a data source, receiving the data when it changes.
This arrangement is called the observer pattern.

4. LiveData is a special observable class which is lifecycle-aware, and only notifies active observers.
To make change value of MutableLiveData: setValue() method must be called from the main thread.
To change from inside a background thread, you can use postValue().

5. Lifecycle-aware components perform actions in response to a change in the lifecycle status of another component, such as activities and fragments.
The androidx.lifecycle package provides classes and interfaces that let you build lifecycle-aware componentsLifecycleOwner is an interface that is used by any class that has an Android lifecycle.

***Lifecycle*** is a class that holds the information about the lifecycle state of a component (like an activity or a fragment) and allows other objects to observe this state.
Lifecycle uses two main enumerations to track the lifecycle status for its associated component:

***Event***
The lifecycle events that are dispatched from the framework and the Lifecycle class. These events map to the callback events in activities and fragments.
***State***
The current state of the component tracked by the Lifecycle object.

**Think of the states as nodes of a graph and events as the edges between these nodes**

![LifeCycle State up and down events ](https://developer.android.com/images/topic/libraries/architecture/lifecycle-states.svg)

```
static State getStateAfter(Event event) {
switch (event) {
case ON_CREATE:
case ON_STOP:
return CREATED;
case ON_START:
case ON_PAUSE:
return STARTED;
case ON_RESUME:
return RESUMED;
case ON_DESTROY:
return DESTROYED;
case ON_ANY:
break;
}
throw new IllegalArgumentException("Unexpected event value " + event);
}

private static Event downEvent(State state) {
switch (state) {
case INITIALIZED:
throw new IllegalArgumentException();
case CREATED:
return ON_DESTROY;
case STARTED:
return ON_STOP;
case RESUMED:
return ON_PAUSE;
case DESTROYED:
throw new IllegalArgumentException();
}
throw new IllegalArgumentException("Unexpected state value " + state);
}

private static Event upEvent(State state) {
switch (state) {
case INITIALIZED:
case DESTROYED:
return ON_CREATE;
case CREATED:
return ON_START;
case STARTED:
return ON_RESUME;
case RESUMED:
throw new IllegalArgumentException();
}
throw new IllegalArgumentException("Unexpected state value " + state);
}

```

A class can monitor the component's lifecycle status by adding annotations to its methods. Then you can add an observer by calling the addObserver() method of the Lifecycle class and passing an instance of your observer, as shown in the following example:

```

public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void connectListener() {
...
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void disconnectListener() {
...
}
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

```

***LifecycleOwner*** is a single method interface that denotes that the class has a Lifecycle. It has one method, getLifecycle(), which must be implemented by the class

Components that implement LifecycleObserver work seamlessly with components that implement LifecycleOwner because an owner can provide a lifecycle, which an observer can register to watch.

6. ADB commands:

./adb shell ps -A |grep lifecycle
./adb shell am kill com.example.android.codelabs.lifecycle

7. Some UI elements, including EditText, save their state using their own onSaveInstanceState implementation.
This state is restored after a process is killed the same way it's restored after a configuration change.

8. Use SavedStateHandle to save ViewModel properties during process kill.
8 changes: 8 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>

<activity
android:name="com.example.android.lifecycles.step7_lifeCycleRegistry.LocationActivity"
android:label="LC Step7 Lifecycle Registry">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2019, 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 com.example.android.lifecycles.step7_lifeCycleRegistry;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.util.Log;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;

public class BoundLocationManager {
public static void bindLocationListenerIn(LifecycleOwner lifecycleOwner,
LocationListener listener, Context context) {
new BoundLocationListener(lifecycleOwner, listener, context);
}

@SuppressWarnings("MissingPermission")
static class BoundLocationListener implements LifecycleObserver {
private final Context mContext;
private LocationManager mLocationManager;
private final LocationListener mListener;

public BoundLocationListener(LifecycleOwner lifecycleOwner,
LocationListener listener, Context context) {
mContext = context;
mListener = listener;
lifecycleOwner.getLifecycle().addObserver(this);
}

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void addLocationListener() {
// Note: Use the Fused Location Provider from Google Play Services instead.
// https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi

mLocationManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mListener);
Log.d("BoundLocationMgr", "Listener added");

// Force an update with the last location, if available.
Location lastLocation = mLocationManager.getLastKnownLocation(
LocationManager.GPS_PROVIDER);
if (lastLocation != null) {
mListener.onLocationChanged(lastLocation);
}else {
Location zero=new Location("zero");
zero.setLatitude(0.0);
zero.setLongitude(0.0);
mListener.onLocationChanged(zero);
}
}


@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void removeLocationListener() {
if (mLocationManager == null) {
return;
}
mLocationManager.removeUpdates(mListener);
mLocationManager = null;
Log.d("BoundLocationMgr", "Listener removed");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright 2019, 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 com.example.android.lifecycles.step7_lifeCycleRegistry;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;

import com.example.android.codelabs.lifecycle.R;

public class LocationActivity extends Activity implements LifecycleOwner {

private static final int REQUEST_LOCATION_PERMISSION_CODE = 1;

private LocationListener mGpsListener = new MyLocationListener();

LifecycleRegistry lifecycleRegistry;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
bindLocationListener();
} else {
Toast.makeText(this, "This sample requires Location access", Toast.LENGTH_LONG).show();
}
}

private void bindLocationListener() {
BoundLocationManager.bindLocationListenerIn(this, mGpsListener, getApplicationContext());
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.location_activity);


lifecycleRegistry=new LifecycleRegistry(this);
lifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_LOCATION_PERMISSION_CODE);
} else {
bindLocationListener();
}
}

@Override
protected void onResume() {
super.onResume();

lifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
} @Override

protected void onPause() {
super.onPause();

lifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
}


@Override
protected void onStop() {
super.onStop();

lifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
}


@Override
protected void onStart() {
super.onStart();

lifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
}

@Override
protected void onDestroy() {
super.onDestroy();
lifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
}

@NonNull
@Override
public Lifecycle getLifecycle() {
return lifecycleRegistry;
}

private class MyLocationListener implements LocationListener {
@Override
public void onLocationChanged(Location location) {
TextView textView = findViewById(R.id.location);
textView.setText(location.getLatitude() + ", " + location.getLongitude());
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

@Override
public void onProviderEnabled(String provider) {
Toast.makeText(LocationActivity.this,
"Provider enabled: " + provider, Toast.LENGTH_SHORT).show();
}

@Override
public void onProviderDisabled(String provider) {
}
}
}
Loading