Lesson 12: Running in the Background (based on Android Studio 3.2)

Posted by Lukeidiot on Fri, 10 May 2019 23:17:10 +0200

Now that we have some knowledge of UI elements and screens, we need to make them responsive. Response is not just about speed - how much work you can accomplish over time. What's more important is how fast the application is. When people say that the application responds, they usually mean that the application will not prevent them from doing what they are trying to do. It will not interfere with them. If you've ever used an application that's just frozen to click on a button, you can appreciate what we're talking about. It won't stop.
Imagine stopping like calling someone. When you dial, you will hear the bell and the sound and you will wait for the other person to answer it. The phone cannot continue unless the other party answers it. We can say that telephone is a kind of blocking operation, because things have to happen in order. You call, you call, another person picks up the phone, and you talk. Neither can happen at the same time. All steps involve some form of "waiting" - or computing terminology, blocking.

Long-running tasks
Users may be able to tolerate daily congestion, such as queuing to renew licenses or groceries, or waiting for someone to pick up the phone, etc. But they may be less tolerant when using your application. Even the Android platform won't tolerate your application if you spend too much time doing what it does: Windows Manager and Active Manager are responsive policemen. When a user clicks a button, or interacts with any view that triggers an event, your application doesn't have much time to do what it should do; in fact, it's only killed by the runtime in five seconds at most. By then, you will see the notorious ANR error (no response to the application). Think of it as Android's BSOD.
According to Android guidelines, applications can do a task in event handlers between 100 and 200 milliseconds - it's not a lot of time, so we really need to make sure that we don't do anything too crazy in event handlers. But it's easier said than done, and there are several situations where we don't have complete control over the event handlers we do inside. We can list several of them here.

  • When we read a file - our programs need to save data or read them at some point in time. File IO operations may be well known and sometimes unpredictable; you just don't know how big the file is. If it's too big, it may take more than 200 seconds to complete the task.
  • When we interact with a database - we interact with the database to provide commands for reading, updating, creating and deleting data. Like files, sometimes we may issue command data that returns a large number of files; processing these records may take some time.
  • When we interact with the network - when we get data, network sockets, we are governed by network conditions. If it's not crowded or disappointed, it's good for us. But it doesn't always go up and it's not always fast; if you write code event handlers that deal with the internal network, you run the risk of ANR.
  • When we use other people's code s - we rely more and more on API s to build our applications, and for good reason: they save us time. But we can't always know how these APIs are built and what kind of operations they operate under the hood (do you really always read the source code of all the APIs you use?)

So, what should we do to keep our applications from encountering ANR? Of course, we can't avoid the things listed above, because most modern (and useful) applications will need to do one or more (or all) of these things. Facts have proved that the answer is the context of things in operation. There are several ways to do this, but in this section, we'll look at how we run our code in AsyncTask.

1. Simulating a long-running task

When the user clicks on "Long Run Task", it will simulate a long run task, but all we do is calculate from 1 to 15; each scale counts takes 2 seconds. We actually hold the user hostage for at least 30 seconds, during which time he can't do much else in the application.

1. New project Async

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="317dp"
        android:gravity="center"
        android:text="Long running task"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        />

    <TextView
        android:id="@+id/textView"
        android:layout_width="184dp"
        android:layout_height="0dp"
        android:layout_marginBottom="55dp"
        android:layout_marginTop="34dp"
        android:gravity="center"
        android:text="TextView"
        android:textSize="18sp"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>
package com.example.administrator.async;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener{

    private String TAG;
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button b = (Button) findViewById(R.id.button);
        Button b2 = (Button) findViewById(R.id.button2);
        tv = (TextView) findViewById(R.id.textView);
        TAG = getClass().getSimpleName();
        b.setOnClickListener(this);
        b2.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                Log.i(TAG, "Clicked");
            }
        });
    }
    //This whole code block is designed to simulate a time-consuming activity inside an event handler
    public void onClick(View v) {
        int i = 0;
        while (i < 15) {
            try {
                //This will halt execution for 10 seconds
                Thread.sleep(2000);
                //Every 10 seconds, we write the value of i to the UI
                tv.setText(String.format("Value of i = %d", i));
                Log.i(TAG, String.format("value of i = %d", i++ ));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

2, test

This code won't go too far. If you click, it will soon encounter the ANR error "Long Run Tasks" button, and then click the other buttons. You'll notice you won't.
It can be clicked because UI threads are waiting for "long-running tasks to be completed" - user interfaces are no longer sensitive.

II. AsyncTask

The problem we encountered above is that when an event handler does something lengthy, the entire user interface freezes, and the user can't do much else - the user is blocked. AsyncTask aims to solve these problems. It aims to make the UI responsive even when performing operations that take a considerable amount of time.

AsyncTask, or asynchronous task, is a class provided by Android to deal with asynchronous tasks. Through this class, UI threads and background threads can communicate. Background threads perform asynchronous tasks and return the results to UI threads.

Why do you need to use asynchronous tasks?

We know that only UI threads in Android, that is, the main thread, can update the UI, while other threads can not directly operate the UI. This advantage is to ensure the stability and accuracy of the UI and avoid confusion caused by multiple threads operating the UI at the same time. But Android is a multi-threaded operating system, and we can never put all tasks in the main thread. Line implementations, such as network operations, file reads and other time-consuming operations, may cause blocking of later tasks if they are all put into the main thread. Android detects this blocking and throws an Application Not Responsed(ANR) error when the blocking time is too long. So we need to put these time-consuming operations into non-main threads to execute. This avoids Android's singleton. Thread model avoids ANR.

Why did AsyncTask come into being?

When it comes to asynchronous tasks, we can think of using threads and thread pools to implement them. Indeed, Android provides us with a mechanism for the main thread to communicate with other threads. But at the same time, Android also provides us with an encapsulated component, AsyncTask. With AsyncTask, we can easily implement asynchronous task processing. AsyncTask can update UI in sub-threads, and also encapsulate and simplify asynchronous operations. Using threads and thread pools to handle asynchronous tasks involves the synchronization and management of threads. When the thread ends, it also needs to use Handler to notify the main thread to update the UI. AsyncTask encapsulates all of these, which makes it easy for us to update the UI in sub-threads.

Constructing generic parameters of AsyncTask subclasses

AsyncTask < Params, Progress, Result > is an abstract class, usually used for inheritance. Inheritance of AsyncTask requires the following three generic parameters:

Params: The parameter type entered when the task is started. What information do you want to pass to the background thread? This is usually the UI element you want to update. When you call execute from it
MainActivity, you need to pass this parameter to AsyncTask. This parameter automatically enters the doInBackground method. In our example, this is a text view object. We want background threads to have access to this UI element

Progress: The type of progress value returned in background task execution. What type of information do you want background threads to return to the onProgressUpdate method so that you can specify how long-running state operates on users? In our example, we want to update the text attribute text view, so this is a String object

Result: The type of result returned after the background task is completed. Which data do you want to use to specify when the status of doInBackground completes the task? In our case, I just want it to return to the real world, so the third parameter is Boolean value.

 

The callback method of constructing AsyncTask subclass mainly includes the following methods:

doInBackground: Must be rewritten to asynchronously execute tasks to be done by background threads, and time-consuming operations will be done in this way.

onPreExecute: Called before performing background time-consuming operations, usually for initialization operations.

onPostExecute: When the doInBackground method is completed, the system automatically calls the method and passes the value returned by the doInBackground method into the method. Through this method, the UI is updated.

onProgressUpdate: When the publishProgress method is called in the doInBackground method to update the progress of task execution, this method is called. Through this method, we can know the progress of task completion.

The following figure describes the role of AsyncTask in resolving the above example.

  • MainActivity creates an AsyncTask object (we basically create a class that extends AsyncTask)
  • Call the execute method of AsyncTask; in this method, we pass the AsyncTask object to the UI element we want to update.
  • AsyncTask has a variety of lifecycle approaches, but the only way to cover mandatory callbacks is doInBackground () - we'll write the operations to be implemented
  • AsyncTask will create a background thread, but such a thread is transparent to us; we don't care about it, because AsyncTask will be the person who manages it.

In the doInBackground () method, we can call publishProgress () periodically. Each time we do this, the runtime will call AsyncTask's onProgressUpdate () method, which will be done in a thread-safe manner. In this approach, we can do some UI updates

Note: Threads are a series of instructions, just like the sequence of instructions we write in our method. However, threads execute in a special way: it executes in the background, so it does not block anything running in the foreground (UI threads). That's why we need to write instructions that take a long time to complete threads.

1. Let's modify the AsyncTask project. First, we need to create a new class that extends from AsyncTask.

File_ New_ Java class, create a new class for Worker, extend AsyncTask

package com.example.administrator.async;

import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;

//The AsyncTask is parameterized; it's a generic type, so we need to pass arguments to it.
//These parameters are <Params,Progress, Result>;

public class Worker extends AsyncTask<TextView, String, Boolean> {
    private String TAG;
/*
The text view is declared at the top of the class so can we access it from onProgressUpdate;
we can't define it yet because we will only get object reference to this text view when
doInBackground gets called
*/

    private TextView tv;

/*
This is the only method we are obliged to override.
Inside this is where we should put the program logic, which may take some time to complete
*/
    @Override
    protected Boolean doInBackground(TextView... textViews) {
 /*
 Now we can define the text view; it was already passed to us when MainActivitycalled the
execute()method. The parameter of this method is an array, but we know that we only passed
one UI object (the text view), so we get only the first element of the array. We can now store that
reference to TextView(tv) variable that we hoisted up in
*/
        tv = textViews[0];
        TAG = getClass().getSimpleName();
        int i = 0;
        while (i++ < 15) {
            try {
                Thread.sleep(2000);
                //On each tick, we will call publishProgress, so it can update the UI
                publishProgress(String.format("Value of i = %d", i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    //Use this method to communicate progress to the user
/*
This method will catch whatever values we passed to the publishProgressmethod. The
parameter of this method is, again, an array. And since we passed only one string to it, we'll
only get the first element and set its value as the text attribute of the text view object.
*/

    @Override
    protected void onProgressUpdate(String... values) {
        tv.setText(values[0]);
        Log.i(TAG, String.format(values[0]));

    }
}

2. We basically repositioned the time-consuming task in MainActivity and placed it in the Worker class. The next step is to update the code in MainActivity.

package com.example.administrator.async;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;



public class MainActivity extends AppCompatActivity
        implements View.OnClickListener{

    private String TAG;
    TextView tv;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button b = (Button) findViewById(R.id.button);
        Button b2 = (Button) findViewById(R.id.button2);
        tv = (TextView) findViewById(R.id.textView);
        TAG = getClass().getSimpleName();
        b.setOnClickListener(this);
        b2.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                Log.i(TAG, "Clicked");
            }
        });
    }

    /*
    //This whole code block is designed to simulate a time-consuming activity inside an event handler
    public void onClick(View v) {
        int i = 0;
        while (i < 15) {
            try {
                //This will halt execution for 10 seconds
                Thread.sleep(2000);
                //Every 10 seconds, we write the value of i to the UI
                tv.setText(String.format("Value of i = %d", i));
                Log.i(TAG, String.format("value of i = %d", i++ ));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    */

    public void onClick(View v) {
 /*
 Create an instance of AsyncTask Worker class. Note that the background execution of the
AsyncTask isn't started by merely creating an instance of it
*/
        Worker worker = new Worker();
/*
The execute method starts the background operation. In this method, we pass whatever we
want to update to the AsyncTask. Note that you can pass more than one UI element to the
execute method, since it will be passed as an array in the doInBackground method of the
AsyncTask
*/
        worker.execute(tv);
    }

}

 

Note: AsyncTask does not mean running very lengthy operations for about a few minutes. Usually, AsyncTaskis are used for operations that last only a few seconds. Anything more than that and Windows Manager / Activity Manager may still kill the application. For long-running operations, you need to use services, but this is beyond the scope of this lesson.

 

3, run ok

 

 

 

Topics: Mobile Android network Windows Database