如何在Android Studio的登录活动模板中实现AsyncTask

8
我想在我的Android应用程序中实现登录活动,我使用了Android Studio的登录活动模板。根据Google的文档(https://developer.android.com/studio/projects/templates#LoginActivity),它应该包括一个可用于我的目的的AsyncTask,但是似乎当前版本的Android Studio不再提供这个功能。 我使用的当前Android Studio版本是3.4(2019年4月10日),我的SDK版本为:
        minSdkVersion 23
        targetSdkVersion 26

我有一个LoginDataSource.java类,其中包含以下代码,我认为这些代码将用于进行身份验证工作。但是,当我在TODO行下方调用我的HTTP方法时,它会给出一个异常,指出无法在主线程中执行异步操作:

/**
 * Class that handles authentication w/ login credentials and retrieves user information.
 */
public class LoginDataSource {

    public Result<LoggedInUser> login(String username, String password) {

        try {
            // TODO: handle loggedInUser authentication
            LoggedInUser fakeUser =
                    new LoggedInUser(
                            java.util.UUID.randomUUID().toString(),
                            "Jane Doe");
            return new Result.Success<>(fakeUser);
        } catch (Exception e) {
            return new Result.Error(new IOException("Error logging in", e));
        }
    }

    public void logout() {
        // TODO: revoke authentication
    }
}

我在之前的项目中使用了asynctask进行HTTP调用,但是它是在同一个Activity内实现的,而不是使用这个模板。 在哪里放置asynctask并调用它最好呢?
谢谢!
模板中的其他类:
LoginRepository.java
/**
 * Class that requests authentication and user information from the remote data source and
 * maintains an in-memory cache of login status and user credentials information.
 */
public class LoginRepository {

    private static volatile LoginRepository instance;

    private LoginDataSource dataSource;

    // If user credentials will be cached in local storage, it is recommended it be encrypted
    // @see https://developer.android.com/training/articles/keystore
    private LoggedInUser user = null;

    // private constructor : singleton access
    private LoginRepository(LoginDataSource dataSource) {
        this.dataSource = dataSource;
    }

    public static LoginRepository getInstance(LoginDataSource dataSource) {
        if (instance == null) {
            instance = new LoginRepository(dataSource);
        }
        return instance;
    }

    public boolean isLoggedIn() {
        return user != null;
    }

    public void logout() {
        user = null;
        dataSource.logout();
    }

    private void setLoggedInUser(LoggedInUser user) {
        this.user = user;
        // If user credentials will be cached in local storage, it is recommended it be encrypted
        // @see https://developer.android.com/training/articles/keystore
    }

    public Result<LoggedInUser> login(String username, String password) {
        // handle login
        Result<LoggedInUser> result = dataSource.login(username, password);
        if (result instanceof Result.Success) {
            setLoggedInUser(((Result.Success<LoggedInUser>) result).getData());
        }
        return result;
    }
}

LoggedInUserView.java

/**
 * Class exposing authenticated user details to the UI.
 */
class LoggedInUserView implements Serializable {
    private String displayName;
    //... other data fields that may be accessible to the UI

    LoggedInUserView(String displayName) {
        this.displayName = displayName;
    }

    String getDisplayName() {
        return displayName;
    }
}

LoginViewModel.java

public class LoginViewModel extends ViewModel {

    private MutableLiveData<LoginFormState> loginFormState = new MutableLiveData<>();
    private MutableLiveData<LoginResult> loginResult = new MutableLiveData<>();
    private LoginRepository loginRepository;

    LoginViewModel(LoginRepository loginRepository) {
        this.loginRepository = loginRepository;
    }

    LiveData<LoginFormState> getLoginFormState() {
        return loginFormState;
    }

    LiveData<LoginResult> getLoginResult() {
        return loginResult;
    }

    public void login(String username, String password) {
        // can be launched in a separate asynchronous job
        Result<LoggedInUser> result = loginRepository.login(username, password);

        if (result instanceof Result.Success) {
            LoggedInUser data = ((Result.Success<LoggedInUser>) result).getData();
            loginResult.setValue(new LoginResult(new LoggedInUserView(data.getDisplayName())));
        } else {
            loginResult.setValue(new LoginResult(R.string.login_failed));
        }
    }

    public void loginDataChanged(String username, String password) {
        if (!isUserNameValid(username)) {
            loginFormState.setValue(new LoginFormState(R.string.invalid_username, null));
        } else if (!isPasswordValid(password)) {
            loginFormState.setValue(new LoginFormState(null, R.string.invalid_password));
        } else {
            loginFormState.setValue(new LoginFormState(true));
        }
    }

    // A placeholder username validation check
    private boolean isUserNameValid(String username) {
        if (username == null) {
            return false;
        }
        if (username.contains("@")) {
            return Patterns.EMAIL_ADDRESS.matcher(username).matches();
        } else {
            return !username.trim().isEmpty();
        }
    }

    // A placeholder password validation check
    private boolean isPasswordValid(String password) {
        return password != null && password.trim().length() > 5;
    }
}

LoginActivity.java

public class LoginActivity extends AppCompatActivity {

    private LoginViewModel loginViewModel;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        loginViewModel = ViewModelProviders.of(this, new LoginViewModelFactory())
                .get(LoginViewModel.class);

        final EditText usernameEditText = findViewById(R.id.username);
        final EditText passwordEditText = findViewById(R.id.password);
        final Button loginButton = findViewById(R.id.login);
        final ProgressBar loadingProgressBar = findViewById(R.id.loading);

        loginViewModel.getLoginFormState().observe(this, new Observer<LoginFormState>() {
            @Override
            public void onChanged(@Nullable LoginFormState loginFormState) {
                if (loginFormState == null) {
                    return;
                }
                loginButton.setEnabled(loginFormState.isDataValid());
                if (loginFormState.getUsernameError() != null) {
                    usernameEditText.setError(getString(loginFormState.getUsernameError()));
                }
                if (loginFormState.getPasswordError() != null) {
                    passwordEditText.setError(getString(loginFormState.getPasswordError()));
                }
            }
        });

        loginViewModel.getLoginResult().observe(this, new Observer<LoginResult>() {
            @Override
            public void onChanged(@Nullable LoginResult loginResult) {
                if (loginResult == null) {
                    return;
                }
                loadingProgressBar.setVisibility(View.GONE);
                if (loginResult.getError() != null) {
                    showLoginFailed(loginResult.getError());
                }
                if (loginResult.getSuccess() != null) {
                    updateUiWithUser(loginResult.getSuccess());
                }
                setResult(Activity.RESULT_OK);

                //Complete and destroy login activity once successful
                finish();
            }
        });

        TextWatcher afterTextChangedListener = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // ignore
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // ignore
            }

            @Override
            public void afterTextChanged(Editable s) {
                loginViewModel.loginDataChanged(usernameEditText.getText().toString(),
                        passwordEditText.getText().toString());
            }
        };
        usernameEditText.addTextChangedListener(afterTextChangedListener);
        passwordEditText.addTextChangedListener(afterTextChangedListener);
        passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {

            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    loginViewModel.login(usernameEditText.getText().toString(),
                            passwordEditText.getText().toString());
                }
                return false;
            }
        });

        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loadingProgressBar.setVisibility(View.VISIBLE);
                loginViewModel.login(usernameEditText.getText().toString(),
                        passwordEditText.getText().toString());
            }
        });
    }

    private void updateUiWithUser(LoggedInUserView model) {
        String welcome = getString(R.string.welcome) + model.getDisplayName();
        // TODO : initiate successful logged in experience
        Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_LONG).show();
    }

    private void showLoginFailed(@StringRes Integer errorString) {
        Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show();
    }
}

我认为你需要查看一些异步示例。这里有一个。 https://dev59.com/0Gkw5IYBdhLWcg3wwNN5#9671602 - Adeel Zafar
我知道如何实现异步任务。我的问题更多是关于在这个模板中的哪里实现它。因为我尝试在// TODO: 处理loggedInUser身份验证的内部调用.execute(),但无法使其工作。 - Narshe
1
你的问题仍然与代码有关,这使得IDE无关紧要。你从哪里获取代码并不重要 - 例如,如果你在GitHub上找到了它,那也不能标记问题为“github” - 这是一个无关紧要的标签。 - Zoe stands with Ukraine
你修复了吗?我处于同样的位置,对MVVM模式有点迷茫。 - Jon Zangitu
我也在等待答案。 - Rohit Kumar
5个回答

2

我还在寻找一个好的解决方案。目前我已经实现了两个监听器接口。

public interface OnLoginListener {
    void onLoginResult(Result result);
}
public interface OnRegistrationListener {
    void onRegistrationResult(Result result);
}

我让我的ViewModel实现了这些接口,并相应地改变了UI,但我不确定这是否符合MVVM模式的最佳实践。 LoginDataSource会传递一个引用给监听器。

private OnLoginListener onLoginListener;
private OnRegistrationListener onRegistrationListener;

并从onPostExecute调用onRegistrationResult方法,或者像我这样从Firebase身份验证的onComplete方法中调用。

@Override
        public void onComplete(@NonNull Task<AuthResult> task) {
            if (task.isSuccessful()) {
                // Sign in success, update UI with the signed-in user's information
                Log.d(TAG, "createUserWithEmail:success");
                FirebaseUser user = mAuth.getCurrentUser();
                onRegistrationListener.onRegistrationResult(new Result.Success<>(user));
            } else {
                // If sign in fails, display a message to the user.
                Log.w(TAG, "createUserWithEmail:failure", task.getException());
                onRegistrationListener.onRegistrationResult(new Result.Error(task.getException()));
            }
        }

我希望这对某些人有所帮助 ;).

1
考虑添加 `

` 标签。

new LoginTask().execute();

在try语句内部,然后在login方法外部添加内部类。

public class LoginTask extends AsyncTask<Void, String, Void>{
 RestTemplate restTemplate = new RestTemplate();
 ...
}

1
LoginViewModel 中使用 kotlinx.coroutines:
import kotlinx.coroutines.*

fun login(username: String, password: String) {
    // launched in a separate asynchronous job
    MainScope().launch {
        val result = withContext(Dispatchers.IO) {
            loginRepository.login(username, password)
        }
        if (result is Result.Success) {
            _loginResult.value =
                LoginResult(success = LoggedInUserView(displayName = result.data.displayName))
        } else {
            _loginResult.value = LoginResult(error = R.string.login_failed)
        }
    }
}

LoginRepositoryLoginDataSourcefun login()方法中添加关键字suspend
LoginDataSource中使用suspendCoroutine处理kotlin协程
添加build.gradle依赖项。
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_version"

0

确保一切正常运作。

最好的位置是LoginViewModel.kt

这里是示例代码。


    package com.example.gosoft.ui.login

import android.content.Context
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import android.util.Patterns
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.example.gosoft.data.LoginRepository
import com.example.gosoft.data.Result

import com.example.gosoft.R
import org.json.JSONObject

class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() {

    private val _loginForm = MutableLiveData<LoginFormState>()
    val loginFormState: LiveData<LoginFormState> = _loginForm

    private val _loginResult = MutableLiveData<LoginResult>()
    val loginResult: LiveData<LoginResult> = _loginResult

    fun login(username: String, password: String, context: Context) {
        // can be launched in a separate asynchronous job

        //http request start

        val queue = Volley.newRequestQueue(context)



        val url = "Your URL"
        var name = "test"
        Log.d("name_init",name)

// Request a string response from the provided URL.
        val JsonObjectRequest =  JsonObjectRequest(
            Request.Method.GET, url, null,
            Response.Listener<JSONObject> { response ->
                // Display the first 500 characters of the response string.
                //textView.text = "Response is: ${response.substring(0, 500)}"
                name = "Aram"
                Log.d("name",name)
                Log.d("Response", response.toString())
                val result = loginRepository.login(username, password, name)
                if (result is Result.Success) {
                    _loginResult.value = LoginResult(success = LoggedInUserView(displayName = result.data.displayName))
                } else {
                    _loginResult.value = LoginResult(error = R.string.login_failed)
                }

            },
            Response.ErrorListener { error ->
                name = "No name"
                Log.d("name_error",name)
                Log.d("volley_error", error.toString())
            })

// Add the request to the RequestQueue.
        Log.d("queue_add",name)
        queue.add(JsonObjectRequest)

        //http request end



//        val result = loginRepository.login(username, password)

//        if (result is Result.Success) {
//            _loginResult.value = LoginResult(success = LoggedInUserView(displayName = result.data.displayName))
//        } else {
//            _loginResult.value = LoginResult(error = R.string.login_failed)
//        }
    }

    fun loginDataChanged(username: String, password: String) {
        if (!isUserNameValid(username)) {
            _loginForm.value = LoginFormState(usernameError = R.string.invalid_username)
        } else if (!isPasswordValid(password)) {
            _loginForm.value = LoginFormState(passwordError = R.string.invalid_password)
        } else {
            _loginForm.value = LoginFormState(isDataValid = true)
        }
    }

    // A placeholder username validation check
    private fun isUserNameValid(username: String): Boolean {
        return if (username.contains('@')) {
            Patterns.EMAIL_ADDRESS.matcher(username).matches()
        } else {
            username.isNotBlank()
        }
    }

    // A placeholder password validation check
    private fun isPasswordValid(password: String): Boolean {
        return password.length > 5;
    }
}


0

我的回答有点晚,但我刚好遇到了这种情况。我在LoginDataSource对象内实现了异步任务:

/**
 * Class that handles authentication w/ login credentials and retrieves user information.
 */
public class LoginDataSource {
    LoggedInUser fakeUser;

    public Result<LoggedInUser> login(String username, String password) {

        try {
            LoginTask loginTask = new LoginTask();
            synchronized (loginTask) {
                loginTask.execute(map).notify();
            }
            if (fakeUser != null) {
                return new Result.Success<>(fakeUser);
            } else {
                return new Result.Error(new IOException("Usuario o contraseña incorrectos")); // closed hanging quotation
            }
        } catch (Exception e) {
            return new Result.Error(new IOException("Error logging in", e));
        }
    }

    public void logout() {
        // TODO: revoke authentication
    }

    private class LoginTask extends AsyncTask<HashMap<String, String>, Void,  Void> {

        /**
         * Override this method to perform a computation on a background thread. The
         * specified parameters are the parameters passed to {@link #execute}
         * by the caller of this task.
         * <p>
         * This will normally run on a background thread. But to better
         * support testing frameworks, it is recommended that this also tolerates
         * direct execution on the foreground thread, as part of the {@link #execute} call.
         * <p>
         * This method can call {@link #publishProgress} to publish updates
         * on the UI thread.
         *
         * @param hashMaps The parameters of the task.
         * @return A result, defined by the subclass of this task.
         * @see #onPreExecute()
         * @see #onPostExecute
         * @see #publishProgress
         */
        @Override
        protected Void doInBackground(HashMap<String, String>... hashMaps) {
            
            // Use retrofit2 or whatever to get it
            fakeUser =
                    new LoggedInUser(
                            java.util.UUID.randomUUID().toString(),
                            "Jane Doe");
            return null;
        }
    }
}

你从哪里获取“map”? - MiloTheGreat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接