我是android的新手 . 我想 Build 一个警报应用程序 . 问题是,警报通知无法正常工作 . 应用程序打开时它唯一的工作 . 假设是上午10点 . 我在上午10点15分设置了警报,然后关闭了应用程序 . 上午10点15分,我将收到一条错误消息“app is stopped working”请帮助 . 提前致谢 .
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.troy.timepickerdialog">
<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">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MyBroadcastReceiver"></receiver>
<service
android:name=".RingtonePlayingService"
android:enabled="true">
</service>
</application>
MainActivity.java
package com.troy.timepickerdialog;
import android.app.AlarmManager;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
import java.util.Calendar;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
public class MainActivity extends AppCompatActivity {
public static final int REQUEST_CODE = 0;
private TimePicker theTimePicker;
private AlarmManager alarmManager;
private PendingIntent pendingIntent;
private Button buttonOn, btnDate, buttonOff;
private int yearX, monthX, dayX, hourX, minX;
private static final int DIALOG_ID = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Calendar cal = Calendar.getInstance();
yearX = cal.get(Calendar.YEAR);
monthX = cal.get(Calendar.MONTH);
dayX = cal.get(Calendar.DAY_OF_MONTH);
theTimePicker = findViewById(R.id.timePicker);
theTimePicker.setIs24HourView(false);
buttonOn = findViewById(R.id.on);
buttonOff = findViewById(R.id.off);
showDatePickerDialog();
alarmManager = (AlarmManager) getSystemService(this.ALARM_SERVICE);
final Intent intent = new Intent(this, MyBroadcastReceiver.class);
buttonOn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hourX = theTimePicker.getCurrentHour();
minX = theTimePicker.getCurrentMinute();
Calendar dateAndTime = Calendar.getInstance();
dateAndTime.setTimeInMillis(System.currentTimeMillis());
dateAndTime.clear();
dateAndTime.set(yearX, monthX, dayX, hourX, minX, 0);
if(dateAndTime.before(Calendar.getInstance())) {
dateAndTime.add(Calendar.DATE, 1);
}
int hour = hourX;
String ampm = "";
if(hour < 12) ampm = "AM";
else ampm = "PM";
if(hour >= 13 && hour <= 23) {
hour -= 12;
}
else if(hour == 0) {
hour = 12;
}
String toastDate = dayX + "/" + (monthX + 1) + "/" + yearX;
Toast.makeText(getApplicationContext(), "Alarm Set To Date: " + toastDate + " & Time: " + hour + " : " + minX + ampm, Toast.LENGTH_SHORT).show();
intent.putExtra("extra", "alarmOn");
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), REQUEST_CODE, intent, FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, dateAndTime.getTimeInMillis(), pendingIntent);
}
});
buttonOff.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(RingtonePlayingService.isRunning == true) {
RingtonePlayingService.mediaPlayer.stop();
Toast.makeText(getApplicationContext(), "Alarm is off", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getApplicationContext(), "Alarm if already off", Toast.LENGTH_SHORT).show();
//intent.putExtra("extra", "alarmOff");
//alarmManager.cancel(pendingIntent);
//sendBroadcast(intent);
}
}
});
}
public void showDatePickerDialog() {
btnDate = findViewById(R.id.date);
btnDate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showDialog(DIALOG_ID);
}
});
}
private DatePickerDialog.OnDateSetListener dPickerListener = new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
yearX = year;
monthX = month;
dayX = dayOfMonth;
}
};
@Override
protected Dialog onCreateDialog(int id) {
if(id == DIALOG_ID) {
DatePickerDialog datePickerDialog = new DatePickerDialog(this, dPickerListener, yearX, monthX, dayX);
datePickerDialog.getDatePicker().setMinDate(System.currentTimeMillis() - 1000);
return datePickerDialog;
//return new DatePickerDialog(this, dPickerListener, yearX, monthX, dayX);
}
return null;
}
}
MyBroadcastReceiver.java
package com.troy.timepickerdialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
/**
* Created by royta on 04-Dec-18.
*/
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent service_intent = new Intent(context, RingtonePlayingService.class);
String fetchExtra = intent.getExtras().getString("extra");
if(fetchExtra.equals("alarmOn")) {
Toast.makeText(context, "Alarming...", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(context, "Alarm is off", Toast.LENGTH_SHORT).show();
}
service_intent.putExtra("extra", fetchExtra);
context.startService(service_intent);
/*
mediaPlayer = MediaPlayer.create(context, R.raw.alarm);
mediaPlayer.setLooping(true);
mediaPlayer.start();
*/
}
}
RingtonePlayingService.java
package com.troy.timepickerdialog;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
/**
* Created by royta on 09-Dec-18.
*/
public class RingtonePlayingService extends Service {
static MediaPlayer mediaPlayer;
static boolean isRunning;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String state = intent.getExtras().getString("extra");
if(state.equals("alarmOn")) {
Log.d("ROYs", "alarmOn state");
mediaPlayer = MediaPlayer.create(this, R.raw.alarm);
mediaPlayer.setLooping(true);
mediaPlayer.start();
this.isRunning = true;
//Notification
NotificationManager mNotificationManager;
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this.getApplicationContext(), "notify_001");
Intent goMainActivity = new Intent(this.getApplicationContext(), MainActivity.class);
PendingIntent pendingIntent_for_Main = PendingIntent.getActivity(this, 0, goMainActivity, 0);
mBuilder.setContentIntent(pendingIntent_for_Main);
mBuilder.setSmallIcon(R.mipmap.ic_launcher_round);
mBuilder.setContentTitle("Voice Reminder");
mBuilder.setContentText("Organize your BackPack.");
mBuilder.setPriority(Notification.PRIORITY_MAX);
mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = "VOICE_REMINDER_ID";
NotificationChannel channel = new NotificationChannel(channelId,
"Voice Reminder",
NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
mBuilder.setChannelId(channelId);
}
mNotificationManager.notify(0, mBuilder.build());
//End Notification
}
else if(this.isRunning && state.equals("alarmOff")) {
Log.d("ROYs", "alarmOff state");
mediaPlayer.stop();
this.isRunning = false;
}
return START_NOT_STICKY;
}
}
Logcat Errors
12-10 19:31:03.934 16959-16959 / com.troy.timepickerdialog E / AndroidRuntime:FATAL EXCEPTION:main进程:com.troy.timepickerdialog,PID:16959 java.lang.RuntimeException:无法启动接收器com.troy . timepickerdialog.MyBroadcastReceiver:java.lang.IllegalStateException:不允许启动服务Intent {cmp = com.troy.timepickerdialog / .RingtonePlayingService(has extras)}:app在后台uid UidRecord {6b8d36d u0a142 TRNB idle procs:1 seq(0 ,0,0)}在android.app.ActivityThread.handleReceiver(ActivityThread.java:3210)的android.app.ActivityThread.-wrap17(未知来源:0)在android.app.ActivityThread $ H.handleMessage(ActivityThread.java) :1678)在Android.os.Handler.dispatchMessage(Handler.java:106)的android.app.Looper.loop(Looper.java:164)在android.app.ActivityThread.main(ActivityThread.java:6543)在java位于com.android.internal.os.ZygoteInit.mai的com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:440)的.lang.reflect.Method.invoke(Native Method) n(ZygoteInit.java:810)引起:java.lang.IllegalStateException:不允许启动服务Intent {cmp = com.troy.timepickerdialog / .RingtonePlayingService(有附加内容)}:app在后台uid UidRecord {6b8d36d u0a142 TRNB idle procs:1 seq(0,0,0)}在android.app.ContextImpl.startService(侧面是1,用于Android.content.ContextWrap)的android.app.ContextImpl.start服务(ContextImpl.java:1478) . 位于android.app.A活动时,我在android.app.Apat.Deleiver(动作线程)的com.troy.timepickerdialog.MyBroadcastReceiver.onReceive(MyBroadcastReceiver.java:33)上的startService(ContextWrapper.java:650) .java:3203)在android.app.Handler.dispatchMessage(Handler.java:)的android.app.ActivityThread $ H.handleMessage(ActivityThread.java:1678)的android.app.ActivityThread.-wrap17(未知来源:0) 106)在android.app.Looper.loop(Looper.java:164)的android.app.ActivityThread.main(ActivityThread.java:6543)java.lang.reflect.M eth.inroid.invoke(Native Method)位于com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)的com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:440)
1 回答
如果应用的目标SDK是26(Android O)或更高,则无法在后台启动服务 . 如果应用程序未打开,则必须启动前台服务,该服务将通过通知显示给用户 .
在你的
BroadcastReceiver
中你应该替换:附:
此外,在
RingtonePlayingService
的onStartCommand
上,您需要将通知设置为启动前景:您可以查看here以获取更多信息 .
另请注意,针对SDK 26的应用必须为其通知创建通知渠道 . 查看更多Notification Channels training