It is not recommended (although it is possible) to call the method get of AsyncTask within the Main Thread, because in addition to blocking a task that should be asynchronous it causes a bad user experience.
With the Main Thread blocked in the onCreate, the user will be seeing that black screen until the task ends, with the risk of having a ANR.
You can see more details about this in my reply: How to use ksoap2 library.
The best way I consider for this kind of use is by using Loaders.
The Loaders appeared in API 11 (Android 3.0) as an adaptation of the AsyncTask for the life cycle both of Fragments how much of Activity.
For versions prior to API 11, you can use Support Library v4 that it makes the compatibility, simply extend FragmentActivity.
That means that the Loader is highly related to the life cycle of the Activity or of Fragment, and its management is done automatically by LoaderManager.
One important detail to consider using is that the first time you create the Loader it will run the processing. But in case the Activity will be destroyed, no matter if processing is over or not, it will always update the Activity correct. This means that on Monday Activity the LoaderManager will reuse the Loader previous avoiding unnecessary processing.
To use a Loader, I will consider using the Support Library, but the 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
}
}
Like you made her APIConnect as an inner class of Activity and non-static, the APIConnect implicitly has a reference to the Activity, so just call the methods that update the UI there in the method onPostExecute.
If the APIConnect whether external or static, would have to use some standard to upgrade, whether using Observer or with a reference to the Activity.
In your case, a sketch 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 method setTextInActivity may be stated in its Activity, that the APIConnect will have access.
Using a Inner Class to AsyncTask has an implicit reference to the Activity, what is bad thinking about the life cycle of Activity, which causes a Memory Leak and given that the Activity is a very large object, can cause long-term problems.
The Memory Leak is caused as follows:
- One
AsyncTask is initiated (having the implicit reference to the Activity).
- In the meantime, before the end of the
AsyncTask, to Activity is destroyed. Generating a new Activity.
- While the
AsyncTask not end, the Activity destroyed will not be collected by Garbage Collector, keeping a heavy and unnecessary object in memory. And furthermore, when the AsyncTask finish, the old Activity is who will be updated, can cause several errors, since it has already been destroyed.
A simple solution would be to create a subclass of AsyncTask external and use the standard Observer to update the UI. Remembering to cancel the AsyncTask and remove the reference from Listener when the Activity is destroyed.
Class APIConnect
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 Activity
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:
- http://developer.android.com/guide/components/loaders.html
- http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html
Raphael, I don’t recommend calling the method
getofAsyncTaskwithin theonCreate. The methodgetwill block theMain Threadwaiting for the result, which will cause a bad user experience if this task takes long. This way will lock your app, with that black screen, until the end of your task. I recommend using the methodonPostExecuteto update the UI.– Wakim
How would that be in this case? I need the content returned by
System.APIRequest(APIAddress)can be accessible.– Rafael Alexandre
Like you made her
APIConnectas an inner class ofActivityand non-static, theAPIConnectimplicitly has a reference to theActivity, so just call the methods that update the UI there in the methodonPostExecute. If theAPIConnectwhether external or static, would have to use some standard to upgrade, whether usingObserveror with a reference to theActivity.– Wakim