当前位置: 首页 > 知识库问答 >
问题:

FCM服务被杀死后,我刷我的应用程序从多任务托盘

段干英杰
2023-03-14

我有一个通过FCM数据消息触发来电的应用程序(优先级:高,没有通知有效载荷)。当app处于前台或后台时,app接收呼叫。对于上述情况,在锁定屏幕中接收通知(来电)。

但由于某种原因,当关闭应用程序或从多任务托盘中刷卡时,电话就不再接到了。我认为服务被扼杀了。我能做什么?

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<service
        android:name=".firebase.MyFirebaseMessagingService"
        android:exported="true"
        android:enabled="true">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>

工作流当收到FCM通知时,一个后台服务启动,然后该服务将自身更改为具有“来电”通知的前台服务。我也在使用全屏意图。下层是我用来服务的

class IncomingCallNotificationService : Service() {

private val TAG = IncomingCallNotificationService::class.java.simpleName
var isRunning = false
private lateinit var soundUri: Uri

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + applicationContext.packageName + "/" + R.raw.incoming)
    val videoCallDetails: VideoNotification?= intent.getParcelableExtra(Constants.VIDEO_CALL_DETAILS)
    val notificationId = intent.getIntExtra(Constants.INCOMING_CALL_NOTIFICATION_ID, 0)
    val action = intent.action
    if (action != null) {
        when(action){
            ACTION_INCOMING_CALL_NOTIFICATION -> { handleIncomingCall(videoCallDetails!!, notificationId) }
            ACTION_ACCEPT -> {
                endForeground()
                val intent = Intent(this, VideoActivity::class.java)
                intent.action = ACTION_INCOMING_CALL_NOTIFICATION
                intent.putExtra(Constants.ROOM_CODE, videoCallDetails?.roomCode)
                intent.putExtra(Constants.ROOM_NAME, videoCallDetails?.roomName)
                intent.putExtra(Constants.ACCESS_TOKEN, videoCallDetails?.accessToken)
                intent.putExtra(Constants.CALLER_NAME, videoCallDetails?.userName)
                intent.putExtra(
                    Constants.CALL_RESPONSE_ACTION_KEY,
                    Constants.INCOMING_CALL_SCREEN
                )
                intent.putExtra(Constants.CALL_TYPE, Constants.VIDEO_CALL)
                intent.putExtra(Constants.NOTIFCATION_ID, notificationId)
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                intent.action = ACTION_ACCEPT
                startActivity(intent)
            }
            ACTION_REJECT -> {
                endForeground()
                val it = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
                applicationContext.sendBroadcast(it)
            }
        }
    }
    return START_NOT_STICKY
}


override fun onBind(intent: Intent): IBinder? {
    return null
}

private fun createNotification(
    videoCallDetails: VideoNotification,
    notificationId: Int,
    channelImportance: Int
): Notification? {
    val intent = Intent(this, VideoActivity::class.java)
    intent.action = ACTION_INCOMING_CALL_NOTIFICATION
    intent.putExtra(Constants.ROOM_CODE, videoCallDetails.roomCode)
    intent.putExtra(Constants.ROOM_NAME, videoCallDetails.roomName)
    intent.putExtra(Constants.ACCESS_TOKEN, videoCallDetails.accessToken)
    intent.putExtra(Constants.CALLER_NAME, videoCallDetails.userName)
    intent.putExtra(Constants.CALL_RESPONSE_ACTION_KEY, Constants.INCOMING_CALL_SCREEN)
    intent.putExtra(Constants.CALL_TYPE, Constants.VIDEO_CALL)
    intent.putExtra(Constants.INCOMING_VIDEO_CALL, true)
    intent.putExtra(Constants.NOTIFCATION_ID, notificationId)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
    val pendingIntent = PendingIntent.getActivity(
        this,
        notificationId,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
    /*
     * Pass the notification id and call sid to use as an identifier to cancel the
     * notification later
     */
    val extras = Bundle()
    extras.putString(Constants.ROOM_CODE, videoCallDetails.roomCode)
    extras.putString(Constants.ROOM_NAME, videoCallDetails.roomName)
    extras.putString(Constants.ACCESS_TOKEN, videoCallDetails.accessToken)
    extras.putString(Constants.CALLER_NAME, videoCallDetails.userName)
    extras.putString(Constants.CALL_RESPONSE_ACTION_KEY, Constants.INCOMING_CALL_SCREEN)
    extras.putString(Constants.CALL_TYPE, Constants.VIDEO_CALL)

    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        buildNotification(
            videoCallDetails.userName + " is calling.",
            pendingIntent,
            extras,
            videoCallDetails,
            notificationId,
            createChannel(channelImportance)!!
        )
    } else {
        val builder = NotificationCompat.Builder(this)
            .setSmallIcon(R.drawable.ic_call_end_white_24dp)
            .setContentTitle(INCOMING_VIDEO_CALL_TEXT)
            .setContentText(videoCallDetails.userName + " is calling.")
            .setAutoCancel(true)
            .setExtras(extras)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setContentIntent(pendingIntent)
            .setTimeoutAfter(MomsezeApplication.notificationTimeOut)
            .setGroup("test_app_notification")
            .setColor(Notification.DEFAULT_LIGHTS)
            .setSound(soundUri)
        val notification = builder.build()
        notification.flags = Notification.FLAG_INSISTENT
        return notification
    }
}

@TargetApi(Build.VERSION_CODES.O)
private fun buildNotification(
    text: String, pendingIntent: PendingIntent, extras: Bundle,
    videoCallDetails: VideoNotification,
    notificationId: Int,
    channelId: String
): Notification? {
    val rejectIntent = Intent(applicationContext, IncomingCallNotificationService::class.java)
    rejectIntent.action = Constants.ACTION_REJECT
    rejectIntent.putExtra(Constants.VIDEO_CALL_DETAILS, videoCallDetails)
    rejectIntent.putExtra(Constants.INCOMING_CALL_NOTIFICATION_ID, notificationId)
    val piRejectIntent = PendingIntent.getService(
        applicationContext,
        0,
        rejectIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
    val acceptIntent = Intent(applicationContext, IncomingCallNotificationService::class.java)
    acceptIntent.action = ACTION_ACCEPT
    acceptIntent.putExtra(Constants.VIDEO_CALL_DETAILS, videoCallDetails)
    acceptIntent.putExtra(Constants.INCOMING_CALL_NOTIFICATION_ID, notificationId)
    val piAcceptIntent = PendingIntent.getService(
        applicationContext,
        0,
        acceptIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
    val builder = NotificationCompat.Builder(applicationContext, channelId)
        .setSmallIcon(R.drawable.ic_call_end_white_24dp)
        .setContentTitle(INCOMING_VIDEO_CALL_TEXT)
        .setContentText(text)
        .setCategory(Notification.CATEGORY_CALL)
        .setFullScreenIntent(pendingIntent, true)
        .setExtras(extras)
        .setSound(soundUri)
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .setAutoCancel(true)
        .addAction(android.R.drawable.ic_menu_delete, getString(R.string.decline), piRejectIntent)
        .addAction(android.R.drawable.ic_menu_call, getString(R.string.answer), piAcceptIntent)
        .setFullScreenIntent(pendingIntent, true)
        .setTimeoutAfter(MomsezeApplication.notificationTimeOut)
    val notification = builder.build()
    notification.flags = Notification.FLAG_INSISTENT
    return notification
}

@TargetApi(Build.VERSION_CODES.O)
private fun createChannel(channelImportance: Int): String? {
    var callInviteChannel = NotificationChannel(
        Constants.VOICE_CHANNEL_HIGH_IMPORTANCE,
        "Primary Voice Channel", NotificationManager.IMPORTANCE_HIGH
    )
    var channelId = Constants.VOICE_CHANNEL_HIGH_IMPORTANCE
    if (channelImportance == NotificationManager.IMPORTANCE_LOW) {
        callInviteChannel = NotificationChannel(
            Constants.VOICE_CHANNEL_LOW_IMPORTANCE,
            "Primary Voice Channel",
            NotificationManager.IMPORTANCE_DEFAULT
        )
        channelId = Constants.VOICE_CHANNEL_LOW_IMPORTANCE
    }
    val audioAttributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_NOTIFICATION)
        .build()
    callInviteChannel.setSound(soundUri, audioAttributes)
    callInviteChannel.lightColor = Color.GREEN
    callInviteChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
    val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.createNotificationChannel(callInviteChannel)
    return channelId
}

private fun handleIncomingCall(videoCallDetails: VideoNotification, notificationId: Int) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        setCallInProgressNotification(videoCallDetails, notificationId)
    }
    sendCallInviteToActivity(videoCallDetails, notificationId)
}

private fun endForeground() {
    stopForeground(true)
}

@TargetApi(Build.VERSION_CODES.O)
private fun setCallInProgressNotification(
    videoCallDetails: VideoNotification,
    notificationId: Int
) {
    if (isAppVisible()) {
        startForeground(
            notificationId, createNotification(
                videoCallDetails,
                notificationId,
                NotificationManager.IMPORTANCE_LOW
            )
        )
    } else {
        startForeground(
            notificationId, createNotification(
                videoCallDetails,
                notificationId,
                NotificationManager.IMPORTANCE_HIGH
            )
        )
    }
}

/*
 * Send the CallInvite to the VoiceActivity. Start the activity if it is not running already.
 */
private fun sendCallInviteToActivity(videoCallDetails: VideoNotification, notificationId: Int) {
    if (Build.VERSION.SDK_INT >= 29 && !isAppVisible()) {
        return
    }
    val intent = Intent(this, VideoActivity::class.java)
    intent.action = ACTION_INCOMING_CALL_NOTIFICATION
    intent.putExtra(Constants.ROOM_CODE, videoCallDetails.roomCode)
    intent.putExtra(Constants.ROOM_NAME, videoCallDetails.roomName)
    intent.putExtra(Constants.ACCESS_TOKEN, videoCallDetails.accessToken)
    intent.putExtra(Constants.CALLER_NAME, videoCallDetails.userName)
    intent.putExtra(Constants.CALL_RESPONSE_ACTION_KEY, Constants.INCOMING_CALL_SCREEN)
    intent.putExtra(Constants.CALL_TYPE, Constants.VIDEO_CALL)
    intent.putExtra(Constants.INCOMING_VIDEO_CALL, true)
    intent.putExtra(Constants.NOTIFCATION_ID, notificationId)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    this.startActivity(intent)
}

private fun isAppVisible(): Boolean {
    return ProcessLifecycleOwner
        .get()
        .lifecycle
        .currentState
        .isAtLeast(Lifecycle.State.STARTED)
}

private fun createRandomCode(codeLength: Int): Int {
    val chars = "1234567890".toCharArray()
    val sb = StringBuilder()
    val random: Random = SecureRandom()
    for (i in 0 until codeLength) {
        val c = chars[random.nextInt(chars.size)]
        sb.append(c)
    }
    return sb.toString().toInt()
}

override fun onCreate() {
    super.onCreate()
    isRunning = true
    LocalBroadcastManager.getInstance(this).registerReceiver(
        broadCastReceiver, IntentFilter(
            Constants.NOTIFCATION_ID
        )
    )
}

override fun onDestroy() {
    super.onDestroy()
    isRunning = false
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadCastReceiver)
}

private val broadCastReceiver = object : BroadcastReceiver() {
    override fun onReceive(contxt: Context?, intent: Intent?) {
        endForeground()
    }
}
}

共有1个答案

凤晨朗
2023-03-14

您可以从onstartcommand()返回start_not_sticky。当你从最近的任务列表中刷取应用程序时,Android会删除承载应用程序的操作系统进程(以及所有组件activityservice等)。如果希望Android在这种情况下自动重新启动服务,则需要从onstartcommand()返回start_sticky

 类似资料:
  • 在我的应用程序中,我有一个在后台(和前台)运行。 在这个中,有一个,当它完成时,我想在某个中启动应用程序并在中自动执行一些操作,即使我的应用程序被杀死或关闭,我也希望它能正常工作。 我看到了一些答案,这是其中之一,但它对我不起作用。我找到了一个更好的解决方案,并将其作为答案发布在下面,但仍然只有在应用程序关闭但未完全关闭时才有效。 如何从后台启动,即使应用程序已关闭或关闭?

  • 问题内容: 如何杀死linux中最后一个生成的后台任务? 例: 问题答案: bash中有一个特殊的变量: $!扩展为在后台执行的最后一个进程的PID。

  • 我的目标是每12小时更新一次我的应用令牌(使用网络请求),无论应用程序是在后台还是被杀死,所以我使用workmanager来解决这个问题。但周期性任务仅在应用程序处于后台或打开状态时有效,但当我杀死应用程序时,周期性任务停止以更新我的应用程序令牌。 这是我的代码: 我正在使用workmanager 2.4.0版本

  • 当我的应用程序前台服务被终止时,不会收到FCM通知,更清楚的是,我正试图让我的应用程序始终在后台运行,我使用前台服务来实现这一点,有时应用程序未被ANDROID系统终止,前台通知会出现15个多小时,但其他情况下,当我的前台通知被终止,并且我发送FCM通知时,它在1小时或更短时间内被终止,而我的设备没有收到FCM通知!!这里有什么问题!我怎样才能解决它!

  • 问题内容: 我是android的新手,我的论文就像android的“ Battery Doctor Saver”一样。我的第一个问题是选择多个应用程序并立即将其杀死。老实说,我已经遇到了第一大问题,但是我的问题是我一次只能杀死一个应用程序。因此,这是我的代码,谢谢您对我的问题的立即答复:)。 问题答案: 创建onButton,然后单击您的kill按钮。从ListView中收集所有选择的按钮及其位置

  • 另外,是否有可能在保留用户数据的同时清除使用ADB的应用程序的缓存?似乎清除了所有用户数据。我只想清除缓存。 我之所以这么问是因为我正在对一些用户应用程序进行性能测试。为了使每个测试有效,我希望确保没有任何用户应用程序有任何任务,活动,服务和缓存已经在后台。