如果从“最近”中删除应用程序,Android Oreo 接收器将停止

Posted

技术标签:

【中文标题】如果从“最近”中删除应用程序,Android Oreo 接收器将停止【英文标题】:Android Oreo Receiver Stops If Removed App From Recents 【发布时间】:2018-04-14 10:36:17 【问题描述】:

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 注册接收器运行时。然后我在*** 上搜索在运行时注册它,但看起来没有合适的线程被接受为答案。即使从Android Oreorecents 中移除,如何才能使接收器再次准备好?

提前谢谢你。

【问题讨论】:

***.com/questions/37787291/… 找到了一个尝试它...也许它会工作...??!! 不,没有用 【参考方案1】:

我删除了不相关的帖子。我正在发布最终答案,因为它可能对其他人有所帮助。感谢@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。希望它可以帮助像我这样的人..!!

【讨论】:

【参考方案2】:

根据 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;

【讨论】:

以上是关于如果从“最近”中删除应用程序,Android Oreo 接收器将停止的主要内容,如果未能解决你的问题,请参考以下文章

Android - WebSocket - 从最近使用的应用程序清除应用程序时,连接从服务中删除

从最近的任务中删除应用程序时,Android静态BroadcastReceiver无法正常工作

以编程方式从最近的应用程序中删除应用程序

Android:在应用程序关闭时从后台推送动态通知

android应用刷新系统多媒体库(增加or删除多媒体文件)

从 Android 中最近的应用程序中刷出应用程序时服务停止