“What will be hot and what to learn in 2022? This article is participating in the” Talk about 2022 Technology Trends “essay campaign.

Android has been around for a long time, and the way it’s been developed is a lot different from the early days, especially the MAD development technology that Google has been so keen to promote in recent years. Many developers are already using the technology, consciously or unconsciously, and want to fully summarize MAD’s vision, composition, strengths, and some learning tips as we explore technology trends in 2022.

MAD, short for Modern Android Development, is a new Development technology proposed by Google for the Android platform. It aims to guide us to make use of the officially launched technologies for efficient App development. Sometimes Google translates it into modern Android development, sometimes it translates it into new Android development, and I think the former is a radical but appropriate translation.

Following are the components of MAD to help you quickly understand the technical concept of MAD. If you’re interested in one of the languages, toolkits, or frameworks, be sure to try it out for future development.

Content preview

  1. 【Modern Android Development】 The origin and composition of the new Android Development technology
  2. Android Studio demonstrates the key features of the official Android IDE
  3. Android App Bundle: A brief overview of Google’s new App formats
  4. Kotlin: What are the advantages of Android’s first development language
  5. Jetpack is a collection of major frameworks that Android continues to update and demonstrates the issues and benefits that they address one by one
  6. [Jetpack Compose] takes you through a major change in the way UI development is done on Android

Modern Android Development

Officials are constantly improving the App development experience: from IDE to language to framework, these new technologies are becoming more sophisticated and more trivial. Come up with a whole new concept to integrate these loose technologies and make it easy to introduce and promote, and easy for developers to understand.

MAD is a new concept proposed to provide excellent development experience in language, tools, frameworks and other levels. Its vision and advantages:

  • Efforts to build: The convergence of Google in the Android industry more than ten years of foreword development experience
  • Easy to get started: provides plenty of demos and detailed documentation, suitable for projects of all stages and sizes
  • Start Fast: Offers Jetpack, a development framework that significantly reduces boilerplate code, and Jetpack Compose, a UI toolkit
  • Free choice: A wide variety of frameworks can be used with traditional languages, native development, and open source frameworks
  • Coherence: Consistent development experience achieved by compatible development frameworks for different devices

What it covers:

  • Android Studio: The official IDE for continuous improvement
  • Android App Bundle: Advanced application packaging and distribution
  • Kotlin: The first programming language
  • Jetpack: A development suite that brings together a large number of development frameworks, independent of AOSP
  • Jetpack Compose: UI toolkit for major Changes to the Android platform

At the same time, the official MAD technology provides certification tests and skills scoring plug-in, you can experience after a period of practice:

  • MAD Certification
  • Android StudioMAD SkillsScoring plug-in

2. The Android Studio

The early days of Android Studio were criticized for being memory hungry, buggy, and hard to use, and developers were reluctant to part with Eclipse. With Google and developers working together, AS is becoming more stable and powerful, and many of the features of AS can be used to make development more efficient. Like Chrome, AS offers developers the flexibility to choose between three different versions for different needs.

version instructions
Stable Release Stable release, latest version isThe Arctic Fox | 2020.3.1
Release candidate For the upcoming release of the next version, you can experience new features and optimizations in advance. The latest version isBunblebee | 2021.1.1
Canary Experimental version, unstable but can try leading experimental features, the latest version isHow | 2021.2.1

Here are a few useful features of AS.

2.1 the Database Inspector

The Database Inspector provides real-time access to Database files generated by the Jetpack Room framework, as well as real-time editing and deployment to devices. It is much more efficient and intuitive than the SQLite command or additional export and DB tool required before.

2.2 Layout/Motion Editor

Layout Editor has many advantages, I do not know if you are familiar with the use of:

  • You can edit the UI intuitively: drag view controls and change constraint pointing at will
  • Flexibly switch the preview under different configurations (device, theme, language, screen orientation, etc.), eliminating the need for real machine debugging
  • collocationToolsThe tag is free to customize the UI, ensuring that it is only for debugging and does not affect the actual logic. For example, there are two controls in the layout, the default above isinvisible, want to confirm the effect of the above controls on the overall layout if visible. That does not need to change the controlvisibilityProperty to preview layout changes, add Tools:visibility=true

Motion Editor is a visual design Editor that supports motionLayouts-type layouts, making it easier to create and preview and debug animations.

The Layout Inspector allows you to View the detailed Layout of a process and a screen, showing all properties of the View tree. Great for situations where it’s not convenient to debug code or parse other apps. Now supports direct checking of Compose’s UI layout.

2.3 Realtime Profilers

AS’s Realtime Profilers tool helps us to monitor and detect problems in four areas, including sometimes seeing internal instance and variable details without other App code.

  • CPU: Performance profiler checks CPU activity. Switching to Frames also allows interface lag tracking
  • Memory: Identify Memory leaks and jitter that can cause an application to stall, freeze, or even crash. You can capture heap dumps, enforce garbage collection, and track Memory allocations to locate Memory problems
  • Battery: Monitors CPU usage, network wireless devices, and GPS sensors, and visually displays how much power each component is consuming. Where is your application using unnecessary power
  • Network: Displays real-time Network activity, including sent and received data and the current number of connections. This allows you to examine how and when your application transfers data and optimize your code appropriately

2.4 APK Analyzer

Apk can consume network traffic to download and storage space to install. The size of its volume will affect App installation and retention, so it is particularly necessary to analyze and optimize its volume.

The APK Analyzer of AS can help to do the following:

  • Quick analysis of Apk components, including DEX, Resources, and Manifest Size and ratio, helps us optimize the direction of code or Resources
  • Diff Apk to understand the differences between versions and pinpoint the source of the increase in volume
  • Analysis of other APKs, including looking at general resources and analyzing code logic, further dismantling and Bug locating

2.5 Other Features

The space only introduces a few features, there are many others, you need to explore:

  • Improved performance, embedded in the AS interfaceFast Emulator
  • Preview and edit the Compose layout in real time and support direct interactionCompose Preview
  • forJetpack WorkManagerBackground Task Inspector
  • .

By contrast, Google’s official introduction to the new Features of Android Studio is much newer and more comprehensive.

3. Android App Bundle

The Android App Bundle is a publishing format that contains all of your app’s compiled code and resources and hands over APK generation and signing to Google Play.

This new format will have a greater impact on 3rd Party apps for overseas markets than on apps for the domestic market. But as the build format of the future, understanding and adaptation will come sooner or later.

  • It optimizes the build of Apk for the target device, such as presetting only the corresponding architecturesoDocuments, images and language resources. The volume can be compressed to improve installation success and reduce the amount of unloading
  • Support easy creationInstant App, free installation, direct startup, trial experience
  • Meet the modular application development, improve the compilation speed and development efficiency of large projects

Google takes the.aab format very seriously and promotes it: as of last year, in August 2021, new apps must use it to be available on Google Play.

Fun’s article “AAB Righting! APK is out of the picture” has a full explanation of AAB technology.

4. The Kotlin

A modern programming language that makes developers happier.

Kotlin is a new JVMS language developed by JetBrains in 2011, and there are several reasons for Android developers to choose Kotlin for their apps:

  • Google IO2019 announced that Kotlin has become the official programming language of choice for the Android platform, which means it will be supported by the Google giants on Android to achieve a superior programming experience beyond Java
  • throughKMMKotlin Multiplatform Mobile implements cross-mobile support
  • Server-side, natural support for back-end development
  • throughKotlin/JSCompiled intoJavaScriptSupport front-end development
  • Almost as fast as Java, and even better than Java under incremental compilation

4.1 Kotlin’s excellent programming experience on Android

  • Kotlin code is concise and readable: a lot of boilerplate code is reduced to shorten the time it takes to write and read the code

  • Can call each other with Java, flexible collocation

  • Easy to learn, especially for Android developers who are familiar with Java

  • Code security, where the compiler checks for code errors

  • Proprietary coroutine mechanism greatly simplifies asynchronous programming

  • A number of Android specific KTX extensions are available

  • The only developer language that supports Android’s new UI programming approach, Compose

Many well-known apps have been developed with Kotlin, such as Evernote, Twiiter, Pocket, WeChat and so on.

Let’s take a few of Kotlin’s typical features and briefly introduce their advantages with some code.

4.2 Simplifying function declarations

The brevity of Kotlin’s syntax is evident in many areas, such as the simplification of function declarations.

A Java function that contains conditional statements is written as follows:

    String generateAnswerString(int count, int countThreshold) {
        if (count > countThreshold) {
            return "I have the answer.";
        } else {
            return "The answer eludes me."; }}Copy the code

Java support for ternary operators can simplify things further.

    String generateAnswerString(int count, int countThreshold) {
        return count > countThreshold ? "I have the answer." : "The answer eludes me.";
    }
Copy the code

Kotlin’s syntax does not support ternary operators, but can achieve the same simplification effect:

    fun generateAnswerString(count: Int, countThreshold: Int): String {
        return if (count > countThreshold) "I have the answer." else "The answer eludes me."
    }
Copy the code

It can also omit braces and return keywords and simplify further by using assignment. This way of writing is very close to the everyday expression of language, advanced ~

    fun generateAnswerString(count: Int, countThreshold: Int): String =
        if (count > countThreshold) "I have the answer." else "The answer eludes me."
Copy the code

Decomcompiling Class reveals that it is actually still written as a ternary operator, a syntax sugar that shows up in many places in Kotlin 😅.

   public final String generateAnswerString2(int count, int countThreshold) {
      return count > countThreshold ? "I have the answer." : "The answer eludes me.";
   }
Copy the code

4.3 Higher-order functions

Before introducing higher-order functions, let’s look at an example of passing a callback interface into a function.

In general, a callback interface needs to be defined, the calling function passes in an instance of the interface implementation, the function does some processing and then executes the callback. The implementation of the interface can be simplified with the help of Lambda expressions.

interface Mapper {
    int map(String input);
}

class Temp {
    void main(a) {
        stringMapper("Android", input -> input.length() + 2);
    }

    int stringMapper(String input, Mapper mapper) {
        // Do something.returnmapper.map(input); }}Copy the code

Kotlin does not need to define an interface and simply passes in an anonymous callback function as an argument. (If the anonymous function is the last parameter, the method body can be separated to increase readability)

Such functions that take functions as arguments or return values are conveniently called higher-order functions.

class Temp {
    fun main(a) {
        stringMapper("Android") {input -> input.length + 2}}fun stringMapper(input: String, mapper: (String) - >Int): Int {
        // Do something.return mapper(input)
    }
}
Copy the code

In fact, this is syntactic sugar, and the compiler will default interfaces to help implement higher-order functions.

4.4 a Null security

Null security is arguably a feature of the Kotlin language. Think of Java’s traditional Null handling as nothing more than a Null judgment or guard statement before a call, which is cumbersome and easy to miss.

void function(Bean bean) {
    // Null check
    if(bean ! =null) {
        bean.doSometh();
    }

    // or the guard statement
    if (bean == null) {
        return;
    }
    bean.doSometh();
}
Copy the code

Kotlin requires that variables be declared nullable when they are defined: with? That is, it may be empty, otherwise it is not empty. If passed as an argument to a function, it must be of the same type as whether it is null or not, otherwise it will not compile.

For example, the following functionA() call to functionB() will cause a compilation failure, but functionB() parameters were not added when declared? Is a non-empty type, then the parameter can be used directly in the function without the risk of NPE.

fun functionA(a) {
    var bean: Bean? = null
    functionB(bean)
}

fun functionB(bean: Bean) {
    bean.doSometh()
}
Copy the code

To compile, you can add? To the variable bean declaration Remove and assign a normal value.

But a lot of times the value of a variable is uncontrollable, and we can’t guarantee that it’s not null. Can you optionally add the parameter bean to compile? In the statement. This parameter cannot be used directly in the function, and requires explicit Null handling, such as:

  • Add? Before use? Is qualified to trigger the call if the parameter is not empty
  • Add before use!!!!!If the argument is empty or not, the call is triggered. This mandatory call tells the developer that there is a risk of an NPE
    fun functionB(bean: Bean?). {
        // bean.dosometh () // still calling directly will cause compilation failure

        // only if it is not nullbean? .doSometh()// Or force call, the developer is aware of the NPE riskbean!! .doSometh() }Copy the code

It will be easy to summarize:

  • The argument must be of a non-null type, and the instance passed must also be non-null
  • Arguments are nullable types, and internal calls must be explicitly Null handled

A decompression of Null processing shows that non-null types are essentially annotated by @notnull. Is a manual NULL judgment.

   public final int stringMapper(@NotNull String str, @NotNull Function1 mapper) {...return ((Number)mapper.invoke(str)).intValue();
   }

   private final void function(String bean) {
      if(bean ! =null) {
         boolean var3 = false; Double.parseDouble(bean); }}Copy the code

4.5 Coroutines Coroutines

Before introducing Coroutines, let’s review how Java or Android communicates between threads. What are the pain points?

For example, AsyncTask, Handler, HandlerThread, IntentService, RxJava, and LiveData. They all suffer from complex, error-prone, non-concise, redundant callbacks.

For example, a simple scenario requesting a network login: We need to create a new thread to request it, and then pass the result back to the main thread via Handler or RxJava, where the login request must explicitly be written in a non-UI thread.

void login(String username, String token) {
    String jsonBody = "{ username: \"$username\", token: \"$token\"}";
        Executors.newSingleThreadExecutor().execute(() -> {
        Result result;
        try {
            result = makeLoginRequest(jsonBody);
        } catch (IOException e) {
            result = new Result(e);
        }
        Result finalResult = result;
        new Handler(Looper.getMainLooper()).post(() -> updateUI(finalResult));
    });
}

Result makeLoginRequest(String jsonBody) throws IOException {
    URL url = new URL("https://example.com/login");
    HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
    httpURLConnection.setRequestMethod("POST"); . httpURLConnection.connect();int code = httpURLConnection.getResponseCode();
    if (code == 200) {
        // Handle input stream ...
        return new Result(bean);
    } else {
        return newResult(code); }}Copy the code

Kotlin’s Coroutines simplify concurrency by encoding asynchronous operations in a sequential manner without blocking the calling thread.

It has the following asynchronous programming advantages:

  • The suspended thread does not block the original thread
  • Support to cancel
  • Better support for Jetpack components via KTX extensions

Using coroutines for asynchronous processing becomes clear and concise, and because the specified time-consuming logic runs on the worker thread, the UI can be updated directly without administrative thread switching.

fun login(username: String, token: String) {
    val jsonBody = "{ username: \"\$username\", token: \"\$token\"}"
    GlobalScope.launch(Dispatchers.Main) {
        val result = try {
            makeLoginRequest(jsonBody)
        } catch(e: Exception) { Result(e) }
        updateUI(result)
    }
}

@Throws(IOException::class)
suspend fun makeLoginRequest(jsonBody: String): Result {
    val url = URL("https://example.com/login")
    var result: Result
    withContext(Dispatchers.IO) {
        val httpURLConnection = url.openConnection() as HttpURLConnection
        httpURLConnection.run {
            requestMethod = "POST". } httpURLConnection.connect()val code = httpURLConnection.responseCode
        result = if (code == 200) {
            Result(bean)
        } else {
            Result(code)
        }
    }
    return result
}
Copy the code

4.6 KTX

KTX is a Kotlin extension designed specifically for the Android library to provide concise and easy-to-use Kotlin code.

For example, using SharedPreferences to write data, we encode it like this:

void updatePref(SharedPreferences sharedPreferences, boolean value) {
    sharedPreferences
            .edit()
            .putBoolean("key", value)
            .apply();
}
Copy the code

The introduction of the KTX extension function makes it even cleaner.

fun updatePref(sharedPreferences: SharedPreferences, value: Boolean) {
    sharedPreferences.edit { putBoolean("key", value) }
Copy the code

This is just the tip of the KTX extension iceberg, but there are plenty of useful extensions and Kotlin’s advantages to learn and practice, such as:

  • Greatly concise syntaxlet.alsoEqual extension function
  • Save memory overheadinlinefunction
  • Flexible and richDSLfeatures
  • Asynchronously retrieving dataFlow

5. Jetpack

Jetpack literally means Rocket man, and the frame’s Logo can be seen as an Android with a rocket strapped to it. Google’s name is very clear, hoping that these frameworks will serve as enablers for Android development: enabling App development and experience growth at a rapid rate.

Jetpack is divided into architecture, UI, basic features, and specific features. The architecture section is a new design, covering a series of frameworks that Google has spent a lot of energy developing, which is the focus of this chapter.

The parts outside the architecture are actually components of the AOSP itself that have been optimized and integrated into the Jetpack architecture and are not mentioned here.

  • Architecture: A new design, the core of the framework
  • Outside: redesign of AOSP’s own components
    • UI
    • Basis function
    • A specific function

Jetpack has the following advantages when implementing a feature:

  • Provide best practices for the Android platform
  • Eliminate boilerplate code
  • Framework performance across different versions and vendors to achieve device consistency
  • Google official stable guidance, maintenance and continuous upgrades

For those who are interested in the background of Jetpack, I wrote an article titled “Jetpack’s Past and present life through the Transitions of Preference components.” Below, let’s take a few typical frameworks in Jetpack to understand and learn about its specific advantages.

5.1 the View Binding

What are the common ways to bind a View instance in a layout? What are the disadvantages?

Usual practice disadvantages
findViewById() NPE risk, heavy binding code, type conversion risk
@ButterKnife NPE risk, additional annotation code, not applicable to multi-module projects (APTTool parsing Library is limited)
KAEThe plug-in NPE risk, risk of manipulating other layouts, Kotlin language exclusive. deprecated

AS now uses the ViewBinding framework by default to bind our views.

Here’s a quick look at how it works:

<! --result_profile.xml-->
<LinearLayout . >
    <TextView android:id="@+id/name" />
</LinearLayout>
Copy the code

Once the ViewBinding framework is initialized, the View instance can be manipulated directly without additional binding processing.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        val binding = ResultProfileBinding.inflate(layoutInflater)

        setContentView(binding.root)
        binding.name.text = "Hello world"}}Copy the code

The principle is simple: the compiler generates a bound class file with the same name as the layout, and then caches Root views and other View instances with preset ids in the layout during initialization. In fact, both the annotations above, the plugin, and the framework are essentially View bindings implemented through findViewById, but wrapped.

The ViewBinding framework ameliorates the pitfalls of common practice, but it’s not perfect. Special cases still need to use the usual practices, such as operating outside the layout of the system View instance ContentView, ActionBar, etc.

advantage limited
Null security: Views with a preset ID are cached, otherwise they cannot be used with a ViewBinding, preventing NPE at compile time Views outside the binding layout still need findViewById
Type safety: The ViewBinding caches the View instance when it already handles the matching type Dependency configurations with different layouts still need to handle Null (for example, horizontal and vertical layouts)
Code simplicity: boilerplate code without binding
Layout exclusive: Uncluttered, layout file – specific class

5.2 Data Binding

In general, the following steps are required to reflect data to the UI:

  1. Creating a UI layout
  2. Bind the View instance in the layout
  3. The data is updated to the corresponding properties of the View one by one

The DataBinding framework eliminates steps 2 and 3 above. It requires us to declare the relationship between the data and the UI in the layout of step 1, such as the data source of the text content, the logical condition of whether it is visible, etc.

<layout .>
    <data>
        <import type="android.view.View"/>
        <variable
            name="viewModel" type="com.example.splash.ViewModel" />
    </data>

    <LinearLayout .>
        <TextView
            .
            android:text="@{viewModel.userName}"
            android:visibility="@{viewModel.age >= 18 ? View.VISIBLE : View.GONE}"/>
    </LinearLayout>
</layout>
Copy the code

The DataBinding layout above shows that the text is displayed only when the ViewModel’s age property is older than 18, and the text content comes from the ViewModel’s userName property.

val binding = ResultProfileBinding.inflate(layoutInflater)
binding.viewModel = viewModel
Copy the code

There is no need to bind and manually update the View in the Activity. After initializing the Activity like a ViewBinding, you specify the data source, and subsequent UI displays and refreshes will be triggered automatically. DataBinding also has a lot of tricks you can use.

5.3 Lifecycle

Listening to the Activity life cycle and dealing with it is the most important part of App development. There are usually two ideas as follows.

Often thought specific disadvantages
basis Overrides the Activity’s corresponding lifecycle function directly Cumbersome, highly coupled
The advanced usingApplication#registerLifecycleCallbackUnified management Callbacks are fixed, activities need to be differentiated, and logic intrudes into applications

The Lifecycle framework manages the Lifecycle efficiently.

Lifecycle framework needs to define a LifecycleObserver, annotate OnLifecycleEvent for lifecycle-related processing and specify the corresponding Lifecycle state. For example, onCreate initiates the connection, onStart initiates the connection, and onPause disconnects the connection.

class MyLifecycleObserver(
    private val lifecycle: Lifecycle
) : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun init(a) {
        enabled = checkStatus()
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun start(a) {
        if (enabled) {
            connect()
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun stop(a) {
        if (connected) {
            disconnect()
        }
    }
}
Copy the code

Then add an observation to the corresponding Activity:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle){... MyLifecycleObserver(lifecycle).also { lifecycle.addObserver(it) } } }Copy the code

A simple example of Lifecycle shows that management of the Lifecycle becomes clear and can be decoupled from the Activity code.

Moving on to the small example above: What if init() is an asynchronous time-consuming operation?

If init is asynchronous, init may not complete during the onStart state callback, and the start connection processing may be skipped. This is where Lifecycle’s State mechanism comes in handy.

It is simple to do the start link processing again when the callback is initialized asynchronously, but with the STARTED State condition. This ensures that the connection can be manually executed after the connection is skipped during onStart, and that the connection is only executed when the Activity is STARTED or later.

class MyLifecycleObserver(...). : LifecycleObserver {@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun init(a) {
        checkStatus { result ->
            if (result) {
                enable()
            }
        }
    }

    fun enable(a) {
        enabled = true
        // When initialization is complete, ensure that only connections are performed in STARTED and later states
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            if(! connected) { connect() } } } ... }Copy the code

5.4 the Live Data,

LiveData is a new observable data storage framework. As shown in the following example, it is very convenient to encapsulate and transmit data:

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        // Send the requested data
        value = price
    }

    // Only when the screen is active
    override fun onActive(a) {
        stockManager.requestPriceUpdates(listener)
    }

    // Remove request in inactive state
    override fun onInactive(a) {
        stockManager.removeUpdates(listener)
    }
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        // Register for observation
        StockLiveData("Tesla").run { observe(this@MainActivity, Observer { ... })}}}Copy the code

In addition to supporting asynchronous data transfer, LiveData has many advantages:

  • withLifecycleFrame depth binding
  • With life cycle awareness, data is not transmitted to inactive observers
  • The observer destroyed the autofree data to avoid memory leaks
  • supportRoomRetrofitThe framework
  • Supports merging multiple data sources for unified observationMediatorLiveData(Save the ugly processing of multiple LiveData and multiple observes)

However, it must be said that there are various problems in the positioning and use of LiveData, and the official attitude is changing all the time. After understanding LiveData, we usually use Flow to complete asynchronous data provision.

5.5 Room

What are the pain points of developing databases on Android?

  • You need to implement SQLite Helper instances and implement initialization and CRUD commands
  • Handle asynchronous operations yourself
  • Cursor instances need to be handled carefully
    • Field correspondence
    • The index is aligned
    • Shut down

Room, officially launched, provides an abstraction layer on SQLite that simplifies database development with annotations. So that you can access your database efficiently while taking full advantage of SQLite’s power.

Entity, Dao and Database need to be defined to complete the configuration of the Database, and other Database implementation can be entrusted to the framework.

@Entity
class Movie() : BaseObservable() {
    @PrimaryKey(autoGenerate = true)
    var id = 0

    @ColumnInfo(name = "movie_name", defaultValue = "Harry Potter")
    lateinit var name: String
    ...
}
Copy the code
@Dao
interface MovieDao {
    @Insert
    fun insert(vararg movies: Movie?).: LongArray?

    @Delete
    fun delete(movie: Movie?).: Int

    @Update
    fun update(vararg movies: Movie?).: Int

    @get:Query("SELECT * FROM movie")
    valallMovies: LiveData<List<Movie? >? >}Copy the code
@Database(entities = [Movie::class], version = 1)
abstract class MovieDataBase : RoomDatabase() {
    abstract fun movieDao(a): MovieDao

    companion object {
        @Volatile
        private var sInstance: MovieDataBase? = null
        private const val DATA_BASE_NAME = "jetpack_movie.db"

        @JvmStatic
        fun getInstance(context: Context): MovieDataBase? {
            if (sInstance == null) {
                synchronized(MovieDataBase::class.java) {
                    if (sInstance == null) {
                        sInstance = createInstance(context)
                    }
                }
            }
            return sInstance
        }

        private fun createInstance(context: Context): MovieDataBase {
            return Room.databaseBuilder(context.applicationContext,
                    MovieDataBase::class.java, DATA_BASE_NAME).build()
        }
    }
}
Copy the code

After the ViewModel initializes the DataBase interface, it uses the DAO interface it provides to perform operations, and then uses LiveData to transmit data to the UI.

class MovieViewModel(application: Application) : AndroidViewModel(application) {
    private valmediatorLiveData = MediatorLiveData<List<Movie? >? > ()private val db: MovieDataBase?
    init {
        db = MovieDataBase.getInstance(application)
        if(db ! =null) {
            mediatorLiveData.addSource(db.movieDao().allMovies) { movieList ->
                if(db.databaseCreated.value ! =null) {
                    mediatorLiveData.postValue(movieList)
                }
            }
        };
    }

    fun getMovieList(owner: LifecycleOwner? , observer:Observer<List<Movie? >? >? {
        if(owner ! =null&& observer ! =null)
            mediatorLiveData.observe(owner, observer)
    }
}
Copy the code

Room has many advantages worth choosing as the first choice for database development:

  • Simple and efficient, through simple annotations can complete the creation of the database and CRUD encapsulation
  • Return directly to the target POJO instance, avoiding the risk of handling the Cursor itself
  • Supports transaction processing, database migration, relational database and other complete functions
  • Support LiveData, Flow, and other observational queries
  • AS’s Database Inspector allows you to view, edit, and deploy Room’s Database in real time
  • Built-in asynchronous processing

5.6 the View Model

Along with AppCompat and Lifecycle, the ViewModel framework is one of the most important foundations of the Jetpack framework. It’s more than that, but we want to explore its role in data caching.

How to handle Activity redraw caused by vertical/horizontal switching? You can also configure ConfigurationChange to manually restore important states, or save data to BundleState and manually restore data at onCreate or other opportunities.

Thanks to the fact that the ViewModel instance is not destroyed after the Activity is redrawn, its cached data is not affected by external configuration changes, ensuring that the data can be automatically restored without processing.

Here we define a ViewModel that provides a method for retrieving data that returns a 30-year-old friend named Ellison. The Activity takes the VM instance, watches the data change, and reflects the data to the UI. When the screen orientation changes, the TextView of the name and age is automatically restored without additional processing.

class PersonContextModel(application: Application) : AndroidViewModel(application) {
    val personLiveData = MutableLiveData<Person>()
    val personInWork: Unit
        get() {
            val testPerson = Person(30."Ellison")
            personLiveData.postValue(testPerson)
        }
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?).{...val model = ViewModelProvider(this).get(
            PersonContextModel::class.java
        )

        model.personLiveData.observe(this, Observer { person: Person ->
            binding.name.setText(person.name)
            binding.age.setText(person.age.toString())
        })
        binding.get.setOnClickListener({ view -> model.personInWork })
    }
}
Copy the code

The ViewModel has many advantages:

  • Lifecycle based implementations store and manage data related to the interface in a life-cycle oriented manner
  • The VM instance is stored before the screen is destroyed and restored after reconstruction, allowing data to survive configuration changes such as screen rotation
  • Can be used to share data between fragments
  • Serves as the VM layer of the MVVM architecture as a medium for data and UI interaction
  • .

5.7 CameraX

To complete a camera preview function, using Camera2 requires the following procedures, which will be tedious:

With CameraX, a few dozen lines of code can do the preview.

    private void setupCamera(PreviewView previewView) {
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
                ProcessCameraProvider.getInstance(this);
        cameraProviderFuture.addListener(() -> {
            try {
                mCameraProvider = cameraProviderFuture.get();
                bindPreview(mCameraProvider, previewView);
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    private void bindPreview(@NonNull ProcessCameraProvider cameraProvider,
                             PreviewView previewView) {
        mPreview = new Preview.Builder().build();
        mCamera = cameraProvider.bindToLifecycle(this,
                CameraSelector.DEFAULT_BACK_CAMERA, mPreview);
        mPreview.setSurfaceProvider(previewView.getSurfaceProvider());
    }
Copy the code

Above is the architecture of CameraX, and you can see that the underlying structure is still Camera2, with highly encapsulated interfaces and vendor-defined libraries.

There are many advantages to using it as a new camera frame:

  • Code is simple and easy to use
  • Automatically binding Lifecycle automatically determines when the camera is turned on, when a shooting session is created, and when to stop and close
  • Unified camera development experience for multiple devices: domestic and foreign mainstream platforms are supported, and Domestic HuamiOV is supporting and contributing to this framework
  • Extensions perfect for portrait, HDR, Night and Beauty modes
Using CameraX, Monzo cut more than 9,000 lines of code and reduced visitor turnover in the registration process by a factor of five

It’s a banking services company with an app of the same name that offers digital financial services only on mobile devices. Their mission is to teach everyone how to make money. To complete the registration of new customers, the Monzo app takes a picture of an identification document (such as a passport, driver’s license or ID card) and a video of a selfie to prove that the document belongs to the applicant.

Earlier versions used the camera2 API. Random crashes and abnormal behavior on some devices resulted in 25% of potential customers not being able to proceed with the id shooting and selfie video steps.

5.8 Other Frameworks

Space is limited, but there are plenty of other great frameworks in the Jetpack collection to explore.

The framework role Competing goods
DataStore An asynchronous, consistent, lightweight data storage framework that supports key-value pairs and object data SharedPreferences, MMKV
StartUp A framework that simplifies component initialization for application startup and improves application startup performance
Navigation Simplified screen jump, support label navigation, drawer navigation and other complex design of the routing framework ARouter
ActivityResult A new framework for transferring data between activities and fragments onActivityResult/Intent
Paging3 Load on demand paging load framework that saves network traffic and memory consumption
WorkManager Schedule a deferrable asynchronous task framework that can run after exiting an application or restarting a device. JobService, Alarm, Broadcast
Hilt Android – specific DI framework to quickly establish dependencies and life cycles between Dagger2, Koil
AppCompat Provides Base classes for activities, Dialogs, and Views, compatible with a lot of Jetpack processing
ViewPager2 Implement a new framework for classic TAB navigation design ViewPager
.

While developing a feature, see if wheels are available, especially official ones.

5.9 Officially recommended Application Architecture

I have made some supplements in the official recommended architecture. General App recommends the following architecture components.

  • Try a UI architecture with single Activity and multiple fragments
  • throughNavigationnavigation
  • ViewModelComplete data and UI interaction
  • LiveDataObservational data
  • RoomDataStoreResponsible for local data
  • RetrofitResponsible for network data
  • As a whole byHiltdependent

Architecture is by no means a fixed pattern; it can be tailored to actual needs and best practices

6. Jetpack Compose

Jetpack Compose is a new UI toolkit that Google has been working on for five years to build Android’s native interface. Why reinvent the wheel when Android has been around for years and its UI is already mature? 🤔

The reason:

  • XML layouts are long and tedious: With complex layouts, the screen can’t be viewed vertically
  • Nesting in View programming has a performance impact: improper layout results in a doubling of measurement performance
  • Manually updating views is complex and error-prone
  • The declarative interface model is gaining in popularity: it simplifies the UI building and updating steps and only makes the necessary changes

Its development history:

  1. 17 years project
  2. Three years of internal investigations and experiments followed
  3. The dev version was released in early 20 and the alpha version was released in mid-20
  4. Beta version was released in early 21
  5. April 21 global Challenge promotion
  6. It was officially released in July 21

6.1 Compose Challenge

In the early part of last year, Google launched its Global Compose challenge for four weeks, offering more than 500 Lego bricks and dozens of Pixel phones as prizes, prompting tens of thousands of Android developers to try and submit their games.

  • The challenge of the first week was to make a pet adoption App. I spent a weekend making a LovePet and got the Lego building that had traveled across the ocean. After submitting screenshots of my work on Twitter, I received many thumbs up from foreigners, which was a great experience.
  • Later challenges include timer App, duplicate App design works, make a weather App with imagination, etc

These contests actually cover most of the technologies that Compose needs to use. Google’s strong promotion also shows its determination and attention, will become an important UI writing way on the Android platform in the future, get on the bus soon! 💪

6.2 Programming Ideas

Let’s use a small example showing the “Hello World” text to get a sense of the obvious difference in Compose’s programming thinking.

  • Traditional UI programming

    We know each other so well. A common action is to define an XML, place it in the Activity’s setContentView(), and then hand it over to the system to load.

<androidx.constraintlayout.widget.ConstraintLayout .>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"  . />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
class MainActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?).{... setContentView(R.layout.activity_main) } }Copy the code
  • Compose programming mode

The Compose UI toolkit relies on the Composable annotations to declare the functions that present the UI as Composable functions, and the Compose compiler is responsible for marking and displaying the components within the Composable functions.

The portion of the layout should be placed in the Compose function.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?).{... setContent { SimpleComposable() } }@Composable
    fun SimpleComposable(a) {
        Text("Hello World")}}Copy the code

6.3 Advanced Examples

Take a look at this simple dynamic effect and ask yourself: How much code would you need to implement it using traditional View programming?

Layout and logic are not easy to implement with less than 100 lines of code:

  • CardView + LinearLayout (ImageView + TextView)
  • Code: Listen, expand, and hide TextView, and consider shadows and fade animations)

What if you use Compose? You only need 10 lines.

  1. Composable function assembles rounded componentsCard+ Layout components verticallyColumn
  2. Column nested image componentsImageAnd animation componentsAnimatedVisibilityThe text component of the packageText
  3. The click event for Column updates expanded or hiddenstateTo trigger the reorganization of AnimatedVisibility and refresh whether Text is displayed or not
@Composable
fun JetpackCompose(a) {
    Card {
        var expanded by remember { mutableStateOf(false) } Column(Modifier.clickable { expanded = ! expanded }) { Image(painterResource(R.drawable.jetpack_compose)) AnimatedVisibility(expanded) { Text( text ="Jetpack Compose",
                    style = MaterialTheme.typographt.h2,
                )
            }
        }
    }
}
Copy the code

6.4 advantage

Compose has a number of advantages that should be explored for you.

  • Declarative UI: Just the description interface, the Compose system takes care of the rest

  • State-driven: The interface updates automatically with the status

  • Efficient Rendering: Fixed measurement, level nesting performance is still O(n)

  • The Preview view with AS provides real-time viewing and direct UI interaction

  • Compatible with traditional View tree programming, can be mixed use

  • Support Material Design Design language

  • With the Jetpack framework

  • Based on Kotlin, simple code, lots of Kotlin-specific apis

  • There are also cross-platform layouts: Desktop, Web

For example, you can use Compose to implement a new screen first, or modify an existing screen, and gradually improve the learning and practice of Compose. However, the Compose UI toolkit currently has limited support for components in some scenarios, such as WebView and CameraView, which still need to work with the Android native View mode.

6.5 the Sample

  • The official Sample

    Compose is an official, professional, and comprehensive App designed by Compose for eight mainstream scenarios. Github.com/android/com…

  • Tetris

Fun takes the custom Compose component and state management to the extreme, and with timers and various animation implementations, it’s worth learning more about Compose technology. Github.com/vitaviva/co…

  • ComposeBird himself recreated the popular Flappy Bird with Compose, inspired by Fun’s Tetris game, for anyone interested in learning how to implement it. Github.com/ellisonchan…

future

This time, we introduced many new technologies covered by MAD, and you can feel that Google is constantly innovating technology. From the tools to the language, the framework to the way of distribution are in an all-round improvement, the previous years of cultivation of technology waste waste, never relent.

There are two main roles in the life of a product: developer and consumer.

  • Improve the development efficiency of developers
  • Improve the product experience for consumers

However, the emergence of new things is accompanied by the decline of old things, how should developers treat the old technology, how to treat the constantly emerging, uncertain new technology? Google and Jetbrains have come up with three technologies, including Flutter, KMM, and Compose Multiplatform, that can’t be implemented by anyone.

I’ve summed up a few four-word phrases to share my feelings and attitudes with you:

  • Don’t ignore, understand, and keep up: Stay tuned in case you can’t read what technology is being used or even understand someone else’s code
  • Embrace change, experiment, and be prepared: Find an interesting point of entry to learn and experience new technology with an open mind
  • Do not rely on, understand the principle, learn to imitate: it is not enough to use, need to understand the implementation, make sure when the pit comes
  • Whether in-depth, depending on opinion, their own assessment: appropriate trade-offs, even wait-and-see, some technology is a flash in the pan

Information resources

Official data

The document home page, home page and branch page of the various learning points are fully explained from the level of background, ideas, APIS and usage methods. Can help you quickly understand and master the relevant technology.

technology address
MAD Developer. The android. Google. Cn/modern – lead…
AS developer.android.google.cn/studio
AAP Developer. The android. Google. Cn/platform/te…
Kotlin kotlinlang.org/,developer.android.google.cn/kotlin
Jetpack developer.android.google.cn/jetpack
Compose Developer. The android. Google. Cn/jetpack/com…

The official Sample

The sincere work of Google’s excellent developer relations engineers develops and continues to maintain detailed samples for languages, tools, and frameworks. Help everyone to learn these techniques, and appropriate reference.

Learning object address
Kotlin Github.com/MindorksOpe…
Compose Github.com/android/com…
Jetpack Github.com/android/sun…

My article

Compose series:

  • “In one go: The Perfect Re-creation of Flappy Bird with Compose!”

Jetpack series:

  • “An in-depth look at the Cornerstone of the Jetpack Framework -AppCompat”
  • “Why is Jetpack CameraX recommended?”
  • “A new option for Databases on Android, Jetpack Room”
  • “What are the improvements and limitations of Jetpack Hilt”
  • “Jetpack’s Past and Present Life through the Change of Preference Components”
  • “CameraX + Huawei ScanKit: The Ultimate Qr Code Scanning Solution”
  • “Jetpack’s new SplashScreen: creating a new App SplashScreen”
  • “Jetpack 叒 New Member DragAndDrop Framework: Greatly simplifying drag-and-drop gesture development”

My Sample

content Address/Name
Jetpack Demo Github.com/ellisonchan…
ComposeBird Github.com/ellisonchan…