AsyncTask return value late - java

I have an authentication interface with an email field and a button.
When i click the button an AsyncTask should verify if the email exist in a google app engine datastore or not.
This is the code for my asyncTask:
public class ConnexionAsyncTask extends AsyncTask<Object, Object, Inscrit> {
private static InscritApi inscritApi = null;
private Context context;
String email;
ProgressDialog dialog;
public ConnexionAsyncTask(Context context, String email) {
this.context = context;
dialog = new ProgressDialog(context);
this.email = email;
}
#Override
protected void onPreExecute() {
dialog.setMessage("Connexion en cours");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
}
#Override
protected Inscrit doInBackground(Object... params) {
if (inscritApi == null) {
InscritApi.Builder builder = new InscritApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
.setRootUrl( // some url );
inscritApi = builder.build();
}
try {
return inscritApi.get(email).execute();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Inscrit inscrit) {
MainActivity main = (MainActivity) context;
main.setInscrit(inscrit);
dialog.dismiss();
}}
And this is the MainActivity code:
public class MainActivity extends AppCompatActivity {
Inscrit inscrit;
Button btncnx;
EditText emailcnx;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btncnx = (Button) findViewById(R.id.btncnx);
emailcnx = (EditText) findViewById(R.id.emailcnx);
btncnx.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ConnexionAsyncTask task = new ConnexionAsyncTask(MainActivity.this, emailcnx.getText().toString());
task.execute();
if (inscrit == null)
Toast.makeText(MainActivity.this, "not exist", Toast.LENGTH_SHORT).show();
else
Toast.makeText(MainActivity.this, "exist", Toast.LENGTH_SHORT).show();
}
});
}
public void setInscrit(Inscrit inscrit) {
this.inscrit = inscrit;
}}
So the code should work like this:
the MainActivity should give the "inscrit" variable to the ConnexionAsyncTask
the ConnexionAsyncTask should verify if the email exist in the datastore or not and then put the result (Inscrit instance or null) in the "inscrit" variable with a setter
the MainActivity should verify if "inscrit" is null or not and show a toast
When i run my code i have to click 2 times to get the real result for example if i put "user#gmail.com" and this email exist of course in the datastore it will show me "not exist" for the first time and exist for second that's mean that the AsyncTask return the value just after the verification.
If i return value with .execute().get() it works but it blocks the ui thread and i want to show a progress Dialog.
I've tried to use a callback interface but it doesn't work either.

You should do the checking
if (inscrit == null)
Toast.makeText(MainActivity.this, "not exist", Toast.LENGTH_SHORT).show();
else
Toast.makeText(MainActivity.this, "exist", Toast.LENGTH_SHORT).show();
after your AsyncTask has finished executing. Basically, you are safe to check on inscrit nullability onPostExecute.

Related

Implement Back Pressed In Android Fragments

I've been stuck in a situation and i need some help over here. There are many articles on this topic here but none of them answered my question. I want to implement onBackPressed() in fragments and show dialog box which shows to exit the application or not. Any help would be appreciated.
LoginFragment.java
public class LoginFragment extends Fragment {
public static final String TAG = LoginFragment.class.getSimpleName();
private EditText mEtEmail;
private EditText mEtPassword;
private Button mBtLogin;
private TextView mTvRegister;
private TextView mTvForgotPassword;
private TextInputLayout mTiEmail;
private TextInputLayout mTiPassword;
private ProgressBar mProgressBar;
private CompositeSubscription mSubscriptions;
private SharedPreferences mSharedPreferences;
#NonNull
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_login,container,false);
mSubscriptions = new CompositeSubscription();
initViews(view);
initSharedPreferences();
return view;
}
private void initViews(View v) {
mEtEmail = v.findViewById(R.id.et_email);
mEtPassword = v.findViewById(R.id.et_password);
mBtLogin = v.findViewById(R.id.btn_login);
mTiEmail = v.findViewById(R.id.ti_email);
mTiPassword = v.findViewById(R.id.ti_password);
mProgressBar = v.findViewById(R.id.progress);
mTvRegister = v.findViewById(R.id.tv_register);
mTvForgotPassword = v.findViewById(R.id.tv_forgot_password);
mBtLogin.setOnClickListener(view -> login());
mTvRegister.setOnClickListener(view -> goToRegister());
mTvForgotPassword.setOnClickListener(view -> showDialog());
}
private void initSharedPreferences() {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
}
private void login() {
setError();
String email = mEtEmail.getText().toString();
String password = mEtPassword.getText().toString();
int err = 0;
if (!validateEmail(email)) {
err++;
mTiEmail.setError("Email should be valid !");
}
if (!validateFields(password)) {
err++;
mTiPassword.setError("Password should not be empty !");
}
if (err == 0) {
loginProcess(email,password);
mProgressBar.setVisibility(View.VISIBLE);
} else {
showSnackBarMessage("Enter Valid Details !");
}
}
private void setError() {
mTiEmail.setError(null);
mTiPassword.setError(null);
}
private void loginProcess(String email, String password) {
mSubscriptions.add(NetworkUtil.getRetrofit(email, password).login()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse,this::handleError));
}
private void handleResponse(Response response) {
mProgressBar.setVisibility(View.GONE);
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(Constants.TOKEN,response.getToken());
editor.putString(Constants.EMAIL,response.getMessage());
editor.apply();
mEtEmail.setText(null);
mEtPassword.setText(null);
Intent intent = new Intent(getActivity(), HomeActivity.class);
startActivity(intent);
}
private void handleError(Throwable error) {
mProgressBar.setVisibility(View.GONE);
if (error instanceof HttpException) {
Gson gson = new GsonBuilder().create();
try {
String errorBody = ((HttpException) error).response().errorBody().string();
Response response = gson.fromJson(errorBody,Response.class);
showSnackBarMessage(response.getMessage());
} catch (IOException e) {
e.printStackTrace();
}
} else {
showSnackBarMessage("No Internet Connection!");
}
}
private void showSnackBarMessage(String message) {
if (getView() != null) {
Snackbar.make(getView(),message,Snackbar.LENGTH_SHORT).show();
}
}
private void goToRegister(){
FragmentTransaction ft = getFragmentManager().beginTransaction();
RegisterFragment fragment = new RegisterFragment();
ft.replace(R.id.fragmentFrame,fragment,RegisterFragment.TAG);
ft.addToBackStack(null).commit();
}
private void showDialog(){
ResetPasswordDialog fragment = new ResetPasswordDialog();
fragment.show(getFragmentManager(), ResetPasswordDialog.TAG);
}
#Override
public void onDestroy() {
super.onDestroy();
mSubscriptions.unsubscribe();
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements ResetPasswordDialog.Listener {
public static final String TAG = MainActivity.class.getSimpleName();
private LoginFragment mLoginFragment;
private ResetPasswordDialog mResetPasswordDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
loadFragment();
}
}
private void loadFragment() {
if (mLoginFragment == null) {
mLoginFragment = new LoginFragment();
}
getFragmentManager().beginTransaction().replace(R.id.fragmentFrame, mLoginFragment, LoginFragment.TAG).commit();
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String data = intent.getData().getLastPathSegment();
Log.d(TAG, "onNewIntent: " + data);
mResetPasswordDialog = (ResetPasswordDialog) getFragmentManager().findFragmentByTag(ResetPasswordDialog.TAG);
if (mResetPasswordDialog != null)
mResetPasswordDialog.setToken(data);
}
#Override
public void onPasswordReset(String message) {
showSnackBarMessage(message);
}
private void showSnackBarMessage(String message) {
Snackbar.make(findViewById(R.id.activity_main), message, Snackbar.LENGTH_SHORT).show();
}
}
In My Login Fragment, I want to show a dialog box "Do you want to exit the application or not". On Yes it dismiss the current fragment and end the activity otherwise it'll remain active. Help please!
You can even try this way
MainActivity.java
#Override
public void onBackPressed() {
if (getFragmentManager() != null && getFragmentManager().getBackStackEntryCount() >= 1) {
String fragmentTag = getFragmentManager().findFragmentById(R.id.frame_container).getTag();
if(fragmentTag.equals(LoginFragment.getTag())){
// show Dialog code
}else{
super.onBackPressed();
}
} else {
super.onBackPressed();
}
}
Add this code in your main activity so that when login fragment is added and you click backpress, then on first if the fragment is added to fragment transaction, then first it finds the fragment and check if its tag is equals to the login fragment tag. Then if both tag matches, then you can show your exit alert dialog.
Android team has prepared a new way of handling the back button pressed on Fragments for us, so you should check this out. It's called OnBackPressedDispatcher.
You need to register OnBackPressedCallback to the fragment where do you want to intercept back button pressed. You can do it like this inside of the Fragment:
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
OnBackPressedCallback callback = new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
//show exit dialog
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

Only the original thread that created a view hierarchy can touch its views error in asynctask

I'm using View.OnClickListener. Code is as given below:
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.appCompatButtonLogin:
AsyncTaskRunner runner = new AsyncTaskRunner();
runner.execute();
break;
case R.id.textViewLinkRegister:
// Navigate to RegisterActivity
Intent intentRegister = new Intent(getApplicationContext(), RegisterActivity.class);
startActivity(intentRegister);
break;
}
}
My AsyncTask class is like this:
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
ProgressDialog progressDialog = new ProgressDialog(LoginActivity.this);
#Override
protected void onPreExecute() {
if (progressDialog == null) {
progressDialog.setIndeterminate(false);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setCancelable(false);
progressDialog.setMessage("Please wait!");
progressDialog.show();
}
super.onPreExecute();
}
#Override
protected String doInBackground(String... strings) {
try {
verifyFromSQLite();
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
private void verifyFromSQLite() {
if (inputValidation.isInputEditTextFilled(textInputEditTextEmail, textInputLayoutEmail, getString(R.string.error_message_email))) {
return;
}
if (inputValidation.isInputEditTextEmail(textInputEditTextEmail, textInputLayoutEmail, getString(R.string.error_message_email))) {
return;
}
if (inputValidation.isInputEditTextFilled(textInputEditTextPassword, textInputLayoutPassword, getString(R.string.error_message_email))) {
return;
}
if (databaseHelper.checkUser(textInputEditTextEmail.getText().toString().trim()
, textInputEditTextPassword.getText().toString().trim())) {
Intent accountsIntent = new Intent(activity, UsersListActivity.class);
accountsIntent.putExtra("EMAIL", textInputEditTextEmail.getText().toString().trim());
emptyInputEditText();
startActivity(accountsIntent);
} else {
Toast.makeText(LoginActivity.this, "Please check your credentials", Toast.LENGTH_SHORT).show();
}
}
private void emptyInputEditText() {
textInputEditTextEmail.setText(null);
textInputEditTextPassword.setText(null);
}
#Override
protected void onPostExecute(String s) {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
}
When I run my code, I get an exception like this:
Only the original thread that created a view hierarchy can touch its views
I'm trying to separate UI from non-UI part in asynctask, how can I fix this?
You can't make changes to UI in a background task.
Move this code:
Intent accountsIntent = new Intent(activity, UsersListActivity.class);
accountsIntent.putExtra("EMAIL", textInputEditTextEmail.getText().toString().trim());
emptyInputEditText();
startActivity(accountsIntent);
and
Toast.makeText(LoginActivity.this, "Please check your credentials", Toast.LENGTH_SHORT).show();
to onPostExecute().
You can set values to boolean flags for these cases in doInBackground() and check them in onPostExecute() and act accordingly.

Progress Dialog: android.view.WindowLeaked: has leaked window DecorView#6995336[] that was originally added here

I tried to change the orientation of my device on recycler view but it always crashes when progress dialog shows up.
How to solve this?
Here is my code:
private class LoadOrdersListAgent extends AsyncTask {
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(OrdersActivity.this);
ordersList = new ArrayList<>();
rvor = findViewById(R.id.recycler_view_orders_agent);
emptytv = findViewById(R.id.empty_view_orders_agent);
emptytv.setVisibility(View.GONE);
rvor.setHasFixedSize(true);
rvor.setLayoutManager(new LinearLayoutManager(OrdersActivity.this));
rvor.setItemAnimator(new DefaultItemAnimator());
dialog.setMessage("Loading....");
dialog.show();
}
#Override
protected void onPostExecute(Void aVoid) {
final OrdersAdapter adapter = new OrdersAdapter(getApplicationContext(), ordersList);
rvor.setAdapter(adapter);
rvor.setLayoutManager(new LinearLayoutManager(OrdersActivity.this));
srl.setRefreshing(false);
if (dialog.isShowing()) {
dialog.dismiss();
}
if (ordersList.isEmpty()) {
Log.d("TESTING.....", "LIST OF ORDERS ----->" + ordersList);
rvor.setVisibility(View.GONE);
srl.setVisibility(View.GONE);
emptytv.setVisibility(View.VISIBLE);
} else {
rvor.setVisibility(View.VISIBLE);
srl.setVisibility(View.VISIBLE);
emptytv.setVisibility(View.GONE);
}
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
#Override
protected Void doInBackground(Void... voids) {
ordersList = OrdersApi.getOrders(url, key);
return null;
}
}
private void swipeOrderLayout() {
srl = findViewById(R.id.swipe);
srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
if (new CheckNetworkUtil(OrdersActivity.this).isNetworkAvailable()) {
new LoadOrdersListAgent().execute();
// new LoadOrdersListAdmin().execute();
} else
Toast.makeText(OrdersActivity.this, "No Internet Connection!", Toast.LENGTH_SHORT).show();
srl.setRefreshing(false);
}
});
}
I got this error when i was Finishing/Destroying the activity without Dismissing progress Dialogue.
Solution use dialog.dismiss(); to dismiss the progress dialogue before destroying or pausing the activity
in your case remove the if condition and just call dialog.dismiss(); in postExecute method
Declare your ProgressDialog in Global using :
Add this code above onCreate() :
private ProgressDialog dialog;
Add this code within a onCreate():
dialog = new ProgressDialog(OrdersActivity.this);
dialog.setMessage("Loading....");
dialog.show();
Add this code within as onPreExecute method ,
if (!dialog.isShowing()) {
dialog.show();
}

Show dialog when user becomes offline and dismiss it when user becomes online (with broadcastReceiver)

this is my base activity that extends class activity. I make my other activities extend this base class:
public abstract class Base extends Activity {
private BroadcastReceiver netStateReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResourceId());
}
protected abstract int getLayoutResourceId();
#Override
protected void onPause() {
if (netStateReceiver != null) {
unregisterReceiver(netStateReceiver);
netStateReceiver = null;
}
super.onPause();
}
#Override
protected void onResume() {
if (netStateReceiver == null) {
netStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, Intent intent) {
final Dialog offline = new Dialog(context, android.R.style.Theme_Light);
//A change occurred in connection state. Check whether user has been become online or offline:
if (!CheckNet()) {
//User became offline (show offline dialog):
offline.setContentView(R.layout.activity_offline);
offline.setTitle("offline");
offline.getWindow().setBackgroundDrawableResource(R.color.transparent);
offline.show();
final Button retry = (Button) offline.findViewById(R.id.button6);
retry.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (CheckNet()) {
offline.dismiss();
}
}
});
}
else {
//User became online (dismiss offline dialog):
if (offline.isShowing()) {
offline.dismiss();
}
}
}
};
registerReceiver(netStateReceiver, new IntentFilter(Values.CONNECTIVITY_RECEIVER_ACTION));
}
super.onResume();
}
private boolean CheckNet() {
final ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo activeNetwork = conMgr.getActiveNetworkInfo();
return (activeNetwork != null && activeNetwork.isConnectedOrConnecting());
}
}
As you see in code I have registered a receiver for checking connectivity status.
I want when user becomes offline a dialog be shown to user and notify him that he is offline and should become online to continue. This part works good.
I also want when that dialog is showing and in the moment user becomes online this dialog be dismissed, but this part doesn't work and dialog stays on the display.
What's the problem, how can I dismiss the dialog?
You create a new dialog on every broadcast instead of using the dialog you already created before.
Make the dialog variable a member variable of the activity class, then it should work.
private Dialog offline;
#Override
protected void onResume() {
if (netStateReceiver == null) {
netStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, Intent intent) {
if (!CheckNet()) {
if(offline==null || !offline.isShowing()){
offline = new Dialog(context, android.R.style.Theme_Light);
}
...
} else {
//User became online (dismiss offline dialog):
if (offline!=null && offline.isShowing()) {
offline.dismiss();
}
}
Make it global access to your dialog object reference :
final Dialog offline = new Dialog(context, android.R.style.Theme_Light);
Then you will able to close your dialog.
You are creating a new dialog everytime you receive a broadcast, so the dialog you dismissed is a whole different dialog than the one used to show "you are currently offline".
Try putting your "offline" dialog in the activity instead of within the onReceive callback.
A simple example would be:
public abstract class Base extends Activity {
private BroadcastReceiver netStateReceiver;
final Dialog offline;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResourceId());
// You create the dialog here instead within the onReceive callback
offline = new Dialog(this, android.R.style.Theme_Light);
offline.setContentView(R.layout.activity_offline);
offline.setTitle("offline");
offline.getWindow().setBackgroundDrawableResource(R.color.transparent);
}
#Override
protected void onResume() {
if (netStateReceiver == null) {
netStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, Intent intent) {
if (!CheckNet()) {
// Your dialog already exists, just show it immediately
offline.show();
final Button retry = (Button) offline.findViewById(R.id.button6);
retry.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (CheckNet()) {
offline.dismiss();
}
}
});
}
else {
//User became online (dismiss offline dialog):
if (offline.isShowing()) {
offline.dismiss();
}
}
}
};
registerReceiver(netStateReceiver, new IntentFilter(Values.CONNECTIVITY_RECEIVER_ACTION));
}
super.onResume();
}
}

ProgressDialog in another AsyncTask class not showing when called from Activity

I have a class called RestClient that gets some information from my webService and then return and I'm trying to make a Progress dialog run while it is accessing the internet. And as I use this class in more than one place I won't make in the Activity itself. Here is my RestClient class:
public class RestClient extends AsyncTask<URL, String, String> {
private Context context;
private String string;
public RestClient(Context context, String string)
{
this.context = context;
this.string = string;
}
#Override
protected void onPreExecute() {
dialog = ProgressDialog.show(context, "Buscando seu Produto","Por favor, espere um momento...",true ,false);
//I've already tried:
/*ProgressDialog dialog = new ProgressDialog(context);
dialog.setTitle("Buscando seu Produto");
dialog.setMessage("Por favor, espere um momento...");
dialog.setIndeterminate(true);
dialog.setCancelable(false);*/
dialog.show();
super.onPreExecute();
}
#Override
protected String doInBackground(URL... params) {
try {
//Some WebService gets and Json conversions using my string variable
//and some Thread.sleep that counts 2000 miliseconds to do all the queries
dialog.dismiss();
} catch (IOException | InterruptedException |JSONException e) {
e.printStackTrace();
dialog.dismiss();
return e.getMessage();
}
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}
And in my activity I call the class RestClient when I click a button like this:
--- EDIT : I forgot to mention that I have an AlertDialog in this same activity that CAN be shown sometimes before and after the ProgressDialog ---
private Button buttonConfirm;
private EditView evString;
private String theString;
private String returnFromExecute;
private RestClient restClient;
private AlertDialog.Builder dialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_access_webservice);
evString = (EditText) findViewById(R.id.editViewMyString);
buttonConfirm = (Button) findViewById(R.id.buttonConfirm);
dialog = new ProgressDialog(IdentificacaoDeProdutoActivity.this);
dialog.setTitle("Error");
dialog.setMessage("Please try again");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
buttonConfirmar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
theString = evString.getText().toString();
if(!(theString!=null && theString.trim().length()>0)) //To check if theString is not null
{
dialog.show();
}
restClient = new RestClient(AccessWebserviceActivity.this, theString);
//Then I call execute and put a Thread.sleep a bit longer to compensate the ones I have in my doInBackground
restClient.execute();
try {
Thread.sleep(2050);
} catch (Exception e) {
dialog.show();
return;
}
}
}
}
The problem is that my ProgressDialog never shows. I've already tried getParent(), getApplication() and getApplicationContext() instead of AccessWebserviceActivity.this but none have worked. Someone Have any idea what is happening and what should I do?
you have not created progress dialog try this.
ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog= new ProgressDialog(context);
dialog.setMessage("on Progress");
dialog.show();
super.onPreExecute();
}
returnFromExecute = restClient.get();
Remove that statement. You have already:
restClient.execute();
That should do.
The result of doInBackground() you should handle in onPostExecute(). It cannot be handled or retrieved in onCreate().
You need to call
dialog = ProgressDialog.show(context, "Buscando seu Produto","Por favor, espere um momento...",true ,false);
and remove
dialog.show();
Also put your dialog.dismiss(); method in onPostExecute(). This dialog.dismiss() method is good in catch block but what's its purpose if you are calling this method in try block. It will remove progress dialog as soon as you call this AsyncTask.
After a lot of researches about Threads and Process I found out that I had to encapsulate the all the code I have after my
RestClient.execute in a
new Thread(new Runnable() { public void run() { // My code } });
so that the execution of the code happened in background as well as the WebService query.
EDIT:
Even if creating a new Thread works, it is not recommended! The right thing to do would be to create another class that extends AsyncTask to do job.

Categories