Ứng dụng tiếp tục chạy khi dịch vụ nền trước bị dừng lần cuối


9

Tôi đã bắt gặp một hành vi trong quản lý quy trình của Android kết hợp với các dịch vụ tiền cảnh, điều đó thực sự làm tôi bối rối.

Điều gì hợp lý với tôi

  1. Khi bạn vuốt ứng dụng của mình từ 'Ứng dụng gần đây', HĐH sẽ hoàn tất quy trình ứng dụng trong tương lai gần.
  2. Khi bạn vuốt ứng dụng của mình từ 'Ứng dụng gần đây' trong khi chạy dịch vụ nền trước, ứng dụng sẽ tồn tại.
  3. Khi bạn dừng dịch vụ tiền cảnh trước khi vuốt ứng dụng của bạn từ 'Ứng dụng gần đây', bạn sẽ nhận được tương tự như đối với 1).

Điều gì làm tôi bối rối

Khi bạn dừng dịch vụ nền trước trong khi không có hoạt động nào ở nền trước (ứng dụng KHÔNG xuất hiện trong 'Ứng dụng gần đây'), tôi sẽ hy vọng ứng dụng sẽ bị giết ngay bây giờ.

Tuy nhiên, điều này không xảy ra, quá trình ứng dụng vẫn còn sống.

Thí dụ

Tôi đã tạo ra một ví dụ tối thiểu cho thấy hành vi này.

Dịch vụ tiền cảnh:

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import timber.log.Timber

class MyService : Service() {

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onCreate() {
        super.onCreate()
        Timber.d("onCreate")
    }

    override fun onDestroy() {
        super.onDestroy()
        Timber.d("onDestroy")

        // just to make sure the service really stops
        stopForeground(true)
        stopSelf()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Timber.d("onStartCommand")
        startForeground(ID, serviceNotification())
        return START_NOT_STICKY
    }

    private fun serviceNotification(): Notification {
        createChannel()

        val stopServiceIntent = PendingIntent.getBroadcast(
            this,
            0,
            Intent(this, StopServiceReceiver::class.java),
            PendingIntent.FLAG_UPDATE_CURRENT
        )
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("This is my service")
            .setContentText("It runs as a foreground service.")
            .addAction(0, "Stop", stopServiceIntent)
            .build()
    }

    private fun createChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(
                NotificationChannel(
                    CHANNEL_ID,
                    "Test channel",
                    NotificationManager.IMPORTANCE_DEFAULT
                )
            )
        }
    }

    companion object {
        private const val ID = 532207
        private const val CHANNEL_ID = "test_channel"

        fun newIntent(context: Context) = Intent(context, MyService::class.java)
    }
}

BroadcastReceiver dừng dịch vụ:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

class StopServiceReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        val serviceIntent = MyService.newIntent(context)

        context.stopService(serviceIntent)
    }
}

Hoạt động:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startService(MyService.newIntent(this))
    }
}

Tệp kê khai:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.christophlutz.processlifecycletest">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".MyService"/>
        <receiver android:name=".StopServiceReceiver" />
    </application>

</manifest>

Hãy thử các cách sau:

  1. Bắt đầu ứng dụng, dừng dịch vụ tiền cảnh, xóa ứng dụng khỏi 'Ứng dụng gần đây'
  2. Bắt đầu ứng dụng, xóa ứng dụng khỏi 'Ứng dụng gần đây', dừng dịch vụ tiền cảnh

Bạn có thể thấy trong LogCat của Android Studio rằng quy trình ứng dụng được đánh dấu [DEAD] cho trường hợp 1 nhưng không phải cho trường hợp 2.

Vì nó khá dễ tái tạo nên nó có thể là một hành vi có chủ đích, nhưng tôi không tìm thấy bất kỳ đề cập thực sự nào về điều này trong các tài liệu.

Có ai biết những gì đang xảy ra ở đây?

Câu trả lời:


0

Điều này phụ thuộc vào những gì dịch vụ tiền cảnh thực sự làm. Nếu nó sử dụng các luồng, kết nối mạng, I / O tập tin, v.v. tiêu thụ bộ nhớ một cách tích cực, ngay cả khi bạn cố gắng dừng dịch vụ, nó sẽ không bị phá hủy, do đó quá trình sẽ tồn tại. Điều này cũng bao gồm trong bất kỳ cuộc gọi lại giao diện nào còn tồn tại trong khi bạn đang cố gắng dừng dịch vụ. Đặc biệt là các luồng vẫn chạy (thậm chí bị gián đoạn) và các dịch vụ bị ràng buộc chặn vòng đời đang dừng dịch vụ đúng cách.

Đảm bảo rằng bạn không bị rò rỉ bộ nhớ trong dịch vụ của mình và đóng mọi kết nối (cơ sở dữ liệu, mạng, v.v.), xóa tất cả các cuộc gọi lại khỏi tất cả các giao diện trước khi dừng dịch vụ của bạn. Bạn có thể chắc chắn rằng dịch vụ sẽ bị hủy nếu onDestroy()được gọi.

Đối với các quy trình khác nhau: Tôi tin rằng lý do khiến quy trình tồn tại là vì hệ thống nhìn theo cách mà dịch vụ có thể bắt đầu lại trong một thời gian, do đó, nó giữ cho quy trình tồn tại trong một thời gian ngắn. Ít nhất, đó là những gì tôi đã quan sát, bởi vì ngay cả sau khi onDestroy()được gọi, quá trình vẫn tồn tại, tôi đã có thể nhìn thấy nó từ trình gỡ lỗi.

Nếu bạn muốn đảm bảo (mặc dù cách này không được khuyến nghị) rằng quy trình sẽ bị giết chắc chắn sau khi mọi thứ bị phá hủy, bạn luôn có thể gọi đây để tự chấm dứt quá trình:

android.os.Process.killProcess(android.os.Process.myPid());

Bạn có thể tái tạo hành vi với ví dụ từ câu hỏi & mdash; không có kết nối, chuỗi hoặc bất cứ điều gì khác tiếp tục chạy. onDestroy(Dịch vụ) được gọi, nhưng quá trình này vẫn tồn tại lâu sau khi mọi thứ biến mất. Ngay cả khi HĐH giữ cho quá trình tồn tại trong trường hợp dịch vụ được khởi động lại, tôi không hiểu tại sao nó không làm điều tương tự khi bạn dừng dịch vụ trước, sau đó xóa ứng dụng khỏi khoảng cách. Hành vi được hiển thị có vẻ không trực quan, đặc biệt là đã có những thay đổi gần đây đối với giới hạn thực hiện nền, vì vậy thật tuyệt khi biết cách đảm bảo quá trình được dừng lại
David Medenjak

@DavidMedenjak Hãy thử sử dụng getApplicationContext().startService(MyService.newIntent(this));thay vì những gì bạn đang sử dụng và cho tôi biết kết quả. Tôi tin rằng hoạt động vẫn tồn tại vì bối cảnh hoạt động được sử dụng thay vì bối cảnh ứng dụng. Ngoài ra nếu bạn đang thử nghiệm trên Oreo trở lên, hãy thử sử dụnggetApplicationContext().startforegroundService(MyService.newIntent(this));
Furkan Yurdakul

0

Hệ thống Android được biết đến với sự tự nhận thức về bộ nhớ, sức mạnh của bộ xử lý và các ứng dụng xử lý suốt đời - nó tự quyết định có giết chết một quá trình không (giống với các hoạt động và dịch vụ)

Đây là tài liệu chính thức về vấn đề này.

Nhìn vào những gì nó nói về tiền cảnh

Sẽ chỉ có một vài quy trình như vậy trong hệ thống và những quy trình này sẽ chỉ bị giết như là phương sách cuối cùng nếu bộ nhớ quá thấp đến mức thậm chí các quy trình này không thể tiếp tục chạy. Nói chung, tại thời điểm này, thiết bị đã đạt đến trạng thái phân trang bộ nhớ, vì vậy hành động này là bắt buộc để giữ cho giao diện người dùng phản ứng nhanh.

và các quy trình có thể nhìn thấy (Dịch vụ hàng đầu là một quy trình có thể nhìn thấy )

Số lượng các quy trình này đang chạy trong hệ thống ít bị ràng buộc hơn các quy trình nền trước, nhưng vẫn được kiểm soát tương đối. Các quy trình này được coi là cực kỳ quan trọng và sẽ không bị giết trừ khi làm như vậy là bắt buộc để giữ cho tất cả các quy trình tiền cảnh chạy.

Điều đó có nghĩa là HĐH Android sẽ có quá trình ứng dụng của bạn chạy miễn là nó có đủ bộ nhớ để hỗ trợ tất cả các quy trình nền trước . Ngay cả khi bạn dừng nó - hệ thống chỉ có thể di chuyển nó đến các quy trình được lưu trong bộ nhớ cache và xử lý nó theo cách giống như hàng đợi. Cuối cùng, nó sẽ bị giết theo một trong hai cách - nhưng thường thì nó không phải là để bạn quyết định. Thành thật mà nói, bạn không nên quan tâm đến tất cả những gì đang xảy ra với quy trình ứng dụng của mình sau (và miễn là) tất cả các phương thức vòng đời của Android được gọi. Android biết rõ hơn.

Tất nhiên, bạn android.os.Process.killProcess(android.os.Process.myPid());nên hủy quy trình nhưng không được khuyến khích vì nó phá vỡ vòng đời của các phần tử Android thích hợp và các cuộc gọi lại thích hợp có thể không được gọi, do đó, ứng dụng của bạn có thể hoạt động sai trong một số trường hợp.

Hy vọng nó giúp.


0

Trên Android, hệ điều hành sẽ quyết định ứng dụng nào bị giết.

Có một thứ tự ưu tiên:
Các ứng dụng có dịch vụ nền trước hiếm khi bị giết, thay vào đó, các ứng dụng và dịch vụ khác sẽ bị hủy sau một thời gian không hoạt động và đó là những gì xảy ra với ứng dụng của bạn.

Khi bạn hoàn thành dịch vụ tiền cảnh, điều này làm tăng khả năng hệ thống giết ứng dụng của bạn nhưng không có nghĩa là nó sẽ bị giết ngay lập tức.


0

Các màn hình Gần đây [...] là một giao diện người dùng hệ thống cấp mà danh sách mới truy cập các hoạt độngnhiệm vụ .

Bạn có thể có một số hoạt động hoặc tác vụ từ một ứng dụng duy nhất trong danh sách. Đây không phải là danh sách Ứng dụng gần đây .

Do đó, không có mối tương quan trực tiếp giữa các yếu tố của Màn hình gần đây và quy trình ứng dụng.

Dù sao đi nữa , nếu bạn đóng hoạt động cuối cùng của ứng dụng và không có bất cứ thứ gì khác chạy trong quy trình (như dịch vụ tiền cảnh), quy trình sẽ được xóa sạch.

Vì vậy, khi bạn dừng một tiền cảnh đang chạy (bằng stopService()hoặc stopSelf()và không ràng buộc), hệ thống cũng sẽ dọn sạch quá trình mà nó đang chạy.

Vì vậy, nó thực sự là một hành vi dự định .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.