当前位置:主页 > 资料 >

Architecture Components: Lifecycle
栏目分类:资料   发布日期:2017-07-08   浏览次数:

导读:本文为去找网小编(www.7zhao.net)为您推荐的Architecture Components: Lifecycle,希望对您有所帮助,谢谢! In a recent article onLocationServices we had a Fragment implementation which was tightly coupled to the Play Se

本文为去找网小编(www.7zhao.net)为您推荐的Architecture Components: Lifecycle,希望对您有所帮助,谢谢! 去找(www.7zhao.net欢迎您



In a recent article onLocationServices we had a Fragment implementation which was tightly coupled to the Play Services location provider. This is problematic because, if we need to use different location providers for different build variants then this tight coupling to our Fragment makes it difficult to abstract away. Architecture Components provides us with a new lifecycle framework which gives us the ability to invert the dependencies, so that our Fragment implementation is completely agnostic of the location provider. 去找(www.7zhao.net欢迎您

To demonstrate how we can do this, we’ll create a project with two different location providers for different product flavors. One will use the Play Services location provider which we covered in the previous article, and the second will use a dummy provider which will always return the location of Vicarage Road Stadium (home of Watford Football Club).

本文来自去找www.7zhao.net

Let’s first take a look at the build.gradle for our app module: 本文来自去找www.7zhao.net

apply plugin: 'com.android.application'
 
ext {
    supportLibraryVersion = "25.4.0"
    playServicesVersion = "11.0.1"
    architectureComponentsVersion = "1.0.0-alpha3"
}
 
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.stylingandroid.location.services"
        minSdkVersion 17
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    lintOptions {
        disable 'OldTargetApi', "GradleDependency"
    }
    productFlavors {
        dummy
        playServices
    }
}
 
dependencies {
    compile "com.android.support:appcompat-v7:${supportLibraryVersion}"
    compile "com.android.support:design:${supportLibraryVersion}"
    compile "com.android.support:support-v4:${supportLibraryVersion}"
    compile 'com.android.support.constraint:constraint-layout:1.1.0-beta1'
    compile "android.arch.lifecycle:runtime:${architectureComponentsVersion}"
    compile "android.arch.lifecycle:extensions:${architectureComponentsVersion}"
    annotationProcessor "android.arch.lifecycle:compiler:${architectureComponentsVersion}"
    playServicesCompile "com.google.android.gms:play-services-location:{playServicesVersion}"
}
 欢迎访问www.7zhao.net 

We define the two flavors named dummy and playServices ; include the necessary lifecycle libraries and annotation processor; and include play-services-location but only for the playServices flavor.

copyright www.7zhao.net

We can then set up separate source trees for the two build flavors, each will contain its own LocationProvider implementation: 去找(www.7zhao.net欢迎您

去找(www.7zhao.net欢迎您

This is easy enough we can quite easily create this set up without any help from Architecture Components. Different location providers may have differing requirements as to where we need to hook them into the Fragment lifecycle. For the Play Services provider we want to kick off the registration early because it will be around 300ms until we get our first location update. However the dummy provider will be almost instantaneous. So we need to hook the Play Services implementation into onStart , whereas the dummy implementation will generate an immediate update, so we want to wait until later in the lifecycle (specifically onResume ) to trigger it. Although this is a somewhat contrived example, different location providers may require different things to happen at different stages of the Fragment lifecycle.

内容来自www.7zhao.net

One way of solving this would be to have the Fragment implementation make calls in to the LocationProvider at every lifecycle state transition. Then the LocationProvider is responsible for performing the necessary actions when certain lifecycle state transitions occur, but this requires us to add a bit of boilerplate to our Fragment implementation. If we then have other abstractions to external services, we need to start duplicating this for each of them.

欢迎访问www.7zhao.net

This is where the Architecture Lifecycle Components can help us. They allow us to hook an external component, such as our LocationProvider , to the Fragment lifecycle with a minimum of code. 欢迎访问www.7zhao.net

Let’s take a look at our LocationFragment implementation: 本文来自去找www.7zhao.net

public class LocationFragment extends LifecycleFragment implements LocationListener {
    private static final String FRACTIONAL_FORMAT = "%.4f";
    private static final String ACCURACY_FORMAT = "%.1fm";
 
    private TextView latitudeValue;
    private TextView longitudeValue;
    private TextView accuracyValue;
 
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        new LocationProvider(getContext(), getLifecycle(), this);
    }
 
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_location, container, false);
        latitudeValue = (TextView) view.findViewById(R.id.latitude_value);
        longitudeValue = (TextView) view.findViewById(R.id.longitude_value);
        accuracyValue = (TextView) view.findViewById(R.id.accuracy_value);
        return view;
    }
 
    @Override
    public void updateLocation(CommonLocation location) {
        String latitudeString = createFractionString(location.getLatitude());
        String longitudeString = createFractionString(location.getLongitude());
        String accuracyString = createAccuracyString(location.getAccuracy());
        latitudeValue.setText(latitudeString);
        longitudeValue.setText(longitudeString);
        accuracyValue.setText(accuracyString);
    }
 
    private String createFractionString(double fraction) {
        return String.format(Locale.getDefault(), FRACTIONAL_FORMAT, fraction);
    }
 
    private String createAccuracyString(float accuracy) {
        return String.format(Locale.getDefault(), ACCURACY_FORMAT, accuracy);
    }
}
 
copyright www.7zhao.net

The first thing we must do is extend LifecycleFragment . This is a temporary measure while the Architecture Components library is not yet at a stable release. Once it has been released then the support library Fragment implementation will contain the necessary code and we need not bother with this.

www.7zhao.net

We also implement LocationListener which is a callback interface which we’ll look at in a moment. 本文来自去找www.7zhao.net

In onAttach() we create our LocationProvider instance which takes a Context , a Lifecycle , and an instance of LocationListener (which LocationFragment implements). The interesting one here is the Lifecycle – this is obtained from the getLifecycle() method, and it is this that is currently implemented in LifecycleFragment , but will move to Fragment once the library is fully released. The Lifecycle is an abstraction of the Fragment lifecycle and is what our LocationProvider uses to get notified of Fragment lifecycle state transitions. 去找(www.7zhao.net欢迎您

Finally we implement the one method which is defined in LocationListener : updateLocation() . This will get called whenever our LocationProvider gets updated location data.

去找(www.7zhao.net欢迎您

Let’s now take a look at the dummy LocationProvider implementation:

内容来自www.7zhao.net

public class LocationProvider implements LifecycleObserver {
    private static final double LATITUDE = 51.649836;
    private static final double LONGITUDE = -0.401486;
    private static final float ACCURACY = 5f;
    private static final CommonLocation LOCATION = new CommonLocation(LATITUDE, LONGITUDE, ACCURACY);
 
    private final Lifecycle lifecycle;
    private final LocationListener locationListener;
 
    public LocationProvider(@NonNull Context context, Lifecycle lifecycle, @NonNull LocationListener listener) {
        this.lifecycle = lifecycle;
        this.locationListener = listener;
        lifecycle.addObserver(this);
    }
 
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void unregisterObserver() {
        lifecycle.removeObserver(this);
    }
 
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void sendDummyLocation() {
        locationListener.updateLocation(LOCATION);
    }
}
 
欢迎访问www.7zhao.net

First we must implement LifecycleObserver to receive lifecycle callback. Next we need to register the class as an observer of the lifecycle. Finally we specify which lifecycle events we’re interested in using the @OnLifecycleEvent annotations, and the annotated methods will get called whenever those lifecycle events occur.

内容来自www.7zhao.net

The PlayServices LocationProvider uses the same approach, but actually hooks up to different parts of the Fragment lifecycle:

内容来自www.7zhao.net

public class LocationProvider implements LifecycleObserver {
    private final Context context;
    private final Lifecycle lifecycle;
    private final LocationListener locationListener;
 
    private FusedLocationProviderClient fusedLocationProviderClient = null;
 
    public LocationProvider(@NonNull Context context, Lifecycle lifecycle, @NonNull LocationListener listener) {
        this.context = context;
        this.lifecycle = lifecycle;
        this.locationListener = listener;
        lifecycle.addObserver(this);
    }
 
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void registerForLocationUpdates() {
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        FusedLocationProviderClient locationProviderClient = getFusedLocationProviderClient();
        LocationRequest locationRequest = LocationRequest.create();
        Looper looper = Looper.myLooper();
        locationProviderClient.requestLocationUpdates(locationRequest, locationCallback, looper);
    }
 
    @NonNull
    private FusedLocationProviderClient getFusedLocationProviderClient() {
        if (fusedLocationProviderClient == null) {
            fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
        }
        return fusedLocationProviderClient;
    }
 
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void unregisterForLocationUpdates() {
        if (fusedLocationProviderClient != null) {
            fusedLocationProviderClient.removeLocationUpdates(locationCallback);
        }
    }
 
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void unregisterObserver() {
        lifecycle.removeObserver(this);
    }
 
    private LocationCallback locationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);
            Location lastLocation = locationResult.getLastLocation();
            double latitude = lastLocation.getLatitude();
            double longitude = lastLocation.getLongitude();
            float accuracy = lastLocation.getAccuracy();
            CommonLocation location = new CommonLocation(latitude, longitude, accuracy);
            locationListener.updateLocation(location);
        }
    };
 
}
 内容来自www.7zhao.net 

So we have easily been able to hook up these different implementations to different parts of the Fragment lifecycle without having to leak any implementation specific in to the LocationFragment code.

copyright www.7zhao.net

If we consider what is going on internally, it’s pretty much the approach we looked at earlier: the Lifecycle object will make callbacks to any registered observers for every lifecycle state transition, and it is down to the individual observers to decide which ones they are interested in. However the additional code that we’ve had to add to the LocationFragment is actually pretty minimal, and this will reduce even further once the Architecture Components library is fully released and integrated with the Android framework. 去找(www.7zhao.net欢迎您

While this certainly benefits us, there is actually a better way of doing it. I have quite deliberately done an initial implementation by hooking on to lifecycle events for two reasons: firstly, it is an important foundation for some of the principles which we’ll be covering later in this series; and, secondly, the lifecycle approach certainly is useful in some circumstances and is useful to know! In the next article we’ll take things a stage further, and see how we can further simplify our LocationFragment .

内容来自www.7zhao.net

The source code for this article is available . www.7zhao.net

Many thanks to Yiğit Boyar and Sebastiano Poggi for proof-reading services – this post would have been much worse without their input. Any errors or typos that remain are purely my fault! 欢迎访问www.7zhao.net

© 2017,Mark Allison. All rights reserved.

本文来自去找www.7zhao.net

www.7zhao.net


本文原文地址:https://blog.stylingandroid.com/architecture-components-lifecycle/

以上为Architecture Components: Lifecycle文章的全部内容,若您也有好的文章,欢迎与我们分享! 内容来自www.7zhao.net

Copyright ©2008-2017去找网版权所有   皖ICP备12002049号-2 皖公网安备 34088102000435号   关于我们|联系我们| 免责声明|友情链接|网站地图|手机版