advantage
stable
- Reduce memory leaks: It is easy for a novice to write code that causes memory leaks where threads are switched, but the probability of errors is greatly reduced if threads are switched over to the framework.
- Reduce crashes: In my development experience, most crashes are caused by null Pointers. In general, the thread callback is most prone to problems. When the UI is destroyed, the child thread still operates the UI, which is easy to cause crash. The framework has a complete life cycle. After UI destruction, the framework forcibly stops the child threads, which greatly reduces the probability of crash.
lightweight
- Minimum dependencies: Lifecycle and support lib only.
- Implementation lean: only a few dozen classes
Tip: These two dependency libraries are almost always included in any new Android Studio project, meaning almost zero dependencies.
Low access cost
- Low intrusion: No existing code needs to be modified
- Seamless embedding: Can be used as a View indirectly, regardless of MVP or MVC, adding a View to it doesn’t affect your structure at all.
simple
- Native friendly: You hardly need to learn the framework API to get started.
- People familiar with React and Flutter are very easy to learn
For details, go down to the basic tutorial.
The decoupling
The power of MVVM is that the UI is separated from the logic, so you don’t care about the UI when you process the logic, and you don’t care where you get the data when you write the UI.
To update, you modify the data directly, which automatically triggers re-rendering. There is no need to worry about performance because by default, the original View is not discarded, only an update is triggered.
public class StatefulUserWidget extends StatefulWidget<View, UserWidget> { private UserBean user = UserDataSource.getInstance().getUser(); public StatefulUserWidget(Context context, Lifecycle lifecycle) { super(context, lifecycle); } @Override protected State<UserWidget> createState(Context context) { return StateUtils.create(new UserWidget(context, lifecycle, user)); } @Override public void initWidget(UserWidget widget) { widget.setOnClickListener(v -> setState(() -> { user = UserDataSource.getInstance().getUser(); })); update(); } @Override public void update() { super.update(); widget.setUser(user); }}
Copy the code
A click event is set on the widget in the initWidget method, which refetches data and automatically triggers an UPDATE to the UI. The setState method is called to trigger updates. Similar to react and flutter, updates need to be placed in this method otherwise updates will not be triggered.
High reuse
The design idea of the framework is similar to flutter’s “Everything’s a Widget”, which treats Everything as a control. Controls are independent of each other. Container controls can combine one or more controls, and each control has an independent life cycle. As a result, the reusability of controls is greatly improved.
Convenient life cycle
Thanks to Google’s new introduction of lifecycle, every widget can have a full lifecycle, and even data can have a lifecycle.
Asynchronous support (sending requests synchronously)
Most troublesome for client programming are the various asynchronous calls and state synchronization. Multithreaded programming is difficult, and the slightest mistake can leak memory or crash. The framework internally handles asynchronous requests and automatically cancellations child threads when onDestroy, preventing memory leaks or null-pointer problems caused by async.
The library provides the following methods to support data modification, and developers can choose their own methods.
setState
: Synchronously performs data modification operations (applicable to non-time-consuming data modification operations without thread switching performance consumption)setStateAsync
: Data modification operations performed asynchronously, and the asynchronous thread is automatically stopped when the UI is destroyedsetStateAsyncWithCache
: similar tosetStateAsync
Support for caching.
With it, you can send web requests synchronously. It becomes extremely easy to merge data from multiple requests (for example, request A first, then request B, and merge the result to C).
Cache support
Life caching is hard. A thousand applications with a thousand caches. I’ve seen a lot of very crude caching schemes on the web, mostly done directly at the network layer with interceptors. Because you don’t have to break into the business code. However, the disadvantages of doing so are also great and not flexible enough. While networking libraries like OKHTTP provide support for caching, such as the ability to set up to use only caching or to use only networking, this is still not flexible enough.
If you want precise control over caching, you have to add caching logic to your code for every request. You’ll find that this will cause the same cache logic to be written countless times, which is a nightmare.
However, since the library has asynchronous support, handling caching is also much easier. How you want to use caching is up to you, we provide a policy interface, you just need to implement it.
Page State Management
No data pages, error pages, loading pages, pull-down refresh, and loading more are common in applications.
The common method is BaseActivity BaseFragment, but I don’t want to see them. Once I thought base was a good logical abstraction and encapsulation, but later I found that since the creation of base, migration and reuse almost become zero. Base makes them tightly coupled. If you don’t know what I’m talking about, let me give you an example:
I want to extract an Activity from project A that has similar page and logic for use in project B. The most common way to do this is to copy xxxActivity.java into project B, and you get the idea.
But the library provides a high degree of encapsulation for these page states, so you don’t have to rely on Base anymore. You can have all of these states for an activity or even a button.
For specific usage, please refer to advanced tutorial 1, 3 and 4
The request filtering
I don’t know if it bothers you, but the product tells you that the user may be pushing buttons too hard, asking you to make judgments and reduce unnecessary requests. Sounds very simple demand, to prevent repeated click on the line, but can reach duck frowning, found that things are not simple. One button is only a few lines of code, but dozens and hundreds of buttons? You said you could pull out a BaseButton? What if it’s a Text or fab control? It is true that Base can solve a lot of repetitive code, but correspondingly you have to replace all corresponding controls with Base, the workload is also very large.
This library provides you with a considerate request filter, the default is to filter repeated requests, although not in the UI filter, but the same task request is not repeated execution, this can be assured. If you have other filtering requirements, you can also implement a custom filter.
Retry support
Retry of failed requests is also a common requirement, but it is not easy to implement. There are basically two approaches:
- If you do this at the code level, you need to re-initiate the request in the failed callback, and keep track of the number of times, which is cumbersome.
- If you do it at the network layer, you need to wrap the network layer once and provide a method to set the number of retries. However, this approach has great drawbacks and does not relate well to the business. Retry network requests when they fail because the network layer does not know when to retry? Or do you return unsuccessful flags in the content and try again?
The library also provides retry support, because with asynchronous support, retry is a loop for the framework, but the loop is written for you, you just need to tell the framework the number of retries and when to retry.
Ability to set dynamic properties
Dynamic peels or style changes are also a common requirement, but in order to implement such requirements, developers often need to write the code ahead of time to modify the UI according to the configuration.
The library also provides support for modifying wiget properties using a JSON file. So it’s a matter of getting a new skin or style.
Single-page application (Testing)
The front-end guys should know what this is, that all rendering is displayed on one page, and page hops are controlled by the front-end route. On the client side, all the UI is displayed in one activity.
What’s the point? The biggest problem with Android plug-ins is that the four components need to be registered in the manifest in advance. Although there are some open source projects that solve this problem through the underlying hook method, it is not clear whether the future version of Android will limit this. Moreover, current plugins require the merging of resources, which reduces the success rate.
Dynamic delivery of bytecode execution is also possible for single-page applications. And the success rate is theoretically close to 100%. I plan to try it sometime.
I mean like the front-end has the ability to update at any time, I don’t know if it will be blocked, escape…
Quick start
Import library
[optional] add java8 support
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
...
}
Copy the code
Adding a Maven repository
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Copy the code
Add the dependent
Def support_version = '28.0.0 def lifecycle_version =' 1.1.1 'implementation' com. Making. Ittianyu: relight: 0.1.0 from ' implementation "com.android.support:appcompat-v7:$support_version" implementation "com.android.support:design:$support_version" // Support library depends on this lightweight import implementation "android.arch.lifecycle:runtime:$lifecycle_version"
Copy the code
If java8 is enabled
// alternately - if using Java8, use the following instead of compiler
implementation "android.arch.lifecycle:common-java8:$lifecycle_version"
Copy the code
confusion
If XML support is used, obfuscation must be added, but not if it is not used.
-keep class * extends com.ittianyu.relight.widget.Widget {*; }
Copy the code
Introductory tutorial
1. AndroidWidget
Purpose: To learn simple usage of Android Widgets.
2. StatefulWidget
Objective: To learn the simple usage of StatefulWidget.
3. TextWidget
Objective: To learn the simple use of TextWidget and to be familiar with non-XML interface writing.
4. LinearWidget
Purpose: To learn the simple use of linearWidgets.
5. FrameWidget
Purpose: To learn the simple use of FrameWidget
6. RelativeWidget
Purpose: Learn simple usage of RelativeWidget
7. setStateAsync
Objective: To learn the use of setStateAsync.
Advanced tutorial
1. LceeWidget
Purpose: To learn the use of LceeWidget.
2. UpdateStrategy
Objective: To learn the update strategy of AsyncState.
3.LceermWidget
Objective: To learn the use of LceermWidget.
4.RmWidget
Purpose: To learn the use of RMWidgets.
5.Retryable
Purpose: To learn the request retry API.
6.Cache
Objective: To learn the caching mechanism.
7.StartActivity
Purpose: To learn the use of startActivity in widgets.
8.xml
Objective: To learn about Widget XML visualization support
Widgets
As mentioned above, the design idea of this framework is “Everything’s a Widget”. Widgets are atomic controls with life cycle, which are roughly divided into three categories: native, stateful, and stateless.
native
The underlying widget. Directly involved with native view rendering.
- AndroidWidget: Base class for all Native controls, including lifecycle and native build methods
- BaseAndroidWidget: Inherits AndroidWidget and encapsulates common native properties and Settings
- ViewGroupWidget: Inherits BaseAndroidWidget, similar to ViewGroup, to contain other AndroidWidgets
- FrameWidget: Encapsulates FrameLayout
- LinearWidget: Encapsulates the LinearLayout
- RelativeWidget: Encapsulates a RelativeLayout
- BaseTextWidget: Encapsulates the common properties and setup methods of all views inherited from TextView
- TextWidget: Encapsulates TextView
- ImageWidget: Encapsulates an ImageView
- ButtonWidget: Encapsulates a Button
- EditWidget: Encapsulates the EditText
- RecyclerWidget: Packaging RecyclerView
- SwipeRefreshWidget: Encapsulates SwipeRefreshLayout
stateful
Control with state.
- StatefulWidget: A base class for all stateful controls, with a lifecycle
- LceeWidget: a widget that encapsulates Loading, Content, Empty, and Error
- RmWidget: a control that encapsulates Refresh and LoadMore common states
- LceermWidget: a widget that encapsulates Loading, Content, Empty, Error, Refresh, and LoadMore
stateless
Stateless control.
- StatelessWidget: Base class for all Stateless controls, with a life cycle
Asynchronous thread strategy
The framework uses thread pools to perform asynchronous operations. Considering different applications have different requirements, developers can set their own thread pool policies (it is recommended to set them during initialization).
ThreadPool.set(executorService);
Copy the code
. The default is used Executors newCachedThreadPool (), which is a period of time without the asynchronous task, automatic the release of internal threads, meeting the needs of most applications.
Core method call order
Widget
- Render: external through call
render
Method to get a View and render it - Update: when
StatefulWidget
Passively triggered when the status changes
StatelessWidget
You need to implement a Widget
build() method to complete the build of the Widget
render(first call) -> build -> widget.render -> initWidget
Copy the code
state
setState -> willUpdate -> update -> didUpdate
onDestroy -> dispose
Copy the code
StatefulWidget
You need to implement a State
createState(Context Context) method to build a State object
render(first call) -> createState -> state.init -> state.build -> widget.render -> initWidget
state.setState -> state.update -> widget.update
Copy the code
AndroidWidget
Constructor -> createView Render (first call) -> initView -> initEvent -> initData
Copy the code
Lifecycle
Widgets with a life cycle
- Call to order
render(first call) -> bind lifecycle
Copy the code
It is important to note that Bind Lifecycle is called after the control has been initialized
- The life cycle
Lifecycle is bound to give widgets a full Lifecycle
onStart
onResume
onPause
onStop
onDestroy
Copy the code
BaseAndroidWidget
Native widget with common View property Settings
initView -> initProps
onStart -> updateProps(when has LayoutParams)
Copy the code
InitView is triggered after Render
ViewGroupWidget
render(first call) -> children.render -> super.render(render self) -> add children to ViewGroup
addChildren -> updateChildrenProps -> updateProps
Copy the code
To Do List
The framework
- Basic framework
- Asynchronous support
- Retry support
- Filter support
- Cache support
- Improve the basic attributes and apis of BaseAndroid Widgets
- StartActivity support
- XML support
- Unit test support
- CoroutineState (kotlin coroutines)
- Application State Management (similar to Redux Mobx)
- Android Studio template
Based on the control
- FrameWidget
- LinearWidget
- RelativeWidget
- RecyclerWidget
- TextWidget
- ImageWidget
- SwipeRefreshWidget
- ButtonWidget
- ToolBarWidget
- EditWidget
- FloatingActionButtonWidget
- DrawerWidget
Advanced controls
- LceeWidget
- LceermWidget
- RmWidget
- List
- The Route and the Navigator
The theme of control
- material
The document
- advantage
- Quick start
- Introductory tutorial
- Advanced tutorial
- Widgets
- Asynchronous thread strategy
- The internal structure
- directory
- The English version
- To Do List
Thank you
Thank Guizhou Chuanqing for your support and help all the time
copyright
The Apache License 2.0
Name origin
- rekindle
- Moral: Find hope again
- Message: Life is hard. What I can do is, in your darkest moments, persuade you not to give up hope.
- Conclusion: if you can not understand the message, that you have a very happy, may you never understand. But maybe in the near future, if you do, I hope you’ll remember it.