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 destroyed
  • setStateAsyncWithCache: similar tosetStateAsyncSupport 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 callrenderMethod to get a View and render it
  • Update: whenStatefulWidgetPassively 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 -> initDataCopy 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.