I have created simple application with login feature. I have created separate task for do the login into server called LoginTask and a listener class called LoginListener.
public interface LoginListener {
public void onLoginComplete();
public void onLoginFailure(String msg);
}
public class LoginTask extends AsyncTask<String, Void, Boolean>{
private final LoginListener listener;
private final Context c;
private String msg;
public LoginTask(final Context c, final LoginListener listener) {
this.c = c;
this.listener = listener;
}
#Override
protected Boolean doInBackground(String... args) {
// loging in to server
//return true if success
}
#Override
protected void onPostExecute(Boolean status) {
if(!status){
if(listener != null) listener.onLoginFailure(msg);
return;
}
// the problem is here, listener is null, because activity/fragment destroyed
if(listener != null) listener.onLoginComplete();
}
}
I executed LoginTask from LoginFragment. The LoginFragment implements LoginListener.
public class LoginFragment extends Fragment implements LoginListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.frg_login, container, false);
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
doInitView();
};
private void doInitView(){
Button loginButton = (Button) getActivity().findViewById(R.id.login_btn);
Button regButton = (Button) getActivity().findViewById(R.id.toreg_btn);
ButtonListener listener = new ButtonListener();
loginButton.setOnClickListener(listener);
regButton.setOnClickListener(listener);
}
private void doLogin(){
Activity activity = getActivity();
EditText emailText = (EditText)activity.findViewById(R.id.login_email);
EditText pwdText = (EditText)activity.findViewById(R.id.login_pwd);
String email = emailText.getText().toString().trim();
String pwd = pwdText.getText().toString().trim();
if(StringUtil.isAnyNull(email, pwd)){
Popup.showMsg(getActivity(), "Silahkan lengkapi data", Popup.SHORT);
return;
}
savedEmail = email;
savedPwd = pwd;
String url = getActivity().getResources().getString(R.string.url_login);
Popup.showLoading(getActivity(), "Login", "Please wait...");
LoginTask task = new LoginTask(getActivity(), this);
task.execute(url, email, pwd);
}
private final class ButtonListener implements OnClickListener{
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.login_btn:
doLogin();
break;
case R.id.toreg_btn:
doToRegister();
break;
case R.id.demo_btn:
doDemo();
break;
}
}
}
#Override
public void onLoginComplete() {
// getActivity() is null
((MainActivity)getActivity()).gotoMain();
}
#Override
public void onLoginFailure(String msg) {
}
}
Because of the login task takes time, sometime the device light turn off before the task was finished so activity was destroyed. This caused the task failed to call the listener(fragment). How to solve this problem?
Thanks
AsyncTask should be used for tasks that take a bit longer and return a result into the current activity. However it's not intended for really long running tasks or for those cases where you want to evaluate its results even if the activity has been destroyed. You might consider using a Service here. In any case you shouldn't do updates in onPostExecute() anymore cause the activity context might be gone (see Doctoror Drive's post). Having that service in place, you can either send an Intent or a Broadcast event to the system. Then do the further processing in that intent activity / broadcast receiver.
You can cancel the asynctask in onDestroy() of your LoginActivity.
Override onCancelled() of the asynctask. When the activity is destroyed, a call to onCancelled() will be made instead of onPostExecute()
Here you can avoid a call back to the LoginActivity.
You should use Service or IntentService. because AsyncTask does not record any variables or context of Activity. When you finish login task launch PendingIntent or startActivity(intent). This can be best practice of Android. This way you never get exception.
In onLoginComplete and onLoginFailure check if the fragment is still attached to the activity. If not, do nothing.
#Override
public void onLoginComplete() {
if (isAdded() && !isRemoving() && !isDetached()) {
((MainActivity)getActivity()).gotoMain();
}
}
Related
Hi there I'm thinking about what is the correct and best way to handle Activity, Fragment, AsyncTask and DialogFragments together.
My current state is that I start my Activity and replace its ContentView with my Fragment, in which I got an EditText and one Button.
Tapping my Button executes an AsyncTasks which Requests random things and takes some time. Meanwhile I display a DialogFragment begging for patience.
Desired behavior is that, e.g. I rotate my screen my DialogFragment keeps being displayed for the time my AsyncTask is running. After that I want to show up a simple toast displaying the information I got from my HttpRequest.
Compact overview about how I thought it would work:
BaseFragment keeps a WeakReference to the Activity it's attached to
AsyncTask keeps a WeakReference to Fragment which exectures it
AsyncTasks onPreExecute() shows up the DialogFragment
AsyncTasks onPostExecute() dissmisses the DialogFragment
BaseFragment holds DialogFragment
Unfortunately this is not the way it works, on orientation change my DialogFragment keeps being displayed and no toast is showing up.
What am I doing wrong ?
public class BaseFragment extends Fragment{
private static final String TAG = BaseFragment.class.getSimpleName();
protected WeakReference<AppCompatActivity> mActivity;
private TemplateDialogFragment dialogFragment;
public WeakReference<AppCompatActivity> getAppCompatActivity(){ return mActivity; }
#Override
public void onAttach(Context context) {
if(!(context instanceof AppCompatActivity)) {
throw new IllegalStateException(TAG + " is not attached to an AppCompatActivity.");
}
mActivity = new WeakReference<>((AppCompatActivity) context);
super.onAttach(context);
}
#Override
public void onDetach() {
mActivity = null;
super.onDetach();
}
#Override
public void onStart() {
super.onStart();
showContent();
}
public void showContent(){
}
public void showDialog(String title, String content){
dialogFragment = new TemplateDialogFragment();
Bundle bundle = new Bundle();
bundle.putString(TemplateDialogFragment.DIALOG_TITLE, title);
bundle.putString(TemplateDialogFragment.DIALOG_MESSAGE, content);
dialogFragment.setArguments(bundle);
dialogFragment.show(getFragmentManager(), TemplateDialogFragment.FRAGMENT_TAG);
}
public void notifyTaskFinished(String result) {
dismissDialog();
if(mActivity != null && !mActivity.get().isFinishing()) {
Toast.makeText(mActivity.get().getApplicationContext(), result, Toast.LENGTH_LONG).show();
}
}
private void dismissDialog(){
if(dialogFragment != null && dialogFragment.isAdded()) {
dialogFragment.dismissAllowingStateLoss();
}
}
}
...
public class TemplateFragment extends BaseFragment {
private static final String TAG = TemplateFragment.class.getSimpleName();
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.test_fragment, container, false);
}
#Override
public void showContent() {
super.showContent();
Button startTask = (Button) getAppCompatActivity().get().findViewById(R.id.button0);
final BaseFragment instance = this;
startTask.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
CustomAsyncTask task = new CustomAsyncTask(instance);
EditText input = (EditText) getAppCompatActivity().get().findViewById(R.id.text0);
task.execute(input.getText().toString());
}
});
}
private static class CustomAsyncTask extends AsyncTask<String, Void, String> {
WeakReference<BaseFragment> weakBaseFragmentReference;
private CustomAsyncTask(BaseFragment fragment) {
weakBaseFragmentReference = new WeakReference<>(fragment);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
weakBaseFragmentReference.get().showDialog("Executing", "Working on the request...");
}
#Override
protected String doInBackground(String... params) {
HttpURLConnection con = HttpUrlConnectionFactory.createUrlConnection("https://www.httpbin.org/bytes/" + (params[0] == null ? "1" : params[0]));
return HttpRequester.doGet(con).getResponseAsString();
}
#Override
protected void onPostExecute(String response) {
super.onPostExecute(response);
if(weakBaseFragmentReference.get() == null) {
return;
}
weakBaseFragmentReference.get().notifyTaskFinished(response);
}
}
}
*Edit:
After some time researching this theme I'm sure a Service is the best solution for most of my field of use. Also I used AsyncTaskLoaders a lot, because there is a smooth control of lifecycle....
Use progress bar instead of DialogFragment.
AsyncTask should only be used for tasks that take quite few seconds.
AsyncTask doesn't respect Activity lifecycle, and can lead to memory leaks.
Check some gotchas.
You can try AsyncTaskLoader to survive configuration changes.
I'm developing an IM app using the Quickblox API and I'm currently developing the Sign Up and Login features. Well, my problem is that everytime I try to login to the QBChatService by calling QBChatService.login() I'm getting this error from Log Cat:
E/Event: Could not dispatch event: class regmoraes.jusstalk.session.SessionEvents to subscribing class class regmoraes.jusstalk.session.LoginPresenter
E/Event: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I'm using MVP pattern and EventBus to send events from Models ( I called them Managers) to Presenters.
Here are my classes (interaction order between them at the end):
LoginActivity:
public class LoginActivity extends Activity implements LoginView, View.OnClickListener{
private AutoCompleteTextView mUserField;
private EditText mPasswordField;
private TextView mSignUpTextView;
private Button mLoginButton;
private ProgressBar mProgressBar;
private LoginUIPresenter loginPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mProgressBar = (ProgressBar) findViewById(R.id.login_progress);
mUserField = (AutoCompleteTextView) findViewById(R.id.email);
mPasswordField = (EditText) findViewById(R.id.password);
mLoginButton = (Button) findViewById(R.id.button_sign_in);
mLoginButton.setOnClickListener(this);
mSignUpTextView = (TextView) findViewById(R.id.textView_sign_up);
mSignUpTextView.setOnClickListener(this);
this.loginPresenter = new LoginPresenter(this);
}
#Override
public void showMessageDialog(List errors) {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("chat login errors: " + errors).create().show();
}
#Override
public void startNewActivity(Class activity) {
Intent mIntent = new Intent(this, activity);
startActivity(mIntent);
finish();
}
#Override
public void showProgress(boolean show) {
if(show){
mProgressBar.setVisibility(View.VISIBLE);
mUserField.setVisibility(View.INVISIBLE);
mPasswordField.setVisibility(View.INVISIBLE);
mLoginButton.setVisibility(View.INVISIBLE);
}else{
mProgressBar.setVisibility(View.GONE);
mUserField.setVisibility(View.VISIBLE);
mPasswordField.setVisibility(View.VISIBLE);
}
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.button_sign_in:
loginPresenter.login(mUserField.getText().toString(),
mPasswordField.getText().toString());
break;
case R.id.textView_sign_up:
startNewActivity(SignUpActivity.class);
}
}
#Override
public void showToast(String message, int length) {
Toast.makeText(this, message,length).show();
}
}
LoginPresenter:
public class LoginPresenter implements LoginUIPresenter{
LoginView loginView;
SessionManager sessionManager;
public LoginPresenter(LoginView loginView) {
EventBus.getDefault().register(this);
/*...*/
}
#Override
public void login(String username, String password) {
loginView.showProgress(true);
sessionManager.login(username,password);
}
public void onEvent(SessionEvents sessionEvents){
switch (sessionEvents.getEvent()){
case SessionEvents.LOGIN_SUCCESSFULL:
sessionManager.loginToChatService();
break;
case SessionEvents.LOGIN_FAILED:
loginView.showProgress(false);
loginView.showToast("Problem when connecting", Toast.LENGTH_SHORT);
break;
case SessionEvents.CHAT_SERVICE_CONNECTED:
loginView.startNewActivity(MainActivity.class);
break;
default:break;
}
}
}
SessionManager:
public class SessionManagement implements SessionManager,ConnectionListener {
private String TAG = SessionManagement.class.getName();
private SharedPreferences mSharedPreferences;
private Context mContext;
private SessionEvents sessionEvents;
private QBUser currentUser;
public QBChatService qbChatService;
public SessionManagement(Context context) {
this.mContext = context;
this.mSharedPreferences = (mContext)
.getSharedPreferences("regmoraes.testapp", Context.MODE_PRIVATE);
initChatServiceIfNeeded();
this.sessionEvents = new SessionEvents();
this.qbChatService = QBChatService.getInstance();
}
/* .... */
private void initChatServiceIfNeeded() {
if (!QBChatService.isInitialized()) {
QBChatService.setDebugEnabled(true);
QBChatService.init(mContext);
QBChatService.getInstance().addConnectionListener(this);
}
}
#Override
public void login(final String username, final String password) {
final QBUser qbUser = new QBUser(username,password);
QBAuth.createSession(qbUser, new QBEntityCallbackImpl<QBSession>() {
#Override
public void onSuccess(QBSession qbSession, Bundle params) {
currentUser = qbUser;
currentUser.setId(qbSession.getId());
saveCredentials(currentUser.getLogin(), currentUser.getPassword());
sessionEvents.setEvent(SessionEvents.LOGIN_SUCCESSFULL);
EventBus.getDefault().post(sessionEvents);
}
#Override
public void onError(List<String> errors) {
sessionEvents.setEvent(SessionEvents.LOGIN_FAILED);
EventBus.getDefault().post(sessionEvents);
}
});
}
#Override
public void loginToChatService(){
qbChatService.login(currentUser, new QBEntityCallbackImpl() {
#Override
public void onSuccess() {
try {
qbChatService.startAutoSendPresence(30);
sessionEvents.setEvent(SessionEvents.CHAT_SERVICE_CONNECTED);
EventBus.getDefault().post(sessionEvents);
} catch (SmackException.NotLoggedInException e) {
e.printStackTrace();
}
}
#Override
public void onError(List errors) {
sessionEvents.setEvent(SessionEvents.LOGIN_FAILED);
EventBus.getDefault().post(sessionEvents);
}
});
}
}
This is how my classes interacts when user want to login:
User click on Sign In button in LoginActivity
LoginActivity calls LoginPresenter.signIn()
LoginPresenter calls SessionManager.login()
SessionManager send event LOGIN_SUCESSFULL to LoginPresenter
LoginPresenter calls SessionManager.loginToChatService()
ERROR
I know that the error is because of a Background Thread calling a UI Thread method, but the login method works well, only the loginToChat method that throws this error.
How could I fix this?
Thanks
As #logcat said:
It seems like the onEvent method is triggered by a background thread, unlike Android UI events which are already called on the UI thread for you.
And he was right, the onEvent method was triggered by the SessionManager.loginToChat() method, so to fix this, I had to make the onEvent be triggered on UI thread.
After searching the EvenBus Doc I saw this at the Delivery Threads and Threadmodes section:
EventBus can handle threading for you: events can be posted in threads different from the posting thread. (...)
In EventBus, you may define the thread that will call the event handling method onEvent by using a ThreadMode (...)
MainThread: Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly. Event handlers using this mode must return quickly to avoid blocking the main thread. Example:
// Called in Android UI's main thread
public void onEventMainThread(MessageEvent event) {
textField.setText(event.message);
}
So, what I had to do was to change the onEvent method of LoginPresenter to onEventMainThread! In that way, the LoginPresenter can handle the received event on UI thread.
Inside your loginToChatService() method, try to put the code of the login call inside a runOnUiThread call like this:
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
qbChatService.login(currentUser, new QBEntityCallbackImpl() {
...
}
});
activity should be an instance of Activity (could be this, depending on where your code is located).
My application has a refresh button on the main activity. When the user presses that button, a new thread is created which starts updating the SQLite database. When this thread started, user could possibly get into another activies of the application.
The problem is these other activities(ListActivity) should be updated according to the DB when that background thread is completed. How could I provide that. I tried getting current task with ActivityManager but It requires extra permission which I dont want.
Edit:
Sorry seems I misunderstood you. Please take a look at the following code, it is similar to Chinaski's (you just use an interface for the callback methods) but I added a bit more to ensure you know how to use it in a way that will avoid memory leaks.
Note how the activity detaches during onDestroy -- alternatively you could use a WeakReference, however these days you'd use a Fragment with setRetainInstance(true) and completely avoid the detaching/attaching as the fragment would be retained.
MyAsyncTask
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private Callback mCallback;
private boolean mIsComplete = false;
private boolean mHasCallbackBeenCalled = false;
public MyBackgroundTask(Callback callback) {
mCallback = callback;
}
/** Only safe to call this from the UI thread */
public void attach(Callback callback) {
mCallback = callback;
if (mIsComplete && !mHasCallbackBeenCalled) {
fireCallback();
}
}
/** Only safe to call this from the UI thread */
public void detach() {
mCallback = callback;
}
#Override
public void doInBackground() {
// do the heavy stuff here
return null;
}
#Override
public void onPostExecute(Void result) {
mIsComplete = true;
fireCallback();
}
private void fireCallback() {
if (mCallback != null) {
mCallback.callbackMethod();
mHasCallbackBeenCalled = true;
}
}
public static interface Callback {
public void callbackMethod();
}
}
MyActivity
public class MyActivity extends Activity implements MyAsyncTask.Callback {
private MyAsyncTask mTask;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check for a retained task after a configuration change
// e.g. a rotation
if (getLastNonConfigurationInstance() != null) {
mTask = (MyAsyncTask) getLastNonConfigurationInstance();
// Re-attach the task
mTask.attach(this);
}
}
#Override
public void onDestroy() {
// Detach from task to avoid memory leak
if (mTask != null) {
mTask.detach();
}
super.onDestroy();
}
#Override
public Object onRetainNonConfigurationInstance() {
// Retain the async task duration a rotation
return mTask;
}
/** Callback method */
#Override
public void callbackMethod() {
// Do something here
}
}
You could make a singleton in which you will have your thread and a queue of "tasks". When a task is finished, you check / launch the next task, and when you add a task, you launch it, or add it in the queue if a task is already running.
I don't say this is the best solution, but it's one.
Update1
activity:
public Integer _number = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
if (_number >0)
{
Log.d("onSuccessfulExecute", ""+_number);
}
else
{
Log.d("onSuccessfulExecute", "nope empty songs lists");
}
}
public int onSuccessfulExecute(int numberOfSongList) {
_number = numberOfSongList;
if (numberOfSongList >0)
{
Log.d("onSuccessfulExecute", ""+numberOfSongList);
}
else
{
Log.d("onSuccessfulExecute", "nope empty songs lists");
}
return numberOfSongList;
}
end Update1
UPDATE: AsynchTask has its own external class.
How to pass an value from AsyncTask onPostExecute()... to activity
my code does returning value from onPostExecute() and updating on UI but i am looking for a way to set the activity variable (NumberOfSongList) coming from AsynchTask.
AsyncTask class:
#Override
public void onPostExecute(asynctask.Payload payload)
{
AsyncTemplateActivity app = (AsyncTemplateActivity) payload.data[0];
//the below code DOES UPDATE the UI textView control
int answer = ((Integer) payload.result).intValue();
app.taskStatus.setText("Success: answer = "+answer);
//PROBLEM:
//i am trying to populate the value to an variable but does not seems like the way i am doing:
app.NumberOfSongList = payload.answer;
..............
..............
}
Activity:
public Integer NumberOfSongList;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Several UI Code
new ConnectingTask().execute();
Log.d("onCreate", ""+NumberOfSongList);
}
What about using a setter method? e.g.
private int _number;
public int setNumber(int number) {
_number = number;
}
UPDATE:
Please look at this code. This will do what you're trying to accomplish.
Activity class
public class TestActivity extends Activity {
public int Number;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
Button btnDisplay = (Button) findViewById(R.id.btnDisplay);
btnDisplay.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast toast = Toast.makeText(v.getContext(), "Generated number: " + String.valueOf(Number), Toast.LENGTH_LONG);
toast.show();
}
});
new TestTask(this).execute();
}
}
AsyncTask class
public class TestTask extends AsyncTask<Void, Void, Integer> {
private final Context _context;
private final String TAG = "TestTask";
private final Random _rnd;
public TestTask(Context context){
_context = context;
_rnd = new Random();
}
#Override
protected void onPreExecute() {
//TODO: Do task init.
super.onPreExecute();
}
#Override
protected Integer doInBackground(Void... params) {
//Simulate a long-running procedure.
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, e.getMessage());
}
return _rnd.nextInt();
}
#Override
protected void onPostExecute(Integer result) {
TestActivity test = (TestActivity) _context;
test.Number = result;
super.onPostExecute(result);
}
}
Just a word of caution: Be very careful when attempting to hold a reference to an Activity instance in an AsyncTask - I found this out the hard way :). If the user happens to rotate the device while your background task is still running, your activity will be destroyed and recreated thus invalidating the reference being to the Activity.
Create a listener.
Make a new class file. Called it something like MyAsyncListener and make it look like this:
public interface MyAsyncListener() {
onSuccessfulExecute(int numberOfSongList);
}
Make your activity implement MyAsyncListener, ie,
public class myActivity extends Activity implements MyAsyncListener {
Add the listener to the constructor for your AsyncTask and set it to a global var in the Async class. Then call the listener's method in onPostExecute and pass the data.
public class MyCustomAsync extends AsyncTask<Void,Void,Void> {
MyAsyncListener mal;
public MyCustomAsync(MyAsyncListener listener) {
this.mal = listener;
}
#Override
public void onPostExecute(asynctask.Payload payload) {
\\update UI
mal.onSuccessfulExecute(int numberOfSongList);
}
}
Now, whenever your AsyncTask is done, it will call the method onSuccessfulExecute in your Activity class which should look like:
#Override
public void onSuccessfulExecute(int numberOfSongList) {
\\do whatever
}
Good luck.
When an Activity terminates, e.g. after screen orientation changing, is that possible to change an AsyncTask activity context? Else it will create an error because when the activity terminates AsyncTask's activity context is gone too.
My homework done is the following:
public void onSaveInstanceState(Bundle savedInstanceState) <- doesn't solve
public Object onRetainNonConfigurationInstance() <- doesn't solve
android:configChanges="keyboardHidden|orientation"
<- solved but doesn't handle well relative layouts
What do you pass on your onRetainNonConfigurationInstance()? What I do is pass an object to it containing the AsyncTask, and then I try to retrieve the value in getLastNonConfigurationInstance().
EDIT: On second thought, it would depend on what you want to do after a configuration change. If you want to terminate the AsyncTask, and then call cancel() on it. If you want to continue its processing even after an orientation change, then you have to hold on to the task.
You can do that by saving the Activity in the AsyncTask like this:
private MyAsyncTask searchTask;
#Override
public void onCreate(Bundle savedInstance){
super.onCreate(savedInstance);
if (getLastNonConfigurationInstance()!=null) {
SavedObject savedObj = (SavedObject)getLastNonConfigurationInstance();
searchTask = savedObj.getAsyncTask();
searchTask.attach(this);
} else {
searchTask = new MyAsyncTask(this);
searchTask.execute();
}
}
#Override
public Object onRetainNonConfigurationInstance(){
searchTask.detach();
final SavedObject savedObj = new SavedObject();
savedObj.setAsyncTask(searchTask);
return savedObj;
}
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
MyActivity parentActivity = null;
MyAsyncTask (MyActivity activity) {
attach(activity);
}
void attach(MyActivity activity) {
this.parentActivity=activity;
}
void detach() {
parentActivity=null;
}
// Do your thread processing here
}
private class SavedObject {
private MyAsyncTask asyncTask;
public void setAsyncTask(MyAsyncTask asyncTask){
this.asyncTask = asyncTask;
}
public MyAsyncTask getAsyncTask() {
return asyncTask;
}
}
in the OnCancel method of your asynch task put finish();
public void onCancel(DialogInterface dialog) {
cancel(true);
dialog.dismiss();
finish();
}