It’s time to update your weapon

Time:2021-7-22

preface

Android JetpackI think you’re all familiar with it,Android KTXLiveDataRoomWait, a series of libraries are all fromJetpack。 that JetpackIn the end what is it? What are the things you haven’t used yet?GoogleWhat’s the reason for this? Today, we’re going to work together to improve our brainJetpack composition。( It’s a long space. I suggest you like it and pay attention to mark.)

introduce

Google I / O in 2018,JetpackThe official introduction is as follows:

Jetpack is a set of libraries, tools and guides to help developers write quality applications more easily. These components help you follow best practices, get rid of the task of writing boilerplate code, and simplify complex tasks so that you can focus on the code you need.

If we ponder over this introduction, we can explain the problem just now.

JetpackIn the end what is it?

  • It’s a set of libraries, tools and guides. To put it bluntly, it is a collection of libraries or tools, and these tools serve as a guide for our high-quality application, which is equivalent toOfficial recommendationPractice.

googleWhat’s the reason for this series?

  • Standardize developers to develop high-quality applications faster and better. all the time,Android DevelopmentAre full of a large number of non-standard operations and repetitive code, such as life cycle management, development process duplication, project architecture selection and so on. thereforeGoogleIn order to standardize the development behavior, this set of guidelines is launched to enable developers toBetter, faster, more standardWe are able to develop high-quality applications.

Of course, the practice in the past two years has proved itJetpackIt is convenient, fast and high quality. Therefore, as developers, we should apply these tools earlier and improve our qualityDevelopment efficiencyAnd regulate our own development behavior. Now let’s learn about it togetherJetpackA guide to all the tools in. GOGOGO!

Let’s start with an overview of the official website:
(reminder ❤️This paper analyzes the components in strict accordance with the sequence shown in the figure below. If necessary, you can enter from the directory or directly search and view them.)

Jetpack.jpg

Jetpack basic components

Android KTX

Android KTX is a set of kotlin extensions included in Android jetpack and other Android libraries. KTX extensions can provide concise and idiomatic kotlin code for jetpack, Android platform and other APIs. For this reason, these extensions make use of various functions of kotlin language

thereforeAndroid KTXIt’s based onkotlinIt is convenient to develop and use.

For example:
Now there’s a need for twoSet arrayAdd the data of and assign the value to the newSet array。 Under normal conditions, the function can be realized

val arraySet1 = LinkedHashSet()
    arraySet1.add(1)
    arraySet1.add(2)
    arraySet1.add(3)

    val arraySet2 = LinkedHashSet()
    arraySet2.add(4)
    arraySet2.add(5)
    arraySet2.add(6)

    val combinedArraySet1 = LinkedHashSet()
    combinedArraySet1.addAll(arraySet1)
    combinedArraySet1.addAll(arraySet2)

This code is really smelly and long, it doesn’t matter, introduceCollection KTXTry to implement the extended library again

dependencies {
        implementation "androidx.collection:collection-ktx:1.1.0"
    }
    
    // Combine 2 ArraySets into 1.
    val combinedArraySet = arraySetOf(1, 2, 3) + arraySetOf(4, 5, 6)

It’s that simple. It’s easy to usekotlinThe extension function extends the attributes, expands the set related functions, and simplifies the code.
becausekotlinAll kinds of features of the library have led to a series of extension libraries, includingFragment KTX,Lifecycle KTXwait.

Official documents
Demo code address

AppCompat

I don’t know if you have found that the original activity class inherited by activity is required to be changed to inheritanceAppCompatActivityClass. This appcompatactivity class belongs toAppCompatLibrary, which mainly includes support for the implementation of material design interface, and similarly includesActionbar, appcompatdialog and shareactionproviderThere are four key classes.

So what’s the difference between appcompatactivity class and activity class?

  • AppCompatActivity, similar to the original actionbar activity, an activity with a title bar. Specifically, it is an activity with a toolbar.

Here’s another oneShareActionProviderYou may use it less. This class is used to integrate the sharing function in the menu bar.
adoptsetShareIntent(Intent intent)Method you can set the content you want to share in the menu. The specific usage can be referred toOfficial website description

Official documents

Auto

So you don’t have to worry about vehicle specific hardware differences (such as screen resolution, software interface, knobs and touch controls) when writing applications. Users can access your app through Android auto app on their mobile phone. Alternatively, when connected to a compatible vehicle, an app on a handheld device running Android 5.0 (or higher) can communicate with an app projected to the vehicle via Android auto.

Android AutoI think it’s a little strange. But when it comes to Carplay, are you familiar with it? you ‘re right,Android AutoIt’s Google’s car phone connectivity solution. Most of the cars sold in China are not equipped with Google’s Android auto (the wall is too high to reach), so we have little contact. But it is still widely used abroad.

So the first mock exam module is for development.Android AutoRelated applications, such as music playing app and instant messaging app, can communicate with on-board system.

How to make your app support Android auto?

//Add

Then you can do the related development. How to test? You can’t let me test it in the car..
Don’t worry, the official provided the simulator –Android Auto Desktop Head Unit emulator(DHU) in theSDK ToolsIt can be downloaded.
If you are interested, you can goLearn more about official website documents

Official documents

testing

Using the jetpack benchmark library, you can quickly benchmark kotlin or Java code in Android studio. The library processes warm ups, measures code performance, and outputs benchmarking results to the Android studio console.

This module is about a library for testing performanceBenchmarkIn fact, testing is time-consuming, so we can use it to testUI performance, image loading performance and so on. Now let’s implement a method to test the image loading performance

To facilitate us to create a benchmark module directly, right-clickNew > Module >Benchmark Module
This will help us import the library, and then weandroidTest—javaCreate our test case class bitmapbenchmark under the directory, and add two test case methods.

androidTestImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.0.0'
    
private const val JETPACK = "images/test001.jpg"

@LargeTest
@RunWith(AndroidJUnit4::class)
class BitmapBenchmark {

    @get:Rule
    val benchmarkRule = BenchmarkRule()

    private val context = ApplicationProvider.getApplicationContext()
    private lateinit var bitmap: Bitmap

    @Before
    fun setUp() {
        val inputStream = context.assets.open(JETPACK)
        bitmap = BitmapFactory.decodeStream(inputStream)
        inputStream.close()
    }


    @Test
    fun bitmapGetPixelBenchmark() {
        val pixels = IntArray(100) { it }
        benchmarkRule.measureRepeated {
            pixels.map { bitmap.getPixel(it, 0) }
        }
    }

   //Test 100 pixel image rendering time
    @Test
    fun bitmapGetPixelsBenchmark() {
        val pixels = IntArray(100) { it }
        benchmarkRule.measureRepeated {
            bitmap.getPixels(pixels, 0, 100, 0, 0, 100, 1)
        }
    }
}

Then right clickBitmapBenchmarkClass to run. Note that you need to run on the real machine. The console prints out the time consumption of the two methods

Started running tests

benchmark:         2,086 ns BitmapBenchmark.bitmapGetPixelsBenchmark
benchmark:        70,902 ns BitmapBenchmark.bitmapGetPixelBenchmark
Tests ran to completion.

This is it.BenchmarkI understand the simple use of librarybenchmarkOn the basis of unit testing, this module can provide more functions of performance testing, such as execution time. But in actual use, it seems that we all use less? Later, I will try to see if old fellow iron can be commented on.

Official documents
Demo code address

Multi DEX processing

This should be familiar to everyone,65536 methodNumber limit. Since 65536 is equal to 64 x 1024, this restriction is called “64K reference restriction”. It means singleDEX fileThe total number of methods referenced inside is limited to 65536, and more than this number of methods will be packaged into multiple DEX.

terms of settlement:

  • Android5.0Next, you need to add the multidex support library. The specific method is to introduce the library, enable the multidex and modify the application.
  • Android5.0Above, start multidex by default, no need to import library.

What’s the problem? Why does 5.0 support this function by default?

  • Android 5.0Previous platform versions used Dalvik runtime to execute application code. Dalvik limited the application to only one classes.dex bytecode file per APK. In order to bypass this limitation, we have to manually add the multidex support library.
  • Android 5.0And later use a runtime called art, which itself supports loading multiple DEX files from APK files. Art performs precompilation during application installation, scans classesn.dex files, and compiles them into a single. Oat file for Android devices to execute.

Official documents

security

The security library provides security best practice implementations related to reading and writing static data as well as key creation and verification.

The security here refers to data security, and the libraries involved areSecurity librarySpecifically, secure reading and writing of files and shared preferences of security settings.
I don’t know how you used to encrypt files. I encrypted the data and then wrote it to the file. Now I use itSecurity The library will be much more convenient.

First, import the code

dependencies {
        implementation "androidx.security:security-crypto:1.0.0-alpha02"
    }

Security libraryIt mainly includes two categories
1)EncryptedFile
Read and write an encrypted file to generateEncryptedFileAfter that, the normal opening of the file is garbled, that is, encrypted, it needs to be encrypted
Encryptedfile related API. Let’s see how to read and write!

//Write data
    fun writeData(context: Context, directory: File) {
        val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
        val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

        val fileToRead = "my_sensitive_data.txt"
        val encryptedFile = EncryptedFile.Builder(
            File(directory, fileToRead),
            context,
            masterKeyAlias,
            EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
        ).build()

        val fileContent = "MY SUPER-SECRET INFORMATION"
            .toByteArray(StandardCharsets.UTF_8)
        encryptedFile.openFileOutput().apply {
            write(fileContent)
            flush()
            close()
        }
    }
    
    //Read data
    fun readData(context: Context, directory: File) {
        // recommended that you use the value specified here.
        val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
        val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

        val fileToRead = "my_sensitive_data.txt"
        val encryptedFile = EncryptedFile.Builder(
            File(directory, fileToRead),
            context,
            masterKeyAlias,
            EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
        ).build()

        val inputStream = encryptedFile.openFileInput()
        val byteArrayOutputStream = ByteArrayOutputStream()
        var nextByte: Int = inputStream.read()
        while (nextByte != -1) {
            byteArrayOutputStream.write(nextByte)
            nextByte = inputStream.read()
        }

        val plaintext: ByteArray = byteArrayOutputStream.toByteArray()
    }

2)EncryptedSharedPreferences

val sharedPreferences = EncryptedSharedPreferences
        .create(
        fileName,
        masterKeyAlias,
        context,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )

    val sharedPrefsEditor = sharedPreferences.edit()

Official documents
Demo code address

test

Test application is an indispensable step in Android project, includingFunction test, integration test, unit test。 The main point here is to write test cases in the form of code to test the stability and integrity of the application.

Specifically, there are two test directories in Android Studio:

  • Android test directoryTests that run on real or virtual devices should be included.
  • Test directoryIt should include tests that run on the local computer, such as unit tests.

The specific test writing can be seen in this official projecttesting-samples

Official documents

TV

Android TVIt is widely used in China. Most TVs in the market are Android systems and support APK installation. Huawei Hongmeng system also supports APK installation. So basically, our mobile phone applications can be directly installed on the TV, but the UI focus and other aspects need to be improved.
Let’s talk about the configuration of TV application from four aspectsConfiguration, hardware, keys and testing
1)to configure
First of all, when you declare an activity in androidmanifest.xml, if you want to be compatible with the TV version and the mobile version, you can set different startup activities, mainly in the form of settingsandroid.intent.category.LEANBACK_LAUNCHERfilter:

//Mobile start activity
   

     
        
        
     
   
   
   //TV start activity

2)Hardware
The hardware mainly includes how to judge whether the current running environment is TV environment and whether some functions of TV hardware exist.

//Judge whether the current running environment is TV environment
    val uiModeManager = getSystemService(UI_MODE_SERVICE) as UiModeManager
    if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
        Log.d(TAG, "Running on a TV Device")
    } else {
        Log.d(TAG, "Running on a non-TV Device")
    }
    
    //Check whether some functions of TV hardware exist
    // Check if android.hardware.touchscreen feature is available.
    if (packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
        Log.d("HardwareFeatureTest", "Device has a touch screen.")
    }

3) Key
The interface events in TV mainly include:

BUTTON_ B、BACK 	 return
   BUTTON_ SELECT、BUTTON_ A、ENTER、DPAD_ CENTER、KEYCODE_ NUMPAD_ ENTER 	 choice
   DPAD_ UP、DPAD_ DOWN、DPAD_ LEFT、DPAD_ RIGHT 	 Navigation

Key configuration includes:

nextFocusDown 	 Defines the next view to get focus when the user navigates down.
   nextFocusLeft 	 Defines the next view that gets focus when the user navigates to the left.
   nextFocusRight 	 Defines the next view that gets focus when the user navigates to the right.
   nextFocusUp 	   Defines the next view to get focus when the user navigates up.

4)test
Similarly, the test of TV app can directly pass the test of TV simulatorAVD ManagerCreate a new TV simulator inside.

Official documents

Wear OS by Google

Google’s watch system is also developed using Android. It seems that there is no base in ChinaWear OS And as far as I know, there are very few foreign wearos devices, so they are widely usedWatchOSEven Google’s app nest doesn’t support wearos. So we just need to know about this part. If you are interested, you can go and have a lookOfficial demo

Official documents

Jetpack architecture component

The components of this module are designed forMVVMFramework services, but each library can be used independently, which is also an important module in jetpack.
Let’s talk about it brieflyMVVM,Model—View—ViewModel。

  • Model layerMainly refers to data, such as server data, local database data, so network operation and database reading is this layer, only save data.
  • View layerMainly refers to UI related, such as XML layout file, activity interface display
  • ViewModel layerMVVM is the core of MVVM. To connect view and model, you need to show the data of model to view and convert the operation data of view to model layer, so it is equivalent to a two-way binding.

So we need to,databindingData binding, one-way or two-way.viewmodelData management, binding view and data.lifecycleLife cycle management.LiveDataTimely feedback of data.
Can’t wait to see the magic of each library with me.

Data binding

A data binding library is a supporting library that enables you to bind interface components in a layout to data sources in an application in a declarative format rather than programmatically.

Mainly refers to the data binding libraryDataBindingThe following is a detailed introduction from six aspects

Configure the application to use data binding:

android {
        ...
        dataBinding {
            enabled = true
        }
    }

1)Layout and binding expressions
Through data binding, we can make the view in XML layout file bind and assign values to data objects, and we can write expressions with the help of expression language to handle the event of view assignment. For example:

//Layout activity_ main.xml
    
    
       
           
       
       
    
    
    //Entity class user
    data class User(val name: String)
    
    
    //Activity assignment
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(
                this, R.layout.activity_main)
        binding.user = User("Bob")
    }

adopt@{} Symbols, data objects can be used in the layout, and assignment objects can be obtained through databindingutil. also@{} The expression language supports a variety of operators, including arithmetic operators, logical operators and so on.

2)Observable data objects
Observability refers to the ability of an object to inform other objects of changes in its data. With data binding libraries, you can make objects, fields, or collections observable.

For example, in the user class just mentioned above, we change the name attribute to an observable object,

data class User(val name: ObservableField)
   
   val userName = ObservableField()
   userName.set("Bob")

   val binding: ActivityMainBinding = DataBindingUtil.setContentView(
                this, R.layout.activity_main)
   binding.user = User(userName)

Then bind to the layout, and the user’snameProperty is the observed object, ifuserNameChange, inside the layoutTextViewThe display data will also change, which is the observable data object.

3)Generated binding class

Just now we got the binding layout throughDataBindingUtil.setContentViewMethod generates the activitymainbinding object and binds the layout. So how is the activitymainbinding class generated? Just use your layoutlayoutAfter compilation, the binding class will be generated automatically. The class name is based on the name of the layout file, and it will be converted toPascalCase and add the binding suffix at the end.

The normal creation of binding objects is as follows:

//Activity
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: MyLayoutBinding = MyLayoutBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
    
    
    //Fragment
    @Nullable
    fun onCreateView( inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        mDataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_layout, container, false)
        return mDataBinding.getRoot()
    }

4)Binder Adapter

The adapter here refers to the property settings in the layout,android:text="@{user.name}" For example, the library will look up and accept theuser.getName()Of the returned type setText(arg)method.
The important thing is that we can customize the adapter, that is, the attributes in the layout. We can define its name and function freely. Let’s have a look

@BindingAdapter("imageUrl")
    fun loadImage(view: ImageView, url: String) {
        Picasso.get().load(url).into(view)
    }

Define an externally accessible method in a classloadImage, notes@BindingAdapterThe attribute in it is the name of the attribute you need to define. Here, imageurl is set. So it can be used in layoutapp:imageUrl, and pass the value as string type, the system will find the adapter method and execute it.

5)Binding layout views to architectural components
This is a practical application. It is combined with other components of jetpack to form a complete systemMVVMHierarchical architecture.

// Obtain the ViewModel component.
        val userModel: UserViewModel by viewModels()

        // Inflate view and obtain an instance of the binding class.
        val binding: ActivityDatabindingMvvmBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_databinding_mvvm)

        // Assign the component to a property in the binding class.
        binding.viewmodel = userModel
        
    
        
    
    
    class UserViewModel : ViewModel() {
    val currentName: MutableLiveData by lazy {
        MutableLiveData()
    }

    init {
        currentName.value="zzz"
    }
}

6)Bidirectional data binding

What we have just introduced is one-way binding, that is, view is bound to data objects in the layout, so how can data objects also bind to view? that isView changesThe data object can also receive the message and form theBidirectional binding

It’s very simple. For example, for an EditText, the requirement is that when EditText changes, the user object name data will also change. Just change the previous “@ {}” to “@ = {}”

//Layout activity_ main.xml

It’s very simple. Similarly, this bidirectional binding function also supports customization. Let’s have a look

object SwipeRefreshLayoutBinding {

    //Method 1: bind data to view
    @JvmStatic
    @BindingAdapter("app:bind_refreshing")
    fun setSwipeRefreshLayoutRefreshing(swipeRefreshLayout: SwipeRefreshLayout,newValue: Boolean) {
        if (swipeRefreshLayout.isRefreshing != newValue)
            swipeRefreshLayout.isRefreshing = newValue
    }

    //Method 1: bind will be notified when the view changes_ Refresh changed, and get the data of view from this method
    @JvmStatic
    @InverseBindingAdapter(attribute = "app:bind_refreshing",event = "app:bind_refreshingChanged")
    fun isSwipeRefreshLayoutRefreshing(swipeRefreshLayout: SwipeRefreshLayout): Boolean =swipeRefreshLayout.isRefreshing
            
    //Method 3, how to change the view to affect the data content  
    @JvmStatic
    @BindingAdapter("app:bind_refreshingChanged",requireAll = false)
    fun setOnRefreshListener(swipeRefreshLayout: SwipeRefreshLayout,bindingListener: InverseBindingListener?) {
        if (bindingListener != null)
            swipeRefreshLayout.setOnRefreshListener {
                bindingListener.onChange()
            }
    }
}

For a brief explanation, first of all, through thebind_refreshingAttribute, which sets the dataviewModel.refreshingBind to the view, so that the data changes, and the view will also change. And then when the view changes, through theInverseBindingAdapterComment, will callbind_refreshingChangedEvent, and bind_ The refreshingchanged event tells us when the view will modify the data. In this case, when swiperefreshlayout slides down, the data will be changed, so the data object will change fromisSwipeRefreshLayoutRefreshingMethod to get the latest value, that is, the data updated from view.

One point to note here is that the dead loop problem should be considered in bidirectional binding. When the view is changed, the corresponding data object will be updated. At the same time, the update will inform the view layer to refresh the UI, and then the view is changed, which will cause the data object to be updated, and the infinite loop will continue. So the way to prevent the dead loop is to judge the data state of the view and update the view when it changes.

Official documents
Demo code address

Lifecycles

Lifecycle aware components can perform actions in response to changes in the lifecycle state of another component, such as activity and fragment. These components help you write more organized and often streamlined code that is easier to maintain.

LifecyclesIt is called lifecycle aware component, which can sense and respond to the change of lifecycle state of another component (such as activity and fragment).

Some people may wonder why I need to import a library when there are only a few life cycles? With a library, you don’t have to write a life cycle. What’s the advantage?
For example, let you feel it.

First, import the library, which can be imported according to the actual situation of the project

// ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
        // LiveData
        implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
        // Lifecycles only (without ViewModel or LiveData)
        implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
        //.......

Now there’s a location monitor that needs to be installed in theActivityIt is turned on when it is started and turned off when it is destroyed. The normal code is as follows:

class BindingActivity : AppCompatActivity() {

    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }
    public override fun onStart() {
        super.onStart()
        myLocationListener.start()       
    }
    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
    }

    internal class MyLocationListener(
            private val context: Context,
            private val callback: (Location) -> Unit
    ) {
        fun start() {
            // connect to system location service
        }
        fun stop() {
            // disconnect from system location service
        }
    }
    
}

At first glance, there is no problem, right? But if there are more classes that need to manage the life cycle, it is not easy to manage. All classes should be managed in activity, which is easy to miss.
So the solution is to do itdecoupling To enable classes that need to manage their lifecyclesSelf managementIn this way, the activity will not be missed and bloated. Upper Code:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
       lifecycle.addObserver(myLocationListener)
    }



    internal class MyLocationListener (
            private val context: Context,
            private val callback: (Location) -> Unit
    ): LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun start() {

        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun stop() {
            // disconnect if connected
        }
    }

It’s very simple. Just implement itLifecycleObserverInterface, the method to be executed in each life cycle can be executed in the form of annotation. Then in the activityaddObserverJust bind.

alike,LifecycleIt also supports user-defined life cycle, as long as inheriting the lifecycle owner, and thenmarkStateMethod to set the life cycle of your own class

class BindingActivity : AppCompatActivity(), LifecycleOwner {

    private lateinit var lifecycleRegistry: LifecycleRegistry

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleRegistry = LifecycleRegistry(this)
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }

    public override fun onStart() {
        super.onStart()
        lifecycleRegistry.markState(Lifecycle.State.STARTED)
    }
}

Official documents
Demo code address

LiveData

Livedata is an observable data storage class. Unlike conventional observable classes, livedata has life cycle awareness, which means that it follows the life cycle of other application components (such as activity, fragment or service). This awareness ensures that livedata only updates application component observers in an active lifecycle state.

LiveDataIs an observable data storage class.
Wait, this introduction seems familiar? Yes, when we said data binding, we have an observable data objectObservableField。 What’s the difference between the two?

1) LiveDataIt has the ability to perceive the life cycle of activities. What’s the advantage of this? A very common point is that it can reduce memory leaks and crashes. Think about the past when you returned data for the network interface in your project, you had to judge whether the current interface was destroyed or not. Now livedata has solved this problem for you.

Why can we solve the problem of collapse and leakage?

  • There is no memory leak
    The observer is bound to the lifecycle object and self cleans up after its associated lifecycle is destroyed.
  • No crash due to activity stop
    If the observer’s lifecycle is inactive (such as returning an activity in the stack), it will not receive any live data events.
  • Automatic life cycle judgment and callback method
    If the life cycle of an observer is in the started or resumed state, livedata will consider that the observer is in the active state and call the onactive method. Otherwise, if the livedata object does not have any active observers, it will call the oninactive () method.

2) Livedata is more flexible in updating data, not necessarily changing data, but calling methods(postvalue or setValue)To update the UI or other operations.

okay. Let’s take a more intuitive look

//Import library:
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"

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

        private val listener = { price: BigDecimal ->
            value = price
        }

        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }

        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }
    }
    
    public class MyFragment : Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            val myPriceListener: LiveData = StockLiveData("")
            myPriceListener.observe(this, Observer { price: BigDecimal? ->
                //Monitor the data changes of live data. If setValue or postvalue is called, the onchanged method will be called
                //Update UI data or other processing
            })
        }
    }

This is a stock data object,StockManagerIf the object has an active observer, it monitors the stock market. If there is no active observer, it can disconnect the monitoring.
When the stock information changes, the stock data object will pass throughsetValueMethods the data were updated to reflect the observer’s onchanged method. What should be noted here is thatsetValueMethod can only be called on the main thread, not thepostValueIs called on another thread.
WhenFragmentWhen the observer’s life cycle changes,LiveDataThe observer will be removed and messages will no longer be sent, so the crash problem will be avoided.

Official documents
Demo code address

Navigation
The navigation component is designed for applications with one primary activity and multiple fragment destinations. The main activity is associated with the navigation map and contains a navhostfragment responsible for exchanging destinations as needed. In an application with multiple activity destinations, each activity has its own navigation map.

So to put it bluntly,NavigationThat’s oneFragmentThe management framework of.
How to achieve it? Create activity, fragment and connect.

1)Import library

def nav_version = "2.3.0"
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

2)Create three fragments and one activity

3)Create RES / navigation / my_ Nav.xml file

Create a new file in the res foldernavigationDirectory and create a new onemy_nav.xmlDocuments. Configure each fragment, where:

  • app:startDestinationProperty represents the fragment that is initially displayed
  • android:nameProperty represents the corresponding fragment path
  • actionFor instance, myfragment1 can jump to myfragment2.
  1. Modify the layout file of the activity:

As you can see, the activity layout file is just a fragment control, named navhostfragment,navGraphFor the mynavigation file you just created.

5)After configuration, you can set specific jump logic.

override fun onClick(v: View) {
    //Without parameters
 v.findNavController().navigate(R.id.action_blankFragment_to_blankFragment2)
   //With parameters
    var bundle = bundleOf("amount" to amount)
    v.findNavController().navigate(R.id.confirmationAction, bundle)
 
    }
    
    //Receiving data
    tv.text = arguments?.getString("amount")

It should be noted that the official suggestion of jump is to useSafe ArgsThis plug-in can generate simpleObject and builderClass to browse and access any associated parameters in a type safe manner. I won’t elaborate here. Those who are interested can goTake a look at the official website

Official documents
Demo code address

Room

Room persistence library provides an abstraction layer on the basis of SQLite, so that users can make full use of the powerful functions of SQLite and enjoy a more robust database access mechanism.

thereforeRoomIt’s a database framework. The problem is that there are so many database components on the market, such asormLite,greendaoWait, why does Google have a room? What are its advantages?

  • Performance advantagesA database operation mainly includes: constructing SQL statement – compiling statement – passing in parameters – executing operation.ORMLiteIt is mainly obtained by reflection when obtaining parameter attribute values, so the speed is slow.GreenDaoWhen constructing SQL statements, it is through code splicing, so it is slow.RoomSQL statements are generated through the annotation of interface methods, that is, SQL statements are generated when they are compiled into bytecode, so they run faster.
  • Support other components of jetpack (such as livedata, paging) and rxjavaIt’s like taking advantage of the current advantageous environment to bring you some unique advantages. Of course, it is much more convenient to use in practice, such asliveDataWith this method, the UI can be updated automatically after data query.

Since the room is so excellent, let’s use it.
There are three main points for room access:DataBase、Entity、Dao。 Corresponding to database, table and data access respectively.

1)First, import the Library:

apply plugin: 'kotlin-kapt'

    dependencies {
      def room_version = "2.2.5"

      implementation "androidx.room:room-runtime:$room_version"
      kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor

      // optional - Kotlin Extensions and Coroutines support for Room
      implementation "androidx.room:room-ktx:$room_version"

      // optional - RxJava support for Room
      implementation "androidx.room:room-rxjava2:$room_version"
    }

2)Establish database class, declare database table member, database name, database version, singleton, etc

@Database(entities = arrayOf(User::class), version = 1)
abstract class UserDb : RoomDatabase() {

    abstract fun userDao(): UserDao

    companion object {
        private var instance: UserDb? = null

        @Synchronized
        fun get(context: Context): UserDb {
            if (instance == null) {
                instance = Room.databaseBuilder(context.applicationContext,
                    UserDb::class.java, "StudentDatabase").build()
            }
            return instance!!
        }
    }
}

3)To create a table, you can set primary key, foreign key, index, auto increment, etc

@Entity
data class User(@PrimaryKey(autoGenerate = true) val id: Int,
                val name: String)

4)Dao, data manipulation

@Dao
interface UserDao {

    @Query("SELECT * FROM User")
    fun getAllUser(): DataSource.Factory

    @Query("SELECT * FROM User")
    fun getAllUser2(): LiveData>

    @Query("SELECT * from user")
    fun getAllUser3(): Flowable>

    @Insert
    fun insert(users: List)
}

Then you can operate the database, it’s very simple.
Official documents
Demo code address

Paging

The paging library helps you load and display a small piece of data at a time. Loading some data on demand will reduce the usage of network bandwidth and system resources.

thereforePagingIs a paging library, mainly used for recycleview list display. Now I’ll talk about the usage of padding with room.
When using paging, you should pay attention to two classes:Pagedlist and pagedlistadapter
1)PagedList
Used to load application data block, bind data list, set data page, etc. In combination with the aboveRoomI went on to write a demoUserModelData management:

class UserModel(app: Application) : AndroidViewModel(app) {
    val dao = UserDb.get(app).userDao()
    var idNum = 1

    companion object {
        private const val PAGE_SIZE = 10
    }

    //Initialize pagedlist
    val users = LivePagedListBuilder(
        dao.getAllUser(), PagedList.Config.Builder()
            .setPageSize(PAGE_SIZE)
            .setEnablePlaceholders(true)
            .build()
    ).build()

    //Insert user
    fun insert() = ioThread {
        dao.insert(newTenUser())
    }

    //Get new 10 users
    fun newTenUser(): ArrayList {
        var newUsers = ArrayList()
        for (index in 1..10) {
            newUsers.add(User(0, "bob${++idNum}"))
        }
        return newUsers
    }

}

2)PagedListAdapter
To use recycleview, you need to use the adatper, so you need to bind aPagedListAdapterAdapter for:

class UserAdapter : PagedListAdapter(diffCallback) {
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        holder.bindTo(getItem(position))
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder =
        UserViewHolder(parent)

    companion object {

        private val diffCallback = object : DiffUtil.ItemCallback() {
            override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem.id == newItem.id

            override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem == newItem
        }
    }

    class UserViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)) {

        private val tv1 = itemView.findViewById(R.id.name)
        var user: User? = null

        fun bindTo(user: User?) {
            this.user = user
            tv1.text = user?.name
        }
    }
}

It’s also used here DiffUtil.ItemCallbackClass, used to compare data and update data.

OK, the data source and adapter are all set. The next step is to listen to the data and refresh the data

//Listen to users' data and call the submitlist method when the data changes
        viewModel.users.observe(this, Observer(adapter::submitList))

Yes, that’s it. MonitoringPagedListAnd call the pagedlistadapter’ssubmitListmethod.
This layering is cool enough. In fact, this is the advantage that paging or jetpack brings to our project. The layers are decoupled, and the adapter does not need to maintain the list data source.

Official documents
Demo code address

ViewModel

The ViewModel class is designed to store and manage interface related data in a lifecycle oriented manner. The ViewModel class allows data to persist after configuration changes such as screen rotation.

FinallyViewModelIn fact, the previous demo has been used many times,ViewModelThe main task is to separate view data from interface controller logic. Why do you do this? The main purpose is to solve two problems

  • In the past, if the activity was destroyed by the system or needs to be re created, the temporary data of the page would be lost, which needs to be saved through theonSaveInstanceState()Method to save and read from the oncreate method. And a large amount of data is even more inconvenient.
  • In activity, it is inevitable that there are some asynchronous calls, so it is easy to cause these calls to exist when the interface is destroyed. Then there will be a memory leak or a direct crash.

thereforeViewModelBorn or decoupled, if I manage the data separately and add the life cycle, then these problems can be solved. And when the owner activity is completely destroyed,ViewModelIt is calledonCleared() Method to clean up resources.

Next, let’s take a look at how ViewModel is used

def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"


class SharedViewModel : ViewModel() {
    var userData = MutableLiveData()

    fun select(item: User) {
        userData.value = item
    }

    override fun onCleared() {
        super.onCleared()
    }
}

class MyFragment1 : Fragment() {
    private lateinit var btn: Button

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val model=activity?.let { ViewModelProvider(it).get(SharedViewModel::class.java) }
        btn.setOnClickListener{
            model?.select(User(0,"bob"))
        }
    }
}

class MyFragment2 : Fragment() {
    private lateinit var btn: Button

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val model=activity?.let { ViewModelProvider(it).get(SharedViewModel::class.java) }
        model?.userData?.observe(viewLifecycleOwner, Observer { item ->
            // Update the UI
        })
    }
}

In fragment, get theviewmodelAnd then do data monitoring and other operations. Wait, what can you find?
By the way, data communication. Different fragments can be shared using their parent activityViewModelFor data communication, great. There are many other usages, go to the project to find it slowly!

Official documents
Demo code address

WorkManager

Using the workmanager API, you can easily schedule deferrable asynchronous tasks that should run even when the application exits or the device restarts.

Listen to this introduction is very magical, application exit and device restart can automatically run? By radio? How is the data saved? Heard that you can also perform periodic asynchronous tasks, sequential chain call Oh! Next, decrypt one by one

  • About application exit and device restart
    If the app is running,WorkManagerWill start a new thread in the app process to run the task; If the app is not running,WorkManagerChoose an appropriate way to schedule background tasks — depending on the system level and app status, workmanager may useJobScheduler,FireBase JobDispatcherperhapsAlarmManager
  • About data preservation
    WorkManagerThe created task data will be saved to the databaseRoomFramework. Then restart and other time periods will go to the database to find the tasks that need to be arranged for execution, and then judgeconstraint conditionAnd then it can be executed.

What scenarios does this API apply to? Think about it. It works reliably, and it can be periodic and asynchronous.
By the way, send logs. Can passWorkManagerSet the cycle task and execute the task of sending log once a day. Moreover, it can ensure the reliable operation of your tasks, which can be uploaded to. Of course, it also supports monitoring task results

1)Import library

dependencies {
      def work_version = "2.3.4"
        // Kotlin + coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"

        // optional - RxJava2 support
        implementation "androidx.work:work-rxjava2:$work_version"

        // optional - GCMNetworkManager support
        implementation "androidx.work:work-gcm:$work_version"
      }

2) New task class, inheritanceWorker, rewritedoWorkMethod to return the task result.

class UploadLogcatWork(appContext: Context, workerParams: WorkerParameters) :
    Worker(appContext, workerParams) {

    override fun doWork(): Result {

        if (isUploadLogcatSuc()) {
            return Result.success()
        } else if (isNeedRetry()){
            return Result.retry()
        }

        return Result.failure()
    }

    fun isUploadLogcatSuc(): Boolean {
        var isSuc: Boolean = false
        return isSuc
    }

    fun isNeedRetry(): Boolean {
        var isSuc: Boolean = false
        return isSuc
    }
}

3)Finally, set constraints(whether network is needed, whether low power is supported, whether charging execution is supported, delay, etc.), execute tasks (single task or cycle task)

//Set constraints
        val constraints =
            Constraints.Builder()
                //Use when linking to the Internet
                .setRequiredNetworkType(NetworkType.CONNECTED)
                //Is it executed when the device is idle
                .setRequiresDeviceIdle(false)
                //Is it executed when the battery is low
                .setRequiresBatteryNotLow(true)
                //Is it executed when memory is low
                .setRequiresStorageNotLow(true)
                //Do you want to execute when charging
                .setRequiresCharging(true)
                //Delayed execution
                .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS)
                .build()

        //Set cycle task
        val uploadRequest =
            PeriodicWorkRequestBuilder(1, TimeUnit.HOURS)
                .setConstraints(constraints)
                .addTag("uploadTag")
                .build()

        //Implementation
        WorkManager.getInstance(applicationContext).enqueue(uploadRequest)


        //Monitor execution results
        WorkManager.getInstance(this)
//. getworkinfosbytaglivedata ("uploadtag") // get work through tag
            . getworkinfobyidlivedata (uploadrequest. ID) // get work through ID
            .observe(this, Observer {
                it?.apply {
                    when (this.state) {
                        WorkInfo.State.BLOCKED -> println("BLOCKED")
                        WorkInfo.State.CANCELLED -> println("CANCELLED")
                        WorkInfo.State.RUNNING -> println("RUNNING")
                        WorkInfo.State.ENQUEUED -> println("ENQUEUED")
                        WorkInfo.State.FAILED -> println("FAILED")
                        WorkInfo.State.SUCCEEDED -> println("SUCCEEDED")
                        else -> println("else status ${this.state}")
                    }
                }

            })

4)In addition, it also supports task cancellation, task chain sequential call and so on

//Cancel
    fun cancelWork(){
  WorkManager.getInstance(applicationContext).cancelAllWorkByTag("uploadTag")
    }

    fun startLineWork(){
        //Picture filter 1
        val filter1 = OneTimeWorkRequestBuilder()
            .build()
        //Picture filter 2
        val filter2 = OneTimeWorkRequestBuilder()
            .build()
        //Image compression
        val compress = OneTimeWorkRequestBuilder()
            .build()
        //Picture upload
        val upload = OneTimeWorkRequestBuilder()
            .build()

        WorkManager.getInstance(applicationContext)
            .beginWith(listOf(filter1, filter2))
            .then(compress)
            .then(upload)
            .enqueue()
    }

Official documents
Demo code address

Jetpack behavior component

CameraX

CameraX is a jetpack support library designed to help you simplify the development of camera applications. It provides a consistent and easy-to-use API surface for most Android devices and is backward compatible to Android 5.0 (API level 21).
Although it uses the function of Camera2, it uses a simpler and use case-based method, which has the ability of life cycle awareness. It also solves device compatibility issues, so you don’t need to add device specific code to the code base. These features reduce the amount of code that needs to be written when adding camera features to the application.

I think we all know about itCamera APIandCamera2 APISummary is just two words, not easy to use. Ha ha, I feel that in my impression, I want to take a picture. Shouldn’t I just call a code to complete it. But with the previous API, I need to manage the camera instance, set various things related to sufraceview, as well as preview size and image size, process and set various monitoring and so on. I feel dizzy.

Maybe the government heard my complaint, soCameraXHere, cameraX is based oncamera2It is encapsulated and provides us with a simpler solution to solve our previous dilemma. coming

// CameraX core library using the camera2 implementation
    def camerax_version = "1.0.0-beta06"
    // The following line is optional, as the core library is included indirectly by camera-camera2
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    // If you want to additionally use the CameraX Lifecycle library
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    // If you want to additionally use the CameraX View class
    implementation "androidx.camera:camera-view:1.0.0-alpha13"
    // If you want to additionally use the CameraX Extensions library
    implementation "androidx.camera:camera-extensions:1.0.0-alpha13"
    
    
    
    
    //Initialize camera
    private fun initCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(Runnable {
            try {
                val cameraProvider = cameraProviderFuture.get()
                val preview = Preview.Builder().build()


                //Picture shooting use case
                mImageCapture = ImageCapture.Builder()
                    .setFlashMode(ImageCapture.FLASH_MODE_AUTO)
                    .build()

                //Configuration parameters (rear camera, etc.)
                // Choose the camera by requiring a lens facing
                val cameraSelector =
                    CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                        .build()

                //Specifies the lifecycle to be associated with the camera, which tells cameraX when to configure a camera shooting session and ensures that the camera state changes accordingly as the lifecycle transitions.
                val camera: Camera = cameraProvider.bindToLifecycle(
                    this,
                    cameraSelector,
                    preview,
                    mImageCapture
                )

                //Camera preview
                preview.setSurfaceProvider(view_finder.createSurfaceProvider())

            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }, ContextCompat.getMainExecutor(this))
    }

    //Take a picture and save it
    fun takePhoto(view: View?) {
        if (mImageCapture != null) {
            val outputFileOptions: OutputFileOptions = OutputFileOptions.Builder(cretaeFile()).build()

            //Take photos
            mImageCapture?.takePicture(
                outputFileOptions,
                ContextCompat.getMainExecutor(this),
                object : ImageCapture.OnImageSavedCallback {
                    override fun onImageSaved(@NonNull outputFileResults: OutputFileResults) {
                        //Saved successfully
                        Log.e(TAG, "success")
                    }

                    override fun onError(@NonNull exception: ImageCaptureException) {
                        //Save failed
                        Log.e(TAG, "fail")
                    }
                })
        }
    }

It’s very convenient to use, and it can bind the life cycle of the current activity, which involves another componentLifecycleBy binding an event once, the camera state can be changed accordingly with the transition of the life cycle.
In addition, we should pay attention to get the camera permission first.

Official documents
Demo code address

Download Manager

Downloadmanager download manager is a system service that handles long-running HTTP downloads. The client can request to download the URI to a specific target file. The download manager will perform the download in the background, be responsible for HTTP interaction, and try the download again after failure or cross connection change and system restart.

DownloadManagerYou should be familiar with it,android2.3Open the API provided, it’s very convenient to download files, including whether to set notification display, download folder name, file name, download progress status query, etc. come

class DownloadActivity : AppCompatActivity() {

    private var mDownId: Long = 0
    private var mDownloadManager: DownloadManager? = null
    private val observer: DownloadContentObserver = DownloadContentObserver()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    //Configure the download parameters and enqueue starts downloading
    fun download(url: String) {
        mDownloadManager =
            this.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        val request = DownloadManager.Request(Uri.parse(url))
        //Set folder file name
        request.setDestinationInExternalPublicDir("lz_download", "test.apk")
        //Set allowed network types
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
        //File type
        request.setMimeType("application/zip")
        //Set whether the notification is displayed
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        //Set notification bar title
        request.setTitle("apk download")
        //Set notification bar content
        request.setDescription("*** apk")

        mDownId = mDownloadManager!!.enqueue(request)

 contentResolver.registerContentObserver(mDownloadManager!!.getUriForDownloadedFile(mDownId), true, observer)
    }

    //Query the download status through ContentProvider
    fun queryDownloadStatus(){
        val query = DownloadManager.Query()
        //Search by downloaded ID
        //Search by downloaded ID
        query.setFilterById(mDownId)
        val cursor: Cursor = mDownloadManager!!.query(query)
        if (cursor.moveToFirst()) {
            //Bytes downloaded
            val downloadBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
            //Total bytes
            val allBytes= cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
            //Status
            when (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
                DownloadManager.STATUS_PAUSED -> {
                }
                DownloadManager.STATUS_PENDING -> {
                }
                DownloadManager.STATUS_RUNNING -> {
                }
                DownloadManager.STATUS_SUCCESSFUL -> {
                    cursor.close()
                }
                DownloadManager.STATUS_FAILED -> {
                    cursor.close()
                }
            }

        }
    }

    //Cancel download, delete file
    fun unDownLoad(view: View?) {
        mDownloadManager!!.remove(mDownId)
    }


    override fun onDestroy() {
        super.onDestroy()
        contentResolver.unregisterContentObserver(observer)
    }


    //Monitor Downloads
    inner class DownloadContentObserver : ContentObserver(Handler(Looper.getMainLooper())) {
        override fun onChange(selfChange: Boolean) {
            queryDownloadStatus()
        }
    }

}

The demo should be written clearly. It should be noted that the download ID is used to save, cancel and query the download progress status. Monitoring download progress is mainly through observationgetUriForDownloadedFileMethod to get the progress by observing the database changes pointed to by the URI.

Official documents
Demo code address

Media and broadcasting

Android multimedia framework supports playing a variety of common media types, so that you can easily integrate audio, video and pictures into the application.

Here, media and playback refer to audio and video related content, mainly involving two related categories:

  • MediaPlayer
  • ExoPlayer

MediaPlayerNeedless to say, everyone should have used it. I’ll mention it later.
ExoPlayerIt’s a separate library and an open source media player project of Google. It’s said that it’s the player used by YouTube app, so its function is better than that of GoogleMediaPlayerPowerful, support all kinds of customization, can work withIJKPlayerIt’s comparable, but it’s more complicated to use.

1)MediaPlayer

//Play local files
        var mediaPlayer: MediaPlayer? = MediaPlayer.create(this, R.raw.test_media)
        mediaPlayer?.start()

        //Set up the right to play continuously on screen with wake_ Lock use
        mediaPlayer?.setScreenOnWhilePlaying(true)


        //Play locally available URIs
        val myUri: Uri = Uri.EMPTY
        val mediaPlayer2: MediaPlayer? = MediaPlayer().apply {
            setAudioStreamType(AudioManager.STREAM_MUSIC)
            setDataSource(applicationContext, myUri)
            prepare()
            start()
        }

        //Play network files
        val url = "http://........"
        val mediaPlayer3: MediaPlayer? = MediaPlayer().apply {
            setAudioStreamType(AudioManager.STREAM_MUSIC)
            setDataSource(url)
            prepare()
            start()
        }


        //Release
        mediaPlayer?.release()
        mediaPlayer = null

2)ExoPlayer

compile 'com.google.android.exoplayer:exoplayer:r2.X.X'
   
    var player: SimpleExoPlayer ?= null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_exoplayer)

        //Initialization
        player = SimpleExoPlayer.Builder(this).build()
        video_view.player = player
        player?.playWhenReady = true

        //Set playback resources
        val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(
            this,
            Util.getUserAgent(this, "yourApplicationName")
        )
        val uri: Uri = Uri.EMPTY
        val videoSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
            .createMediaSource(uri)
        player?.prepare(videoSource)
    }

    private fun releasePlayer() {
        //Release
        player?.release()
        player = null
    }

Doesn’t it seem complicated? Ha ha, you need to discover more powerful functions.

Official documents
Demo code address

notice

Notification refers to the message that Android displays outside the application interface, which aims to provide users with reminders, communication information from others or other real-time information in the application. Users can click the notification to open the application, or they can directly perform an operation in the notification.

You should know all about this. Go straight to the last one

private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "mychannel"
            val descriptionText = "for test"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
                description = descriptionText
            }
            // Register the channel with the system
            val notificationManager: NotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    private fun showNotification(){
        val intent = Intent(this, SettingActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)

        val builder = NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("My notification")
            .setContentText("Hello World!")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            // Set the intent that will fire when the user taps the notification
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)

        with(NotificationManagerCompat.from(this)) {
            notify(1, builder.build())
        }

    }

Official documents

jurisdiction

The role of permissions is to protect the privacy of Android users. Android applications must request permission to access sensitive user data (such as contacts and text messages) and certain system functions (such as cameras and the Internet). Depending on the access function, the system may grant permissions automatically or prompt the user to approve the request.

You should be familiar with the authority.

  • Danger authority. After 6.0, you need to apply for the dangerous permission, and it is recommendedRxpermissions Library
  • Permissions for optional hardware features. For applications that use hardware, such as cameras, if you want to Google PlayIf you are allowed to install your application on a device without this function, you need to configure the permissions of the hardware function as unnecessary:
  • Custom permissions. Some students may not have contact with this. We know that if we set the activityexportedIf the property is true, others can access our activity through the package name and activity name. What if we don’t want everyone to access my activity? Can passCustom permissionsrealization. come
//Application a

    
    
    
     
       


//Application B

Official documents

Preferences

It is recommended to use androidx preference library to integrate user configurable settings into your application. This library manages the interface and interacts with the storage space, so you only need to define individual settings that users can configure. This library comes with material theme, which can provide consistent user experience between different devices and operating system versions.

I’m confused to see the title. What’s the setting? My settings page official can help me write? Then I went to studyPreference LibraryHey, really, if your app itself isMaterial styleYou can use this directly. However, it is also because of the fixed style that it is rarely used in various apps.
Let’s have a look

implementation 'androidx.preference:preference:1.1.0-alpha04'
   
   //res-xml-setting.xml
   


    
        
    

    
        

        
            
        
    



class SettingFragment : PreferenceFragmentCompat() {
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.setting, rootKey)
        val feedbackPreference: Preference? = findPreference("feedback")

        feedbackPreference?.setOnPreferenceClickListener {
            Toast.makeText(context,"hello Setting",Toast.LENGTH_SHORT).show()
            true
        }
    }
}


class SettingActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_setting)

        supportFragmentManager
            .beginTransaction()
            .replace(R.id.settings_container, SettingFragment())
            .commit()
    }
    
}

First, create a new onexmlFile, which is equivalent to setting the layout of the page, including those categories, those options, and the function of the options.
Then create a new onefragmentInherited fromPreferenceFragmentCompatHere you can bind the XML file and set the click event.
Finally, add fragment to activity. ✌️

Let’s have a look at the rendering
jetpack-setting.jpg

Official documents
Demo code address

share

One of the great advantages of Android applications is that they can communicate and integrate with each other. If a function is not the core of an application and already exists in another application, why redevelop it?

Sharing here mainly refers toSharing between applicationsFor example, email function, open web function, we can directly call the system application or other three-party applications to help us complete these functions, which is the meaning of sharing.

//Sender
    val sendIntent: Intent = Intent().apply {
        action = Intent.ACTION_SEND
        putExtra(Intent.EXTRA_TEXT, "This is my text to send.")
        type = "text/plain"
    }

    val shareIntent = Intent.createChooser(sendIntent, null)
    startActivity(shareIntent)
    
    //Receiver

Official documents

section

Slicing is an interface template, which can display rich and dynamic interactive content in Google search application and Google assistant. Slicing supports interaction beyond the full screen application experience and helps users perform tasks faster. You can build slices as enhancements to application operations.

This introduction is a bit vague, but when it comes toSliceDo you have any impression? In 2018, Google I / 0 announced a new interface operationAction & Slice。 And this slice is the slice. What can he do? Users can quickly use a specific function in the app. As long as developers import slice functions, users will appear when using search, Google play store, Google assitant or other built-in functionsSliceSuggestions for operation of the system.

To put it bluntly, some functions of your application can be displayed and operated in other applications.

So, if your app is published inGooglePlayAfter all, it’s another experiment made by Google in order to make the application portable.

How to develop this function? It’s very simple, just one step, right clickNew—other—Slice ProviderThat’s it.
The slice library, provider and sliceprovider classes are all configured. It’s convenient. Post the code:

class MySliceProvider : SliceProvider() {
    /**
     * Construct the Slice and bind data if available.
     *Slice matching
     */
    override fun onBindSlice(sliceUri: Uri): Slice? {
        val context = context ?: return null
        val activityAction = createActivityAction() ?: return null
        return if (sliceUri.path.equals("/hello") ) {
            Log.e("lz6","222")
            ListBuilder(context, sliceUri, ListBuilder.INFINITY)
                .addRow(
                    ListBuilder.RowBuilder()
                        .setTitle("Hello World")
                        .setPrimaryAction(activityAction)
                )
                .build()
        } else {
            // Error: Path not found.
            ListBuilder(context, sliceUri, ListBuilder.INFINITY)
                .addRow(
                    ListBuilder.RowBuilder()
                        .setTitle("URI not found.")
                        .setPrimaryAction(activityAction)
                )
                .build()
        }
    }

    //Slice click event
    private fun createActivityAction(): SliceAction? {
        return SliceAction.create(
            PendingIntent.getActivity(
                context, 0, Intent(context, SettingActivity::class.java), 0
            ),
            IconCompat.createWithResource(context, R.drawable.ic_launcher_foreground),
            ListBuilder.ICON_IMAGE,
            "Open App"
        )
    }

}

The above is the important code of slicingonBindSliceIt is used to match the URI. For example, if the URI is / Hello, a listbuilder will be displayed.createActivityActionMethod is in response to the slice click event.
You can see that in androidmanifest. XML, theproviderSo the principle of slicing is throughContentProviderForm, so that the external can access the provider, and then respond to related events or display related views.

OK, the next step is to test the slicing. The complete slicing URI isslice-content://{authorities}/{action}So here it isslice-content://com.panda.jetpackdemo.slice/hello

Where can I use it? An app is available for testingslice-viewer
After downloading, configure the URI, and you will be prompted to access the slice permission of the application. Click OK to see the slice content (note that it’s better to use the simulator to test, and the slice permission pop-up window may not pop up on the real machine). As shown in the figure below, click hello to jump to the front of uscreateActivityActionMethod.

slice.jpg

Official documents
Demo code address

Jetpack interface component

Animation and transition

When the interface changes in response to user actions, you should animate the layout transition. These animations provide feedback to the user about their actions and help keep the user focused on the interface.

Animation is also a cliche. When it comes to animation, we all think about itFrame animation, attribute animation, interpolation animationwait. Today we categorize some animations that you are familiar with and unfamiliar with from different angles.

1)Animating bitmaps

  • AnimationDrawable。 A series of paintable resources are loaded in succession to create animation. That is, attribute animation, which forms animation by setting the image of each frame.
  • AnimatedVectorDrawable。 Add animation effects to the properties of vector paintable objects, such as rotating or changing path data to make them into other pictures.

Among them, we mainly talk about the followingAnimatedVectorDrawable,VectorDrawableSVG is a scalable vector graphics, which uses XML code to describe the image. Here’s an example

//res-drawable-vectordrawable.xml

    
        
    


//res-animator-path_morph.xml


    


//res-animator-rotation.xml




//The above two animation files and one SVG image are used to generate the animated vector executable animation
//res-drawable-animatiorvectordrawable.xml


    
    



//Layout file activity_ vector.xml
    
        
//activity
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_vector)
        imageView.setOnClickListener {
            (imageView.drawable as Animatable).start()
        }
    }

OK, after running, click on the image, you will find a circle at the same time will change the animation, feel a bit like the earth rotation and revolution, interested students can achieve their own.

2)Animating interface visibility and actions
This part is mainly attribute animation. The principle of attribute animation is to update the attributes of view object in a period of time, and redraw the view with the changes of attributes. that isValueAnimatorAnd the technology derived from itViewPropertyAnimatorandObjectAnimator。 It is mainly applied to the basic animation of the control itself and custom view animation.

3)Action based on physical characteristics
This part can make the animation use the physical laws of the real world as much as possible to make it look more natural. For example, spring animation and throwing animation. Here’s an example of spring animation

def dynamicanimation_version = "1.0.0"
    implementation "androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version"

        val springForce = SpringForce(0.0f)
            . setdampingratio (0f) // set damping
            . setstiffness (0.5f) // set stiffness

        imageView2.setOnClickListener {
            SpringAnimation(imageView2, DynamicAnimation.TRANSLATION_Y).apply {
                spring = springForce
                Setstartvelocity (500F) // set speed
                start()
            }
        }

4)Animating layout changes
With the help of Android’s transition framework, you only need to provideStart layout and end layoutTo add animation effects to various movements in the interface. That is to say, we only need to provide two scenes, representing before and after the animation, and then the animation can be generated automatically. It should be noted that the two scenarios are actually on the same page.

//Layout of two scenes
    

        
    
    
//Scene one

    
    


//Scene 2


    
    


//Get the scene, start the animation between scenes, change from scene one to scene two

        val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
        val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
        val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this)

        titletv.setOnClickListener {
            TransitionManager.go(anotherScene)
        }

5)stayActivityAdd animation between
Just now, it was an animation between different scenes on the same page. What about different pages? What about the animation between different activities? It’s easier. Ha ha, you can use it in thestyleSet specific animation in, you can also set transition animation directly, you can also set transition animationShared controlComplete the transition animation.

//Define animation in styles
      @transition/explode
      @transition/explode
    

//Set transition animation, you can set shared control in two layouts, Android: transitionname = "robot"
        val intent = Intent(this, Activity2::class.java)
        // create the transition animation - the images in the layouts
        // of both activities are defined with android:transitionName="robot"
        val options = ActivityOptions
                .makeSceneTransitionAnimation(this, androidRobotView, "robot")
        // start the new activity
        startActivity(intent, options.toBundle())

Official documents
Demo code address

emoticon

Emoji compact support library is designed to make Android devices compatible with the latest emoticons in time. It prevents your application from displaying the missing emoticon characters in the form of @, which means that your device does not have the appropriate font to display text. By using Emoji compact support library, your application users can get the latest emoticons without waiting for Android OS update.

This module is the first mock exam to provide compatibility:EmojiCompatThrough the correspondingUnicode encodingTo recognize Emoji emoticons, they are replaced by Emoji spans, and finally Emoji emoticons are presented.

emoji.png

//Import library
implementation "com.android.support:support-emoji:28.0.0"

//Initialization
EmojiCompat.Config config = new BundledEmojiCompatConfig(this);
EmojiCompat.init(config);
       
//Replacement components

Official documents

Fragment

A fragment represents a part of a behavior or interface in a fragmentactivity. You can combine multiple fragments in one activity to build a multi pane interface and reuse a fragment in multiple activities. You can think of a fragment as a modular component of an activity, which has its own life cycle, can receive its own input events, and you can add or remove fragments when the activity is running (which is a bit like a “child activity” that can be reused in different activities).
A fragment must always be hosted in an activity and its lifecycle is directly affected by the host activity lifecycle.

I didn’t think of itfragmentIt’s also classified as jetpack. Ha ha, I’ll post one hereI think well written articlesAlthough the article is older, it can help you understand it betterFragment
Of course, the government also released the management framework of fragment——Navigation, interested in this article can search.

Official documents

layout

The layout defines the interface structure in the application (for example, the interface structure of an activity). All elements in the layout are built using the hierarchy of view and ViewGroup objects. View usually draws content that users can view and interact with. However, ViewGroup is an invisible container that defines the layout structure of view and other ViewGroup objects

The layout section focuses on two new layoutsConstraintLayoutandMotionLayout

  • ConstraintLayoutIt’s used a lot now. It’s really easy to use, especially for complex large layouts. It’s related to relative layout, but it’s more flexible. It can also be used with the layout editor of Android studio. There are many specific usages. Please pasteLink to official website
  • MotionLayoutIs a layout type that helps you manage motion and widget animation in your application. Motionlayout isConstraintLayoutBased on its rich layout functions, it is constructed.

thereforeMotionLayoutIt’s constraint layout with animation. Here’s an example

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta8'



    




//scene_01.xml

The operation effect is as follows:
motionlayout.gif

Mainly throughapp:layoutDescription="@xml/scene_01"Set the animation scene, and then in the scene_ 01 you can set the start and end positions and animation attributes in the scene, and you can complete the animation setting. Isn’t it a little bitCustom viewWell, the key is to lay out an XML file! Why don’t you try?

Official documents
Demo code address

palette

Excellent visual design is the key to successful application, and color scheme is the main part of design. Palette library is a support library for extracting salient colors from images to help you create visually appealing applications.

Unexpectedly, Android also has an official palette libraryPalette。 So what exactly can this palette do? It is mainly used to analyze the imagesColor characteristics。 For example, dark color, light color, bright color, soft color, text color, main tone, etc.

implementation 'com.android.support:palette-v7:28.0.0'

    //Synchronous analysis of images and obtain examples
    fun createPaletteSync(bitmap: Bitmap): Palette = Palette.from(bitmap).generate()

   //Asynchronously analyze the image and get the instance
    fun createPaletteAsync(bitmap: Bitmap) {
        Palette.from(bitmap).generate { palette ->
            // Use generated instance
        val mutedColor = palette!!.getMutedColor(Color.BLUE)
        //Main tone
        val rgb: Int? = palette?.vibrantSwatch?.rgb
        //Text color
        val bodyTextColor: Int? = palette?.vibrantSwatch?.bodyTextColor
        //Color of the title
        val titleTextColor: Int? = palette?.vibrantSwatch?.titleTextColor 
        }
    }

Official documents
Demo code address

summary

Finally, it’s over. You should be full, ha ha.
I hope this article can make you familiarJetpackI want to know more about you.
Of course, this is far from enough. In my opinion, this article is more like an examplePopular science articlesIt just tells you what members of the jetpack family are and what their use is. In the actual project, we also need to establishMVVMTo understand the design meaning of each component deeply and use the component flexibly. If you are interested, I will complete a MVVM project later, and record the whole process in the form of an article( There is also an official item in the annexJetpack practice project
Finally, I hope everyone can passjetpackBuild a high-quality, simple and high-quality project architecture, so as to liberate the productivity and become the coreEfficient people

enclosure:

The official demo of jetpack – Sunflower
All demos related to the article

My official account: code blocks, three questions every day, and detailed analysis, to help you become a offer harvester.


One of you is the motivation I share ❤️。

Recommended Today

VBS obtains the operating system and its version number

VBS obtains the operating system and its version number ? 1 2 3 4 5 6 7 8 9 10 11 12 ‘************************************** ‘*by r05e ‘* operating system and its version number ‘************************************** strComputer = “.” Set objWMIService = GetObject(“winmgmts:” _  & “{impersonationLevel=impersonate}!\\” & strComputer & “\root\cimv2”) Set colOperatingSystems = objWMIService.ExecQuery _  (“Select * from […]