"Jetpack Application Guide" Notes

ViewModel

hold UI data

Activity/Fragment only display data and handle user interaction.

ViewModel holds UI data.

life cycle

The reconstruction of Activity will not affect the life cycle of ViewModel.

There is only one life cycle function of ViewModel onCleared(), and this function will be called only when the page represented by the Activity is destroyed.
insert image description here

ViewModel Quote Context

The life cycle of ViewModel is longer than that of Activity, so you should not hold a reference to Activity in ViewModel, otherwise it will cause a memory leak.

ViewModel does not recommend introducing Activity, but what if Context is needed in ViewModel? One of the following methods can be used:
1. Use Context.getApplicationContext();
2. Use AndroidViewModel, which is a subclass of ViewModel, which receives Application as Context internally;

Instantiation of ViewModel

XXXViewModel mXXXViewModel = new ViewModelProvider(this).get(XXXViewModel.class);

The difference between ViewModel and onSaveInstanceState()

  1. Data difference
    onSaveInstanceState() can only save a small amount of UI data that can be serialized, and cannot save big data such as Bitmap.
    ViewModel has no such restrictions.

  2. Data persistence
    onSaveInstanceState() can save a small amount of UI data in the following two situations:
    ① The process of the application is terminated due to memory limitations when it is in the background.
    ② Configuration changes.

ViewModels can only persist data in the case of destruction on configuration changes, not across terminated processes.

LiveData

use

LiveData can be understood as a data container. It wraps the data so that the data becomes an observer, and when the data changes, the observer can be notified.

ViewModel holds UI data, and Activity/Fragment is responsible for displaying the data. If the UI data changes, LiveData notifies Activity/Fragment to refresh the data. So LiveData is usually used in ViewModel.

basic use

LiveData is an abstract class and cannot be used directly. Usually we use its subclass MutableLiveData.

Observe the data wrapped by LiveData through the LiveData.observe() method. Conversely, when we want to modify the data wrapped by LiveData, we can do it through the LiveData.postValue()/LiveData.setValue() method. postValue() is called in non-UI thread, and setValue() is called in UI thread.

notification update

LiveData can perceive the life cycle of the page, and only when the page is in the active state (Lifecycle.State.STARTED or Lifecycle.State.RESUMED) will the LiveData notification be received. If the page is destroyed (Lifecycle.State.DESTROYED), then LiveData will Automatically clears associations with pages, thus avoiding memory leaks.

Normally, LiveData only sends updates when the data changes, and only to active observers. An exception to this behavior is that observers also receive updates when they change from inactive to active. Also, if the observer changes from inactive to active a second time, it will only receive updates if the value has changed since the last time it became active.

LiveData.observeForever() is used in the same way as observe, the difference is that when the data changes, no matter what state the page is in, it can receive notifications. Therefore, you must call removeObserver() after use to remove the observer and avoid memory leaks.

ViewModel+LiveData implements communication between Fragments

public class OneFragment extends Fragment {
    
    

	public void onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    
    
		/*
		关键在于ViewModelProvider的构造函数传入的是getActivity()而不是Fragment.this,
		这样才能保证每个Fragment得到的是同一个ViewModel,从而共享LiveData
		*/
		XXXViewModel mXXXViewModel = new ViewModelProvider(getActivity()).get(XXXViewModel.class);
	}
}

//TwoFragment与OneFragment类似

summary

The essence of LiveData is observer mode + perception life cycle.

DataBinding

easy to use

  1. Start DataBinding
android {
    
    
	……
	dataBinding {
    
    
		enabled = true;
	}
}
  1. Tag Layout File
    Add tags outside the root of the layout file <layout>. The purpose of this is to tell the DataBinding library to generate the Binding class corresponding to the layout file.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
	/*
	以下是实际布局
	……
	*/
</layout>
  1. Define layout variables
<layout xmlns:android="http://schemas.android.com/apk/res/android">
	<data>
		<variable
			name = "变量名"
			type = "类全名"/>
		/*
		或者使用<import>标签引入类
		<import type = "类全名"/>
		<variable
			name = "变量名"
			type = "类名称"/>
		*/
		
	</data>
	/*
	以下是实际布局
	……
	*/
</layout>
  1. Get the Binding class
//该方法给Activity设置布局文件的同时,返回Binding类。
XXXBinding mXXXBinding = DataBindingUtil.setContentView(this, R.layout.xxx);
  1. Assignment of layout variables
    Binding provides two methods of assigning values ​​to layout variables:
    ①General method: XXXBinding.setVariable(BR.变量名, 变量);
    ②Assignment method for specific layout variables:XXXBinding.set变量名(变量)

  2. Layout Expression
    The format of a layout expression: @{}.
    For example: @{布局变量.字段},@{方法调用的表达式}

<data>
	<import type = "xxx.xxx.TestUtil"/>
	<variable
		name = "book"
		type = "xxx.xxx.Book"/>
</data>

<!-- 在布局中引用静态类-->
<TextView
	android:text="@{TestUtil.getText()}"/>

<TextView
	android:text="@{book.name}"/>

Layout expressions are far more than these usages. For details, see: Data Binding Detailed Explanation (2) - Layout and Binding Expressions

  1. Activity final appearance
public class TestActivity extends Activity {
    
    
	protected void onCreate(Bundle savedInstanceState) {
    
    
		super.onCreate(savedInstanceState);
		XXXBinding mXXXBinding = DataBindingUtil.setContentView(this, R.layout.xxx);
		Book book = new Book();
		book.name = "Jetpack应用指南";
		mXXXBinding.setBook(book);
	}
}

event binding

DataBinding supports the use of layout expressions to handle View event responses.
Specific method: assign a layout expression to the event attribute of View in the layout file.
This is equivalent to using the layout expression to implement the callback of the corresponding listener. This practice is known as event binding .

Correspondence between event attributes and listeners

The event attribute name depends on the listener method name. For example, View.OnClickListener has onClick()the method, and View.OnLongClickListener has onLongClick()the method, so the properties of the event are android:onClick, android:onLongClick.
For the click event, in order to avoid the conflict of multiple click events, Google also defines some special event processing, such as:

Class How to set the listener properties on binding
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

There are two types of layout expressions for event binding: reference method and binding listener .

reference method

Use layout variables to respond to events. This method requires that the parameters and return value must match those of the listener . If the parameters or the return value do not match, an error will be reported at compile time.

public class EventHandler {
    
    

	public void onClickHandle(View view) {
    
    
		System.out.println("按钮被点击了");
	}
}


<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="eventHandler"
            type="xxx.xxx.EventHandler" />
    </data>
    ...
    <Button
           ...
            android:onClick="@{eventHandler::onClickHandle}"
           ... />
    ...
</layout>

When using Reference Method, the generated listener encapsulates the method call for the layout variable. The listener object is created and assigned when the layout variable is set. If the layout variable is null, the listener will not be created.

/*
“引用方法”的监听器创建原理如下伪代码所示
伪代码是在运行时运行的。
*/
xxxBinding.setEventHandler(EventHandler eventHandler) {
    
    
	if(eventHandler != null) {
    
    
		button.setOnClickListener(new OnClickListener() {
    
    
				public void onClick(View view) {
    
    
					eventHandler.onClickHandle(view);
				}
		});
	}
}

bind listener

Use lambdas in layout files to respond to events. The method only requires that the return value matches the listener's expected return value .

public class Tester {
    
    
	
	public boolean testLongClick() {
    
    
		return false;
	}
}

<Button
   ...
   android:onClick="@{()->tester.testLongClick()}"
   ... />

Binding listeners allow custom parameters.

public class Tester {
    
    

	public boolean testLongClick(View v, String info) {
    
    
		Toast.makeText(v.getContext(), info, Toast.LENGTH_LONG).show();
	}
}

<Button
   ...
   android:onClick="@{(view)->tester.testLongClick(view, '你好')}"
   ... />

"Bind Listener" will automatically create the necessary listener and register events for it when compiling (the listener is created at the beginning, and it will not be judged whether the layout variable is empty until it is triggered, and no execution will be performed if it is empty. operate)

/*
“绑定监听器”的监听器创建原理如下伪代码所示
伪代码是在编译时运行的。
*/
button.setOnClickListener(new OnClickListener() {
    
    
	public void onClick(View view) {
    
    
		if(tester != null) {
    
    
			tester.testLongClick(view, "你好");
		}});

ternary expression

If you need to use an expression with a predicate (for example, a ternary expression), you can use the return value type that matches the listener as the expression, such as void for the onCLick attribute, and Boolean for the onLongClick attribute.

    android:onClick="@{(view)->view.isEnabled()?activity.showSign(view, user):void}"
    android:onLongClick="@{(v)->v.isEnabled()?activity.showSign(user):false}"

Binding of secondary pages

We call the page directly referenced by Activity/Fragment the first-level page, and the page referenced by the label in the first-level page is called the second-level page.

How to pass layout variables from the first level page to the second level page?

After a layout variable is defined in the first-level layout , the variable can not only be received and used in the first-level layout, but also become an attribute of bookthe namespace . The purpose of this attribute is to pass layout variables to the secondary layout.xmlns:app
book

//一级页面
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
	<data>
		<variable
			name = "book"
			type = "xxx.xxx.Book"/>
	</data>

	<LinearLayout
		android:orientation="vertical"
		android:layout_width="match_parent"
		android:layout_height="match_parent">

		<include
			layout="@layout/layout_content"
			app:book="@{book}">
	</LinearLayout>
</layout>

//二级页面
/**
在二级页面layout_content中,需要定义一个与一级页面相同的布局变量,
用来接收传递过来的数据。收到book变量后即可使用该变量了。
*/
<layout xmlns:android="http://schemas.android.com/apk/res/android">
	<data>
		<variable
			name = "book"
			type = "xxx.xxx.Book"/>
	</data>

	<TextView
		……
		android:text="@{book.name}"/>
</layout>

BindingAdapter

Binding Adapter (BindingAdapter) is to convert the attribute expression in the layout into the corresponding method call to set the value .
The so-called setting value is divided into two types:
①Setting the attribute value, such as calling the setText() method
②Setting the event listener, such as calling the setOnClickListener() method.
It also allows you to customize the calling method of setting the value and provide your own binding logic.

BindingAdapter from the DataBinding library

Many XXXBindingAdapter classes are provided in the DataBinding library, which enable android native controls to support attribute expressions.

//DataBinding库下ViewBindingAdapter的部分源码
public class ViewBindingAdapter {
    
    

	@BindingAdapter({
    
    "android:padding"})
	public static void setPadding(View view, float paddingFloat) {
    
    
		final int padding = pixelsToDimensionPixelSize(paddingFloat);
		view.setPadding(padding, padding, padding, padding);
	}
}

Guess you like

Origin blog.csdn.net/jiejingguo/article/details/118567220