Android 7,8和Oxygen OS:如果应用程序被杀,广播接收器无法正常工作

时间:2018-04-17 09:27:32

标签: android broadcastreceiver alarmmanager

发布Android 7后,如果应用程序因从最近的任务列表中滑出而被杀死,我的闹钟接收器没有收到广播接收器。
以下是我的AlarmReceiver中的一部分,它扩展了BroadCastReceiver。

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(Constants.TAG, "event received");
        setContext(context);

        /* 
        *** My logic here *** 
        */

        Log.d(Constants.TAG, "alarm received");
    }
}

我正在设置闹钟并为闹钟注册AlarmReceiver类的地方。

public class UnityNotificationManager
{
    public static void SetNotification(int id, long delayMs, String title, String message, String ticker, int sound, int vibrate,
                                       int lights, String largeIconResource, String smallIconResource, int bgColor, String bundle, String dataString)
    {
        Context currentActivity = UnityPlayer.currentActivity;
        AlarmManager am = (AlarmManager)currentActivity.getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(Constants.ALARM_RECEIVER);
        intent.setClass(currentActivity, AlarmReceiver.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);

        intent.putExtra("ticker", ticker);
        intent.putExtra("title", title);
        intent.putExtra("message", message);
        intent.putExtra("id", id);
        intent.putExtra("color", bgColor);
        intent.putExtra("sound", sound == 1);
        intent.putExtra("vibrate", vibrate == 1);
        intent.putExtra("lights", lights == 1);
        intent.putExtra("l_icon", largeIconResource);
        intent.putExtra("s_icon", smallIconResource);
        intent.putExtra("bundle", bundle);
        intent.putExtra("dataString", dataString);

        PendingIntent pendingIntent = PendingIntent.getBroadcast(currentActivity,
                id, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        am.cancel(pendingIntent);

        long finalTime = System.currentTimeMillis() + delayMs;

        if (Build.VERSION.SDK_INT < 23) {
            am.set(AlarmManager.RTC_WAKEUP,finalTime, pendingIntent);
        } else {
            am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,finalTime, pendingIntent);
        }
        Log.d(Constants.TAG, "event fired finalTime = "+ finalTime);
    }
}

上面使用的Constants.ALARM_RECEIVER的值与广播AlarmReceiver将侦听的清单中定义的操作名称相同。

<receiver android:name="com.example.app.AlarmReceiver">
      <intent-filter>
        <action android:name="com.example.app.alarm.action.trigger" />
      </intent-filter>
    </receiver>

使用显式广播的原因是android文档声明发布Android Oreo后,android应用程序将无法在后台使用隐式广播来提高性能。相同的链接:https://developer.android.com/about/versions/oreo/background.html#broadcasts

我还尝试使用WakefulBroadCastReceiver来避免在Android设备上通过Doze模式进行优化,但事实证明这个类在Android Oreo中已被弃用。相同的链接:https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html

上述代码适用于几乎所有其他制造商,除了运行在氧气操作系统上的One Plus设备。有没有办法解决这个问题,因为如果应用程序以相同的方式被杀死,WhatsApp等应用程序的通知将继续有效。

附加信息:
1.我在One Plus 5T上运行命令adb shell dumpsys package <package_name> | grep stopped并返回以下输出:

User 0: ceDataInode=2039857 installed=true hidden=false suspended=false stopped=false notLaunched=false enabled=0 instant=false

反过来表明,在从最近的任务列表中滑动应用程序时,应用程序不会停止。此外,在应用程序信息页面上启用了“强制停止”按钮,表明应用程序尚未强制停止。

  1. 如果我手动将电池优化设置从“优化”更改为“未优化”,我能够使通知正常工作,但由于其他应用程序的通知即使处于优化状态也能正常工作,我不相信这是我问题的解决方案。
  2. 有人可以建议任何修复或解决方法来修复Oxygen OS上运行Android 7或更高版本的设备上的通知问题吗?

2 个答案:

答案 0 :(得分:0)

  

是的,我也发现很难获得参考样本代码,因为8.0以后总是运行任务有限制。

不要在intent-actions中放置任何Manifest.xml,如下所示:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />

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

<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


<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"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

    <receiver
        android:name=".Receiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

    <service
        android:name=".CatchNumbers"
        android:enabled="true"
        android:exported="true" />
    <service
        android:name=".WatchMan"
        android:enabled="true"
        android:exported="true" >

    </service>

    <activity android:name=".developer_activity" />
    <activity android:name=".WhiteListActivity" />
    <activity android:name=".Contacts" />
</application>

请注意

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
我没有插入

,当我在服务中注册运行时接收器时,android studio会自动插入它。这是必要的。

在我的receiver.java上,每个boot_complete i在前台服务中注册接收器运行时。如果你想让你的接收器工作,这是必须的:

package com.example.rushi.instapromo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;

public class Receiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
    Log.d("BootTest : ", "\nOnBootReceiver - Received a broadcast!");
    Toast.makeText(context, "OnBootReceiver Received a broadcast!!", Toast.LENGTH_LONG).show();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
        context.startForegroundService(new Intent(context, WatchMan.class));
    }
    else
    {
        context.startService(new Intent(context, WatchMan.class));
    }
}
}

请注意,Watchman.java是注册运行时接收器的前台服务,如果不使用此方法,它将永远不会工作。我已经尝试了很多并发明了这是唯一的方式。

在前台服务中注册接收器,如

public class WatchMan extends Service
{
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
NotificationChannel notificationChannel;
String NOTIFICATION_CHANNEL_ID = "17";

private BroadcastReceiver mCallBroadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        String PhoneNumber = "UNKNOWN";
        Log.d("RECEIVER :  ","IS UP AGAIN....");

        try
        {
            String action = intent.getAction();
            if(action.equalsIgnoreCase("android.intent.action.PHONE_STATE"))
            {
                if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING))
                {
                    PhoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
                    Log.d("RECEIVER : ","Incoming number : "+PhoneNumber);

                    // update in database and goto catchnumber to sms

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    {
                        context.startForegroundService(new Intent(context, CatchNumbers.class));
                    }
                    else
                    {
                        context.startService(new Intent(context, CatchNumbers.class));
                    }
                }
                if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_IDLE))
                {
                    PhoneNumber = "UNKNOWN";
                }
                if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
                {
                    Log.d("RECEIVER :  ","OUTGOING CALL RECEIVED....");

                    // UPDATED in database and JUST GOTO catchnumber to sms

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    {
                        context.startForegroundService(new Intent(context, CatchNumbers.class));
                    }
                    else
                    {
                        context.startService(new Intent(context, CatchNumbers.class));
                    }
                }
            }
            if(action.equalsIgnoreCase("android.intent.action.NEW_OUTGOING_CALL"))
            {
                PhoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
                Log.d("RECEIVER : ","Outgoing number : "+PhoneNumber);

                // update in database and BUT DO NOT GOTO catchnumber to sms
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            Log.e("RECEIVER : ", "Exception is : ", e);
        }
    }
};

public WatchMan() { }

@Override
public void onCreate()
{
    super.onCreate();
    Log.d("WatchMan : ", "\nOnCreate...");

    IntentFilter CallFilter = new IntentFilter();
    CallFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
    CallFilter.addAction("android.intent.action.PHONE_STATE");
    this.registerReceiver(mCallBroadcastReceiver, CallFilter);

    Log.d("WatchMan : ", "\nmCallBroadcastReceiver Created....");

    mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
    mBuilder = new NotificationCompat.Builder(this, null);
    mBuilder.setContentTitle("Insta Promo")
            .setContentText("InstaPromo Service ready")
            .setTicker("InstaPromo Service ready")
            .setSmallIcon(R.drawable.ic_launcher_background)
            .setPriority(Notification.PRIORITY_HIGH)
            .setDefaults(Notification.DEFAULT_ALL)
            .setVisibility(Notification.VISIBILITY_PUBLIC)
            .setOngoing(true)
            .setAutoCancel(false);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
        notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);

        // Configure the notification channel.
        notificationChannel.setDescription("Channel description");
        notificationChannel.enableLights(true);
        notificationChannel.setLightColor(Color.RED);
        notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
        notificationChannel.enableVibration(true);
        notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
        mNotifyManager.createNotificationChannel(notificationChannel);

        mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
        startForeground(17, mBuilder.build());
    }
    else
    {
        mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
        //startForeground(17, mBuilder.build());
        mNotifyManager.notify(17, mBuilder.build());
    }
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    Log.d("WatchMan : ", "\nmCallBroadcastReceiver Listening....");

    //return super.onStartCommand(intent, flags, startId);

    return START_STICKY;
}

@Override
public void onDestroy()
{
    this.unregisterReceiver(mCallBroadcastReceiver);
    Log.d("WatchMan : ", "\nDestroyed....");
    Log.d("WatchMan : ", "\nWill be created again....");
}

@Override
public IBinder onBind(Intent intent)
{
    // TODO: Return the communication channel to the service.
    throw new UnsupportedOperationException("Not yet implemented");
}
}

请注意我的注册和未注册的NEW_OUTGOING_CALLPHONE_STATE意图行为。 CatchNumbers是另一个service,我用来做任何我想要的传入和传出号码。

  

您的意图行为可能与我不同..但是,如果您希望它在8.0以后实施,这是唯一的方法。经过测试,我正在使用此代码。它支持4.2到Android P api等级29。在每次重启时,这两个意图过滤器和接收器都可以在我的CatchNumbers中使用。希望它真的能帮助你。或其他人。

答案 1 :(得分:0)

我观察到相同的行为。该错误似乎是Oxygen OS不允许广播接收器从PendingIntent开始。解决方案是在BroadcastReceiver上使用Service代替。

将您的代码从BroadcastReceiver的onReceive()移至服务的onStartCommand()方法,然后在PendingIntent中设置此Service类。

...
intent.setClass(currentActivity, AlarmService.class);
...
PendingIntent pendingIntent = PendingIntent.getService(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
...

希望这会有所帮助。