The sudden end of the Flutter network request for Dio -- the application of CancelToken

Sweet date

Programmer Xiao Ming is very happy today, because today is the first anniversary of his love with his girlfriend. As we all know, it is difficult for programmers to find girlfriends. Xiao Ming is currently the only programmer in their office, and has become the envy of many programmers. Xiao Ming's work efficiency is also very high today. The keyboard is going to fly. His happy keyboard tapping is ringing in the whole office. When it was time to get off work, Xiao Ming had already submitted the code and rushed downstairs for a date - of course, he didn't forget to buy flowers.

My girlfriend is also very happy to see Xiao Ming. They have long planned the arrangement of this day. A young couple recalled their experiences this year and kept saying sweet words - they attracted the envy of many people with backpacks and plaid shirts.

The phone rang

At more than 9 p.m., at the end of their romantic dinner, Xiao Ming's girlfriend said, "let's go back!", Xiao Ming understood and was about to take his girlfriend out when the phone rang! The phone rang! The phone rang! Xiao Ming has a bad feeling in his heart. He takes back his hand and takes out his mobile phone from his pocket. Seeing the name displayed on the mobile phone, he's going to collapse! That was a call from their leader.
"Xiao Ming, hurry back to the company. The code you submitted today has a Bug!"
"Well, is it urgent?"
"Urgent! Don't you need to call you if it's not urgent?"
"Well... I'll go back right away."
Xiao Ming took a helpless look at his girlfriend.
"The leader asked me to go back and change the Bug!"
"Can't we go tomorrow? We're dating!" His girlfriend looked unhappy.
"No! Our programmers find that the Bug needs to be changed immediately!"
Xiao Ming finished, picked up his backpack and hurried to the company, leaving his girlfriend standing there alone. Xiao Ming didn't hear a sentence from his girlfriend: "I don't think we are suitable..."

get down to business

The above story is very common in our daily life. Changing bugs is not the foundation for our programmers to survive? In fact, one thing is often interrupted, and the same is true in network requests. For example, just entering a page, the network request is still in progress, and then the user exits the page. In fact, the request at this time is meaningless. It's ok if it's a simple request, but it's good if you can cancel the request when downloading files and making a series of requests.

Dio provided a cancellation token (CancelToken) mechanism is used to cancel outstanding requests. Each request can carry a CancelToken object. When the cancel method of CancelToken is called, it will notify the request to stop the current request, so as to interrupt the request. This is like Xiaoming being called by the leader to change the Bug when he is dating. His appointment is equivalent to It's gone!

Use of CancelToken

Let's take a look at a simple example. First, the optional parameter cancelToken is added to various methods of our HttpUtil class, and the corresponding prompt is displayed after the cancellation is detected.

static Future sendRequest(HttpMethod method, String url,
      {Map<String, dynamic> queryParams,
      dynamic data,
      CancelToken cancelToken}) async {
    try {
      //... Omit request code
    } on DioError catch (e) {
      // Check whether the error is caused by the cancellation request. If it is, print the cancellation reminder
      if (CancelToken.isCancel(e)) {
        EasyLoading.showInfo('The leader told you to go back and change Bug la');
      } else {
        EasyLoading.showError(e.message);
      }
    } on Exception catch (e) {
      EasyLoading.showError(e.toString());
    }

    return null;
  }

Then we simulate a cancellation request. What we requested is the article list of Nuggets' personal home page. First, we created a CancelToken object, and then passed it to the corresponding request. After the request is made, we immediately call the cancel method to cancel the request. async and await cannot be used here. Because await will wait for the request to complete, the then method of Future (similar to Promise) is used here. After receiving the response, we judge whether the request is cancelled according to the isCancelled attribute of the CancelToken object, and update the status variable _hasBug.

void _bugHappened() {
  CancelToken token = CancelToken();
  JuejinService.listArticles('70787819648695', cancelToken: token)
      .then((value) => {
            setState(() {
              _hasBug = token.isCancelled;
            })
          })
      .onError((error, stackTrace) => {print(error.toString())});

  token.cancel();
}

_ hasBug is used to control the interface display, indicating whether there is a Bug. If there is no Bug, Xiao Ming can continue dating. If there is a Bug, Xiao Ming has to go back to the company to change the Bug. We use a button to trigger the Bug event.

class _AppointmentPageState extends State<AppointmentPage> {
  bool _hasBug = false;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_hasBug ? 'blamed Bug!' : 'Dating...',
            style: Theme.of(context).textTheme.headline4),
      ),
      body: Container(
        child: Center(
          child: Image.asset(_hasBug ? 'images/bug.png' : 'images/dating.png'),
        ),
      ),
      floatingActionButton: IconButton(
        icon: Icon(Icons.call),
        onPressed: () {
          _bugHappened();
        },
      ),
    );
  }
  
  //...
}

Operation results

The running results are shown in the figure below. You can see that after clicking the button, the request is cancelled - Xiaoming has to go back and change the Bug (10000 grass mud horses float in Xiaoming's heart)!

practical application

If we want to cancel the unfinished request before exiting the page, we can use CancelToken to cancel it. We can call the cancel method of CancelToken in the deactivate method of the State lifecycle function (which will be called before dispose). To demonstrate the effect, let's go to Github, a website with slow requests.
Because the CancelToken object is used in different methods, it needs to be defined as a member attribute. The complete code is as follows.

class _AppointmentPageState extends State<AppointmentPage> {
  bool _hasBug = false;
  CancelToken _token;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_hasBug ? 'blamed Bug!' : 'Dating...',
            style: Theme.of(context).textTheme.headline4),
      ),
      body: Container(
        child: Center(
          child: Image.asset(_hasBug ? 'images/bug.png' : 'images/dating.png'),
        ),
      ),
      floatingActionButton: IconButton(
        icon: Icon(Icons.call),
        onPressed: () {
          _bugHappened();
        },
      ),
    );
  }

  void _bugHappened() {
    _token = CancelToken();
    HttpUtil.get('https://www.github.com', cancelToken: _token)
        .then((value) => {
              if (mounted)
                {
                  setState(() {
                    _hasBug = _token.isCancelled;
                  })
                }
            })
        .onError((error, stackTrace) => {});
  }

  @override
  void deactivate() {
    if (_token != null) {
      _token.cancel('dispose');
    }
    super.deactivate();
  }
}

The business process is as follows:

  • After entering the interface, click the phone icon button at the bottom to start the request
  • Click to return to the interface_ Whether the token is empty. If not, call the cancel method to cancel the request. The cancel method can receive an optional parameter for the reason why the table name is cancelled.

If the network condition is not good and your hand speed is fast enough (the king's skill comes in handy), you can see that a reminder will be displayed after returning, indicating that our request has been cancelled.

It should be noted here that since we called the setState method in the then callback of the network request, this method cannot be called after dispose, otherwise it may lead to memory leakage. Therefore, before calling, we judge whether mounted is true. If yes, it means that it has not been disposed, and we can safely call the setState method.

Next, let's study the source code of Dio and see the implementation mechanism of CancelToken.

Postscript

Advise programmers, important words three times: please remember to turn off your appointment! Please remember to turn it off! Please remember to turn it off!

Keywords: iOS Android Flutter

Added by ben.hornshaw on Thu, 16 Dec 2021 17:52:06 +0200