2011-07-11 23 views
64

Cập nhật: Tôi chưa tìm thấy giải pháp thực sự cho vấn đề. Những gì tôi đã đưa ra là một phương pháp tự động kết nối lại với một thiết bị bluetooth trước đó bất cứ lúc nào kết nối bị mất. Nó không phải là lý tưởng, nhưng nó có vẻ hoạt động khá tốt. Tôi rất muốn nghe bất kỳ đề xuất thêm về điều này mặc dù.Dịch vụ Foreground bị giết bởi Android

Tôi gặp vấn đề tương tự như trong câu hỏi này: Service being killed while holding wake lock and after calling startForeground bao gồm thiết bị (Asus Transformer), thời gian trước khi ngừng dịch vụ (30-45 phút), sử dụng khóa đánh thức, sử dụng của startForeground(), và thực tế là vấn đề không xảy ra nếu ứng dụng được mở khi màn hình tắt.

Ứng dụng của tôi duy trì kết nối bluetooth với một thiết bị khác và gửi dữ liệu giữa hai thiết bị, vì vậy thiết bị phải luôn hoạt động mọi lúc để nghe dữ liệu. Người dùng có thể bắt đầu và ngừng dịch vụ theo ý muốn, và trên thực tế, đây là cách duy nhất tôi đã triển khai để bắt đầu hoặc ngừng dịch vụ. Khi dịch vụ khởi động lại, kết nối bluetooth với thiết bị khác bị mất.

Theo câu trả lời trong câu hỏi được liên kết, startForeground() "làm giảm khả năng dịch vụ bị giết, nhưng không ngăn chặn được". Tôi hiểu rằng đó là trường hợp, tuy nhiên tôi đã thấy nhiều ví dụ về các ứng dụng khác mà không có vấn đề này (Tasker, ví dụ).

Tính hữu ích của ứng dụng của tôi sẽ giảm đáng kể mà không có khả năng dịch vụ chạy cho đến khi người dùng dừng lại. Có cách nào để tránh điều này???

Tôi thấy điều này trong logcat của tôi bất cứ khi nào dịch vụ được dừng lại:

ActivityManager: No longer want com.howettl.textab (pid 32321): hidden #16 
WindowManager: WIN DEATH: Window{40e2d968 com.howettl.textab/com.howettl.textab.TexTab paused=false 
ActivityManager: Scheduling restart of crashed service com.howettl.textab/.TexTabService in 5000ms 

EDIT: Tôi cũng nên lưu ý, điều này dường như không xảy ra trên các thiết bị khác mà tôi đang kết nối với: HTC Legend chạy Xyanogen

EDIT: đây là sản phẩm của adb shell dumpsys activity services:

* ServiceRecord{40f632e8 com.howettl.textab/.TexTabService} 

intent={cmp=com.howettl.textab/.TexTabService} 

packageName=com.howettl.textab 

processName=com.howettl.textab 

baseDir=/data/app/com.howettl.textab-1.apk 

resDir=/data/app/com.howettl.textab-1.apk 

dataDir=/data/data/com.howettl.textab 

app=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=-25m42s124ms 

startRequested=true stopIfKilled=false callStart=true lastStartId=1 

Bindings: 

* IntentBindRecord{40a02618}: 

    intent={cmp=com.howettl.textab/.TexTabService} 

    [email protected] 

    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} 

All Connections: 

    ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8} 

Và đầu ra của adb shell dumpsys activity:

0.123.
* TaskRecord{40f5c050 #23 A com.howettl.textab} 

numActivities=1 rootWasReset=false 

affinity=com.howettl.textab 

intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab} 

realActivity=com.howettl.textab/.TexTab 

lastActiveTime=4877757 (inactive for 702s) 

* Hist #1: ActivityRecord{40a776c8 com.howettl.textab/.TexTab} 

    packageName=com.howettl.textab processName=com.howettl.textab 

    launchedFromUid=2000 app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104} 

    Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab } 

    frontOfTask=true task=TaskRecord{40f5c050 #23 A com.howettl.textab} 

    taskAffinity=com.howettl.textab 

    realActivity=com.howettl.textab/.TexTab 

    base=/data/app/com.howettl.textab-1.apk/data/app/com.howettl.textab-1.apk data=/data/data/com.howettl.textab 

    labelRes=0x7f060000 icon=0x7f020000 theme=0x0 

    stateNotNeeded=false componentSpecified=true isHomeActivity=false 

    configuration={ scale=1.0 imsi=0/0 loc=en_CA touch=3 keys=2/1/1 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=6} 

    launchFailed=false haveState=true icicle=Bundle[mParcelledData.dataSize=1644] 

    state=STOPPED stopped=true delayedResume=false finishing=false 

    keysPaused=false inHistory=true visible=false sleeping=true idle=true 

    fullscreen=true noDisplay=false immersive=false launchMode=2 

    frozenBeforeDestroy=false thumbnailNeeded=false 

    connections=[ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}] 

...

ProC#15: adj=prcp /F 40e75070 959:android.process.acore/10006 (provider) 

      com.android.providers.contacts/.ContactsProvider2<=Proc{40bb0098 2995:com.howettl.textab/10104} 

ProC#16: adj=bak+2/F 40bb0098 2995:com.howettl.textab/10104 (foreground-service) 

Những xuất hiện để hiển thị các dịch vụ đang chạy trong foreground.

+0

Hãy xem câu trả lời này - có thể phù hợp với bạn http://stackoverflow.com/a/21157035/624109 – Muzikant

Trả lời

164

Phím tắt Okey. Tôi đã trải qua địa ngục và trở lại vấn đề này. Đây là cách tiến hành. Có lỗi. Bài đăng này mô tả cách phân tích lỗi trong quá trình triển khai và khắc phục sự cố.

Để tóm tắt, đây là cách mọi thứ được cho là hoạt động.Các dịch vụ đang chạy sẽ được dọn dẹp thường xuyên và chấm dứt sau mỗi 30 phút hoặc lâu hơn. Các dịch vụ muốn tồn tại lâu hơn điều này phải gọi Service.startForeground, nơi đặt thông báo trên thanh thông báo để người dùng biết rằng dịch vụ của bạn đang chạy vĩnh viễn và có khả năng hút tuổi thọ pin. Chỉ có 3 quy trình dịch vụ có thể tự đề cử là dịch vụ tiền cảnh tại bất kỳ thời điểm nào. Nếu có nhiều hơn ba dịch vụ tiền cảnh, Android sẽ chỉ định dịch vụ lâu đời nhất làm ứng cử viên cho việc nhặt rác và chấm dứt.

Thật không may, có lỗi trong Android liên quan đến việc ưu tiên dịch vụ tiền cảnh, được kích hoạt bởi các kết hợp khác nhau của cờ ràng buộc dịch vụ. Mặc dù bạn đã chỉ định chính xác dịch vụ của mình như một dịch vụ nền trước, Android có thể chấm dứt dịch vụ của bạn, nếu có bất kỳ kết nối nào với các dịch vụ trong quá trình của bạn. Chi tiết được đưa ra dưới đây.

Lưu ý rằng rất ít dịch vụ cần phải là dịch vụ nền trước. Nói chung, bạn chỉ cần trở thành dịch vụ tiền cảnh nếu bạn có kết nối internet hoạt động liên tục hoặc lâu dài thuộc loại nào đó có thể được bật và tắt hoặc bị người dùng hủy. Ví dụ về các dịch vụ cần tình trạng tiền cảnh: máy chủ UPNP, tải xuống lâu các tệp rất lớn, đồng bộ hóa hệ thống tệp bằng wi-fi và phát nhạc.

Nếu thỉnh thoảng bạn chỉ thỉnh thoảng bỏ phiếu hoặc chờ đợi trên máy thu phát sóng hệ thống hoặc sự kiện hệ thống, bạn nên bật dịch vụ của mình trên bộ hẹn giờ hoặc trả lời người phát sóng. . Đó là hành vi được thiết kế cho các dịch vụ. Nếu bạn chỉ đơn giản là phải sống sót, sau đó đọc tiếp.

Sau khi đã chọn hộp trên các yêu cầu nổi tiếng (ví dụ: Gọi Service.startForeground), địa điểm tiếp theo để xem là các cờ bạn sử dụng trong các cuộc gọi Context.bindService. Các cờ được sử dụng để liên kết ảnh hưởng đến mức ưu tiên của quy trình dịch vụ đích theo nhiều cách khác nhau. Đặc biệt nhất, việc sử dụng các cờ ràng buộc nhất định có thể làm cho Android hạ cấp không chính xác dịch vụ tiền cảnh của bạn sang dịch vụ thông thường. Mã được sử dụng để chỉ định mức độ ưu tiên của quá trình đã bị khuấy động khá nhiều. Đáng chú ý, có các bản sửa đổi trong API 14+ có thể gây ra lỗi khi sử dụng các cờ ràng buộc cũ hơn; và có lỗi xác định trong 4.2.1.

Bạn của bạn trong tất cả các tiện ích này là tiện ích sysdump, có thể được sử dụng để xác định mức độ ưu tiên mà Trình quản lý hoạt động đã chỉ định quy trình dịch vụ của bạn và phát hiện các trường hợp được ưu tiên không chính xác. Nhận dịch vụ của bạn lên và chạy, và sau đó thực hiện lệnh sau đây từ một dấu nhắc lệnh trên máy chủ của bạn:

quá trình

dumpsys adb shell activity> tmp.txt

Sử dụng notepad (không wordpad/ghi) để kiểm tra nội dung.

Trước tiên hãy xác minh rằng bạn đã quản lý thành công để chạy dịch vụ của mình ở trạng thái tiền cảnh. Phần đầu tiên của tệp dumpsys chứa một mô tả các thuộc tính ActivityManager cho mỗi quá trình. Hãy tìm một dòng như sau tương ứng với ứng dụng của bạn trong phần đầu tiên của tập tin dumpsys:

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

Xác minh rằng foregroundServices = true trong phần sau. Đừng lo lắng về các thiết lập ẩn và trống; họ mô tả trạng thái của các hoạt động trong tiến trình, và dường như không có liên quan đặc biệt đến các quy trình với các dịch vụ trong chúng. Nếu foregroundService không đúng, bạn cần gọi Service.startForeground để làm cho nó đúng.

Điều tiếp theo bạn cần xem xét là phần gần cuối tệp có tiêu đề "Danh sách xử lý LRU (được sắp xếp theo oom_adj):".Các mục nhập trong danh sách này cho phép bạn xác định xem Android có thực sự phân loại ứng dụng của bạn như một dịch vụ nền trước hay không. Nếu quá trình của bạn nằm ở dưới cùng của danh sách này, đó là một ứng cử viên chính cho tiêu diệt tóm tắt. Nếu quá trình của bạn ở gần đầu danh sách, nó hầu như không thể phá hủy.

Hãy nhìn vào một dòng trong bảng này:

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

Đây là một ví dụ về một dịch vụ foreground đã xong tất cả mọi thứ đúng. Trường khóa ở đây là trường "adj =". Điều đó cho thấy mức độ ưu tiên mà quy trình của bạn được chỉ định bởi ActivityManagerService sau khi mọi thứ đã được thực hiện. Bạn muốn nó là "adj = prcp" (dịch vụ tiền cảnh có thể nhìn thấy); hoặc "adj = vis" (quá trình hiển thị với hoạt động) hoặc "fore" (quá trình có hoạt động tiền cảnh). Nếu đó là "adj = svc" (quy trình dịch vụ) hoặc "adj = svcb" (dịch vụ kế thừa?) Hoặc "adj = bak" (quy trình nền trống), thì quy trình của bạn là ứng cử viên có khả năng chấm dứt và sẽ bị chấm dứt không ít thường xuyên hơn mỗi 30 phút ngay cả khi không có bất kỳ áp lực nào để đòi lại bộ nhớ. Các cờ còn lại trên dòng hầu hết là thông tin gỡ lỗi chẩn đoán cho các kỹ sư của Google. Quyết định chấm dứt được thực hiện dựa trên các lĩnh vực điều chỉnh. Tóm lại,/FS chỉ ra một dịch vụ nền trước;/FA chỉ ra một tiến trình tiền cảnh với một hoạt động./B cho biết dịch vụ nền. Nhãn ở cuối cho biết quy tắc chung mà quy trình được chỉ định mức độ ưu tiên. Thông thường nó phải khớp với trường adj =; nhưng giá trị adj = có thể được điều chỉnh lên hoặc xuống trong một số trường hợp do cờ ràng buộc trên các liên kết hoạt động với các dịch vụ hoặc hoạt động khác.

Nếu bạn đã vấp một lỗi với cờ ràng buộc, dòng dumpsys sẽ trông như thế này:

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

Lưu ý cách thức giá trị của trường adj được đặt không đúng để "adj = bak" (trống quá trình nền), mà dịch gần như "xin vui lòng chấm dứt tôi ngay bây giờ để tôi có thể kết thúc sự tồn tại vô nghĩa này" cho các mục đích của quá trình nhặt rác. Cũng lưu ý cờ (fg-service) ở cuối dòng cho biết rằng "quy tắc dịch vụ forground đã được sử dụng để xác định cài đặt" adj ". Mặc dù thực tế là các quy tắc dịch vụ fg đã được sử dụng, quá trình này được gán một cài đặt điều chỉnh "bak", và nó sẽ không tồn tại lâu dài Một cách đơn giản, đây là một lỗi.

Vì vậy, mục tiêu là đảm bảo rằng quy trình của bạn luôn được "adj = prcp" (hoặc tốt hơn). đạt được mục tiêu đó là tinh chỉnh cờ ràng buộc cho đến khi bạn quản lý để tránh các lỗi trong phân bổ ưu tiên

Dưới đây là những lỗi tôi biết. (1) Nếu BẤT CỨ dịch vụ hoặc hoạt động nào đã bao giờ kết nối với dịch vụ bằng cách sử dụng Context.BIND_ABOVE_CLIENT, bạn chạy rủi ro rằng thiết lập adj = sẽ bị hạ cấp xuống "bak" ngay cả khi liên kết đó không hoạt động nữa. Điều này đặc biệt đúng nếu bạn cũng có các ràng buộc giữa các dịch vụ. Một lỗi rõ ràng trong các nguồn 4.2.1. (2) Chắc chắn không bao giờ sử dụng BIND_ABOVE_CLIENT cho một ràng buộc dịch vụ tới dịch vụ. Không sử dụng nó cho các kết nối hoạt động tới dịch vụ. Cờ được sử dụng để thực hiện hành vi BIND_ABOVE_CLIENT dường như được đặt trên cơ sở mỗi quá trình, thay vì cơ sở mỗi kết nối, do đó, nó kích hoạt lỗi với các ràng buộc dịch vụ tới dịch vụ ngay cả khi không có hoạt động dịch vụ đang hoạt động ràng buộc với bộ cờ. Dường như có vấn đề với việc thiết lập ưu tiên khi có nhiều dịch vụ trong quá trình này, với các ràng buộc dịch vụ tới dịch vụ. Sử dụng Context.BIND_WAIVE_PRIORITY (API 14) trên các ràng buộc dịch vụ tới dịch vụ có vẻ hữu ích. Bối cảnh.BIND_IMPORTANT dường như là một ý tưởng hay hơn khi liên kết từ Hoạt động với dịch vụ. Làm như vậy sẽ làm tăng mức độ ưu tiên của quy trình của bạn lên cao hơn khi Hoạt động ở mặt trước, mà không gây ra bất kỳ tác hại rõ ràng nào khi Hoạt động bị tạm dừng hoặc kết thúc.

Nhưng nhìn chung, chiến lược là điều chỉnh cờ bindService của bạn cho đến khi sysdump cho biết rằng quy trình của bạn đã nhận được mức độ ưu tiên chính xác.

Vì mục đích của tôi, sử dụng Ngữ cảnh.BIND_AUTO_CREATE | Bối cảnh.BIND_IMPORTANT cho các ràng buộc Hoạt động tới dịch vụ và Ngữ cảnh.BIND_AUTO_CREATE | Bối cảnh.BIND_WAIVE_PRIORITY cho các ràng buộc dịch vụ tới dịch vụ dường như làm điều đúng. Số dặm của bạn có thể khác nhau.

Ứng dụng của tôi khá phức tạp: hai dịch vụ nền, mỗi dịch vụ có thể giữ trạng thái dịch vụ tiền cảnh độc lập, cộng thêm một phần ba cũng có thể sử dụng trạng thái dịch vụ nền trước; hai trong số các dịch vụ liên kết với nhau theo điều kiện; thứ ba liên kết với người đầu tiên, luôn luôn. Ngoài ra, Activites chạy trong một quá trình riêng biệt (làm cho hình ảnh động mượt mà hơn). Chạy các hoạt động và dịch vụ trong cùng một quá trình dường như không tạo ra bất kỳ sự khác biệt nào.

Thực hiện các quy tắc cho quá trình nhặt rác, (và mã nguồn sử dụng để tạo ra các nội dung của các tập tin sysdump) có thể được tìm thấy trong lõi android tập tin

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

Bon cơ hội.

PS: Đây là cách diễn giải các chuỗi sysdump cho Android 5.0. Tôi đã không làm việc với họ, do đó, làm cho họ những gì bạn sẽ. Tôi tin rằng bạn muốn 4 là 'A' hoặc 'S' và 5 là "IF" hoặc "IB" và 1 càng thấp càng tốt (có thể dưới 3, vì chỉ có 3 ba quy trình dịch vụ tiền cảnh được duy trì hoạt động trong cấu hình mặc định).

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 
+1

@Robin Davies, tôi có một câu hỏi nhỏ. Tôi có thực sự cần gọi 'bindService()' nếu tôi cần Dịch vụ liên tục đang chạy không? Là nó không đủ chỉ cần gọi 'startForeground()' trong dịch vụ? Để liên lạc với máy chủ, tôi sử dụng EventBus. –

+0

Bạn gọi Context.bindService từ một Hoạt động để dịch vụ chạy ngay từ đầu. Phương thức Service.startService được gọi bằng mã trong dịch vụ để di chuyển một dịch vụ có các beens bắt đầu vào trạng thái "tiền cảnh". Tôi sẽ giả định rằng thư viện EventBus đang gọi Context.bindService thay mặt bạn vào một thời điểm nào đó để bắt đầu dịch vụ. Nếu có một cách khác để bắt đầu một dịch vụ, tôi không quen với nó. –

+1

Bài đăng tuyệt vời! Được thăng hạng. Một phần tôi muốn thêm vào nhận xét này, mà tôi tin là có liên quan. Nếu bạn muốn một dịch vụ liên tục chạy, như Robin đã đề cập bạn cần phải bắt đầu nó bằng cách nào đó. Có thể gọi startService (Intent service) trực tiếp bên trong hoạt động của bạn thay vì bindService(), và sau đó khi dịch vụ của bạn khởi động, bạn có thể gọi phương thức startForeground(). Tôi gọi nó trong lớp onStartCommand() của lớp dịch vụ. Theo như tôi biết, điều này sẽ làm cho dịch vụ của bạn không bị ràng buộc, nhưng giữ cho nó đang chạy các vấn đề tài nguyên đang chờ xử lý.Hy vọng rằng điều này sẽ giúp một ai đó. – Dave

5

Nếu nó đang nói "không còn muốn ..." thì quá trình đó không có dịch vụ đang hoạt động trong đó hiện đang ở trạng thái startForeground(). Kiểm tra để đảm bảo cuộc gọi của bạn thực sự thành công - bạn thấy thông báo được đăng, không có thư nào trong nhật ký tại thời điểm đó phàn nàn về bất kỳ điều gì, v.v. Ngoài ra, hãy sử dụng "dịch vụ hoạt động dumpb shell của adb shell" để xem trạng thái dịch vụ của bạn và đảm bảo dịch vụ đó thực sự được đánh dấu là tiền cảnh. Ngoài ra nếu nó là tiền cảnh chính xác thì trong đầu ra của "hoạt động dumpsys adb shell" bạn sẽ thấy trong phần hiển thị điều chỉnh OOM của các quá trình mà quá trình của bạn hiện đang ở mức tiền cảnh do dịch vụ đó.

+0

Cảm ơn bạn đã trợ giúp! Tôi đã chỉnh sửa câu hỏi của mình với đầu ra của các lệnh bạn đã đề cập. Họ dường như chỉ ra rằng dịch vụ đang chạy ở nền trước. – howettl

+0

Có một phần mã tôi có thể đăng có thể giúp chẩn đoán không? – howettl

+1

Nó chắc chắn không nên bị giết trong khi ở phía trước, và tôi biết chắc chắn rằng những thứ như Âm nhạc trong nền tảng tiêu chuẩn thì không. Xem xét việc nộp một lỗi với mã để tái tạo vấn đề. Một điều cần xem xét là nếu bạn đang di chuyển vào và ra khỏi nền trước tại bất kỳ điểm nào có thể cho phép nó bị giết. – hackbod

Các vấn đề liên quan