Câu hỏi Khi đăng xuất, xóa lịch sử Hoạt động rõ ràng, ngăn chặn nút “quay lại” từ việc mở các Hoạt động đã đăng nhập


Tất cả các hoạt động trong ứng dụng của tôi yêu cầu người dùng phải đăng nhập để xem. Người dùng có thể đăng xuất khỏi hầu hết mọi hoạt động. Đây là yêu cầu của ứng dụng. Tại bất kỳ thời điểm nào nếu người dùng đăng xuất, tôi muốn gửi người dùng đến Đăng nhập Activity. Tại thời điểm này, tôi muốn hoạt động này nằm ở cuối ngăn xếp lịch sử để nhấn nút "quay lại" sẽ trả về người dùng về màn hình chính của Android.

Tôi đã nhìn thấy câu hỏi này hỏi một vài địa điểm khác nhau, tất cả đều được trả lời với các câu trả lời tương tự (mà tôi phác thảo ở đây), nhưng tôi muốn đặt nó ở đây để thu thập phản hồi.

Tôi đã thử mở hoạt động Đăng nhập bằng cách đặt Intent cờ để FLAG_ACTIVITY_CLEAR_TOP có vẻ như được nêu trong tài liệu, nhưng không đạt được mục tiêu đặt hoạt động Đăng nhập ở cuối ngăn xếp lịch sử và ngăn người dùng điều hướng trở lại các hoạt động đã đăng nhập trước đây. Tôi cũng đã thử sử dụng android:launchMode="singleTop" cho hoạt động Đăng nhập trong tệp kê khai, nhưng điều này cũng không hoàn thành mục tiêu của tôi (và dường như không có hiệu lực).

Tôi tin rằng tôi cần phải xóa lịch sử ngăn xếp hoặc hoàn thành tất cả các hoạt động đã mở trước đây.

Một tùy chọn là có hoạt động của từng hoạt động onCreate kiểm tra trạng thái đăng nhập và finish() nếu chưa đăng nhập. Tôi không thích tùy chọn này, vì nút quay lại sẽ vẫn có sẵn để sử dụng, điều hướng trở lại khi các hoạt động đóng lại.

Tùy chọn tiếp theo là duy trì LinkedList các tham chiếu đến tất cả các hoạt động mở có thể truy cập tĩnh từ mọi nơi (có thể sử dụng các tham chiếu yếu). Khi đăng xuất, tôi sẽ truy cập danh sách này và lặp lại tất cả các hoạt động đã được mở trước đó, gọi finish() trên mỗi cái. Tôi sẽ sớm bắt đầu triển khai phương pháp này.

Tôi muốn sử dụng một số Intent Tuy nhiên, việc lừa cờ để thực hiện điều này. Tôi rất vui khi thấy rằng tôi có thể đáp ứng các yêu cầu của ứng dụng mà không phải sử dụng một trong hai phương pháp mà tôi đã nêu ở trên.

Có cách nào để thực hiện điều này bằng cách sử dụng Intent hoặc cài đặt tệp kê khai hoặc là tùy chọn thứ hai của tôi, duy trì LinkedList các hoạt động đã mở là lựa chọn tốt nhất? Hoặc là có một lựa chọn khác mà tôi hoàn toàn nhìn ra?


209
2018-06-09 16:53


gốc


rất giống, gần như chính xác cùng một câu hỏi được đăng ở đây: osdir.com/ml/Android-Developers/2010-06/msg00414.html - skyler
Giải pháp này thậm chí còn tốt hơn! Không có chương trình phát sóng cần thiết. [nhập mô tả liên kết tại đây] [1] [1]: stackoverflow.com/questions/5520499/… - Doug996InKC
Đối với trường hợp đăng xuất, tôi khuyên bạn không nên phụ thuộc vào Chuyển đổi hoạt động, nhưng thay vào đó có mỗi Hoạt động chỉ nên sử dụng khi đăng nhập kiểm tra trạng thái đăng nhập toàn cục trên mỗi hồ sơ và nếu không phải là từ chối hiện tại bằng cách ngay lập tức hướng người dùng đến nơi khác (chẳng hạn như hoạt động đăng nhập). Sau đó, bạn cố gắng để có được Hoạt động đặt hàng chính xác cho khả năng sử dụng mục đích thay vì cho Bảo vệ những người. - Chris Stratton


Các câu trả lời:


Tôi có thể đề nghị bạn một cách tiếp cận khác IMHO mạnh mẽ hơn. Về cơ bản, bạn cần phát thông báo đăng xuất tới tất cả các Hoạt động của bạn cần phải ở trạng thái đăng nhập. Vì vậy, bạn có thể sử dụng sendBroadcast và cài đặt BroadcastReceiver trong tất cả các hành động của bạn. Một cái gì đó như thế này:

/** on your logout method:**/
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("com.package.ACTION_LOGOUT");
sendBroadcast(broadcastIntent);

Người nhận (Hoạt động an toàn):

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    /**snip **/
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.package.ACTION_LOGOUT");
    registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("onReceive","Logout in progress");
            //At this point you should start the login activity and finish this one
            finish();
        }
    }, intentFilter);
    //** snip **//
}

199
2018-06-09 18:24



@Christopher, mỗi hoạt động đăng ký chương trình phát sóng khi nó được tạo. Khi nó chuyển sang nền (tức là, một hoạt động mới đến đầu ngăn xếp), onStop () của nó sẽ được gọi, nhưng nó vẫn có thể nhận được các chương trình phát sóng. Bạn chỉ cần chắc chắn rằng bạn gọi unregisterReceiver () trong onDestroy () thay vì trong onStop (). - Russell Davis
Điều này có hoạt động nếu một hoạt động nào đó trong ngăn xếp bị tắt bởi hệ điều hành để khôi phục bộ nhớ không? I E. hệ thống sẽ xem xét nó thực sự hoàn thành sau khi phát sóng ở trên được gửi đi, và sẽ không tái tạo nó trên nhấn nút quay lại? - Jan Żankowski
Trong khi điều này có vẻ như một giải pháp thanh lịch, điều quan trọng cần lưu ý rằng đây không phải là đồng bộ. - Che Jami
Một giải pháp tốt đẹp, nhưng thay vì sử dụng một đăng ký nhận Broadcast như mô tả trong đoạn mã trên, bạn nên sử dụng LocalBroadcastManager.getInstance (this) .registerReceiver (...) và LocalBroadcastManager.getInstance (this) .unregisterReceiver (..) . Ngoài ra, ứng dụng của bạn có thể nhận được ý định từ bất kỳ ứng dụng nào khác (mối quan tâm về bảo mật) - Uku Loskit
@Warlock Bạn là chính xác. Cạm bẫy đối với phương pháp này là khi một hoạt động trong ngăn xếp phía sau bị phá hủy bởi hệ thống (có thể xảy ra vì nhiều lý do khác nhau, chẳng hạn như kịch bản bộ nhớ thấp). Trong trường hợp đó, trường hợp Hoạt động sẽ không có mặt để nhận được chương trình phát sóng. Nhưng hệ thống sẽ vẫn điều hướng trở lại thông qua Hoạt động đó bằng cách tạo lại nó. Điều này có thể được kiểm tra / sao chép bằng cách bật cài đặt nhà phát triển "Không giữ hoạt động" - Eric Schlenz


Có vẻ như một nghi thức của đoạn văn mà một lập trình Android mới dành một ngày nghiên cứu vấn đề này và đọc tất cả các chủ đề StackOverflow này. Tôi bây giờ mới được khởi xướng và tôi để lại dấu vết của kinh nghiệm khiêm nhường của tôi để giúp một người hành hương tương lai.

Đầu tiên, không có cách nào rõ ràng hay ngay lập tức để làm điều này cho mỗi nghiên cứu của tôi (as of September 2012). Bạn nghĩ bạn có thể đơn giản startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK) nhưng Không.

Bạn có thể làm startActivity(new Intent(this, LoginActivity.class)) với FLAG_ACTIVITY_CLEAR_TOP - và điều này sẽ khiến khung tìm kiếm trong ngăn xếp, tìm phiên bản ban đầu của LoginActivity của bạn, tạo lại và xóa phần còn lại của ngăn xếp (lên trên). Và kể từ khi đăng nhập có lẽ là ở dưới cùng của ngăn xếp, bây giờ bạn có một ngăn xếp trống và nút Back chỉ thoát khỏi ứng dụng.

NHƯNG - điều này chỉ hoạt động nếu trước đó bạn đã để lại phiên bản gốc của LoginActivity đó sống ở chân ngăn xếp của bạn. Nếu, giống như nhiều người lập trình, bạn đã chọn finish() cái đó LoginActivity khi người dùng đã đăng nhập thành công, thì nó không còn trên cơ sở ngăn xếp và FLAG_ACTIVITY_CLEAR_TOP ngữ nghĩa không áp dụng ... bạn sẽ tạo ra một ngữ nghĩa mới LoginActivity trên đầu trang của ngăn xếp hiện có. Đó là gần như chắc chắn không phải những gì bạn muốn (hành vi kỳ lạ, nơi người dùng có thể 'trở lại' theo cách của họ ra khỏi đăng nhập vào một màn hình trước đó).

Vì vậy, nếu bạn đã có finish()'d LoginActivity, bạn cần phải theo đuổi một số cơ chế để xóa ngăn xếp của bạn và sau đó bắt đầu một công cụ mới LoginActivity. Nó có vẻ như câu trả lời bởi @doreamon trong chủ đề này là giải pháp tốt nhất (ít nhất là với con mắt khiêm tốn của tôi):

https://stackoverflow.com/a/9580057/614880

Tôi mạnh mẽ nghi ngờ rằng các tác động phức tạp của việc bạn để lại LoginActivity còn sống đang gây ra rất nhiều sự nhầm lẫn này.

Chúc may mắn.


146
2017-09-17 23:06



Tôi chỉ muốn cảm ơn bạn đã viết giải pháp và phân tích toàn diện nhất cho vấn đề này. - VicVu
Câu trả lời tốt. Bí quyết FLAG_ACTIVITY_CLEAR_TOP, mà hầu hết mọi người khuyên sử dụng, chỉ không hoạt động nếu bạn đã hoàn thành LoginActivity. - Konsumierer
Cảm ơn câu trả lời này. Chi tiết về FLAG_ACTIVITY_CLEAR_TOP đã giúp tôi rất nhiều. - Adam Johns
Cảm ơn câu trả lời.Phiên bản khác giống như khi có phiên (ví dụ như fb) ngay cả khi chúng tôi không gọi hoạt động Đăng nhập để không có hoạt động Đăng nhập trong ngăn xếp. Sau đó, phương pháp đề cập ở trên là vô ích. - Prashanth Debbadwar


CẬP NHẬT

siêu finishAffinity() phương pháp này sẽ giúp giảm mã nhưng đạt được điều tương tự. Nó sẽ kết thúc hoạt động hiện tại cũng như tất cả các hoạt động trong ngăn xếp, sử dụng getActivity().finishAffinity() nếu bạn đang ở trong một mảnh.

finishAffinity(); 
startActivity(new Intent(mActivity, LoginActivity.class));

TRẢ LỜI GỐC

Giả sử rằng LoginActivity -> HomeActivity -> ... -> SettingsActivity call signOut ():

void signOut() {
    Intent intent = new Intent(this, HomeActivity.class);
    intent.putExtra("finish", true);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // To clean up all activities
    startActivity(intent);
    finish();
}

HomeActivity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    boolean finish = getIntent().getBooleanExtra("finish", false);
    if (finish) {
        startActivity(new Intent(mContext, LoginActivity.class));
        finish();
        return;
    }
    initializeView();
}

Điều này làm việc cho tôi, hy vọng rằng nó là hữu ích cho bạn quá. :)


102
2018-03-06 08:16



Tôi nghĩ rằng giải pháp của bạn giả định rằng người dùng sẽ nhấp vào signOut và quay trở lại chỉ một hoạt động (HomeActivity). Nếu bạn có 10 hoạt động trên ngăn xếp thì sao? - Igor Ganapolsky
Nếu bạn có 10 hoạt động ở phía trên cùng của HomeActivity, cờ FLAG_ACTIVITY_CLEAR_TOP sẽ giúp làm sạch tất cả chúng. - thanhbinh84
Điều này chỉ có thể hoạt động nếu khi khởi động HomeActivity, bạn sẽ có được OnCreate of HomeActivity. Đơn giản chỉ cần bắt đầu hoạt động tại nhà sẽ không nhất thiết phải tái tạo nó trừ khi nó đã được hoàn thành hoặc bị phá hủy rồi. Nếu HomeActivity không cần phải được tái tạo, OnCreate sẽ không được gọi và sau khi bạn đăng xuất, bạn sẽ ngồi trên hoạt động ở nhà của bạn. - topwik
Đây là một giải pháp khả thi và liên quan đến việc viết mã ít hơn rất nhiều tính năng đơn giản (cho người dùng) như đăng xuất. - Subin Sebastian
Cờ Intent.FLAG_ACTIVITY_CLEAR_TOP giúp dọn sạch tất cả các hoạt động bao gồm HomeActivity, do đó phương thức onCreate () sẽ được gọi khi bạn khởi động lại hoạt động này. - thanhbinh84


Nếu bạn đang sử dụng API 11 hoặc cao hơn, bạn có thể thử điều này: FLAG_ACTIVITY_CLEAR_TASK- có vẻ như đang giải quyết chính xác vấn đề bạn đang gặp phải. Rõ ràng là trước đám đông API 11 sẽ phải sử dụng một số kết hợp của việc có tất cả các hoạt động kiểm tra thêm, như @ doreamon gợi ý, hoặc một số thủ thuật khác.

(Cũng lưu ý: để sử dụng điều này bạn phải vượt qua FLAG_ACTIVITY_NEW_TASK)

Intent intent = new Intent(this, LoginActivity.class);
intent.putExtra("finish", true); // if you are checking for this in your other Activities
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | 
                Intent.FLAG_ACTIVITY_CLEAR_TASK |
                Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();

66
2018-02-04 22:54



Làm việc như một say mê. Cảm ơn nhiều! Khi tôi phát triển trên API tối thiểu 14, đó là điều duy nhất để triển khai. - AndyB
Tôi nghĩ chúng ta không cần FLAG_ACTIVITY_CLEAR_TOP khi sử dụng giải pháp này cho LoginActivity. - Bharat Dodeja
Tôi nghĩ chỉ là finish(); sẽ thực hiện công việc để ngăn chặn việc quay trở lại sau khi đăng xuất. Cần thiết lập cờ là gì? - Pankaj


Tôi đã dành một vài giờ về điều này quá ... và đồng ý rằng FLAG_ACTIVITY_CLEAR_TOP nghe như những gì bạn muốn: xóa toàn bộ ngăn xếp, ngoại trừ hoạt động đang được khởi chạy, vì vậy nút Quay lại sẽ thoát khỏi ứng dụng. Tuy nhiên, như Mike Repass đã đề cập, FLAG_ACTIVITY_CLEAR_TOP chỉ hoạt động khi hoạt động bạn đang khởi chạy đã có trong ngăn xếp; khi hoạt động không có ở đó, lá cờ không làm gì cả.

Phải làm gì? Đặt hoạt động đang khởi chạy trong ngăn xếp với FLAG_ACTIVITY_NEW_TASK, làm cho hoạt động đó bắt đầu một nhiệm vụ mới trên ngăn xếp lịch sử. Sau đó thêm cờ FLAG_ACTIVITY_CLEAR_TOP.

Bây giờ, khi FLAG_ACTIVITY_CLEAR_TOP đi để tìm hoạt động mới trong ngăn xếp, nó sẽ ở đó và được kéo lên trước khi mọi thứ khác bị xóa.

Đây là chức năng đăng xuất của tôi; tham số Xem là nút được đính kèm của hàm.

public void onLogoutClick(final View view) {
    Intent i = new Intent(this, Splash.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(i);
    finish();
}

30
2018-06-19 14:15



Sẽ không hoạt động trên API <= 10 như FLAG_ACTIVITY_CLEAR_TASK chưa được thêm - YouYou
@christinac Bạn đang nói về FLAG_ACTIVITY_CLEAR_TOP và đoạn mã của bạn có FLAG_ACTIVITY_CLEAR_TASK; đó là hợp lệ sau đó? - Marian Paździoch


Rất nhiều câu trả lời. Có thể là người này cũng sẽ giúp-

Intent intent = new Intent(activity, SignInActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
this.finish();

4
2017-07-22 09:38





Sử dụng nó sẽ hữu ích cho bạn. Câu trả lời xbakesx đã được sửa đổi một chút.

Intent intent = new Intent(this, LoginActivity.class);
if(Build.VERSION.SDK_INT >= 11) {
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
} else {
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
startActivity(intent);

3
2018-06-25 08:37





Giải pháp được chấp nhận là không chính xác, nó có vấn đề như sử dụng một máy thu phát sóng không phải là một ý tưởng tốt cho vấn đề này. Nếu hoạt động của bạn đã được gọi là phương thức onDestroy (), bạn sẽ không nhận được người nhận. Giải pháp tốt nhất là có giá trị boolean trên các tùy chọn được chia sẻ của bạn và kiểm tra nó trong phương thức onCreate () của activty của bạn. Nếu nó không được gọi khi người dùng không đăng nhập, sau đó kết thúc hoạt động. Đây là mã mẫu cho điều đó. Vì vậy, đơn giản và làm việc cho mọi điều kiện.

protected void onResume() {
  super.onResume();
  if (isAuthRequired()) {
    checkAuthStatus();
  }
}

private void checkAuthStatus() {
  //check your shared pref value for login in this method
  if (checkIfSharedPrefLoginValueIsTrue()) {
    finish();
  }
}

boolean isAuthRequired() {
  return true;
}

3
2017-07-09 16:32



Đã nhiều năm rồi, nhưng tôi tin rằng tôi đã làm cả hai. Mỗi Hoạt động mở rộng LoggedInActivity, đã kiểm tra trạng thái đăng nhập của người dùng trong onCreate (). LoggedInActivity cũng lắng nghe chương trình phát sóng "người dùng đăng xuất", và đã làm bất cứ điều gì cần làm để đáp ứng điều này. - skyler
có lẽ bạn nên đặt checkAuthStatus vào onResume() methode. Vì khi bạn nhấn nút quay lại, hoạt động có nhiều khả năng được tiếp tục thay vì được tạo. - maysi
@maysi Cảm ơn bạn đã đề xuất, có cách này nút quay lại cũng hoạt động chính xác, tôi đã cập nhật mục nhập - Yekmer Simsek


Câu trả lời được chọn là thông minh và khéo léo. Đây là cách tôi đã làm nó:

LoginActivity là hoạt động gốc của tác vụ, được đặt android: noHistory = "true" cho nó trong Manifest.xml; Giả sử bạn muốn đăng xuất khỏi SettingsActivity, bạn có thể thực hiện như sau:

    Intent i = new Intent(SettingsActivity.this, LoginActivity.class);
    i.addFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK
            | Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(i);

1
2017-12-18 13:19





Đây là giải pháp tôi đưa ra trong ứng dụng của tôi.

Trong LoginActivity của tôi, sau khi xử lý đăng nhập thành công, tôi bắt đầu một lần đăng nhập khác nhau tùy thuộc vào cấp API.

Intent i = new Intent(this, MainActivity.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    startActivity(i);
    finish();
} else {
    startActivityForResult(i, REQUEST_LOGIN_GINGERBREAD);
}

Sau đó, trong phương thức onActivityForResult của LoginActivity của tôi:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB &&
        requestCode == REQUEST_LOGIN_GINGERBREAD &&
        resultCode == Activity.RESULT_CANCELED) {
    moveTaskToBack(true);
}

Cuối cùng, sau khi xử lý đăng xuất trong bất kỳ Hoạt động nào khác:

Intent i = new Intent(this, LoginActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

Khi trên Gingerbread, làm cho nó như vậy nếu tôi nhấn nút quay lại từ MainActivity, LoginActivity là ngay lập tức ẩn. Trên Honeycomb và sau này, tôi chỉ cần hoàn thành LoginActivity sau khi xử lý đăng nhập và nó được tái tạo đúng sau khi xử lý đăng xuất.


1
2017-11-04 22:56



Nó không làm việc cho tôi. Trong trường hợp của tôi, tôi sử dụng fragmentactivity. Phương thức Onactivityresult không được gọi. - Mohamed Ibrahim


Thỉnh thoảng finish() không làm việc

Tôi đã giải quyết vấn đề đó với

finishAffinity()


1
2017-08-31 04:43