如何在 Android Studio 的登录活动模板中实现 AsyncTask
Posted
技术标签:
【中文标题】如何在 Android Studio 的登录活动模板中实现 AsyncTask【英文标题】:How to implement AsyncTask in Login Activity template from Android Studio 【发布时间】:2019-09-11 05:07:14 【问题描述】:我想在我的 android 应用中实现一个登录活动,我使用 Android Studio 模板进行登录活动。 根据这里的谷歌文档: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 调用,但它在使用它的同一个活动中,而不是使用这个模板。 哪里应该是放置 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();
【问题讨论】:
我认为您需要查看一些异步示例。这是一个。 ***.com/a/9671602/10936389 我知道如何实现异步任务。我的问题将更多关于在此模板中实现它的 where 。因为我无法通过在 // TODO: 中调用 .execute() 来使其工作:handle loggedInUser authentication 您的问题仍然与代码有关,因此与 IDE 无关。从哪里获得代码无关紧要 - 例如,如果您在 GH 上找到它,这并不意味着您可以标记问题github
- 这是一个无关紧要的标记。
你解决了这个问题吗?我处于同样的位置,对 MVVM 模式有点迷失。
我也在等待答复。
【参考方案1】:
我还在寻找一个好的解决方案。目前我最终实现了 2 个 Listeners 接口。
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()));
我希望这对某人有帮助;)。
【讨论】:
【参考方案2】:考虑添加
new LoginTask().execute();
在try语句内,然后在login方法外添加内部类
public class LoginTask extends AsyncTask<Void, String, Void>
RestTemplate restTemplate = new RestTemplate();
...
【讨论】:
【参考方案3】:在 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)
在 LoginRepository 和 LoginDataSource 中将关键字 suspend
添加到 fun login()
方法。
Handle kotlin coroutine 在 LoginDataSource 例如suspendCoroutine
添加build.gradle
依赖:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_version"
【讨论】:
【参考方案4】:确保一切正常。
最好的地方是 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;
【讨论】:
【参考方案5】:我的回答有点晚了,但我就是在这种情况下。我在 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;
【讨论】:
以上是关于如何在 Android Studio 的登录活动模板中实现 AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章
在 android studio 中使用 SQLite 显示上次登录的用户数据
用户登录但未登录时的Android Studio按钮onclick重定向条件
如何在Android Studio的活动底部制作Android xml Linearlayout?
如何在所有活动中使用Android Studio默认导航抽屉[重复]
android.os.NetworkOnMainThreadException 试图从 android 活动访问数据库。 Android Studio [重复]