แอพยังคงทำงานต่อไปเมื่อบริการเบื้องหน้าหยุดทำงานล่าสุด


9

ฉันเจอพฤติกรรมในการจัดการกระบวนการของ Android ร่วมกับบริการเบื้องหน้าที่ทำให้ฉันสับสนจริงๆ

มีเหตุผลอะไรสำหรับฉัน

  1. เมื่อคุณปัดแอพของคุณจาก 'แอพล่าสุด' ระบบปฏิบัติการควรเสร็จสิ้นกระบวนการแอปในอนาคตอันใกล้
  2. เมื่อคุณปัดแอพของคุณจาก 'แอพล่าสุด' ขณะที่ใช้บริการเบื้องหน้าแอพจะยังคงอยู่
  3. เมื่อคุณหยุดให้บริการเบื้องหน้าก่อนที่จะปัดแอปของคุณจาก 'แอพล่าสุด' คุณจะได้รับเหมือนกับ 1)

ฉันสับสนอะไร

เมื่อคุณหยุดใช้บริการเบื้องหน้าในขณะที่ไม่มีกิจกรรมใดในเบื้องหน้า (แอพไม่ปรากฏใน 'แอพล่าสุด') ฉันคาดว่าแอพจะถูกฆ่าทันที

อย่างไรก็ตามสิ่งนี้ไม่ได้เกิดขึ้นกระบวนการแอพยังมีชีวิตอยู่

ตัวอย่าง

ฉันได้สร้างตัวอย่างขั้นต่ำที่แสดงพฤติกรรมนี้

ForegroundService:

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 เพื่อหยุดบริการ:

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)
    }
}

กิจกรรม:

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))
    }
}

รายการ:

<?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>

ลองใช้วิธีต่อไปนี้:

  1. เริ่มแอพหยุดบริการเบื้องหน้านำแอพออกจาก 'แอพล่าสุด'
  2. เริ่มแอพลบแอพจาก 'แอพล่าสุด' หยุดบริการส่วนหน้า

คุณสามารถดูได้ใน LogCat ของ Android Studio ว่ากระบวนการแอปมีการทำเครื่องหมาย [DEAD] สำหรับกรณีที่ 1 แต่ไม่ใช่สำหรับกรณีที่ 2

เนื่องจากมันค่อนข้างง่ายที่จะทำซ้ำมันอาจเป็นพฤติกรรมที่ตั้งใจ แต่ฉันไม่พบการพูดถึงสิ่งนี้ในเอกสาร

ไม่มีใครรู้ว่าเกิดอะไรขึ้นที่นี่

คำตอบ:


0

ขึ้นอยู่กับว่าบริการเบื้องหน้าทำอะไรได้จริง หากใช้เธรดการเชื่อมต่อเครือข่ายไฟล์ I / O ฯลฯ ที่ใช้หน่วยความจำอย่างแข็งขันแม้ว่าคุณจะพยายามที่จะหยุดการให้บริการก็จะไม่ถูกทำลายดังนั้นกระบวนการจะยังคงอยู่ นอกจากนี้ยังรวมถึงการโทรกลับอินเทอร์เฟซใด ๆ ที่ยังคงอยู่ในขณะที่คุณพยายามที่จะหยุดบริการ โดยเฉพาะอย่างยิ่งเธรดที่ยังคงทำงานอยู่ (แม้ถูกขัดจังหวะ) และบริการที่ถูกบล็อกจะป้องกันวงจรชีวิตที่หยุดให้บริการอย่างถูกต้อง

ตรวจสอบให้แน่ใจว่าคุณไม่มีหน่วยความจำรั่วในบริการของคุณและปิดทุกการเชื่อมต่อ (ฐานข้อมูลเครือข่าย ฯลฯ ... ) ลบการเรียกกลับทั้งหมดจากอินเทอร์เฟซทั้งหมดก่อนที่จะหยุดบริการของคุณ คุณสามารถตรวจสอบให้แน่ใจว่าบริการจะถูกทำลายถ้าonDestroy()เรียกว่า

สำหรับกระบวนการที่แตกต่าง: ฉันเชื่อว่าเหตุผลที่กระบวนการยังมีชีวิตอยู่นั่นคือระบบจะเห็นว่าบริการอาจเริ่มต้นใหม่อีกครั้งในระยะเวลาหนึ่งดังนั้นจึงทำให้กระบวนการใช้งานได้ในระยะเวลาอันสั้น อย่างน้อยนั่นคือสิ่งที่ฉันสังเกตเพราะแม้หลังจากที่onDestroy()ถูกเรียกกระบวนการยังมีชีวิตอยู่ฉันก็สามารถเห็นได้จากโปรแกรมดีบั๊ก

หากคุณต้องการให้แน่ใจว่า (แม้ว่าจะไม่แนะนำวิธีนี้) ว่ากระบวนการได้รับการฆ่าอย่างแน่นอนหลังจากทุกอย่างถูกทำลายคุณสามารถเรียกสิ่งนี้เพื่อยุติกระบวนการเอง:

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

คุณสามารถสร้างลักษณะการทำงานขึ้นใหม่ด้วยตัวอย่างจากคำถาม & mdash; ไม่มีการเชื่อมต่อเธรดหรือสิ่งอื่นใดที่ทำงานอยู่ onDestroy(บริการ) ได้รับเรียก แต่กระบวนการยังคงมีชีวิตอยู่นานหลังจากทุกอย่างหายไป แม้ว่าระบบปฏิบัติการจะยังคงใช้งานได้ในกรณีที่บริการเริ่มต้นใหม่ฉันไม่เห็นว่าทำไมมันไม่ทำสิ่งเดียวกันเมื่อคุณหยุดให้บริการก่อนจากนั้นจึงลบแอปออกจากสำรับ พฤติกรรมที่ปรากฏดูเหมือนไม่ได้ใช้งานง่ายโดยเฉพาะอย่างยิ่งการเปลี่ยนแปลงล่าสุดของข้อ จำกัด การดำเนินการพื้นหลังดังนั้นจึงเป็นเรื่องดีที่ได้ทราบวิธีการตรวจสอบให้แน่ใจว่ากระบวนการหยุดทำงาน
David Medenjak

@DavidMedenjak ลองใช้getApplicationContext().startService(MyService.newIntent(this));แทนสิ่งที่คุณใช้และบอกผลลัพธ์ให้ฉัน ฉันเชื่อว่ากิจกรรมยังคงมีชีวิตอยู่เพราะบริบทของกิจกรรมถูกใช้แทนบริบทของแอปพลิเคชัน และถ้าคุณกำลังทดสอบโอริโอหรือสูงกว่าลองใช้getApplicationContext().startforegroundService(MyService.newIntent(this));
Furkan Yurdakul

0

ระบบ Android เป็นที่รู้จักกันดีในเรื่องของการรับรู้ในแง่ของหน่วยความจำพลังงานของโปรเซสเซอร์และแอพพลิเคชั่นตลอดชีวิต - มันตัดสินใจด้วยตัวเองว่าจะฆ่ากระบวนการไม่ (เหมือนกันกับกิจกรรมและบริการ)

นี่คือเอกสารอย่างเป็นทางการเกี่ยวกับเรื่องนี้

ดูสิ่งที่มันพูดเกี่ยวกับเบื้องหน้า

จะมีเพียงไม่กี่กระบวนการดังกล่าวในระบบและสิ่งเหล่านี้จะถูกฆ่าเป็นทางเลือกสุดท้ายหากหน่วยความจำเหลือน้อยดังนั้นแม้กระบวนการเหล่านี้จะยังสามารถทำงานต่อไปได้ โดยทั่วไป ณ จุดนี้อุปกรณ์ได้ถึงสถานะเพจจิ้งหน่วยความจำดังนั้นการกระทำนี้จำเป็นเพื่อให้ส่วนติดต่อผู้ใช้ตอบสนอง

และกระบวนการที่มองเห็นได้ (Foreground Service เป็นกระบวนการที่มองเห็นได้ )

จำนวนกระบวนการเหล่านี้ที่ทำงานในระบบนั้นมีขอบเขตที่น้อยกว่ากระบวนการพื้นหน้า แต่ยังคงควบคุมได้ค่อนข้างดี กระบวนการเหล่านี้ถือว่ามีความสำคัญอย่างยิ่งและจะไม่ถูกฆ่าเว้นแต่จะต้องดำเนินการเพื่อให้กระบวนการพื้นหน้าทั้งหมดทำงานอยู่

หมายความว่า Android OS จะมีกระบวนการแอปของคุณทำงานตราบเท่าที่มันจะมีหน่วยความจำเพียงพอที่จะรองรับกระบวนการพื้นหน้าทั้งหมด แม้ว่าคุณจะหยุดมัน - ระบบอาจย้ายไปยังกระบวนการแคชและจัดการในลักษณะที่คล้ายคิว ในที่สุดมันจะถูกฆ่าด้วยวิธีใดวิธีหนึ่ง - แต่โดยปกติแล้วคุณจะไม่ตัดสินใจ ตรงไปตรงมาคุณไม่ควรสนใจสิ่งที่เกิดขึ้นกับกระบวนการแอพของคุณหลังจาก (และตราบใดที่) วิธีการใช้งานวงจร Android ทั้งหมดเรียกว่า Android รู้ดีกว่า

แน่นอนคุณไม่สามารถฆ่ากระบวนการด้วยandroid.os.Process.killProcess(android.os.Process.myPid());แต่ไม่แนะนำเนื่องจากมันรบกวนวงจรชีวิตขององค์ประกอบ Android ที่เหมาะสมและอาจไม่มีการโทรกลับอย่างเหมาะสมดังนั้นแอพของคุณอาจทำงานผิดปกติในบางกรณี

หวังว่ามันจะช่วย


0

บน Android เป็นระบบปฏิบัติการที่ตัดสินว่าแอปพลิเคชันใดถูกฆ่า

มีลำดับความสำคัญ:
แอปพลิเคชันที่มีบริการพื้นหน้าไม่ค่อยถูกฆ่า แต่แอปและบริการอื่น ๆ จะถูกฆ่าหลังจากช่วงเวลาที่ไม่มีกิจกรรมและนั่นคือสิ่งที่เกิดขึ้นกับแอปของคุณ

เมื่อคุณเสร็จสิ้นการให้บริการเบื้องหน้าสิ่งนี้จะเพิ่มความเป็นไปได้ของระบบที่จะฆ่าแอปของคุณ แต่ก็ไม่ได้หมายความว่ามันจะถูกฆ่าทันที


0

หน้าจอล่าสุด [ ... ]เป็น UI ของระบบในระดับที่แสดงรายการเข้าถึงได้เมื่อเร็ว ๆ นี้กิจกรรมและงาน

คุณสามารถมีกิจกรรมหรืองานหลายอย่างจากแอปเดียวในรายการ มันไม่ปพลิเคชันล่าสุดรายการ

ดังนั้นจึงไม่มีความสัมพันธ์โดยตรงระหว่างองค์ประกอบของหน้าจอล่าสุดและกระบวนการแอป

อย่างไรก็ตามถ้าคุณปิดกิจกรรมล่าสุดของแอพและไม่มีอะไรทำงานอยู่ในกระบวนการ (เช่นบริการเบื้องหน้า) กระบวนการเพิ่งจะหมดไป

ดังนั้นเมื่อคุณหยุดการทำงานเบื้องหน้า (โดยstopService()หรือหรือยกเลิกการstopSelf()ผูก) ระบบจะล้างกระบวนการที่กำลังทำงานอยู่

ดังนั้นจึงเป็นที่แน่นอนพฤติกรรมตั้งใจ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.