如果从最近删除应用程序,接收器不会被调用
Receiver not getting invoked if removed app from recents
我正在开发在通话结束时发送短信的应用程序。
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxx.xxxx.xxxx.xxxx">
<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_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<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:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MonitorService"
android:enabled="true"
android:exported="true" />
<service
android:name=".SendText"
android:exported="false" />
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
我已经从 MainActivity
.
获取了所有运行时权限,如果它也高于 Marshmallow
我的收件人:
public class BootReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
Log.d("BootTest : ", "\nOnBootReceiver - Received a broadcast!");
Toast.makeText(context, "InstaPromo Is Ready !!", Toast.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
context.startForegroundService(new Intent(context, MonitorService.class));
}
else
{
context.startService(new Intent(context, MonitorService.class));
}
}
}
实现接收器运行时为:
public class MonitorService extends Service
{
// Notification variables
private BroadcastReceiver mCallBroadcastReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equalsIgnoreCase("android.intent.action.PHONE_STATE"))
{
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING))
{
Log.d("RECEIVER X: ", "INCOMING CALL...");
}
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_IDLE))
{
Log.d("RECEIVER X: ", "CALL ENDS HERE...");
Intent Dispatcher = new Intent(context, SendText.class);
startService(Dispatcher);
}
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
{
Log.d("RECEIVER X: ", "ACTIVE CALL GOING ON...");
}
}
}
};
public MonitorService() { }
@Override
public void onCreate()
{
super.onCreate();
// created notification here
// also registered broadcast receiver here
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
startForeground(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");
}
}
Broadcastreceiver
服务正常 class;即使应用程序从最近的应用程序列表中删除,但如果从最近的应用程序列表中删除,那么在下一个 boot_complete
它再也不会调用接收器。如果应用程序没有从最近的应用程序中删除...那么在每次 boot_complete / 快速启动时它都会被触发并且广播接收器工作正常。我试过android:exclude_from_recents...,但不是这样实现的
谁能帮我解决这个问题。谢谢
更新 2
我研究了 Firebase JobDispatcher
文档 github 并按如下方式使用它:
- 在gradle项目文件
中实现了依赖
- 之后同步成功
- 创建了一个 JobService
- 创建了一个工作并安排了它。
- 它也在 boot_completed 被触发..
public class MyJobService extends JobService
{
@Override
public boolean onStartJob(JobParameters job)
{
// Do some work here
Log.d("MY_JOB :", "STARTED HERE... \n");
return false;
// Answers the question: "Is there still work going on?"
}
@Override
public boolean onStopJob(JobParameters job)
{
Log.d("MY_JOB :", "STOPPED HERE... \n");
return true;
// Answers the question: "Should this job be retried?"
}
}
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(MainActivity.this));
Bundle myExtrasBundle = new Bundle();
myExtrasBundle.putString("some_key", "some_value");
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(MyJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag")
// one-off job
.setRecurring(false)
// don't persist past a device reboot
.setLifetime(Lifetime.FOREVER)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(10, 10+2))
// don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setConstraints(
// only run when device is idle
Constraint.DEVICE_IDLE
)
.setExtras(myExtrasBundle)
.build();
dispatcher.mustSchedule(myJob);
我应该如何在 reboot/boot_complete
上为 运行 安排一次
- 我只想在停止后重复出现。: setRecurring(false),可以设置为 false 还是 true 应该为 true?
- setLifetime(Lifetime.UNTIL_NEXT_BOOT) 将其设置为永远这样就可以了
- setTrigger(Trigger.executionWindow(0, 60)) 没有得到这个
- setReplaceCurrent(false) 是的,没问题。
- 使用指数退避重试这是什么?
如何设置这些标签在BOOT/REBOOT时启动并且只启动一次,启动失败试试,不要再启动..?
每次启动后使用 FirebaseJobDispatcher 重新启动您的服务。有关 JobDispatcher 的更多信息,请参阅 this。
如果用户首次安装或强制停止应用程序,BroadcastReceviers
将不会运行 直到用户 运行 至少 手动运行该应用 。
根据 doc & answer
Note that an application's stopped state is not the same as an Activity's
stopped state. The system manages those two stopped states separately.
Applications are in a stopped state when they are first installed but are not yet launched and when they are manually
stopped by the user (in Manage Applications). (They mean force
stop an app)
htc devices add com.htc.intent.action.QUICKBOOT_POWERON
>> 从此answer.
<receiver android:enabled="true" android:name=".receivers.BootUpReceiver">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
- 你不能从后台应用程序启动
service
除非有例外:java.lang.IllegalStateException: Not allowed to start service Intent (my_service)
:应用程序在后台如果你仍然需要在设备启动时启动服务,你现在可以使用 new JobIntentService.
将 BroadcastReceiver 和 JobIntentService 添加到清单中
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<service android:name=".MyService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
为您的 JobIntentService 排队工作:
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
MyService.enqueueWork(context, new Intent());
}
}
}
定义您的 JobIntentService:
public class MyJobIntentService extends JobIntentService {
public static final int JOB_ID = 0x01;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, MyJobIntentService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
// your code
}
/**
* This will be called if the JobScheduler has decided to stop this job. The job for
* this service does not have any constraints specified, so this will only generally happen
* if the service exceeds the job's maximum execution time.
*
* @return True to indicate to the JobManager whether you'd like to reschedule this work,
* false to drop this and all following work. Regardless of the value returned, your service
* must stop executing or the system will ultimately kill it. The default implementation
* returns true, and that is most likely what you want to return as well (so no work gets
* lost).
*/
public boolean onStopCurrentWork() {
return true;
}
}
我正在开发在通话结束时发送短信的应用程序。
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxx.xxxx.xxxx.xxxx">
<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_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<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:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MonitorService"
android:enabled="true"
android:exported="true" />
<service
android:name=".SendText"
android:exported="false" />
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
我已经从 MainActivity
.
我的收件人:
public class BootReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
Log.d("BootTest : ", "\nOnBootReceiver - Received a broadcast!");
Toast.makeText(context, "InstaPromo Is Ready !!", Toast.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
context.startForegroundService(new Intent(context, MonitorService.class));
}
else
{
context.startService(new Intent(context, MonitorService.class));
}
}
}
实现接收器运行时为:
public class MonitorService extends Service
{
// Notification variables
private BroadcastReceiver mCallBroadcastReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equalsIgnoreCase("android.intent.action.PHONE_STATE"))
{
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING))
{
Log.d("RECEIVER X: ", "INCOMING CALL...");
}
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_IDLE))
{
Log.d("RECEIVER X: ", "CALL ENDS HERE...");
Intent Dispatcher = new Intent(context, SendText.class);
startService(Dispatcher);
}
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
{
Log.d("RECEIVER X: ", "ACTIVE CALL GOING ON...");
}
}
}
};
public MonitorService() { }
@Override
public void onCreate()
{
super.onCreate();
// created notification here
// also registered broadcast receiver here
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
startForeground(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");
}
}
Broadcastreceiver
服务正常 class;即使应用程序从最近的应用程序列表中删除,但如果从最近的应用程序列表中删除,那么在下一个boot_complete
它再也不会调用接收器。如果应用程序没有从最近的应用程序中删除...那么在每次 boot_complete / 快速启动时它都会被触发并且广播接收器工作正常。我试过android:exclude_from_recents...,但不是这样实现的
谁能帮我解决这个问题。谢谢
更新 2
我研究了 Firebase JobDispatcher
文档 github 并按如下方式使用它:
- 在gradle项目文件 中实现了依赖
- 之后同步成功
- 创建了一个 JobService
- 创建了一个工作并安排了它。
- 它也在 boot_completed 被触发..
public class MyJobService extends JobService
{
@Override
public boolean onStartJob(JobParameters job)
{
// Do some work here
Log.d("MY_JOB :", "STARTED HERE... \n");
return false;
// Answers the question: "Is there still work going on?"
}
@Override
public boolean onStopJob(JobParameters job)
{
Log.d("MY_JOB :", "STOPPED HERE... \n");
return true;
// Answers the question: "Should this job be retried?"
}
}
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(MainActivity.this));
Bundle myExtrasBundle = new Bundle();
myExtrasBundle.putString("some_key", "some_value");
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(MyJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag")
// one-off job
.setRecurring(false)
// don't persist past a device reboot
.setLifetime(Lifetime.FOREVER)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(10, 10+2))
// don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setConstraints(
// only run when device is idle
Constraint.DEVICE_IDLE
)
.setExtras(myExtrasBundle)
.build();
dispatcher.mustSchedule(myJob);
我应该如何在 reboot/boot_complete
上为 运行 安排一次- 我只想在停止后重复出现。: setRecurring(false),可以设置为 false 还是 true 应该为 true?
- setLifetime(Lifetime.UNTIL_NEXT_BOOT) 将其设置为永远这样就可以了
- setTrigger(Trigger.executionWindow(0, 60)) 没有得到这个
- setReplaceCurrent(false) 是的,没问题。
- 使用指数退避重试这是什么?
如何设置这些标签在BOOT/REBOOT时启动并且只启动一次,启动失败试试,不要再启动..?
每次启动后使用 FirebaseJobDispatcher 重新启动您的服务。有关 JobDispatcher 的更多信息,请参阅 this。
如果用户首次安装或强制停止应用程序,
BroadcastReceviers
将不会运行 直到用户 运行 至少 手动运行该应用 。根据 doc & answer
Note that an application's stopped state is not the same as an
Activity's
stopped state. The system manages those two stopped states separately.Applications are in a stopped state when they are first installed but are not yet launched and when they are manually stopped by the user (in Manage Applications). (They mean force stop an app)
htc devices add
com.htc.intent.action.QUICKBOOT_POWERON
>> 从此answer.<receiver android:enabled="true" android:name=".receivers.BootUpReceiver"> <intent-filter> <category android:name="android.intent.category.DEFAULT" /> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> </intent-filter> </receiver>
- 你不能从后台应用程序启动
service
除非有例外:java.lang.IllegalStateException: Not allowed to start service Intent (my_service)
:应用程序在后台如果你仍然需要在设备启动时启动服务,你现在可以使用new JobIntentService.
将 BroadcastReceiver 和 JobIntentService 添加到清单中
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<service android:name=".MyService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
为您的 JobIntentService 排队工作:
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
MyService.enqueueWork(context, new Intent());
}
}
}
定义您的 JobIntentService:
public class MyJobIntentService extends JobIntentService {
public static final int JOB_ID = 0x01;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, MyJobIntentService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
// your code
}
/**
* This will be called if the JobScheduler has decided to stop this job. The job for
* this service does not have any constraints specified, so this will only generally happen
* if the service exceeds the job's maximum execution time.
*
* @return True to indicate to the JobManager whether you'd like to reschedule this work,
* false to drop this and all following work. Regardless of the value returned, your service
* must stop executing or the system will ultimately kill it. The default implementation
* returns true, and that is most likely what you want to return as well (so no work gets
* lost).
*/
public boolean onStopCurrentWork() {
return true;
}
}