Register now for Android Dev Summit 2019!

Conectar à rede

Para realizar operações de rede no seu aplicativo, o manifesto precisa incluir as seguintes permissões:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Criar uma comunicação de rede segura

Antes de adicionar a funcionalidade de rede ao seu aplicativo, você precisa garantir que os dados e as informações dele permaneçam seguros quando forem transmitidos pela rede. Para fazer isso, siga estas práticas recomendadas de segurança de rede:

Para mais informações sobre como aplicar princípios seguros de rede, consulte as dicas de segurança na rede do Android. Confira também o exemplo do Android NetworkConnect.

Escolher um cliente HTTP

A maioria dos aplicativos Android conectados à rede usa HTTP para enviar e receber dados. A plataforma Android inclui o cliente HttpsURLConnection, compatível com TLS, uploads e downloads de streaming, tempos limite configuráveis, IPv6 e pool de conexão.

Incluir operações de rede em um thread separado

Para evitar a criação de uma IU não responsiva, não execute operações de rede no thread da interface do usuário. Por padrão, o Android 3.0 (nível de API 11) e versões posteriores requerem a execução de operações de rede em um segmento diferente do thread da IU principal. Se você não fizer isso, NetworkOnMainThreadException será acionado.

O snippet de Activity a seguir usa um Fragment sem comando para encapsular operações de rede assíncronas. Mais adiante, você verá como a implementação de Fragment, NetworkFragment, faz isso. Sua Activity também deve implementar a interface DownloadCallback, permitindo que o Fragment faça uma chamada de retorno para a Activity caso ela precise obter status de conectividade ou enviar uma atualização de volta à IU.

Kotlin

class MainActivity : FragmentActivity(), DownloadCallback<String> {

    ...

    // Keep a reference to the NetworkFragment, which owns the AsyncTask object
    // that is used to execute network ops.
    private var networkFragment: NetworkFragment? = null

    // Boolean telling us whether a download is in progress, so we don't trigger overlapping
    // downloads with consecutive button clicks.
    private var downloading = false

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        networkFragment = NetworkFragment.getInstance(supportFragmentManager, "https://www.google.com")
    }

    private fun startDownload() {
        if (!downloading) {
            // Execute the async download.
            networkFragment?.apply {
                startDownload()
                downloading = true
            }
        }
    }
}

Java

public class MainActivity extends FragmentActivity implements DownloadCallback {

    ...

    // Keep a reference to the NetworkFragment, which owns the AsyncTask object
    // that is used to execute network ops.
    private NetworkFragment networkFragment;

    // Boolean telling us whether a download is in progress, so we don't trigger overlapping
    // downloads with consecutive button clicks.
    private boolean downloading = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        networkFragment = NetworkFragment.getInstance(getSupportFragmentManager(), "https://www.google.com");
    }

    private void startDownload() {
        if (!downloading && networkFragment != null) {
            // Execute the async download.
            networkFragment.startDownload();
            downloading = true;
        }
    }
}

No mínimo, sua interface DownloadCallback pode consistir no seguinte:

Kotlin

const val ERROR = -1
const val CONNECT_SUCCESS = 0
const val GET_INPUT_STREAM_SUCCESS = 1
const val PROCESS_INPUT_STREAM_IN_PROGRESS = 2
const val PROCESS_INPUT_STREAM_SUCCESS = 3

interface DownloadCallback<T> {

    /**
     * Indicates that the callback handler needs to update its appearance or information based on
     * the result of the task. Expected to be called from the main thread.
     */
    fun updateFromDownload(result: T?)

    /**
     * Get the device's active network status in the form of a NetworkInfo object.
     */
    fun getActiveNetworkInfo(): NetworkInfo

    /**
     * Indicate to callback handler any progress update.
     * @param progressCode must be one of the constants defined in DownloadCallback.Progress.
     * @param percentComplete must be 0-100.
     */
    fun onProgressUpdate(progressCode: Int, percentComplete: Int)

    /**
     * Indicates that the download operation has finished. This method is called even if the
     * download hasn't completed successfully.
     */
    fun finishDownloading()
}

Java

public interface DownloadCallback<T> {
    interface Progress {
        int ERROR = -1;
        int CONNECT_SUCCESS = 0;
        int GET_INPUT_STREAM_SUCCESS = 1;
        int PROCESS_INPUT_STREAM_IN_PROGRESS = 2;
        int PROCESS_INPUT_STREAM_SUCCESS = 3;
    }

    /**
     * Indicates that the callback handler needs to update its appearance or information based on
     * the result of the task. Expected to be called from the main thread.
     */
    void updateFromDownload(T result);

    /**
     * Get the device's active network status in the form of a NetworkInfo object.
     */
    NetworkInfo getActiveNetworkInfo();

    /**
     * Indicate to callback handler any progress update.
     * @param progressCode must be one of the constants defined in DownloadCallback.Progress.
     * @param percentComplete must be 0-100.
     */
    void onProgressUpdate(int progressCode, int percentComplete);

    /**
     * Indicates that the download operation has finished. This method is called even if the
     * download hasn't completed successfully.
     */
    void finishDownloading();
}

Agora adicione as seguintes implementações dos métodos da interface DownloadCallback à sua Activity:

Kotlin

override fun updateFromDownload(result: String?) {
    // Update your UI here based on result of download.
}

override fun getActiveNetworkInfo(): NetworkInfo {
    val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    return connectivityManager.activeNetworkInfo
}

override fun onProgressUpdate(progressCode: Int, percentComplete: Int) {
    when (progressCode) {
    // You can add UI behavior for progress updates here.
        ERROR -> {
        }
        CONNECT_SUCCESS -> {
        }
        GET_INPUT_STREAM_SUCCESS -> {
        }
        PROCESS_INPUT_STREAM_IN_PROGRESS -> {
        }
        PROCESS_INPUT_STREAM_SUCCESS -> {
        }
    }
}

override fun finishDownloading() {
    downloading = false
    networkFragment?.cancelDownload()
}

Java

@Override
public void updateFromDownload(String result) {
    // Update your UI here based on result of download.
}

@Override
public NetworkInfo getActiveNetworkInfo() {
    ConnectivityManager connectivityManager =
            (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    return networkInfo;
}

@Override
public void onProgressUpdate(int progressCode, int percentComplete) {
    switch(progressCode) {
        // You can add UI behavior for progress updates here.
        case Progress.ERROR:
            ...
            break;
        case Progress.CONNECT_SUCCESS:
            ...
            break;
        case Progress.GET_INPUT_STREAM_SUCCESS:
            ...
            break;
        case Progress.PROCESS_INPUT_STREAM_IN_PROGRESS:
            ...
            break;
        case Progress.PROCESS_INPUT_STREAM_SUCCESS:
            ...
            break;
    }
}

@Override
public void finishDownloading() {
    downloading = false;
    if (networkFragment != null) {
        networkFragment.cancelDownload();
    }
}

Implementar um Fragment sem comando para encapsular operações de rede

Como o NetworkFragment é executado no thread da IU por padrão, ele usa AsyncTask para executar as operações de rede em um thread de segundo plano. Esse Fragment é considerado sem comando porque não faz referência a nenhum elemento da IU. Em vez disso, ele é usado somente para encapsular a lógica e processar eventos de ciclo de vida, deixando o host da Activity para atualizar a IU.

Ao usar a subclasse de AsyncTask para executar operações de rede, tome cuidado para não criar um vazamento de memória no caso em que a Activity que é referenciada pelo AsyncTask é interrompida antes de AsyncTask terminar seu trabalho de segundo plano. Para garantir que isso não aconteça, o snippet a seguir limpa todas as referências à Activity no método onDetach() do Fragment.

Kotlin

private const val TAG = "NetworkFragment"
private const val URL_KEY = "UrlKey"

class NetworkFragment : Fragment() {
    private var mCallback: DownloadCallback<String>? = null
    private var downloadTask: DownloadTask? = null
    private var urlString: String? = null

    companion object {
        /**
         * Static initializer for NetworkFragment that sets the URL of the host it will be
         * downloading from.
         */
        fun getInstance(fragmentManager: FragmentManager, url: String): NetworkFragment {
            val networkFragment = NetworkFragment()
            val args = Bundle()
            args.putString(URL_KEY, url)
            networkFragment.arguments = args
            fragmentManager.beginTransaction().add(networkFragment, TAG).commit()
            return networkFragment
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        urlString = arguments?.getString(URL_KEY)
        ...
    }

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        // Host Activity will handle callbacks from task.
        mCallback = context as? DownloadCallback<String>
    }

    override fun onDetach() {
        super.onDetach()
        // Clear reference to host Activity to avoid memory leak.
        mCallback = null
    }

    override fun onDestroy() {
        // Cancel task when Fragment is destroyed.
        cancelDownload()
        super.onDestroy()
    }

    /**
     * Start non-blocking execution of DownloadTask.
     */
    fun startDownload() {
        cancelDownload()
        mCallback?.also { callback ->
            downloadTask = DownloadTask(callback).apply {
                execute(urlString)
            }
        }
    }

    /**
     * Cancel (and interrupt if necessary) any ongoing DownloadTask execution.
     */
    fun cancelDownload() {
        downloadTask?.cancel(true)
    }

    ...
}

Java

/**
 * Implementation of headless Fragment that runs an AsyncTask to fetch data from the network.
 */
public class NetworkFragment extends Fragment {
    public static final String TAG = "NetworkFragment";

    private static final String URL_KEY = "UrlKey";

    private DownloadCallback<String> mCallback;
    private DownloadTask downloadTask;
    private String urlString;

    /**
     * Static initializer for NetworkFragment that sets the URL of the host it will be downloading
     * from.
     */
    public static NetworkFragment getInstance(FragmentManager fragmentManager, String url) {
        NetworkFragment networkFragment = new NetworkFragment();
        Bundle args = new Bundle();
        args.putString(URL_KEY, url);
        networkFragment.setArguments(args);
        fragmentManager.beginTransaction().add(networkFragment, TAG).commit();
        return networkFragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        urlString = getArguments().getString(URL_KEY);
        ...
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Host Activity will handle callbacks from task.
        mCallback = (DownloadCallback<String>) context;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        // Clear reference to host Activity to avoid memory leak.
        mCallback = null;
    }

    @Override
    public void onDestroy() {
        // Cancel task when Fragment is destroyed.
        cancelDownload();
        super.onDestroy();
    }

    /**
     * Start non-blocking execution of DownloadTask.
     */
    public void startDownload() {
        cancelDownload();
        downloadTask = new DownloadTask(mCallback);
        downloadTask.execute(urlString);
    }

    /**
     * Cancel (and interrupt if necessary) any ongoing DownloadTask execution.
     */
    public void cancelDownload() {
        if (downloadTask != null) {
            downloadTask.cancel(true);
        }
    }

    ...
}

Agora implemente uma subclasse de AsyncTask como uma classe interna privada no seu Fragment:

Kotlin

/**
 * Implementation of AsyncTask designed to fetch data from the network.
 */
private class DownloadTask(callback: DownloadCallback<String>)
    : AsyncTask<String, Int, DownloadTask.Result>() {

    private var mCallback: DownloadCallback<String>? = null

    init {
        setCallback(callback)
    }

    internal fun setCallback(callback: DownloadCallback<String>) {
        mCallback = callback
    }

    /**
     * Wrapper class that serves as a union of a result value and an exception. When the download
     * task has completed, either the result value or exception can be a non-null value.
     * This allows you to pass exceptions to the UI thread that were thrown during doInBackground().
     */
    internal class Result {
        var mResultValue: String? = null
        var mException: Exception? = null

        constructor(resultValue: String) {
            mResultValue = resultValue
        }

        constructor(exception: Exception) {
            mException = exception
        }
    }

    /**
     * Cancel background network operation if we do not have network connectivity.
     */
    override fun onPreExecute() {
        if (mCallback != null) {
            val networkInfo = mCallback?.getActiveNetworkInfo()
            if (networkInfo?.isConnected == false
                    || networkInfo?.type != ConnectivityManager.TYPE_WIFI
                    && networkInfo?.type != ConnectivityManager.TYPE_MOBILE) {
                // If no connectivity, cancel task and update Callback with null data.
                mCallback?.updateFromDownload(null)
                cancel(true)
            }
        }
    }

    /**
     * Defines work to perform on the background thread.
     */
    override fun doInBackground(vararg urls: String): DownloadTask.Result? {
        var result: Result? = null
        if (!isCancelled && urls.isNotEmpty()) {
            val urlString = urls[0]
            result = try {
                val url = URL(urlString)
                val resultString = downloadUrl(url)
                if (resultString != null) {
                    Result(resultString)
                } else {
                    throw IOException("No response received.")
                }
            } catch (e: Exception) {
                Result(e)
            }

        }
        return result
    }

    /**
     * Updates the DownloadCallback with the result.
     */
    override fun onPostExecute(result: Result?) {
        mCallback?.apply {
            result?.mException?.also { exception ->
                updateFromDownload(exception.message)
                return
            }
            result?.mResultValue?.also { resultValue ->
                updateFromDownload(resultValue)
                return
            }
            finishDownloading()
        }
    }

    /**
     * Override to add special behavior for cancelled AsyncTask.
     */
    override fun onCancelled(result: Result) {}
}

Java

/**
 * Implementation of AsyncTask designed to fetch data from the network.
 */
private class DownloadTask extends AsyncTask<String, Integer, DownloadTask.Result> {

    private DownloadCallback<String> mCallback;

    DownloadTask(DownloadCallback<String> callback) {
        setCallback(callback);
    }

    void setCallback(DownloadCallback<String> callback) {
        mCallback = callback;
    }

     /**
     * Wrapper class that serves as a union of a result value and an exception. When the download
     * task has completed, either the result value or exception can be a non-null value.
     * This allows you to pass exceptions to the UI thread that were thrown during doInBackground().
     */
    static class Result {
        public String mResultValue;
        public Exception mException;
        public Result(String resultValue) {
            mResultValue = resultValue;
        }
        public Result(Exception exception) {
            mException = exception;
        }
    }

    /**
     * Cancel background network operation if we do not have network connectivity.
     */
    @Override
    protected void onPreExecute() {
        if (mCallback != null) {
            NetworkInfo networkInfo = mCallback.getActiveNetworkInfo();
            if (networkInfo == null || !networkInfo.isConnected() ||
                    (networkInfo.getType() != ConnectivityManager.TYPE_WIFI
                            && networkInfo.getType() != ConnectivityManager.TYPE_MOBILE)) {
                // If no connectivity, cancel task and update Callback with null data.
                mCallback.updateFromDownload(null);
                cancel(true);
            }
        }
    }

    /**
     * Defines work to perform on the background thread.
     */
    @Override
    protected DownloadTask.Result doInBackground(String... urls) {
        Result result = null;
        if (!isCancelled() && urls != null && urls.length > 0) {
            String urlString = urls[0];
            try {
                URL url = new URL(urlString);
                String resultString = downloadUrl(url);
                if (resultString != null) {
                    result = new Result(resultString);
                } else {
                    throw new IOException("No response received.");
                }
            } catch(Exception e) {
                result = new Result(e);
            }
        }
        return result;
    }

    /**
     * Updates the DownloadCallback with the result.
     */
    @Override
    protected void onPostExecute(Result result) {
        if (result != null && mCallback != null) {
            if (result.mException != null) {
                mCallback.updateFromDownload(result.mException.getMessage());
            } else if (result.mResultValue != null) {
                mCallback.updateFromDownload(result.mResultValue);
            }
            mCallback.finishDownloading();
        }
    }

    /**
     * Override to add special behavior for cancelled AsyncTask.
     */
    @Override
    protected void onCancelled(Result result) {
    }
}

Usar HttpsUrlConnection para buscar dados

No snippet acima, o método doInBackground() é executado em um thread de segundo plano e chama o método auxiliar downloadUrl(). O método downloadUrl() deve pegar o URL fornecido e usá-lo para executar uma solicitação HTTP GET. Uma vez que uma conexão tenha sido estabelecida, use o método getInputStream() para recuperar os dados com um InputStream. O snippet a seguir usa a API HttpsURLConnection para fazer isso:

Kotlin

/**
 * Given a URL, sets up a connection and gets the HTTP response body from the server.
 * If the network request is successful, it returns the response body in String form. Otherwise,
 * it will throw an IOException.
 */
@Throws(IOException::class)
private fun downloadUrl(url: URL): String? {
    var connection: HttpsURLConnection? = null
    return try {
        connection = (url.openConnection() as? HttpsURLConnection)
        connection?.run {
            // Timeout for reading InputStream arbitrarily set to 3000ms.
            readTimeout = 3000
            // Timeout for connection.connect() arbitrarily set to 3000ms.
            connectTimeout = 3000
            // For this use case, set HTTP method to GET.
            requestMethod = "GET"
            // Already true by default but setting just in case; needs to be true since this request
            // is carrying an input (response) body.
            doInput = true
            // Open communications link (network traffic occurs here).
            connect()
            publishProgress(CONNECT_SUCCESS)
            if (responseCode != HttpsURLConnection.HTTP_OK) {
                throw IOException("HTTP error code: $responseCode")
            }
            // Retrieve the response body as an InputStream.
            publishProgress(GET_INPUT_STREAM_SUCCESS, 0)
            inputStream?.let { stream ->
                // Converts Stream to String with max length of 500.
                readStream(stream, 500)
            }
        }
    } finally {
        // Close Stream and disconnect HTTPS connection.
        connection?.inputStream?.close()
        connection?.disconnect()
    }
}

Java

/**
 * Given a URL, sets up a connection and gets the HTTP response body from the server.
 * If the network request is successful, it returns the response body in String form. Otherwise,
 * it will throw an IOException.
 */
private String downloadUrl(URL url) throws IOException {
    InputStream stream = null;
    HttpsURLConnection connection = null;
    String result = null;
    try {
        connection = (HttpsURLConnection) url.openConnection();
        // Timeout for reading InputStream arbitrarily set to 3000ms.
        connection.setReadTimeout(3000);
        // Timeout for connection.connect() arbitrarily set to 3000ms.
        connection.setConnectTimeout(3000);
        // For this use case, set HTTP method to GET.
        connection.setRequestMethod("GET");
        // Already true by default but setting just in case; needs to be true since this request
        // is carrying an input (response) body.
        connection.setDoInput(true);
        // Open communications link (network traffic occurs here).
        connection.connect();
        publishProgress(DownloadCallback.Progress.CONNECT_SUCCESS);
        int responseCode = connection.getResponseCode();
        if (responseCode != HttpsURLConnection.HTTP_OK) {
            throw new IOException("HTTP error code: " + responseCode);
        }
        // Retrieve the response body as an InputStream.
        stream = connection.getInputStream();
        publishProgress(DownloadCallback.Progress.GET_INPUT_STREAM_SUCCESS, 0);
        if (stream != null) {
            // Converts Stream to String with max length of 500.
            result = readStream(stream, 500);
        }
    } finally {
        // Close Stream and disconnect HTTPS connection.
        if (stream != null) {
            stream.close();
        }
        if (connection != null) {
            connection.disconnect();
        }
    }
    return result;
}

Veja que o método getResponseCode() retorna o código de status da conexão. Essa é uma maneira útil de conseguir informações adicionais sobre a conexão. Um código de status de 200 indica que ação foi concluída.

Para ver mais detalhes sobre HttpsURLConnection, consulte o Exemplo do Android NetworkConnect.

Converter InputStream em uma string

Um InputStream é uma fonte legível de bytes. Ao gerar InputStream, é comum decodificá-lo ou convertê-lo em um tipo de dados de destino. Por exemplo, se fizesse o download de dados de imagens, você poderia decodificá-los e exibi-los assim:

Kotlin

val inputStream: InputStream? = null
...
val bitmap: Bitmap = BitmapFactory.decodeStream(inputStream)
findViewById<ImageView>(R.id.image_view)?.apply {
    setImageBitmap(bitmap)
}

Java

InputStream inputStream = null;
...
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setImageBitmap(bitmap);

No exemplo acima, InputStream representa o texto do corpo da resposta. É assim que você deve converter InputStream em uma string para que a Activity possa exibi-lo na IU:

Kotlin

/**
 * Converts the contents of an InputStream to a String.
 */
@Throws(IOException::class, UnsupportedEncodingException::class)
fun readStream(stream: InputStream, maxReadSize: Int): String? {
    val reader: Reader? = InputStreamReader(stream, "UTF-8")
    val rawBuffer = CharArray(maxReadSize)
    val buffer = StringBuffer()
    var readSize: Int = reader?.read(rawBuffer) ?: -1
    var maxReadBytes = maxReadSize
    while (readSize != -1 && maxReadBytes > 0) {
        if (readSize > maxReadBytes) {
            readSize = maxReadBytes
        }
        buffer.append(rawBuffer, 0, readSize)
        maxReadBytes -= readSize
        readSize = reader?.read(rawBuffer) ?: -1
    }
    return buffer.toString()
}

Java

/**
 * Converts the contents of an InputStream to a String.
 */
public String readStream(InputStream stream, int maxReadSize)
        throws IOException, UnsupportedEncodingException {
    Reader reader = null;
    reader = new InputStreamReader(stream, "UTF-8");
    char[] rawBuffer = new char[maxReadSize];
    int readSize;
    StringBuffer buffer = new StringBuffer();
    while (((readSize = reader.read(rawBuffer)) != -1) && maxReadSize > 0) {
        if (readSize > maxReadSize) {
            readSize = maxReadSize;
        }
        buffer.append(rawBuffer, 0, readSize);
        maxReadSize -= readSize;
    }
    return buffer.toString();
}

A sequência de eventos no código até agora é a seguinte:

  1. A Activity inicia um NetworkFragment e o transmite a um URL especificado.
  2. Quando um usuário aciona o método downloadData() da Activity, o NetworkFragment executa DownloadTask.
  3. O método AsyncTask onPreExecute() é executado primeiro (no thread de IU) e cancela a tarefa se o dispositivo não estiver conectado à Internet.
  4. Depois disso, o método AsyncTask doInBackground() é executado no segmento de segundo plano e chama o método downloadUrl().
  5. O método downloadUrl() pega uma string de URL como um parâmetro e usa um objeto HttpsURLConnection para buscar o conteúdo da Web como um InputStream.
  6. O InputStream é transmitido ao método readStream(), que converte o stream em uma string.
  7. Por último, quando o trabalho em segundo plano é concluído, o método AsyncTask de onPostExecute() é executado no thread da IU e usa a DownloadCallback para enviar o resultado de volta à interface de usuário como uma string.

Sobreviver às alterações de configuração

Até agora, você implementou uma Activity que executa uma operação de rede sem erros. Porém, se o usuário decidir alterar a configuração do dispositivo (por exemplo, girar a tela em 90 graus) enquanto doInBackground() estiver sendo executado no thread de segundo plano, a Activity se destruirá e se recriará. Isso fará com que onCreate() seja gerado novamente e um novo NetworkFragment seja referenciado (consulte o guia de alterações do tempo de execução). Assim, a AsyncTask ativa no NetworkFragment original terá uma DownloadCallback que referencia a Activity original que não poderá mais atualizar a IU. Dessa forma, o trabalho de rede feito no thread de segundo plano terá sido desperdiçado.

Para impedir falhas depois dessas alterações de configuração, você precisa manter seu Fragment original e garantir que a Activity reconstruída o referencie. Para fazer isso, faça as seguintes modificações no seu código:

Primeiro, seu NetworkFragment deve chamar setRetainInstance(true) no método onCreate():

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    // Retain this Fragment across configuration changes in the host Activity.
    retainInstance = true
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    // Retain this Fragment across configuration changes in the host Activity.
    setRetainInstance(true);
}

Depois, altere como você inicializa o NetworkFragment no método estático getInstance():

Kotlin

companion object {
    fun getInstance(fragmentManager: FragmentManager, url: String): NetworkFragment {
        // Recover NetworkFragment in case we are re-creating the Activity due to a config change.
        // This is necessary because NetworkFragment might have a task that began running before
        // the config change occurred and has not finished yet.
        // The NetworkFragment is recoverable because it calls setRetainInstance(true).
        var networkFragment = fragmentManager.findFragmentByTag(TAG) as? NetworkFragment
        if (networkFragment == null) {
            networkFragment = NetworkFragment()
            networkFragment.arguments = Bundle().apply {
                putString(URL_KEY, url)
            }
            fragmentManager.beginTransaction()
                    .add(networkFragment, TAG)
                    .commit()
        }
        return networkFragment
    }
}

Java

public static NetworkFragment getInstance(FragmentManager fragmentManager, String url) {
    // Recover NetworkFragment in case we are re-creating the Activity due to a config change.
    // This is necessary because NetworkFragment might have a task that began running before
    // the config change occurred and has not finished yet.
    // The NetworkFragment is recoverable because it calls setRetainInstance(true).
    NetworkFragment networkFragment = (NetworkFragment) fragmentManager
            .findFragmentByTag(NetworkFragment.TAG);
    if (networkFragment == null) {
        networkFragment = new NetworkFragment();
        Bundle args = new Bundle();
        args.putString(URL_KEY, url);
        networkFragment.setArguments(args);
        fragmentManager.beginTransaction().add(networkFragment, TAG).commit();
    }
    return networkFragment;
}

Agora seu aplicativo pode obter dados da Internet!

Observe que há várias outras ferramentas de gerenciamento de threads em segundo plano que podem ajudar você a atingir esse mesmo objetivo. À medida que seu aplicativo fica mais complexo, talvez você ache essas outras ferramentas mais adequadas. Em vez de usar AsyncTask, conheça em detalhes as opções IntentService e AsyncTaskLoader.

Para saber mais sobre isso, consulte estes guias relacionados: