Android Oreo Receiver如果从最近删除应用程序停止

时间:2018-04-14 10:36:18

标签: java android manifest receiver

Receiver适用于从4.2到8.0的所有Android版本。即使应用已从Recent Apps移除但是如果从Android Oreo中的Recent Apps移除,则它永远不会再次触发接收器。

我的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.VIBRATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />

<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />

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

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

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

    <receiver
        android:name=".Receiver"
        android:enabled="true"
        android:exported="true">

        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>

    </receiver>


</application>

我的receiver.java:

public class Receiver extends BroadcastReceiver
{
public String PhoneNumber = "UNKNOWN";

@Override
public void onReceive(Context context, Intent intent)
{

    Log.d("RECEIVER :","CAPTURED THE EVENT.....");

    try
    {
        PhoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
        PhoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
             context.startForegroundService(new Intent(context, WatchMan.class));
        }
        else
        {
             context.startService(new Intent(context, WatchMan.class));
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
        Log.e("RECEIVER EXCEPTION : ", "Exception is : ", e);
    }

}

我想知道我在代码中是否有任何错误? Android Developers Documentation要求使用context注册接收器运行时。然后我搜索在stackoverflow运行时注册它,但看起来没有接受正确的线程作为答案。即使从recents Android Oreo移除,如何让接收器再次准备就绪?

提前感谢你。

2 个答案:

答案 0 :(得分:0)

基于documentation对Android 8中隐式广播的限制,您不能在清单中使用隐式接收器(尽管有一些例外情况,但电话状态接收器不在其中例外)

您必须使用前台服务并在前台服务中注册接收器而不是清单

从清单中删除手机状态接收器

在onCreate of Service中注册接收者:

   @Override
    public void onCreate() {
        super.onCreate();
        phoneStateReceiver = new PhoneStateReceiver();
        registerReceiver(phoneStateReceiver, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED));
    }

在onDestroy中取消注册:

   @Override
    public void onDestroy() {
        unregisterReceiver(phoneStateReceiver);
        super.onDestroy();
    }

为您的服务添加静态方法以启动服务:

// start service even if your app is in stopped condition in android 8+
static void requestStart(@NonNull final Context context, @NonNull final String action){
        final Context appContext = context.getApplicationContext();
        Intent intent = new Intent(appContext, AppService.class);
        intent.setAction(action);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // this is required to start the service if there is 
            // no foreground process in your app and your app is
            // stopped in android 8 or above
            appContext.startForegroundService(intent);
        } else {
            appContext.startService(intent);
        }
    }

onStartCommand

中启动前景
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
         if(ACTION_START.equals(intent.getAction()))
           startForeground(ID, notification);
         else if(ACTION_STOP.equals(intent.getAction()))
           stopForeground(true);

         return START_STICKY;
}

答案 1 :(得分:0)

我删除了不相关的帖子。我发布最终答案,因为它可能会帮助其他人。 @WIZARD的帮助很感激。

PHONE_STATE is implicit and will not be triggered on android Oreo or higher. So just place permissions in manifest like :
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />

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

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

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

    <receiver
        android:name=".MyReceiver"
        android:enabled="true"
        android:exported="true">

        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>

    </receiver>
</application>

从前台服务注册隐式接收器:

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

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

        try
        {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

            if(state == null)
            {
                PhoneNumber = "UNKNOWN";
            }
            else if (state.equals(TelephonyManager.EXTRA_STATE_RINGING))
            {
                PhoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
                Log.d("INCOMING ","Incoming number : "+PhoneNumber);
            }
            if(intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL"))
            {

                PhoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
                Log.d("OUTGOING ","Outgoing number : "+PhoneNumber);

            }


            if(!PhoneNumber.contentEquals("UNKNOWN"))
            {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                {
                    context.startForegroundService(new Intent(context, CatchNumbers.class));
                }
                else
                {
                    context.startService(new Intent(context, CatchNumbers.class));
                }
            }



        }
        catch (Exception e)
        {
            e.printStackTrace();
            Log.e("RECEIVER EXCEPTION : ", "Exception is : ", e);
        }
    }
};

public WatchMan() { }

@Override
public void onCreate()
{
    super.onCreate();

    mBuilder = new NotificationCompat.Builder(this, null);

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

    Log.d("RECEIVER : ", "\nCreated....");

    mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
    mBuilder = new NotificationCompat.Builder(this, null);
    mBuilder.setContentTitle("Insta Promo")
            .setContentText("Insta Promo Is Up..")
            .setTicker("Insta Promo Is Up..")
            .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);
    }

    running = true;

    mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
    startForeground(17, mBuilder.build());



}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    Log.d("RECEIVER : ", "\nOnStartCommand....");
    new Thread(new Runnable()
    {
        public void run()
        {
            while(running)
            {
                try
                {
                    Log.d("RECEIVER : ", "\nALIVE..");
                    Thread.sleep(10000);
                }
                catch (InterruptedException e)
                {
                    Log.d("RECEIVER : ", "\nThread : InterruptedException in Receiver...");
                    Log.e("RECEIVER : ", "\nException is : ", e);
                }
                catch (Exception e)
                {
                    Log.d("RECEIVER : ", "\nThread : Exception Error in Receiver...");
                    Log.e("RECEIVER : ", "\nException is : ", e);
                }
            }

        }

    }).start();



    return START_STICKY;
}

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

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

@Override
public void onTaskRemoved(Intent rootIntent)
{
    super.onTaskRemoved(rootIntent);
    Log.d("SERVICE : ", "\nTask Removed....");
}


}

有一些意图行为如NEW_OUTGOING_CALL和BOOT_COMPLETED被排除在外,可以在接收器中实现并放置在清单中:

public class MyReceiver extends BroadcastReceiver
{

@Override
public void onReceive(Context context, Intent intent)
{
    Log.d("INSTA_BOOT : ", "\nBOOT_COMPLETE_EVENT_OF_INSTA....");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
        context.startForegroundService(new Intent(context, WatchMan.class));
    }
    else
    {
        context.startService(new Intent(context, WatchMan.class));
    }
}
}

因为我想重新注册或想要在REBOOT或BOOT-COMPLETE上重新启动前台服务

CatchNumbers.java is a simple service which performs operation when receiver triggers perticular actions.

它在每次重新启动时都很有效,并且不再需要android:excludeFromRecents="true",因为即使用户在Oreo上从recents删除它,它也会重新启动服务STICKY。希望它可以帮助像我这样的人.. !!