【问题标题】:Android Oreo Receiver Stops If Removed App From Recents如果从“最近”中删除应用程序,Android Oreo 接收器将停止
【发布时间】: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 注册接收器运行时。然后我在stackoverflow 上搜索在运行时注册它,但看起来没有合适的线程被接受为答案。即使从Android Oreorecents 中移除,如何才能使接收器再次准备好?

提前谢谢你。

【问题讨论】:

标签: java android manifest receiver


【解决方案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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-10
      • 1970-01-01
      • 1970-01-01
      • 2022-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多