What is the best way to pass a context to a nested class?

Asked

Viewed 949 times

2

I have a nested class that’s used for a new Thread and it has some Toast to present some information to the user depending on the error.
I tried to pass the context through the constructor, I already created a context variable in the main class but the error persists with the message:

br.com.minhaempresa.teste.Accountaccessactivity$Loginrunnable.run(Accountaccessactivity.java:97)

Code:

public class AccountAccessActivity extends Activity implements AccountAccess
{
private EditText account;
private EditText pass;
private Spinner spinner;
private ProgressBar progressBar;

private Context context;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_account_access);

    account = (EditText)findViewById(R.id.accountInput);
    pass = (EditText)findViewById(R.id.passInput);
    spinner = (Spinner)findViewById(R.id.accTypeSpinner);
    progressBar = (ProgressBar)findViewById(R.id.progressCircle);

    context = this;

    if(!InternetConnectionStatus.isOnline(context))
    {
        Toast.makeText(context ,R.string.noInternet, Toast.LENGTH_LONG).show();
    }
}

@Override
public void login(View v)
{
    LoginForm form = new LoginForm(account.getText(), pass.getText(), spinner.getSelectedItemPosition());

    Runnable runnable = new LoginRunnable(form, this);

    new Thread(runnable).start();
}

@Override
public void forgotPassword(View v)
{

}

@Override
public void newAccount(View v)
{

}

public class LoginRunnable implements Runnable
{
    private LoginForm form;

    public LoginRunnable(LoginForm form, Context context)
    {
        this.form = form;
    }

    @Override
    public void run()
    {
        try
        {
            UserDAO userDAO = new UserDAO(new ConnectionFactory().getConnection());

            userDAO.validadeLogin(form);
        }
        catch (SQLTimeoutException e)
        {
            Toast.makeText(context, R.string.timeLimitExceded, Toast.LENGTH_LONG).show();
        }
        catch (SQLException e)
        {
            Toast.makeText(context, R.string.erroUserOrPass, Toast.LENGTH_LONG).show();
        }
        catch (ClassNotFoundException e)
        {
            Toast.makeText(context, R.string.internalErro, Toast.LENGTH_LONG).show();
        }
    }
}

}

Line 97:

FATAL EXCEPTION: Thread-3642 Process: br.com.minhaempresa.db, PID: 7007
java.lang.Runtimeexception: Can’t create Handler Inside thread that has not called Looper.prepare()
at android.os.Handler. (Handler.java:200)
at android.os.Handler. (Handler.java:114)
at android.widget.Toast$TN. (Toast.java:372)
at android.widget.Toast. (Toast.java:105)
at android.widget.Toast.makeText(Toast.java:264)
at android.widget.Toast.makeText(Toast.java:313)
at br.com.minhaempresa.somdegustacao.Accountaccessactivity$Loginrunnable.run(Accountaccessactivity.java:98)
at java.lang.Thread.run(Thread.java:841)

What is the best way to pass Context to a nested class in this case?

  • 1

    You have omitted the main part from the error log: the exception.

2 answers

2


This has nothing to do with the way the context is past.

The reason for the error is that it is not allowed to access objects that use UI, as is the case Toast, in a Thread other than the Uithread(Mainthread).
If you withdraw the calls to Toast you will see that the error disappears.

There are several ways to solve this problem, one of them is to run the Toast using the method runOnUiThread() of Activity:

public static class LoginRunnable implements Runnable
{
    private LoginForm form;
    private WeakReference<Activity> weakActivity;

    public LoginRunnable(LoginForm form, Activity activity)
    {
        this.form = form;
        weakActivity = new WeakReference<Activity>(activity);
    }

    @Override
    public void run()
    {
        try
        {
            UserDAO userDAO = new UserDAO(new ConnectionFactory().getConnection());

            userDAO.validadeLogin(form);
        }
        catch (SQLTimeoutException e)
        {
            showError(R.string.timeLimitExceded);
        }
        catch (SQLException e)
        {
            showError(R.string.erroUserOrPass);
        }
        catch (ClassNotFoundException e)
        {
            showError(R.string.internalErro);
        }
    }

    private void showError(final int messageId){
        final Activity activity = weakActivity.get();
        if(activity != null) {
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    String message = activity.getString(messageId);
                    Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
                }
            });
        }
    }
}

Note: Declare the class Loginrunnable static and use a Weakreference to retain the reference to Activity avoids any possible memory Leaks.

  • 1

    I finally understood the use of 'runOnUIThread'! I realized that the error was not the context after taking a better look at the exception, thanks!

  • Only that the runOnUIThread runs directly in the User’s main thread, and as the extension commented, if you don’t consider possible memory Leaks, use.

  • @Carlosbridi Note that your solution also has possible problems memory Leaks since it passes Activity to runnable.

  • @I agree, I just added that it runs on the main thread. thanks

  • @Carlosbridi What runs on the Main Thread is just Toast. That’s not the reason to provoke possible memory Leaks. The reason is that the class is not declared as static, thus having a reference to Activity.

  • @Carlosbridi I edited the answer in order to avoid memory Leaks

  • @ramaral Show, thanks for the teachings!

  • @ramaral did not quite understand this 'if(Activity != null)' passage, so I understood 'weakActivity.get()' can return me a null, so what’s the guarantee that my Toast will appear? If you can give me a basic explanation about Weakreference I would be grateful, or if you prefer I open a topic on that.

  • 1

    You should already know that Android can destroy and recreate your Activity in certain situations, for example when the device is rotated from vertical to horizontal position and vice versa. If the class Loginrunnable has a "strong reference" to Activity it can not be destroyed. When Android recreate the Activity will be two Activities in memory. If the reference to Activity, stored by Loginrunnable is weak(Weakreference), will allow the memory occupied by old Activity to be released when Android creates a new.

Show 4 more comments

0

I’ve also had problems with contexts in different classes, and it’s kind of hard to control that. What you can do is create a Listener interface with a method that returns exception to the class you invoked.

Example:

Interface:

public interface DoLoginListener{
    public void onException(String erroMsg) throws Exception;
}

Main class:

public class AccountAccessActivity extends Activity implements AccountAccess, DoLoginListener{

protected void onCreate(Bundle savedInstanceState){

...

}


public void onException(String erroMsg) throws Exception{
   Toast.makeText(this.getApplicationContext(), erroMsg, Toast.LENGTH_LONG).show();
}

}

Login class:

public class Loginrunnable Implements Runnable{ private Loginform form; Private dologinlistener callback;

public LoginRunnable(LoginForm form, DoLoginListener<String> cba)
{
    this.form = form;
    this.callback = cba;
}


@Override
public void run(){
    try{
        UserDAO userDAO = new UserDAO(new ConnectionFactory().getConnection());

        userDAO.validadeLogin(form);
    }
    catch (SQLTimeoutException e){
        callback.onException("Erro de SQLTimeOut");
        //ou podes passar o R.string.<teuErro> 
    }
    catch (SQLException e){
        callback.onException("Erro de SQLException");
    }
    catch (ClassNotFoundException e){
        callback.onException("Erro de ClassNotFoundException ");
    }
}

}

So you know which class you instanced and can display to the user in different ways.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.