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

2

I have a nested class that is used for a new Thread and has some Toast to display some user information depending on the error. I already 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.mycompany.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.mycompany.db, PID: 7007
                                                                                   java.lang.RuntimeException: Can not 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 www.yourweb.com.support.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?

    
asked by anonymous 09.11.2016 / 00:45

2 answers

2

This does not have to do with how context is passed.

The reason for the error is that it is not allowed to access objects that use UI , such as Toast , a Thread > other than the UIThread (MainThread) . If you remove calls to Toast you will see that the error disappears.

There are a number of ways to resolve this problem, one of which is to run Toast using the%

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: Declaring the class LoginRunnable runOnUiThread() and using an # to save the reference to Activity avoids any possible memory leaks .

    
09.11.2016 / 15:03
0

I've also had problems with contexts in different classes, and it's kinda hard work to control this. What you can do is to 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 have instantiated and can display to the user in different ways.

    
09.11.2016 / 10:46