当前位置:网站首页>JetPack - - - LifeCycle、ViewModel、LiveData

JetPack - - - LifeCycle、ViewModel、LiveData

2022-06-13 06:25:00 m0_ forty-seven million nine hundred and fourteen thousand one

  

  Jetpack It's a suite of multiple libraries , Helps developers follow best practices 、 Reduce boilerplate code and write available in a variety of Android Version and the code running in the device consistently , So that developers can focus on really important coding work .

JetPack The advantages of :

Follow best practices :
  AndroidJetpack Components are built using the latest design methods , Backward compatibility , Can reduce crashes and memory leaks .
Eliminate boilerplate code :
  AndroidJetpack Can manage all kinds of tedious Activity( Such as background tasks 、 Navigation and Lifecycle Management ), So you can focus on building great applications .
Reduce inconsistencies :
   These libraries can be found in all kinds of Android Versions and devices operate in a consistent way , Help you reduce complexity .

LifeCycle

 LifeCycle The component lifecycle changes are notified and decoupled , omitted onCreate()、onDestroy() Etc .

1. monitor activity Life cycle of

   Let's start with LifeCycle Timer case for

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Lifecycle.MainActivity">

        <com.example.jetpacktest.Lifecycle.MyChronometer
            android:id="@+id/chronometer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            />

  
     

</androidx.constraintlayout.widget.ConstraintLayout>

Here we refer to a custom timer class (activity Display timing , Pause timing after exiting ).

public class MyChronometer extends Chronometer implements LifecycleObserver {

    private long mElapsedTime;

    public MyChronometer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    // To configure onResume Listening in 
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void startMeter(){
        // Resume the timing and make sure that it is the time before exiting 
        setBase(SystemClock.elapsedRealtime()-mElapsedTime);
        start();
    }

     // To configure onPause Listening in 
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    private void stopMeter(){
        // Record the timing 
        mElapsedTime = SystemClock.elapsedRealtime() - getBase();
        stop();
    }
}

Lifecycle How to use ( Above ): 

1. Realization LifecycleObserver Interface .

2. Configure annotations and specify lifecycle

public class MainActivity extends AppCompatActivity {

    private MyChronometer mViewById;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mViewById = findViewById(R.id.chronometer);
        // binding activity Life cycle monitoring for 
        getLifecycle().addObserver(mViewById);
     
    }
}

  design sketch :

2. monitor service Life cycle of

Let's add this dependency first :

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

stay AndroidManifest.xml Add the permissions required for positioning in the file :

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

This time we passed a Service The case of printing coordinates in to illustrate the use method .

Let's start with a layout xml:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Lifecycle.MainActivity">


        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#03A9F4"
            android:text="start"
            android:onClick="startGPS"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/chronometer"
            app:layout_constraintVertical_bias="0.346" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#00BCD4"
            android:text="stop"
            android:onClick="stopGPS"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/chronometer"
            app:layout_constraintVertical_bias="0.613" />

</androidx.constraintlayout.widget.ConstraintLayout>

stay Xml There are two buttons in the , Start and stop Service.

public class MyLocationService extends LifecycleService {

    public MyLocationService() {
        Log.i("ning","MyLocationService");
        MyLocationObserver myLocationObserver = new MyLocationObserver(this);
        // Binding lifecycle 
        getLifecycle().addObserver(myLocationObserver);
    }
}

stay Service We first inherit LifecycleService, Then initialize the class for location monitoring , And bind your life cycle .

public class MyLocationObserver implements LifecycleObserver {
    private Context mContext;
    private LocationManager mSystemService;
    private MyLocationListener mMyLocationListener;

    public MyLocationObserver(Context pContext) {
        mContext = pContext;
    }

    // monitor Service perform onCreate() When the 
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    private void startGetLocation() {
        Log.i("ning","startGetLocation");
        mSystemService = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        mMyLocationListener = new MyLocationListener();
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        // Turn on Gps
        mSystemService.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1, mMyLocationListener);
    }

  // monitor Service perform onDestroy() When the 
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private void stopGetLocation(){
        Log.i("ning","stopGetLocation");
         // close Gps
        mSystemService.removeUpdates(mMyLocationListener);
    }

    static class MyLocationListener implements LocationListener {

        @Override
        public void onLocationChanged(@NonNull Location location) {
            Log.d("ning","onLocationChanged"+location.getLongitude()+","+location.getLatitude());
        }
    }
}

And in activity It's written in the same way , Inherit LifecycleObserver Interface and add comments .

public class MainActivity extends AppCompatActivity {

    private MyChronometer mViewById;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION}, 1);
            }
        }
    
    }

    public void startGPS(View view) {
            startService(new Intent(this,MyLocationService.class));
    }

    public void stopGPS(View view) {
        stopService(new Intent(this,MyLocationService.class));

    }
}

At last I passed adb Order to modify gps The location of

adb -s emulator-5554 emu geo fix 121.4961236714487 31.24010934431376
adb -s emulator-5554 emu geo fix 122.4961236714487 31.24010934431376

Print the results :

2021-12-20 15:32:15.166 18692-18692/com.example.jetpacktest D/ning: onLocationChanged121.49612333333333,31.240108333333332

3. monitor App Life cycle of

We decided to have a App Life cycle listener class .

public class ApplicationObserver implements LifecycleObserver {
    private static String TAG="TAG";
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    private void onCreate(){
        Log.i(TAG,"Lifecycle.Event.ON_CREATE");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onStop() {
        Log.i(TAG,"Lifecycle.Event.ON_STOP");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onStart() {
        Log.i(TAG,"Lifecycle.Event.ON_START");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void onResume() {
        Log.i(TAG,"Lifecycle.Event.ON_RESUME");
    }



    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    private void onPause() {
        Log.i(TAG,"Lifecycle.Event.ON_PAUSE");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private void onDestroy() {
        Log.i(TAG,"Lifecycle.Event.ON_DESTROY");
    }

}

The method is the same as before .

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
         // Bind the listening class of the life cycle 
        ProcessLifecycleOwner.get().getLifecycle().addObserver(new ApplicationObserver());
    }
}

And then Application Just bind it !

4.Lifecycle The advantages of

1. Help developers build life cycle aware components
2. Components manage their own lifecycle within them , So as to reduce the module coupling
3. Reduce the possibility of memory leaks
4.Activity、Fragment、Application Both LifeCycle Support

Two .ViewModel

1.ViewModel The role of

1. It's between View( View ) and Model( Data model ) The bridge between .

2. View and data can be separated , Can also maintain communication .


 

2.ViewModel Application

We use a case to demonstrate ViewModel Usage of .

First introduce this dependency

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ViewModel.ViewModelActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.23" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="ViewModelClick"
        android:text=" Click Add 1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.532"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.45" />
</androidx.constraintlayout.widget.ConstraintLayout>
public class ViewModelActivity extends AppCompatActivity {

    private ApplicationViewModel mApplicationViewModel;
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model);
        mTextView = findViewById(R.id.textView);
        // initialization viewModel object 
        mApplicationViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(ApplicationViewModel.class);     
         // assignment 
        mTextView.setText(String.valueOf(mApplicationViewModel.num));
    }

    public void ViewModelClick(View view) {
         // Add one to each click 
        mTextView.setText(String.valueOf(mApplicationViewModel.num++));
    }
}

ViewModel Is independent of activity Beyond the life cycle of , We initialized ViewModel, And directly modify ViewModel The value in , such activity Even if it is destroyed , It won't affect ViewModel Value .

                   // To use Content Recommended when AndroidViewModel, When not in context, you can use ViewModel
public class ApplicationViewModel extends AndroidViewModel {
    public int  num;
    public ApplicationViewModel(@NonNull Application application) {
        super(application);
    }
}

Result demonstration :

3.AndroidViewModel

Don't ViewModel In the middle of Context, Can cause memory leaks

If you want to use Context, Please use AndroidViewModel Medium Application

3、 ... and .LiveData

1.LiveData and ViewModel The relationship between

ViewModel The change of data in is the notification page

2.LiveData application

Let's introduce... Through a simple case LiveData Add ViewModel Usage of

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LiveData.LiveDataActivity">

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="RequestLiveData"

        android:text=" request "
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.492"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.237" />
</androidx.constraintlayout.widget.ConstraintLayout>

This is the case Xml file .

public class LiveDataActivity extends AppCompatActivity {

    private TextView mTextView2;
    private MyViewMode mMyViewMode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_data);
        mTextView2 = findViewById(R.id.textView2);
        mMyViewMode = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewMode.class);
        mTextView2.setText(String.valueOf(mMyViewMode.getCurrentSecond().getValue()));
        //LiveData Monitor   Whenever the monitored value changes , Will trigger a callback 
        mMyViewMode.getCurrentSecond().observe(this, pInteger -> mTextView2.setText(String.valueOf(pInteger)));
    }

    // Define timer 
    public void RequestLiveData(final View view) {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                // Not UI Threads   use  Post
                //UI Threads    use set                             Every second plus one 
                mMyViewMode.getCurrentSecond().postValue(mMyViewMode.getCurrentSecond().getValue()+1);
            }
        },1000,1000);
    }
}

stay activity In the initialization ViewModel, Define the timer to increase automatically 1, And by LiveData Every time ViewModel All changes in the will start callback , To update UI.

public class MyViewMode extends ViewModel {
        private MutableLiveData<Integer>  currentSecond;

        public MutableLiveData<Integer> getCurrentSecond(){
            if (currentSecond==null){
                currentSecond= new MutableLiveData<>();
                // assignment 
                currentSecond.setValue(0);
            }
            return currentSecond;
        }
}

stay ViewModel It's initialized in LiveData.

Result demonstration :

  The second case :

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".liveDataFragment.LiveDataPagerActivity">

    <fragment
        android:id="@+id/fragment2"
        android:name="com.example.jetpacktest.liveDataFragment.SecondFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <fragment
        android:id="@+id/fragment"
        android:name="com.example.jetpacktest.liveDataFragment.LiveDataFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/fragment2" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5" />

</androidx.constraintlayout.widget.ConstraintLayout>

Xml The top and bottom of the file fragment, A space line in the middle .

public class LiveDataPagerActivity extends AppCompatActivity {

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

activity Don't do anything? .

public class LiveDataViewModel extends ViewModel {
    // TODO: Implement the ViewModel
    private MutableLiveData<Integer> progress;

    public MutableLiveData<Integer> getProgress(){
        if (progress==null){
            progress=new MutableLiveData<>();
            progress.setValue(0);
        }
        return progress;
    }
}

Same as before

public class LiveDataFragment extends Fragment {
    
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View inflate = inflater.inflate(R.layout.live_data_fragment, container, false);
        final SeekBar seekBar = inflate.findViewById(R.id.seekBar);
        // initialization ViewModel
        final LiveDataViewModel liveDataViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(LiveDataViewModel.class);
        //liveData Listening in 
        liveDataViewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer pInteger) {
                // Configure the progress of the progress bar 
                seekBar.setProgress(pInteger);
            }
        });
        // Progress bar listening 
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                // Configure the progress of the slide 
                liveDataViewModel.getProgress().setValue(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
        return inflate;
    }
}
public class SecondFragment extends Fragment {


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View inflate = inflater.inflate(R.layout.fragment_second, container, false);

        final SeekBar seekBar = inflate.findViewById(R.id.seekBar);
        // initialization ViewModel
        final LiveDataViewModel liveDataViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(LiveDataViewModel.class);
        //liveData Listening in 
        liveDataViewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer pInteger) {
                // Configure the progress of the progress bar 
                seekBar.setProgress(pInteger);
            }
        });
        // Progress bar listening 
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                // Configure the progress of the slide 
                liveDataViewModel.getProgress().setValue(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
        return inflate;
    }
}

The above two fragment What you do is the same , The progress bar assigns progress to LiveData, And through the liveData Listening in , A callback is triggered when the value changes , Reached two fragment The effect of progress bar synchronization

Effect demonstration :

3.LiveData The advantages of

1. Make sure the interface matches the data state
2. No memory leaks
3. Not because of Activity Stop and cause a breakdown
4. No longer need to manually handle the lifecycle
5. Data is always up to date
6. Appropriate configuration changes
7. Shared resources
 

原网站

版权声明
本文为[m0_ forty-seven million nine hundred and fourteen thousand one ]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202270555277180.html