Android AsyncTask Better way of accessing Activity Context - android

It took my quite a while to get this to work, but It's clearly not best practice. In short, I need to show a dialog when my AsyncTask finishes, but getApplicationContext() does not work, neither does passing it as a parameter when creating the AsyncTask. So I've declared a public variable for the context in my AsyncTask class and set it before I execute:
private OnClickListener clickLoadRefs = new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("H","Clicked Load Refs");
RefreshRefPoints refreshRefPoints = new RefreshRefPoints();
refreshRefPoints.myCtx=v.getContext();
refreshRefPoints.execute(v.getContext());
}
};
private class RefreshRefPoints extends AsyncTask<Context, Integer, Integer> {
private Integer nPoints=0;
public Context myCtx;
private ProgressDialog pd;
protected Integer doInBackground(Context... ctx) {
Log.d("H","doInBackground()");
dbHelper.clearRefPoints();
requestRefPoints();
nPoints = parseRefPointsCSV();
return nPoints;
}
protected void onProgressUpdate(Integer... progress) {
}
protected void onPreExecute()
{
pd = ProgressDialog.show(myCtx, "Refreshing Reference Points", "Loading...", true,false);
Log.d( "H", "onPreExecute()" );
}
protected void onPostExecute(Integer result) {
pd.dismiss();
AlertDialog.Builder builder = new AlertDialog.Builder(myCtx);
builder.setTitle("Reference points refresh complete");
builder.setMessage(result+" records loaded");
builder.setPositiveButton("OK",null);
builder.show();
Log.d("H","onPostExecute()");
}...etc
Can anybody just show me the proper way of passing the context?
Thanks

Define a constructor method and pass context a parameter. It would be better.
Here what I meant:
private class RefreshRefPoints extends AsyncTask<Void, Integer, Integer> {
private Integer nPoints=0;
private Context myCtx;
private ProgressDialog pd;
public RefreshRefPoints(Context ctx){
// Now set context
this.myCtx = ctx;
}
}
That's all.

You may also use YourActivityName.this to refer to the activity Context. Because Activites extend Context, so its valid to do so.

Pass context in constructor as
private OnClickListener clickLoadRefs = new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("H","Clicked Load Refs");
RefreshRefPoints refreshRefPoints = new RefreshRefPoints(Your_ActivityName.this);
refreshRefPoints.myCtx=v.getContext();
refreshRefPoints.execute(v.getContext());
}
};
private class RefreshRefPoints extends AsyncTask<Void, Integer, Integer> {
private Integer nPoints=0;
public Context myCtx;
private ProgressDialog pd;
public RefreshRefPoints (Context ctx) {
myCtx = ctx;
}

Related

How to make a Toast in top-level AsyncTask class

I have a top-level Fetch class that extends AsyncTask and I have a MainActivity. I can't get the Fetch class to make a toast due to not having an instance of the MainActivity or its context. I have tried to pass the MainActivity to the Fetch class but it potentially leaks memory. I have tried setting a WeakReference instance of the context, but I can't make a toast from that.
I have read many other posts about this and most seem to have a static inner-class but mine is top-level and I would prefer to keep it that way.
The MainActivity creates an instance of Fetch and then executes it.
public class Fetch extends AsyncTask<Void, Integer, List<List<String>>>
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected List<List<String>> doInBackground(Context... params)
{
// run tasks
}
#Override
protected void onProgressUpdate(Integer... progress)
{
}
#Override
protected void onPostExecute(List<List<String>> result)
{
super.onPostExecute(result);
}
}
One way, in doInBackground:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(<your class name>.this, "hello", Toast.LENGTH_SHORT).show();
}
});
Or in onPostExecute(which invoked on the UI thread after the background computation finishes)
Toast.makeText(<your class name>.this, "hello", Toast.LENGTH_SHORT).show();
Edited: if you want to pass context to AsyncTask,you could do like:
public class MyAsyncTask extends AsyncTask<Void, Integer, List<List<String>>>
private final Context mContext;
public MyAsyncTask(final Context context) {
mContext = context;
}
}
And in MainActivity :
final MyAsyncTask task = new MyAsyncTask(getApplicationContext());
task.execute();
Edited again:
I tested WeakReference successfully.
public class ExampleAsyncTask extends AsyncTask {
private WeakReference<Context> contextRef;
public ExampleAsyncTask(Context context) {
contextRef = new WeakReference<>(context);
}
#Override
protected void onPostExecute(Object result) {
Context context = contextRef.get();
if (context != null) {
Toast.makeText(context, "hello", Toast.LENGTH_SHORT).show();
}
}
}
In MainActivity:
new ExampleAsyncTask(MainActivity.this).execute();
yeah,Don't be worry ,you can use the application context.may be as follow:
implement your App.getContext() in your Application.
and use it in your "Fetch" class and execute it in MainThread.

What is the context in Async task

I am running an Async(doInBackground) task in android.
I need to populate a progress bar for the task. So i am showing a progressDialog in onPreExecute,
The signature of ProgressDialog.show is Show(Context,Title,message)
But what would be the Context here?
#Override
protected void onPreExecute()
{
progress = ProgressDialog.show(???, "Loading", "Please Wait");
}
Create a constructor for your AsyncTask that takes a context as a parameter.
public class async extends AsyncTask<String, Integer, String>{
private Context context;
public async(Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
// Manipulate progress bar
}
Then use this to execute it
async mTask = new async(context).execute(params);
Context can be only of Activity ,Service or Brodcast not of any other class like Asyncktask.So put the Context of that Activity where you are using that AsyncTask class.
You can pass the activity context in the AsyncTask constructor to create the ProgressDialog :
MyAsyncTask constructor :
public MyAsyncTask(Context context){
progressDialog = new ProgressDialog(context, "Loading", "Please wait...");
}
onPreExecute method :
#Override
protected void onPreExecute()
{
progressDialog.show();
}
or store the context and create the dialog in the onPreExecute methods (but I prefer use the first way) :
public class MyAsyncTask extends AsyncTask{
private Context mContext;
public MyAsyncTask(Context context){
this.mContext = context;
}
#Override
protected void onPreExecute()
{
progress = ProgressDialog.show(this.mContext, "Loading", "Please Wait");
}
}
And in activity when you declare MyAsyncTask, you pass the activity:
MyAsyncTask asyncTask = new AsyncTask(this);
asynchTask.execute();
Add this function in your class
private Context getDialogContext() {
Context context;
if (getParent() != null)
context = getParent();
else
context = this;
return context;
}
In your asynctask use it as follows
#Override
protected void onPreExecute()
{
progress = ProgressDialog.show(getDialogContext(), "Loading", "Please Wait");
}
if you want to use only this as context then your Asynctask should be written as an inner class of a class which extends the Activity class. Then your context is the name of the class which extends Activity. Still it is better practice to pass the context like this:
ClassExtendingActivity.this
you can pass current activity view reference like MainActivity.this

Calling AsyncTask from the Activity

For this code snippet( I have excluded the doInBackground(), postExecute() etc. )
How should I pass the Activity parameter while calling the Async Task from the CheckServer Activity?
public class CheckServer extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
HttpTicket ticket= new HttpTicket(); //HOW IS THIS LINE DONE? WHAT PARAM SHOULD BE PASSED?
}
#SuppressWarnings("unused")
private class HttpTicket extends AsyncTask<String, String, String>
{
private Activity activity;
private ProgressDialog dialog;
public HttpTicket(Activity activity) {
this.activity = activity;
}
You can sipmly do
HttpTicket mHttpTicket = new HttpTicket(this);
mHttpTicket.execute();
You could also delete the constructor, and just pass it to OnPreExecute as param. Then you give it in execute(this);
In your Activity onCreate()
HttpTicket ticket= new HttpTicket(Activity.this);
//passing context to the asynctask constructor
ticket.execute();
//call execute to laod asynctask
Define asynctask as below
private class HttpTicket extends AsyncTask<String, String, String>
{
private Activity activity;
private ProgressDialog dialog;
public HttpTicket(Activity activity) {
this.activity = activity;
dialog = new ProgressDialog(activity);
dialog.setTitle("Wait...");
}
protected void onPreExecute()
{
dialog.show();
}
protected String doInBackground(String params)
{
//background opearation
return "string";
}
protected void onPostExecute(String result)
{
dialog.dismiss();
//update ui
}
}

Is it possible to pass a fragment in a constructor?

I am trying to pass my Fragment to an ASyncTask class so that I can update a widget or two in the fragment once the task completes. Here's what I'm dealing with:
public class LoginFragment extends Fragment {
Button loginButton;
TextView loginErrorMsg;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.loginfragment, container, false);
}
public OnClickListener loginListener = new OnClickListener() {
#Override
public void onClick(View v) {
Log.v("LoginF", "onclick");
ProgressDialog progressDialog = new ProgressDialog(getActivity());
progressDialog.setMessage("Logging in...");
LoginTask loginTask = new LoginTask((Polling) getActivity(), progressDialog);
loginTask.execute();
}
};
And the LoginTask:
public class LoginTask extends AsyncTask<String, Void, Integer> {
private ProgressDialog progressDialog;
private Polling activity;
private int id = -1;
private JSONParser jsonParser;
private static String loginURL = "http://davidjkelley.net/android_api/";
private static String registerURL = "http://davidjkelley.net/android_api/";
private static String KEY_SUCCESS = "success";
private static String KEY_ERROR = "error";
private static String KEY_ERROR_MSG = "error_msg";
private static String KEY_UID = "uid";
private static String KEY_NAME = "name";
private static String KEY_EMAIL = "email";
private static String KEY_CREATED_AT = "created_at";
TextView loginErrorMsg = (EditText)activity.findViewById(R.id.loginErrorMsg);
EditText userName = (EditText)activity.findViewById(R.id.emailEditText);
EditText passwordEdit = (EditText)activity.findViewById(R.id.passEditText);
public LoginTask(Polling activity, ProgressDialog progressDialog)
{
this.activity = activity;
this.progressDialog = progressDialog;
}
So I would like to add a third parameter to the constructor of LoginTask, essentially an instance of my LoginFragment. My goal is to update either a TextView or put up a Toast on the screen to clarify whether login succeeds or fails: as right now, the user has no way of telling how the login proceeded. Ideas?
As curious says you don't want to be passing Fragments around (they have a 'link' to the activity which is a context and passing contexts is baaad)
You want to pass a small object that can help you call back from your Task to your Fragment.
I would also use an interface. Here's my example:
Fragment:
public class LoginFragment extends Fragment implements OnClickListener, OnLoginListener{
Button loginButton;
TextView loginErrorMsg;
private ProgressDialog progressDialog;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
progressDialog = new ProgressDialog(activity);
progressDialog.setMessage("Logging in...");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_login, container, false);
loginButton = v.findViewById(R.id.button);
loginButton.setOnClickListener(this);
return v;
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.button:
Log.v("LoginF", "onclick");
progressDialog.show();
LoginTask loginTask = new LoginTask(this);
loginTask.execute();
break;
default:
break;
}
}
#Override
public void onLoginSuccess() {
progressDialog.dismiss();
// Yayy
}
#Override
public void onLoginFailure() {
progressDialog.dismiss();
// Boo
}
}
The ASyncTask:
public class LoginTask extends AsyncTask<String, Void, Integer> {
private final OnLoginListener listener;
public interface OnLoginListener{
public void onLoginSuccess();
public void onLoginFailure();
}
public LoginTask(OnLoginListener listener) {
this.listener = listener;
}
#Override
protected Integer doInBackground(String... params) {
try{
// Something
} catch (SomeException e){
listener.onLoginFailure();
}
return null;
}
#Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
listener.onLoginSuccess();
}
}
If you get your head around interfaces your world will open up and your code will look less like the amazon jungle and more like a well organised garden ;-)
I suggest you use a Callback interface for this purpose. It is generally not a good idea to pass in UI-specific (actually, context-specific) objects to an AsyncTask.
Here's what I suggest. With this approach, you don't even need to pass in your Fragment around.
Disclaimer: I have not actually tried running this code - just typed it off the top of my head. So it may not even compile - it is just intended to be a guide.
interface LoginCallback{
void onLoginSuccess();
void onLoginFailure();
}
//onCreate code
TextView loginErrorMsg = (EditText)activity.findViewById(R.id.loginErrorMsg);
EditText userName = (EditText)activity.findViewById(R.id.emailEditText);
EditText passwordEdit = (EditText)activity.findViewById(R.id.passEditText);
LoginTask loginTask = new LoginTask(new LoginCallback(){
#Override
protected void onLoginSuccess(){
//Update UI
}
#Override
protected void onLoginFailure(){
//Update UI
}
});
loginTask.execute();
//LoginTask code.
public class LoginTask extends AsyncTask<String, Void, Integer> {
LoginCallback callback;
ProgressDialog progressDialog;
public LoginTask(LoginCallback callback){
this.callback = callback;
#Override protected void onPreExecute(){
progressDialog = new ProgressDialog(getActivity());
progressDialog.setMessage("Logging in...");
}
#Override
protected Integer doInBackground(String... params){
//Do you login logic here.
}
#Override
protected void onPostExecute(Integer result) {
progressDialog.dismiss();
if(loginSuccess){
callback.onLoginSuccess();
} else {
callback.onLoginFailure();
}
}
}
}

Finish the calling activity when AsyncTask completes

My calling activity:
public class Hello extends Activity {
public void onCreate(Bundle savedInstanceState) {
MyTask mt = new MyTask(this);
mt.execute();
}
Now In MyTask (an external class):
public class MyTask extends AsyncTask<Void, Void, Void> {
private Context mContext;
public MyTask(Context context) {
mContext = context;
}
//doinbackground, etc
protected void onPostExecute() {
mContext.finish();
}
Other things are working as expected if I remove mContext.finish() above.
But if I'm calling mContext.finish() , I'm getting an error: The method finish() is undefined for the type Context (Eclipse doesn't show finish() when I write mContext. so that suggests I'm using finish() wrongly.)
What do I need to do to finish the calling activity Hello after MyTask completes the task
((Activity)mContext).finish();
Would be the correct way to cast a Context to an Activity and call its finish() method. Not sure why you'd want to finish an Activity from an AsyncTask though
What you can try to do instead of calling context.finish(), why don't you do a callback interface like this:
public interface TaskCallback{
void done();
}
Then you implement this into your Activity
public Hello extends Activity implements TaskCallback{
.....BUNCH OF ACTIVITY CODE.....
public void onCreate(Bundle savedInstanceState) {
MyTask mt = new MyTask(this);
mt.execute();
}
public void done() {
finish();
}
}
And instead of having Context as a parameter you have TaskCallback
public class MyTask extends AsyncTask<Void, Void, Void> {
private TaskCallback mCallback;
public MyTask(TaskCallback callback) {
mCallback = callback;
}
//doinbackground, etc
protected void onPostExecute() {
mCallback.done();
}
There you go, it gives you more flexibility to custom each implementation.
I got the same situation, then I do as follows:
public class MyTask extends AsyncTask<Void, Void, Void> {
private Activity mActivity;
private Context mContext;
public MyTask(Activity activity) {
mActivity = activity;
mContext = mActivity.getApplicationContext();
}
//doinbackground, etc
protected void onPostExecute() {
mActivity.finish();
}
Hope it help :)
Define a method in your activity class like this:
public void FinishAfterAsyncTask()
{
this.finish();
}
And call this method from the OnPostExecute method of the AsynTask class.
You could create a new private AsyncTask extended from your public one.
In this private AsyncTask you have access to the Activity stuff and you can override the onPostExecute method to finish it.
Your truly AsyncTask
public class MyPublicAsyncTask extends AsyncTask<Void, Void, Void> {
Context context;
public GetHorariosAsyncTask(Context ctx){
context = ctx;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
// prepare yourself for an async work
}
#Override
protected Void doInBackground(Void... params) {
// Do yout cool async stuff
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// you're finish, let's tell to user
}
}
Your Activity with private AsyncTask
public class MyActivity extends Activity {
Activity mAct;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mAct = this;
}
private class MyPrivateAsyncTask extends MyPublicAsyncTask {
public MyPrivateAsyncTask(Context ctx) {
super(ctx);
// TODO Auto-generated constructor stub
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
// our async task is completed! let's take care of this activity
mAct.finish();
}
}
}
Can you try
this.finish()
Seems like its because of calling it using mContext that it says undefined.

Categories

Resources