今天調試一個bug的時候,情景例如以下:
一個Activity A,須要用startActivityForResult方法開啟Activity B。Activity B的launch mode被設置為singleTask,那么在Activity B開啟之后的瞬間(未等B返回不論什么result),Activity A中的onActivityResult方法就會被調用。而且收到一個RESULT_CANCEL的request code。
然后在ActivityB中做了一些邏輯之后,在Activity B通過setResult方法返回Activity A的時候,Activity A中的onActivityResult方法就不再被調用。導致數據不刷新。
后來去查看AndroidManifest.xml文檔才發現Activity A。Activity B的lunch mode都定義為singleTask。后來把Activity A,Activity B的lunch mode都改為standard后就正常了。
======================================================================================================
======================================================================================================
以下這篇文字正好解釋了startActivityForResult啟動singleTask的Activity。則onActivitResult()立即回調且resultCode為RESULT_CANCEL的現象 ,以下轉載於:http://blog.csdn.net/sodino/article/details/22101881
問題現象:
在剛安裝完demo應用未登錄不論什么帳號時,通過系統內的分享功能想將文件/圖片等內容"發送給好友"或"發送到我的電腦",觸發登錄界面,但登錄成功后,沒有跳轉到選擇demo好友發送界面,無法繼續發送。本文為Sodino全部,轉載請注明出處:http://blog.csdn.net/sodino/article/details/22101881
代碼分析:
demo中JumpActivity處理着各種外部應用分享入口。通過調試發現進行分享時會推斷是否登錄過,假設未登錄則會跳轉至LoginActivity進行登錄。例如以下代碼:
- private void doShare(booleancheckLogin) {
- // 系統級分享
- Intent intent = getIntent();
- ... ...
- ... ...
- // 沒登錄
- if (checkLogin &&!demo.isLogin()){
- Intent i = newIntent(this, LoginActivity.class);
- i.putExtra("isActionSend",true);
- i.putExtras(extra);
- i.putExtras(i);
- startActivityForResult(i,SHARE_LOGIN_REQUEST);
- return;
- }
- ... ...
- }
查閱代碼得知登錄成功后,則JumpActivity.onActivityResult()將會得到requestCode值為SHARE_LOGIN_REQUEST的回調。為此。在onActivityResult()回調處設置斷點,再次跟進。
設置斷點,運行分享操作進行調試,發現每次運行完startActivityForResult(),則onActivityResult()便立刻被回調了,且resultCode值為RESULT_CANCEL。至些。問題開始有了頭緒。
通過排查。發現LoginActivity在之前有被修改過,其launchMode賦值為singleTask。
分享功能就是在這次修改之后失效了的。僅僅要恢復launchMode為standard,就可以讓onActivityResult()在LoginActivity登錄成功后正常回調回來。運行分享操作,恢復功能。
至此,問題得到解決,但問題原因仍是一頭霧水:
為什么通過startActivityForResult()方式去啟動launchMode=singleTask的Activity。onActivityResult()會被立即回調且resultCode值為RESULT_CANCEL??
原因解析:
經查文檔,發現文檔中還有一類似的方法startActivityForResult(Intent,int,Bundle)有說明例如以下:
Note that this method should only be used with Intent protocols thatare defined to return a result. In other protocols (such as ACTION_MAIN orACTION_VIEW), you may not get the result when you expect. For example,if the activity you are launching uses thesingleTask launch mode, it will not run in your task and thus you willimmediately receive a cancel result.
但這點凝視讓人理解得仍不是非常透徹。繼續搜索,發現文檔(點擊這里)里說了以下的這一種現象。
在下圖中。存在着前兩個棧,當中直接顯示在屏幕上與用戶交互的Back Stack。及還有一個隱藏在后台的Background Task,該棧棧頂的Activity Y其launchMode為singleTask。
假設在Activity 2中調用BackgroundTask中已經啟動過的Activity Y,則Background Task內占領屏幕而且該Task下全部的棧都會保留當前的棧位置及順序push進Back Task形成新的結構,順序由上至下為Activity Y→Activity X→Activity 2→Activity 1。
在Activity Y界面按返回鍵,則ActivityY出棧,Activity X占領屏幕!注意,由Activity2調用的Activity Y,但返回鍵后,回退顯示的是Activity X!所以即使在Activity Y運行setResult(),Activity 2也是無法接收到的。
換回文章開頭的問題,即在JumpActivity處啟動LoginActivity(已經被設置singleTask了),則LoginActivity的setResult()結果有可能不會傳給JumpActivity。
繼續按返回鍵,則才回到Activity 2。
問題結論:
由此,我們再回到先前的問題。
在這樣的Tasks的入棧與出棧設計下。因為可能有Activity X的存在,所以在Activity 2啟動Activity Y時,則直接回調了onActivityResult()並給出了RESULT_CANCEL也就能夠理解了。
======================================================================================================
======================================================================================================
以下這篇文字正好解釋了這個現象。轉載於:http://www.cnblogs.com/tt_mc/p/3586834.html
探索
在Google上搜索android activity onactivityresult singTop找到了一些問題。
stackoverflow
stackoverflow上有些人跟我遇到的問題類似。比方說有一位開發人員把Activity設置成了singleTask模式。onActivityResult就收不到不論什么結果了。當他把singleTask模式改回標准模式,又恢復正常。
這個問題以下給出的答案中。有一位說startActivityForResult的文檔中有這么一句話:
For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result.
意思是:比方說,假設你正載入的activity使用了singleTask的載入模式,它不會在你的棧中運行。而且這樣你會立即收到一個取消的結果。
即在onActivityResult里立即得到一個RESULT_CANCEL.
他還補充說沒有非常好的補救方法。
能夠試試用監聽廣播的方法。
還有一個stackoverflow的問題中,有人直接回答了不能再singleInstance或singleTop模式下使用startActivityForResult()方法。不僅被採納了,票數還不低。
剩下的一個stackoverflow問題中。有人說把singleTask改成singleTop就會正常,得到高達59票並被採納。
實際上我用的就是singTop。但是onActivityResult還是無緣無故被調用了。
startActivityForResult的文檔:
public void startActivityForResult (Intent intent, int requestCode, Bundle options)
Added in API level 16
Launch an activity for which you would like a result when it finished. When this activity exits, your onActivityResult() method will be called with the given requestCode. Using a negative requestCode is the same as calling startActivity(Intent) (the activity is not launched as a sub-activity).
載入一個Activity。當它結束時你會得到結果。當這個Activty退出了,你的onActivityResult()方法會依據給出的requestCode被調用。使用一個負的requestCode和調用startActivity(intent)一樣(activity不被載入成子activity)
Note that this method should only be used with Intent protocols that are defined to return a result. In other protocols (such as ACTION_MAIN or ACTION_VIEW), you may not get the result when you expect. For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result.
注意這種方法僅僅能用於被定義要返回結果的Intent協議。在其它協議中(譬如ACTION_MAIN或ACTION_VIEW),你可能在你想得到結果時得不到。比方,當你正載入的Activity使用的singleTask載入模式,它不會在你的棧中運行,這樣你會立刻得到一個取消的結果。
As a special case, if you call startActivityForResult() with a requestCode >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your activity, then your window will not be displayed until a result is returned back from the started activity. This is to avoid visible flickering when redirecting to another activity.
有一個特例是,當你在初始的onCreate()方法或onResume()方法中用一個大於等於0的請求碼調用startActivityForResult(),你的窗體在被啟動的Activity返回結果前不會顯示。這是為了避免跳轉到還有一Activity時可見的閃爍。
This method throws ActivityNotFoundException if there was no Activity found to run the given Intent.
假設運行所給Intent的Activity沒被找到。該方法會拋出ActivityNotFoundException異常。
Activity的載入模式
Use Cases | Launch Mode | Multiple Instances? | Comments |
---|---|---|---|
Normal launches for most activities | "standard " |
Yes | Default. The system always creates a new instance of the activity in the target task and routes the intent to it. |
"singleTop " |
Conditionally | If an instance of the activity already exists at the top of the target task, the system routes the intent to that instance through a call to itsonNewIntent() method, rather than creating a new instance of the activity. |
|
Specialized launches (not recommended for general use) |
"singleTask " |
No | The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to itsonNewIntent() method, rather than creating a new one. |
"singleInstance " |
No | Same as "singleTask" , except that the system doesn't launch any other activities into the task holding the instance. The activity is always the single and only member of its task. |
singleTop模式,可用來解決棧頂多個反復同樣的Activity的問題。
singleTask模式和后面的singleInstance模式都是僅僅創建一個實例的。
當intent到來,須要創建singleTask模式Activity的時候,系統會檢查棧里面是否已經有該Activity的實例。假設有直接將intent發送給它。
singleInstance模式攻克了這個問題(繞了這么半天才說到正題)。讓這個模式下的Activity單獨在一個task棧中。
這個棧僅僅有一個Activity。導游應用和google地圖應用發送的intent都由這個Activity接收和展示。
總結
后來我改變了onActivityResult里面ResultCode為RESULT_OK時刷新界面的詳細實現方法。但是onActivityResult還是會自己被調用,僅僅是臨時沒觸發不論什么bug。可它還是個定時炸彈啊。
以后在選擇Activity的載入模式時,要考慮onActivtyResult方法與之存在沖突。
參考
- http://stackoverflow.com/questions/8960072/onactivityresult-with-launchmode-singletask
- http://developer.android.com/reference/android/app/Activity.html#startActivityForResult%28android.content.Intent,%20int%29
- http://stackoverflow.com/questions/7910840/android-startactivityforresult-immediately-triggering-onactivityresult
- http://stackoverflow.com/questions/3354955/onactivityresult-called-prematurely
====================================================================================
作者:歐陽鵬 歡迎轉載,與人分享是進步的源泉。
轉載請保留原文地址:http://blog.csdn.net/ouyang_peng
====================================================================================