亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

前景服務被Android殺死

前景服務被Android殺死

一只名叫tom的貓 2019-09-18 19:56:24
更新:我還沒有找到問題的真正解決方案。我所做的是一種在連接丟失時自動重新連接到以前的藍牙設備的方法。它并不理想,但似乎運作得相當好。我很想聽到有關這個的更多建議。我遇到的問題與此問題大致相同:服務在被喚醒時被殺死并且在調用startForeground之后包括設備(Asus Transformer),服務停止前的時間長度(30-45分鐘),使用喚醒鎖定,startForeground()的使用,以及當屏幕熄滅時應用程序打開時不會發生問題的事實。我的應用程序維護與另一臺設備的藍牙連接,并在兩者之間發送數據,因此它必須始終處于活動狀態以偵聽數據。用戶可以隨意啟動和停止服務,實際上這是我實現啟動或停止服務的唯一方法。服務重新啟動后,與其他設備的藍牙連接將丟失。根據鏈接問題中的答案,startForeground()“降低了服務被殺的可能性,但并未阻止它”。我理解是這樣的,但是我已經看到很多其他應用程序沒有這個問題的例子(例如,Tasker)。如果服務能夠在用戶停止之前運行,我的應用程序的實用性將大大降低。有什么方法可以避免這個???每當服務停止時,我都會在logcat中看到這個:ActivityManager: No longer want com.howettl.textab (pid 32321): hidden #16WindowManager: WIN DEATH: Window{40e2d968 com.howettl.textab/com.howettl.textab.TexTab paused=falseActivityManager: Scheduling restart of crashed service com.howettl.textab/.TexTabService in 5000ms編輯:我還應該注意,這似乎沒有出現在我連接的其他設備上:HTC Legend運行Cyanogen編輯:這是輸出adb shell dumpsys activity services:* ServiceRecord{40f632e8 com.howettl.textab/.TexTabService}intent={cmp=com.howettl.textab/.TexTabService}packageName=com.howettl.textabprocessName=com.howettl.textabbaseDir=/data/app/com.howettl.textab-1.apkresDir=/data/app/com.howettl.textab-1.apkdataDir=/data/data/com.howettl.textabapp=ProcessRecord{40bb0098 2995:com.howettl.textab/10104}isForeground=true foregroundId=2 foregroundNoti=Notification(contentView=com.howettl.textab/0x1090087 vibrate=null,sound=null,defaults=0x0,flags=0x6a)createTime=-25m42s123ms lastActivity=-25m42s27ms executingStart=-25m42s27ms restartTime=-25m42s124msstartRequested=true stopIfKilled=false callStart=true lastStartId=1Bindings:* IntentBindRecord{40a02618}:  intent={cmp=com.howettl.textab/.TexTabService}  binder=android.os.BinderProxy@40a9ff70  requested=true received=true hasBound=true doRebind=false  * Client AppBindRecord{40a3b780 ProcessRecord{40bb0098 2995:com.howettl.textab/10104}}    Per-process Connections:      ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}
查看完整描述

3 回答

?
jeck貓

TA貢獻1909條經驗 獲得超7個贊

行。我已經度過了難關,重新回到了這個問題上。這是如何進行的。有bug。該帖子描述了如何分析實現中的錯誤并解決問題。


總結一下,這是事情應該如何運作。運行服務將定期清理并每30分鐘左右終止一次。希望保持活動時間超過此時間的服務必須調用Service.startForeground,它會在通知欄上發出通知,以便用戶知道您的服務是永久運行的并且可能會耗盡電池壽命。在任何給定時間,只有3個服務流程可以指定自己作為前臺服務。如果有三個以上的前臺服務,Android將提名最舊的服務作為清理和終止的候選者。


不幸的是,Android中存在關于優先化前臺服務的錯誤,這些錯誤由服務綁定標志的各種組合觸發。即使您已正確地將您的服務提名為前臺服務,但如果您使用某些綁定標志組合與您的流程中的服務建立了任何連接,則Android可能會終止您的服務。詳情如下。


請注意,很少有服務需要是前臺服務。通常,如果您有一個可以打開或關閉或被用戶取消的某種持續活動或長時間運行的互聯網連接,您只需要成為前臺服務。需要前臺狀態的服務示例:UPNP服務器,非常大的文件的長期下載,通過Wi-Fi同步文件系統,以及播放音樂。


如果您只是偶爾輪詢,或等待系統廣播接收器或系統事件,您最好在計時器上喚醒服務,或響應廣播接收器,然后讓服務一旦完成就死掉。這就是服務的設計行為。如果你只是必須活著,那么請繼續閱讀。


檢查了眾所周知的需求框(例如,調用Service.startForeground)后,下一個要查看的是您在Context.bindService調用中使用的標志。用于綁定的標志以各種意外方式影響目標服務進程的優先級。最特別的是,使用某些綁定標志會導致Android錯誤地將前臺服務降級為常規服務。用于分配流程優先級的代碼已被大量推翻。值得注意的是,API 14+中的修訂版在使用舊的綁定標志時可能會導致錯誤; 并且4.2.1中存在明確的錯誤。


所有這些中的朋友都是sysdump實用程序,可用于確定活動管理器為您的服務進程分配的優先級,并發現已分配不正確優先級的情況。啟動并運行服務,然后從主機上的命令提示符處發出以下命令:


adb shell dumpsys活動進程> tmp.txt


使用記事本(不是wordpad / write)來檢查內容。


首先驗證您是否已成功設法在前臺狀態下運行服務。dumpsys文件的第一部分包含每個進程的ActivityManager屬性的描述。在dumpsys文件的第一部分中查找與您的應用程序對應的以下行:


APP UID 10068 ProcessRecord {41937d40 2205:tunein.service / u0a10068}


在以下部分中驗證foregroundServices = true。不要擔心隱藏和空置設置; 它們描述了流程中的活動狀態,并且似乎與其中包含服務的流程并不特別相關。如果foregroundService不為true,則需要調用Service.startForeground使其成立。


接下來你要看的是標題為“Process LRU list(按oom_adj排序)”的文件末尾附近的部分:“。通過此列表中的條目,您可以確定Android是否已將您的應用程序實際分類為前臺服務。如果您的流程位于此列表的底部,則它是進行摘要清除的主要候選者。如果您的流程接近列表的頂部,那么它幾乎是堅不可摧的。


我們來看看這個表中的一行:


  Proc #31: adj=prcp /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)

這是前景服務的一個例子,它完成了一切。這里的關鍵字段是“adj =”字段。這表示在完成所有操作后,ActivityManagerService分配了您的進程的優先級。你希望它是“adj = prcp”(可見的前臺服務); 或“adj = vis”(具有活動的可見過程)或“前”(具有前景活動的過程)。如果它是“adj = svc”(服務進程),或“adj = svcb”(遺留服務?),或“adj = bak”(空背景進程),那么您的進程可能是終止的候選者,并將被終止即使沒有任何回收記憶的壓力,也不會低于每30分鐘。該行的其余標志主要是Google工程師的診斷調試信息。終止的決定是基于adj字段做出的。簡而言之,/ FS表示前臺服務; / FA表示具有活動的前臺進程。/ B表示后臺服務。最后的標簽表示為該流程分配優先級的一般規則。通常它應該匹配adj =字段; 但是在某些情況下,由于與其他服務或活動的活動綁定上的綁定標志,可以向上或向下調整adj =值。


如果您遇到綁定標志的錯誤,dumpsys行將如下所示:


  Proc #31: adj=bak /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)

注意adj字段的值如何被錯誤地設置為“adj = bak”(空的后臺進程),這大致翻譯為“請立即終止我,以便我可以結束這個無意義的存在”以進行進程清理。還要注意行末尾的(fg-service)標志,表示“forground service rules”用于確定“adj”設置。盡管使用了fg-service規則,但這個過程被分配了一個adj設置。 “bak”,它不會長久存在。顯而易見,這是一個錯誤。


因此,目標是確保您的流程始終獲得“adj = prcp”(或更好)。實現該目標的方法是調整綁定標志,直到您設法避免優先級分配中的錯誤。


以下是我所知道的錯誤。(1)如果任何服務或活動曾使用Context.BIND_ABOVE_CLIENT綁定到服務,則存在adj =設置將降級為“bak”的風險,即使該綁定不再處于活動狀態。如果您還在服務之間綁定,則尤其如此。4.2.1來源中的明顯錯誤。(2)絕對不要將BIND_ABOVE_CLIENT用于服務到服務綁定。不要將它用于活動到服務連接。用于實現BIND_ABOVE_CLIENT行為的標志似乎是基于每個進程設置的,而不是基于每個連接的,因此它會觸發服務到服務綁定的錯誤,即使沒有活動的服務活動也是如此與標志集綁定。當進程中有多個服務時,服務到服務綁定似乎也存在建立優先級的問題。在服務到服務綁定上使用Context.BIND_WAIVE_PRIORITY(API 14)似乎有所幫助。從Activity綁定到服務時,Context.BIND_IMPORTANT似乎是一個好主意。這樣做會在活動處于前臺時將您的流程優先級提高一級,而在暫?;蛲瓿苫顒訒r不會造成任何明顯的傷害。


但總體而言,策略是調整bindService標志,直到sysdump指示您的進程已收到正確的優先級。


為了我的目的,使用Context.BIND_AUTO_CREATE | Activity.to-service綁定的Context.BIND_IMPORTANT和Context.BIND_AUTO_CREATE | 服務到服務綁定的Context.BIND_WAIVE_PRIORITY似乎做對了。您的里程可能不同。


我的應用程序相當復雜:兩個后臺服務,每個后臺服務可以獨立保存前臺服務狀態,另外三個也可以采用前臺服務狀態; 其中兩個服務有條件地相互綁定; 總是第三個與第一個結合。此外,Activites在一個單獨的過程中運行(使動畫更流暢)。在同一過程中運行活動和服務似乎沒有任何區別。


可以在核心android文件中找到清理進程規則的實現(以及用于生成sysdump文件內容的源代碼)


frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.

好的機會。


PS:這是Android 5.0的sysdump字符串的解釋。我沒有和他們一起工作,所以按照你的意愿制作他們。我相信你希望4為'A'或'S',5為“IF”或“IB”,1為盡可能低(可能低于3,因為只有3個三個前臺服務進程保持活動狀態在默認配置中)。


Example:

   Proc # : prcp  F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service)


Format:

   Proc # {1}: {2}  {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10}


1: Order in list: lower is less likely to get trimmed.


2: Not sure.


3:

    B: Process.THREAD_GROUP_BG_NONINTERACTIVE

    F: Process.THREAD_GROUP_DEFAULT


4:

    A: Foreground Activity

    S: Foreground Service

    ' ': Other.


5:

    -1: procState = "N ";

        ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P ";

    ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU";

    ActivityManager.PROCESS_STATE_TOP: procState = "T ";

    ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF";

    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB";

    ActivityManager.PROCESS_STATE_BACKUP:procState = "BU";

    ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW";

    ActivityManager.PROCESS_STATE_SERVICE: procState = "S ";

    ActivityManager.PROCESS_STATE_RECEIVER: procState = "R ";

    ActivityManager.PROCESS_STATE_HOME: procState = "HO";

    ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA";

    ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA";

    ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca";

    ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE";


{6}: trimMemoryLevel


{8} Process ID.

{9} process name

{10} appUid 


查看完整回答
反對 回復 2019-09-18
?
海綿寶寶撒

TA貢獻1809條經驗 獲得超8個贊

如果它說“不再需要......”那么該進程在其中沒有活動的服務當前處于startForeground()狀態。檢查以確保您的呼叫實際上是成功的 - 您看到發布的通知,此時日志中沒有消息抱怨任何事情等。還使用“adb shell dumpsys活動服務”來查看您的服務狀態,并確保它實際上標記為前景。此外,如果它是正確的前景,那么在“adb shell dumpsys activity”的輸出中,您將在顯示由于該服務而您的進程當前處于前臺級別的進程的OOM adj的部分中看到。


查看完整回答
反對 回復 2019-09-18
?
慕容森

TA貢獻1853條經驗 獲得超18個贊

Upvoted。我想在這篇評論中添加一篇,我認為這是相關的。如果你想要一個不斷運行的服務,正如Robin所說,你需要以某種方式啟動它。可以直接在activity中調用startService(Intent服務)而不是bindService(),然后在服務啟動后調用startForeground()方法。我在服務類的onStartCommand()中調用它。據我所知,這應該使您的服務不受限制,但保持運行等待資源問題。希望這有助于某人。

查看完整回答
反對 回復 2019-09-18
  • 3 回答
  • 0 關注
  • 518 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號