The Road of Lower Android Engineers

Life Cycle of Activity

Return to Stack
Android is a taskTo manage an activity, a task is a collection of activities stored on a stack, also known as a return stack. A stack is a last-in-first-out data structure that, by default, places a new activity on the return stack and is at the top of the stack whenever we press the Back key or call finish().When a method destroys an activity, the Activity at the top of the stack will come out of the stack, the Activity at the top of the stack will be re-placed, and the system will always show the Activity at the top of the stack to the user.

Four states of Activtiy:
1 Running state
Activity is running when it is at the top of the stack. Recycling can result in a poor user experience (such as game flicker)

2 Pause state
When an Activity is not at the top of the stack but is still visible, it enters a pause state.Some activities may only occupy part of the screen, so the previous one is not completely covered, that is, it is not at the top of the stack and still visible. For example, dialog-based activities, although at the top of the stack, are still visible. Therefore, the system is not willing to recycle suspended activities.(Recycling visible Activities can be a bad experience for users). The system only recycles when memory is very low

3 Stop state
When an activity is no longer at the top of the stack and is completely invisible, the activity is stopped. For example, if the activity is completely covered by a new activity, the system maintains the corresponding state and member variables for the activity. However, once memory is needed elsewhere, the system is likely to recycle.(Similar to one-click cleanup on a mobile phone, only the Activity at the top of the stack is not recycled, and the stopped activity is recycled)

4 Destruction status
Activities that have been removed from the return stack become destroyed. Activities that are most likely to be recycled by the system ensure sufficient memory

Activity's life cycle
The Activity class defines seven callback methods that cover every part of the Activtiy lifecycle.

onCreate().This method has been overridden in each Activity and is called the first time an Activity is created. This method is generally used to initialize activities, such as loading layouts, binding events, and so on.
onStart().This method is called when invisibility becomes visible
onResume(). This method is called when the Activity is ready to interact with the user. After the call, the Activity must be at the top of the return stack and running
onPause(). This method is called when the system is ready to start or restore another Activity. Usually in this method, some consumed CPU resources are freed up and some critical data is saved, but this method must be executed quickly or the use of a new top-of-stack Activity will be affected. After the call, the Activity is no longer in the foreground.
onStop(), which is called when the Activity is completely invisible.
The difference with OnPause is that if the new Activity launched is a dialog-based Activity, the onPause method will execute and the onStop method will not execute, because onStop() is then completely invisible on behalf of the Activity.
onDestroy(), this method is called before the Activty is destroyed, after which the state of the Activity becomes destroyed.
onRestrat(). This method is called before the Activity changes from stop state to run state, that is, the Activity is restarted. (Calling this method is equivalent to returning to just calling the onCreate method. And onCreate is not called again because the Activity is not recreated)

Activities can be divided into three lifetimes:

Full lifetime: Activity experiences a full lifetime between onCreate() and onDestory() methods. Typically, an activity performs various initialization operations in the onCreate() method and frees memory in the onDestory method.
Operation.

Visible Lifetime: Activities experience a visible lifetime in the onStart() and onStop() methods, during which they are always visible to the user, even if they may not be able to interact with the user (not fully covered by the new Activity)You can reasonably manage those resources that are visible to users through these two methods, such as loading resources in onStart and releasing resources in onStop, so as to ensure that stopped activities do not consume too much memory.

Foreground lifetime: Activity experiences foreground lifetime in onResume() and onPause() methods. In foreground lifetime, activity is always running and can interact with users.

Create two sub-activities, a normal Activity, and a dialog-based Activity to experience the life cycle of the Activity:

//Modify activity_main.xml to add two buttons
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
     <Button
    android:id="@+id/startNormalActivity"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Start NormalActivity"/>

    <Button
        android:id="@+id/startDialogActivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start DialogActivity"/>
</LinearLayout>

//Modify the label of DialogActivity in AndroidManifest using the android:theme property
<activity android:name=".DialogActivity"
            android:theme="@style/Theme.AppCompat.Dialog">

        </activity>

//Modify MainActivity's code
class MainActivity : AppCompatActivity() {

     private val tag= "MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(tag,"onCreate")
        setContentView(R.layout.activity_main)
        startNormalActivity.setOnClickListener {
            val intent = Intent(this,NormalActivity::class.java)
            startActivity(intent)
        }
        startDialogActivity.setOnClickListener {
            val intent = Intent(this,DialogActivity::class.java)
            startActivity(intent)
        }
    }
    override fun onStart() {
        super.onStart()
        Log.d(tag,"onStart")
    }
    override fun onResume() {
        super.onResume()
        Log.d(tag,"onResume")
    }
    override fun onPause() {
        super.onPause()
        Log.d(tag,"onPause")
    }
    override fun onStop() {
        super.onStop()
        Log.d(tag,"onStop")
    }
    override fun onDestroy() {
        super.onDestroy()
        Log.d(tag,"onDestroy")
    }
    override fun onRestart() {
        super.onRestart()
        Log.d(tag,"onRestart")
    }
}

In AndroidManifest.xml, use the android:theme property to specify a theme for the current Activity. There are many built-in themes to choose from or customize your own. Here @style/Theme.AppCompat.Dialog is what we mean by using a dialog-based theme.

What if Activity is recycled?
Activate ActivityB on the basis of ActivityA. In case of insufficient memory, A is recycled by the system. At this time, if the user presses the Back key to return to ActivityA, it will display normally. However, instead of the onRestart method, the onCreate method is executed, in which case A is recreated because A has already been recycled.
The problem is that there may be temporary data and state in A, such as a text input box in A, which starts B after entering a piece of text. Then if A is not recycled due to insufficient system. When you go back to A through the Back key, you will find that the entered text is gone. This is because MainActivity has been recreated.
An onSaveInstanceState() callback method is provided in Activity, which ensures that an activity is called before it is recycled. The onSaveInstanceState() method carries a Bundle-type parameter and Bundle provides a series of methods to save data. For example, using Pu putStirng() Save the string, putInt saves the integer, and so on. Each save method passes in two parameters, the first one is the key, which is then used to get the value from the Bundle. The second parameter is what is actually saved.

//Override the onSaveInstanceState method in MainActivity to temporarily save data
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
        super.onSaveInstanceState(outState, outPersistentState)
        val tmpData =" My some tempdatas"
        outState.putString("data_key",tmpData)
    }

Saved data, where do you recover it? The onCreate() method that you've been using always has a Bundle-type parameter. This parameter is generally null. But if you save data through the onSaveInstanceState() method before the Activity is recycled by the system, this parameter will have all the previously saved data, and you just need to fetch the data through the value method again.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if(savedInstanceState!=null)
        {
            val temData= savedInstanceState.getString("data_key")
            Log.d(tag,"tempData is $temData")
        }
    }

When ActivityA is about to be recycled by the system, the data is saved in the Bundle by onSaveInstanceState(). This way, when A is returned from B, onCreate() of A is called again because a new A is created.Method. At this time, the Bundle is not empty, so the previously stored temporary values can be taken out of the Bundle. Note: A ctivityA to be created will call onCreate method again because A has been recycled by the system. If it is just a regular return by pressing the Back key, the onRestart() method will call onStart and onResume again, not onCreate()Method. This way the savedInstanceState Bundle value is still empty

Bundle holds data in a similar way to Intent. Bundle can also be used in conjunction with Intent. The data that needs to be passed is stored in the Bundle's object, and then the Bundle's object is stored in Intent. When the target Activity is reached, the Bundle is removed from Intent, and then the data is extracted from Bundle.

** In addition, when the mobile screen rotates, the Activity also undergoes a re-creation process, so temporary data of the Activity will be lost in this case. ** Although it can also be solved by the onSaveInstanceState() method, it is not recommended that there be a more elegant solution (ViewModel) for the case of horizontal and vertical screen rotation.

Startup mode of Activity

Activity has four startup modes, standard,sigleTop,sigleTask, and sigleInstance

standard
Standard is the default startup mode for activities, and all activities use this startup mode without explicitly specifying it. Android uses the return stack to manage activities. In standard mode, whenever a new set of Activties is started, it will be stacked on the return stack and placed at the top of the stack. For activities using standard mode,The system doesn't care if the Activity already exists on the return stack, and each time it starts, a new instance of the Activity is created.

//Modify the code in FirstActivity
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.first_layout)
        Log.d("FirstActivity",this.toString())
        button1.setOnClickListener {
            val intent =Intent (this,FirstActivity::class.java)
          startActivity(intent)
        }
    }

Starting FirstActivity on the basis of FirstActivity is logically meaningless, but the print message shows that every time you click a button, a new instance of FirstActivity is created, and there are also three instances of FirstActivity on the return stack, so you need to press the Back key three times to exit the program.

singleTop

SingleTop mode, when the Activity startup mode is specified as singleTop, when the Activity is started, it is found that the top of the stack that returns to the stack is already the Activity, then it is considered that it can be used directly and no new Activity instances will be created.

//Modify FirstActivity startup mode to singleTop
<activity android:name=".FirstActivity"
            android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Run the program and find that an instance of FristActivity has been created, but print information will not appear again no matter how many clicks it makes. Because FirstActivity is already at the top of the stack of return stack, when you want to start another FirstActivity, the Activity at the top of the stack will be used directly, so there will only be one instance of FistsActivity and you can exit the process by pressing the Back key once.Order.
However, it is important to note that when FirstActivity is not at the top of the stack, starting FirstActivity again creates a new instance.

//Modify the FirstActivity code so that it clicks the button to jump to SecondActivity
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.first_layout)
        Log.d("FirstActivity",this.toString())
        button1.setOnClickListener {
            val intent =Intent (this,SecondActivity::class.java)
          startActivity(intent)
        }
    }
    //Modify SecondActivity's code and click the button to jump back to FirstActivity
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        Log.d("SecondActivity",this.toString())
        button2.setOnClickListener {
            val intent = Intent(this,FirstActivity::class.java)
            startActivity(intent)
        }
    }

After running the program, it is found that the system has created two different instances of FirtstActivity. This is because when SecondActivity starts FirstActivity again, Active at the top of the stack is SecondActivity, so a new instance of FirstActivity is created. Pressing Back now returns to SecondActivity, pressing Back again returns to FirstActivity, and then Back again will exit the program.

singleTask

Using singleTop is a good solution to the problem of repeatedly creating a top-of-stack Activity, but if the Activity is not at the top of the stack, Activation in singleTop startup mode will create multiple instances of the Activity. How can you make an Activity exist with only one instance in the context of the entire application?
That is the singleTask startup mode. When an Activity's startup mode is specified as singleTask, each time the Activity is started, the system first checks the instance of the Activity in the return stack. If it finds that it already exists, use the instance directly and place it above (but not below) the Activity.All other activities are stacked together and a new Activity instance is created if it is not found

//Modify the startup mode of FirstActivity in AndroidManifest.xml file to singleTask
<activity android:name=".FirstActivity"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
  
 //Override onRestart() method in FisrtActivity
  override fun onRestart() {
        super.onRestart()
        Log.d("FirstActivity","onRestart")
    }
  //Override onDestory() method in EcondActivity
   override fun onDestroy() {
        super.onDestroy()
        Log.d("SecondActivity","onDestory")
    }

When you start FirstActivity in the EcondActivity, you will find that there is already an instance of FirstActivity in the return stack, and it is under the SecondActivty. Then the SecondActivity will stack from the return stack, and the FirstActivity will become the top of the stack again. So onRestart of FirstActivityBoth the () method and the onDestory method of SecondActivity are executed. Now return to the only instance of FisrtActivity left in the stack and press the Back key to exit the program

singleInstance

Unlike the three startup modes above, the Activity specified as singleInstance mode enables a new return stack to manage this Activity. (If different taskAffinity is specified in singleTask mode, a new return stack is also started.)

What is the meaning of this startup mode? Assuming that one Activity in our program is allowed to be called by other programs, how can we share this instance of Activity with other programs?This cannot be done using the previous startup mode because each application has its own return stack, and the same Activity must create new instances when stacked in different return stacks. This can be solved using the singleInstance mode.

In this mode of singleInstance, there will be a separate return stack to manage the Activity. Whatever application accesses the Activity, it will share a return stack, which solves the problem of sharing Activity instances.

//Make SecondActivity's startup mode singleInstance
<activity android:name=".SecondActivity"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="com.example.activitytest.ACTION_START" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
//Print the return stack ID of FirstActivity
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.first_layout)
    Log.d("FirstActivity","Task id is $taskId")//This taskId actually calls the getTaskid method of the parent class
    button1.setOnClickListener {
         val intent =Intent (this,SecondActivity::class.java)
    startActivity(intent)
        }
    }
//Print the return stack ID of SecondActivity
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        Log.d("SecondActivity","Task id is $taskId")
        button2.setOnClickListener {
            val intent = Intent(this,ThirdActivity::class.java)
            startActivity(intent)
        }
    }
//Print the return stack ID of ThirdActivity
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("ThirdActivity","Task id is $taskId")
        setContentView(R.layout.activity_third)

    }

The Task id of SecondActivity is found to be different from FirstActivity and ThirdActivity, indicating that SecondActivity is stored in a separate return stack and that there is only one Activity in this stack. (Note that if more than one Activity is started by the set singleInstance mode, then each Activity will have its own separate return stack.)

Press the Back key to return, find that ThirdActivity unexpectedly returns to FirstActivity, press Back, return to SecondActivity, and press Back to exit the program. Because FirstActivity and ThirdActivity are stored on the same stack, when ThirdActivity is at the top of the stack, press Back,ThirdActivity exits the stack, then FirstActivity becomes the top stack Activity. Press Back again, then the current return stack is empty, so another top stack Activity, SecondActivity, is displayed. Finally, press Back, all return stacks are empty and the program exits.

Practical Skills of Activity

Some practical skills about Activity

Know which Activity is currently in?

This is a quick way to find out which Activity is corresponding to the interface when you take over other people's code.

Create a new BaseActivity class, which is not created the same way as normal Activity, because there is no need for BaseActivity to be registered in AndroidManifest.xml, so choose to create a normal Kotlin class. Then let BaseActivity inherit from AppcompatActivity and override the onCreate method.

open class BaseActivity :AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("BaseActivity",javaClass.simpleName)
    }
}

A line of logs has been added to the onCreate method to print the class name of the current instance. Note that javaClss in Kotlin means to get the class object of the current instance, which is equivalent to calling the getClass() method in java, while BaseActivity::class.java in Kotlin means to get the class object of the BaseActivity class, which is equivalent to calling BaseActivity.class in Java.
In the code above, we first get the lass object of the current instance, then call simpleName to get the Class name of the current instance.

We need to make BaseActivity the parent of all activities in an Activity project, prefix the BaseActivity class name with the keyword open so that BaseActivity can be inherited. How to modify the inheritance structure of all normal activities so that they no longer inherit from AppcompatActivity,Instead, it inherits from BaseActivity. Since BaseActivity inherits AppcompatActivity again, the existing functionality of all activities in all projects is unaffected, and they still inherit all the features of Activity.

This way, whenever we enter an Activity's interface, the class name of the Activity is printed so that we can always know which Activity the current interface corresponds to.

How do I exit the program anytime, anywhere?
Sometimes it is inconvenient to exit a program, such as the previous singleInstance code example, you have to press the Back key three times to exit. Pressing the Home key only suspends the program and does not exit the program. If our program needs to log off or exit, how can we do that? We need a solution to exit the program anytime, anywhere.
In fact, only a special collection is needed to manage all Activities, which can be combined with Base Activity.

Create a new single class, Activity Collector, as a collection of Activities

object ActivityCollector {
    private val activites = ArrayList<Activity>()

    fun addActivity(activity :Activity)
    {
        activites.add(activity)
    }
    fun removeActivity(activity: Activity)
    {
        activites.remove(activity)
    }
    fun finishAll()
    {
        for (activity in activites)
        {
            if(!activity.isFinishing)
            {
                activity.finish()
            }
        }
        activites.clear()
    }
}

A single class was used because only one Activity collection is required globally. Within the collection, an ArrayList is used to temporarily store the Activity.
Provides an add Activity () method for adding activities to an ArrayList;
Provides a removeActivity() method for removing an Activity from an ArrayList;
Finally, a finishAll() method is provided to destroy all activities stored in Arraylist.

Note that before destroying an Activity, we need to call activity.isFinishing to determine if it is destroying, because it is also possible to destroy the Activity by pressing the Back key, etc. If the Activity is not destroying, we call its finish() method to destroy it.

//Modify BaseActivity's code
open class BaseActivity :AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("BaseActivity",javaClass.simpleName)
        ActivityCollector.addActivity(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        ActivityCollector.removeActivity(this)
    }
}

//Exit the whole program by clicking the button in ThirdActivit y
class ThirdActivity : BaseActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_third)
        button3.setOnClickListener {
            ActivityCollector.finishAll()
        }
    }
}

The addActivity() method of the ActivityCollector is called in BaseActivy's onCreate method, indicating that the Activity currently being created is added to the collection. (Every time a new activity is created, it is added to the collection because the onCreate() method in the normal activity is called.)
Then override the onDestroy() method in BaseActivity and call the removeActivity() method of ActivityColler() to remove an Activity from the collection that is about to be destroyed. (This activity will be removed from the collection whenever an Activity calls the onDestroy() method)
No matter where you want to exit the program later, you just need to call the ActivityCollector.finishAll() method.

You can also add code that kills the current process after destroying all Activity code to ensure that the program exits completely, with the following code killing the process:

android.os.Process.killProcess(android.os.Process.myPid())

KillProcess() is used to kill a process. It receives a process id parameter and we can get the process id of the current program by mypid() method. It is important to note that killProcess() can only be used to kill processes of the current program, not other programs.

Best way to start an Activity?
The way to start an Activity is already familiar. First, build your current "intent" through Intent, then call the startActivity() or startActivityForResult() method to start an Activity, which can also be passed between activities if data is needed, or with Intent.
If two very important string parameters are required in SecondActivity, this is usually the case when starting an Activity that must be passed in:

val intent= Intent(this,SecondActivity::class.java)
            intent.putExtra("param1",data1)
            intent.putExtra("param2",data2)
            context.startActivity(intent)

This is correct, but it actually takes docking into account in re-project development. For example, the part you develop yourself needs to start SecondActivity, but it is not clear what data SecondActivity needs to transfer. There are usually only two ways to do this: one is to read the code of SecondActivity yourself, the other is to ask the person responsible for writing SecondActivity. A different way of writing will do itEasily solve this problem:

class SecondActivity : BaseActivity() {  
    ...
    companion object
    {
        fun actionStart(context: Context,data1:String, data2:String)
        {
            val intent= Intent(context,SecondActivity::class.java)
            intent.putExtra("param1",data1)
            intent.putExtra("param2",data2)
            context.startActivity(intent)
        }
    }
}

A new syntax structure, companion object, is used here, and an actionStart() method is defined in the companion object. This is because Kotlin stipulates that all methods defined in the companion object can use calls similar to static methods.
Look at the actionStart() method, in which the Intent is built, and all the data needed in the SecondActivity is passed in through the parameters of the actionStart() method, stored in the Intent, and finally called startActivity() to start the SecondActivity.(Note that the first parameter for building Intent here is the context, and when all corresponding Activities call this method, the context is sufficient. Just fill in this with a regular Activity, but consider it differently in the fragment)

It is important to be clear at a glance that all the data required by SecondActivity is reflected in the parameters of the method, and that you know exactly what data needs to be passed to start the SecondActivity. In addition, the code to start the Activity is simplified.

button1.setOnClickListener {
           SecondActivity.actionStart(this,"data1","data2")
        }

Develop a good habit of adding similar startup methods to each Activity you write, which makes it easier to start an Activity and saves time for asking questions.

Kotlin: Standard function with, run, apply

Standard functions in Kotlin refer to functions defined in the Standard.kt file, and any Kotlin code is free to call all standard functions.

with function

The with function takes two parameters: the first parameter can be any type of object, and the second parameter is a Lambda expression. The with function provides the context of the first parameter object in the Lambda expression and returns it as a return value using the last line of code in the Lambda expression.

val result = with(obj) {
//This is the context of obj
Return value of the'value'//with function
}

with function: It can make the code simpler when calling multiple methods on the same object in succession

//Try inserting a start word as the beginning and an end word as the end in a list of numbers
    val list = listOf("1","2","3")
    val builder= StringBuilder()

        builder.append("start\n")
        for (num in list)
        {
            builder.append(num).append("\n")
        }
    builder.append("end")
    val result =builder.toString()
    println(result)

The logic of the code is simple, it uses StringBuider to construct a string and print the result. However, we found that the method of the builder object has been called many times in a row, so we can consider using the with function to make the code simpler.

fun AddString()
    {
        val list = listOf("1","2","3")
        val result = with(StringBuilder())
        {
            append("start\n")
            for (num in list)
            {
                append(num).append("\n")
            }
            append("end")
            toString()
        }
        println(result)
    }

First, a StringBuilder object is passed to the first parameter of the with function, and then the context of the entire Lambda expression will be the StirngBuilder object. So instead of calling the builder.append() and builder.toString() methods like that in Lmabda expressions, we can call the append() and toString() methods directly.(that is, there is no need to call a method directly with an instantiated object; in the Lambda expression inside the with function, the method of the object passed in the parameter will be called directly)

run function

The usage and usage scenarios of the run function are very similar to those of the with function, except for syntax changes. The run function is usually not called directly, but rather on the basis of an object; the second run function value receives a Lambda parameter and provides the context for the calling object in the Lambda expression. Other aspects are consistent with the with the with function. Including the use of Lambda expressionsThe last line of code returned as a return value

val result = obj.run{
//This is the context of obj
Return value of the value//run function
}

Use the run function to implement the above code

val list = listOf("1","2","3")
        val result = StringBuilder().run {
            append("start\n")
            for (num in list)
            {
                append(num).append("\n")
            }
            append("end")
            toString()
        }
        println(result)

Overall, the change is minimal, except that the call to the with function and the pass-in StringBuilder object is changed to the run method that calls the StringBuilder object.

apply function
The apply function is similar to the run function in that it calls on an object, accepts only one Lambda parameter, and provides the context of the calling object in the Lmabda expression. However, the apply function cannot specify a return value, but automatically returns the calling object itself.

val result = obj.apply{
//This is the context of obj
}
//result==obj

val list = listOf("1","2","3")
        val result = StringBuilder().apply{
            append("start\n")
            for (num in list)
            {
                append(num).append("\n")
            }
            append("end")
        }
        println(result.toString())

Note that since the apply function cannot specify a return value, it only returns the calling object itself, so the result here is actually a StringBuilder object, so we need to call its toString() method at the end of printing.

Standard functions can be converted to each other in most cases, but you still need to know the difference so that when programming, you can make the best choice.

 val intent= Intent(context,SecondActivity::class.java)
            intent.putExtra("param1",data1)
            intent.putExtra("param2",data2)
            context.startActivity(intent)

For each parameter passed here, the intent.putExtra() method is called once, so it can be streamlined with standard functions.

 val intent= Intent(context,SecondActivity::class.java).apply {
                putExtra("param1",data1)
                putExtra("param2",data2)
            }
            context.startActivity(intent)

Since the context of the Lambda expression is an Intent object, the putExtra() method can be called directly.

Kotlin: Define static methods

Static methods, also known as class methods in some languages, refer to methods that can be invoked without creating instances. All mainstream programming languages support the feature of static methods.

Defining a static method in java is easy, just declare a static keyword on the method

public class Util
{
  	public static void doAction()
  	{
  	   System.out.prinln("do action");
  	}
}

This is a very simple tool class, and the doAction() method above is a static method. Calling a static method does not require creating an instance of the class, but can be called directly in the form of Util.doAction(). Therefore, a static method is well suited for writing a tool class because it is generally not necessary to create an instance and is essentially global.

Kotiln weakens the concept of static methods because Kotlin has better syntax than static methods, single-case classes.

In Kotlin, it is highly recommended to use a singleton class for functions such as tool classes, which are implemented in Kotlin as follows:

object Util {
    fun doAction()
    {
        println("do Action")
    }
}

The doAction() method here, although not static, can still be invoked by using Util.doAction, which is the convenience of the singleton class.

But using the singleton class notation makes all the methods in the whole class call like static methods, and what if we only want one method in the class to be called like a static method? At this point, we can use the companion object mentioned earlier:

class Util {
    fun doAction1()
    {
        println("do Action1")
    }
     companion object
     {
         fun doAction2()
         {
             println("do Action2")
         }
     }
}

This lets Util first become a normal class, then directly define the doAction1() method in the class and the doAction2() method in the companion object. There is a fundamental difference between the two methods. Because the doAction1() method must create an instance of Util before it can be called, but the doAciton2() method can be called directly through Util.doAction2().(similar to static methods)

However, the doAciton2() method is not static. The companion object keyword actually creates an adjoint class inside the Util class, and the doAction2() method is the instance method defined inside the adjoining class. Only Kotlin ensures that there is always only one adjoint class object in the Util class, so it calls Util.doAction2() The method, in effect, calls the doAction2() method of the Util class's companion object.
There are no keywords directly defining static methods in Kotlin, but some syntax features are provided to support calls similar to static methods.

If you want to define a truly static method, Kotlin provides two implementations, annotations and top-level methods.

First is the comment. The preceding singleton classes and companion objects are just syntactically mimics the way static methods are invoked. In fact, they are not static methods, so in the Java code pool, they are invoked as static methods and find that they do not exist. If we add @JvmStatic comment to the method in a singleton class or companion object, then KotliThe compiler compiles these methods into truly static methods.

companion object
     {
         @JvmStatic
         fun doAction2()
         {
             println("do Action2")
         }
     }

Note that the @JvmStatic annotation can only be added to methods in a singleton class or companion object, and if you try to add it to a common method, you will be prompted for a syntax error.

Since the doAction2() method has become a truly static method, it can now be invoked in either Kotlin or Java using the notation Util.doAciton2().

Top-level method

** Top-level method values are those that are not defined in any class. ** For example, the main() method you wrote earlier. Kotlin compiles all the top-level methods into static methods, so if you define a top-level method, it must be static.

To define a top-level method, first create a Kotlin file (Create Type Select File). Create a file named Helper.kt here. So any method we define in this file is a top-level method. For example, define a doSomething() method here.

fun doSomething()
{
    println("do Something")
}

If called in Kotlin code, the top-level method can be called directly anywhere by typing doSomething() without regard to the package name path or creating an instance.

** However, if the doSomething() method is not found in Java code, because there is no top-level method in Java, all methods must be defined in the class. ** Where exactly is the doSomething() method hidden? The Kotlin file we just created is called Helper.kt, and the Kotlin compiler automatically creates a Java class called HelperKt, Something()Methods are defined in the HelperKt class as static methods, so they can be called in Java using the notation of HelperK.doSomething().

public class JavaTest {
    public static void main(String[]args)
    {
        HelperKt.doSomething();
    }
}

Keywords: Android

Added by phu on Thu, 09 Sep 2021 22:46:41 +0300