Solution for handling asynchronous callbacks at the end of activity / fragment

Illegalargumentexception with headache

During Android development, UI related operations can only be executed in the main thread, otherwise the following exceptions will be thrown:

Of course, this is a basic common sense and is not the focus of this paper, but all subsequent discussions focus on this basic common sense. When developing Android applications, if all code is executed in the main thread, it is easy to appear anr, and Android has prohibited the execution of network requests in the main thread after 4.0, so it needs to deal with multi threads more or less. Whether using okhttp (retrofit), which is currently in full swing, or using outdated volley or Android async HTTP, they all support asynchronous requests. The request flow of these asynchronous requests is generally as follows:

The main thread initiates the request

->The network framework starts the worker thread to make network requests

->The worker thread gets the request result

->Return the request result to the main thread through the handler

->The main thread updates the UI and completes a network request

This process seems normal, but it actually implies crisis. The following crash is one example.

How did this collapse happen? The reason is very simple. The worker thread executes a network request. If the request takes too long to execute (due to network delay and other reasons), the activity or fragment no longer exists (destroyed), and the thread does not know this. At this time, after the request data results come back, the data is thrown to the main thread through the handler, In asynchronous callbacks, data updates or progress bar updates are usually performed, but the page no longer exists, so it GG does...

Current solutions

Some people will ask, why did the framework designer not consider this problem? In fact, they did think about it. At present, there are two solutions:

The essence of these two schemes is the same, that is, judge whether the activity is destroyed. If it is destroyed, either cancel the callback or do not execute the callback.

Cancel request at end of activity

Take volley as an example. In the requestqueue class, you can cancel the related network request through tag.

This tag is passed in through request. Settag (object) when the main thread invokes a network request. Tag can be any type. Generally speaking, the following two types can be used:

Context

Bring the context into the request as a tag. When the object holding the context is destroyed, you can notify the requesting thread to cancel. A typical usage scenario is to call request. Cancelall (this) in the ondestroy () method of the activity to cancel all requests related to the context. As for volley, it will judge whether the request is cancelled before the request is sent and after the data comes back.

However, as an Android Developer, you should know that the thread holding the context reference is dangerous. If the thread deadlock, the context reference will be held by the thread all the time, resulting in the context not being released, which is easy to cause memory leakage. If the instance of the context is an activity, the result is disastrous.

Is there a solution? have Thread holds context through weak reference. When the system memory is insufficient and GC is needed, objects holding weak references will be recycled first. However, there are still hidden dangers and the risk of memory leakage.

PATH

Since the problem of holding context is serious, we can uniquely identify a request according to the request path. The object initiating the request needs to maintain a list, record the current request path, and delete the request from the list through the path when the request comes back. When the activity ends but the request is not sent or returned, cancel all requests in the list bound to this activity.

It looks like a good plan, but how does it work? Each activity needs to maintain a list of requests issued by the current page, and then cancel the requests in the list when the activity ends. In the face of dozens or hundreds of activities in an application, such an implementation is undoubtedly painful.

Is there a solution? have This problem can be avoided through good design. A singleton path management class can be used to manage all requests. All issued requests need to be registered in this management class. Requests can be bound to the class name (class) of the currently issued page, so that all requests associated with the page can be cancelled when the page is destroyed.

Activity.isFinishing()

In the asynchronous request process, we notice the last two steps:

->Return the request result to the main thread through the handler

->The main thread updates the UI and completes a network request

In the last two steps, we can add judgment. If the page is destroyed, we can return directly without notifying the main thread to update the UI. In this way, we can solve the problem perfectly.

A similar example is as follows:

Although the problem has been solved, the trouble comes again. If only one person develops well, you need to always remember that when each network callback is executed, you need to judge activity. Isfinishing () in advance. However, multiple developers often work together to complete an app. If one developer fails to judge according to the regulations, the app may have the above hidden dangers. Moreover, it is unrealistic to modify so many network callbacks on the original network basic framework.

Is there a better solution? See below.

Asynchronous callback framework based on lifeful interface

Lifeful interface design

We define lifeful, an interface that does not depend on context or path.

In fact, we only need to let the class with life cycle (generally activity or fragment) implement this interface, and then judge whether the implementation class still exists through this interface, so as to decouple it from the context.

Next, define an interface generator, wrap the implementation class of lifeful interface through weak reference, and return the required relevant information.

Provide a default implementation of this interface:

Then determine whether the object's life cycle is determined through a static method:

Runnable with lifecycle

As for the asynchronous classes dealing with threads, only runnable (thread is also its subclass), so you only need to deal with runnable. Through the wrapper mode, we can judge whether the object still exists through the lifeful interface before processing the real runnable class. If it does not exist, it will be returned directly. For runnable:

Implementation class of lifeful

Finally, let's talk about the implementation classes of lifeful class, mainly including activity and fragment,

In addition to these two classes, if other classes have a life cycle or contain a reference to the life cycle, they can also implement a lifeful interface (such as view, through onattachedtowindow() and ondetachedtowindow()).

Asynchronous call with lifecycle

It is also convenient to call where asynchrony is needed.

summary

This paper focuses on the process of decoupling the processing mode of asynchronous threads corresponding to objects with life cycle in Android when they have been destroyed. By defining the lifeful interface, it realizes the method that does not depend on context or other objects that are easy to cause memory leakage, but can be bound with the life cycle of the object. Well, the above is the whole content of this article. I hope the content of this article can bring some help to Android developers. If you have any questions, you can leave a message.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>