It is not recommended (although it is possible) to call the get
method of AsyncTask
within Main Thread
, because in addition to blocking a task that should be asynchronous, it causes a bad experience for the user. >
With Main Thread
locked in onCreate
, the user will see that black screen until the task finishes, with the risk of having an ANR .
You can see more details about this in my answer: How to use library ksoap2 .
The best way I consider this type of usage is to use Loaders
.
Loaders
appeared in API 11 (Android 3.0) as an adaptation of the AsyncTask
API to the lifecycle of both Fragments
and Activity
.
For versions prior to API 11, you can use the Support Library v4
as it does the compatibility by simply extending FragmentActivity
.
This means that Loader
is highly related to the life cycle of Activity
or Fragment
, and its management is done automatically by LoaderManager
.
An important detail to consider using is that the first time you create Loader
it will perform the processing. But if Activity
is destroyed, it does not matter if the processing is finished or not, it will always update to Activity
correct. This means that in the second Activity
the LoaderManager
will reuse the Loader
previous avoiding unnecessary processing.
To use Loader
, I'll consider using Support Library
, but calls are similar.
Class APIConnectLoader
public class APIConnectLoader extends AsyncTaskLoader<String> {
String mResult;
String mAPIAddress;
public APIConnectLoader(Context context, String APIAddress) {
super(context);
mAPIAddress = APIAddress;
}
/****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/
@Override
public String loadInBackground() {
return System.APIRequest(mAPIAddress);
}
/********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/
@Override
public void deliverResult(String data) {
if(isReset()) {
releaseResources(data);
return;
}
String oldData = mResult;
mResult = data;
if(isStarted()) {
super.deliverResult(data);
}
if(oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/
@Override
protected void onStartLoading() {
if(mResult != null) {
deliverResult(mResult);
}
if (takeContentChanged() || mResult == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
@Override
public void stopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
public void onCanceled(String data) {
releaseResources(data);
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
releaseResources(mResult);
mResult = null;
}
@Override
protected void onStopLoading() {
cancelLoad();
}
protected void releaseResources(String data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
public void refresh() {
mResult = null;
onContentChanged();
}
}
Class MainActivity
public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<String> {
private static String APIAddress = "http://10.0.2.2/APIs/LOGINServer/server.php";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Inicia o Loader, ou recupera o Loader anterior caso exista
// O LoaderManager eh quem ira verificar a existencia de um Loader
// anterior
getSupportLoaderManager().initLoader(ID_DO_LOADER, null, this);
// Se nao usar o Support Library use o getLoaderManager ao inves
// do getSupportLoaderManager
}
@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
// Instancia o AsyncTaskLoader
return new APIConnectLoader(APIAddress);
}
@Override
public void onLoadFinished(Loader<String> loader, String data) {
// Atualizar UI de acordo com o resultado (data)
}
@Override
public void onLoaderReset(Loader<String> loader) {
// Nao precisa fazer nada no caso de uma String,
// Se fosse um cursor, teria que limpar recursos
// referentes ao cursor anterior
}
}
Since you made your APIConnect
as an inner class of Activity
and not static, APIConnect
implicitly has a reference to Activity
, so just call methods that update the UI there in method onPostExecute
.
If the APIConnect
was external or static, you would have to use some default to update, either using Observer
or saving a reference to Activity
.
In your case, an outline would be:
public class APIConnect extends AsyncTask<String, String, String> {
@Override
protected void onPreExecute() {}
@Override
protected String doInBackground(String... params) {
String content;
content = System.APIRequest(APIAddress);
Log.i("HTTP Server", content);
return content;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// Nesse momento podemos atualizar a UI,
// porque esse código esta sendo executado
// na Main Thread.
setTextInActivity(result);
// result é o valor de content do doInBackground
}
}
The setTextInActivity
method can be declared in its Activity
, that APIConnect
will have access.
Using a Inner Class
a AsyncTask
has an implicit reference to Activity
, which is bad considering the Activity
lifecycle, which causes a Memory Leak and since Activity
is a very large object, it can cause problems in the long run.
Memory Leak is caused as follows:
A AsyncTask
is started (having the implicit reference to Activity
).
In the meantime, before the end of AsyncTask
, Activity
is destroyed. Generating a new Activity
.
As long as AsyncTask
does not finish,% destroyed% will not be collected by Activity
, keeping a heavy and unnecessary object in memory. Also, when Garbage Collector
finishes, the old AsyncTask
is who will be updated, which can cause several errors, since it has already been destroyed.
A simple solution would be to create a subclass of the Activity
external and use the AsyncTask
pattern to update the Observer
. Remember to cancel UI
and remove the AsyncTask
reference when Listener
is destroyed.
Class Activity
public class APIConnect extends AsyncTask<String, String, String> {
private APIConnectListener mListener;
// doInBackground continua o mesmo.
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(mListener != null) {
mListener.updateUI(result);
}
}
@Override
protected void onCancelled () {
// Cancelar tudo que estiver fazendo.
// Remover a referência para o Listener, a fim de evitar memory leak
mListener = null;
}
// Getter e Setter do Listener
// Definicao da interface Observer
public static interface APIConnectListener {
public void updateUI(String result);
}
}
Class APIConnect
public class MainActivity extends Activity implements APIConnect.APIConnectListener {
private static String APIAddress = "http://10.0.2.2/APIs/LOGINServer/server.php";
APIConnect mAPIConnect;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAPIConnect = new APIConnect();
mAPIConnect.setAPIConnectListener(this);
mAPIConnect.execute(APIAddress);
}
@Override
public void onDestroy() {
// Cancela a AsyncTask e limpa a referência
mAPIConnect.cancel();
mAPIConnect = null;
}
@Override
public void updateUI(String result) {
// Atualiza a UI com o resultado da APIConnect
}
}
References:
link
link