Android 中 startForeground 错误的错误通知
Bad notification for startForeground error in Android
启动带有通知的前台服务时出现错误。
我正在尝试将 JSON 文件中的数据加载到我的数据库中。由于文件较大,我使用前台服务在通知中显示加载进度。
LoadIntentService.java
import android.app.NotificationManager;
import android.app.Service;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.android.word.database.DbHelper;
import com.android.word.database.WordDictionaryContract;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import static com.android.word.App.CHANNEL_ID;
public class LoadIntentService extends Service {
private static final String LOG_TAG = "LoadIntentService";
private DbHelper dbHelper;
private SQLiteDatabase mDatabase;
private Context mContext;
public LoadIntentService() {
super();
Log.d(LOG_TAG,"LoadIntentService");
}
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
dbHelper = new DbHelper(this);
mDatabase = dbHelper.getWritableDatabase();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final int countMax = intent.getIntExtra("size",0);
Log.d(LOG_TAG," onStartCommand");
final NotificationCompat.Builder notification = new NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle("Loading Dictionary")
.setSmallIcon(R.drawable.ic_file_download_black_24dp)
.setOnlyAlertOnce(true)
.setOngoing(true)
.setProgress(countMax,0,false);
final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1,notification.build());
startForeground(1,notification.build());
new Thread(new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG," start thread");
String json;
String word,definition,audiourl,synonym,antonym;
try {
Log.d(LOG_TAG,"parsing json");
InputStream inputStream = mContext.getAssets().open("worddictionary.json");
int size = inputStream.available();
Log.d(LOG_TAG,"parsing json size: "+size);
byte[] buffer = new byte[size];
inputStream.read(buffer);
inputStream.close();
json = new String(buffer, "UTF-8");
// Log.d(LOG_TAG,"parsing json data: "+json);
JSONArray jsonArray = new JSONArray(json);
for (int i=0;i<jsonArray.length();i++){
notification.setProgress(countMax,i,false);
notificationManager.notify(1,notification.build());
JSONObject obj = jsonArray.getJSONObject(i);
word = obj.getString("word");
definition = obj.getString("definition");
audiourl = obj.getString("audiourl");
synonym = obj.getString("synonym");
antonym = obj.getString("antonym");
ContentValues values = new ContentValues();
values.put("word",word);
values.put("definition",definition);
values.put("audiourl",audiourl);
values.put("synonyms",synonym);
values.put("antonyms",antonym);
long id = mDatabase.insert(WordDictionaryContract.WordDictionaryEntry.TABLE_NAME,null,values);
if (id==-1){
Log.d(LOG_TAG,"insert failed: "+word);
} else{
Log.d(LOG_TAG,"insert success id: "+id);
}
}
Log.d(LOG_TAG,"Dictionary Loaded");
} catch (IOException e) {
e.printStackTrace();
Log.d(LOG_TAG,"Read json error: "+e);
} catch (JSONException e) {
Log.d(LOG_TAG,"Parse json error: "+e.getStackTrace());
e.printStackTrace();
}
stopSelf();
notification.setContentText("Dictionary Loaded").setProgress(0,0,false).setOngoing(false);
notificationManager.notify(1,notification.build());
}
}).start();
return START_NOT_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
App.java
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
public class App extends Application {
public static final String CHANNEL_ID = "loadDataServiceChannel";
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
private void createNotificationChannel(){
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,"Loading Dictionary",
NotificationManager.IMPORTANCE_HIGH
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
错误
2020-09-08 22:42:06.863 5076-5076/com.android.word E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.word, PID: 5076
android.app.RemoteServiceException: Bad notification for startForeground
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1738)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6692)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
我不知道是什么导致了这个错误。我还在 AndroidManifest.xml.
中提供了前台服务权限
这就是我构建通知的方式:
//Helpers
fun updateKibiNotification(context: Context, channelID: String, icon : Int, message : String = this.message): Notification? {
notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(context,channelID)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(icon)
.build()
} else {
Notification.Builder(context)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(icon)
.build()
}
return notification
}
fun createNotificationChannel(
channelName: String,
channelDescription: String = "",
channelID: String,
context: Context
){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(channelID, channelName, importance)
mChannel.description = channelDescription
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
}
这就是我启动前台服务的方式:
private fun setupForeground(){
KibiNotificationBuilder.createNotificationChannel(kibiChannelID,channelID = kibiChannelID, context = this)
val initialMessage = getString(R.string.setup_foreground_text)
startForeground(KibiNotificationBuilder.notificationId,
KibiNotificationBuilder.updateKibiNotification(this, kibiChannelID,R.drawable.notification_icon_background,initialMessage))
Log.d(DEBUG_TAG, "setupForeground: started")
}
你不需要这条线
notificationManager.notify(1,notification.build());
尝试删除它并将 startForeground
放入 onCreate
方法中
@Override
public void onCreate() {
super.onCreate();
final NotificationCompat.Builder notification = new NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle("Loading Dictionary")
.setSmallIcon(R.drawable.ic_file_download_black_24dp)
.setOnlyAlertOnce(true)
.setOngoing(true)
.setProgress(countMax,0,false);
final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// notificationManager.notify(1,notification.build()); <-- remove this
startForeground(1,notification.build());
//... other stuff
}
请注意,与 onCreate
不同,onStartCommand
方法将被多次调用
启动带有通知的前台服务时出现错误。
我正在尝试将 JSON 文件中的数据加载到我的数据库中。由于文件较大,我使用前台服务在通知中显示加载进度。
LoadIntentService.java
import android.app.NotificationManager;
import android.app.Service;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.android.word.database.DbHelper;
import com.android.word.database.WordDictionaryContract;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import static com.android.word.App.CHANNEL_ID;
public class LoadIntentService extends Service {
private static final String LOG_TAG = "LoadIntentService";
private DbHelper dbHelper;
private SQLiteDatabase mDatabase;
private Context mContext;
public LoadIntentService() {
super();
Log.d(LOG_TAG,"LoadIntentService");
}
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
dbHelper = new DbHelper(this);
mDatabase = dbHelper.getWritableDatabase();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final int countMax = intent.getIntExtra("size",0);
Log.d(LOG_TAG," onStartCommand");
final NotificationCompat.Builder notification = new NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle("Loading Dictionary")
.setSmallIcon(R.drawable.ic_file_download_black_24dp)
.setOnlyAlertOnce(true)
.setOngoing(true)
.setProgress(countMax,0,false);
final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1,notification.build());
startForeground(1,notification.build());
new Thread(new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG," start thread");
String json;
String word,definition,audiourl,synonym,antonym;
try {
Log.d(LOG_TAG,"parsing json");
InputStream inputStream = mContext.getAssets().open("worddictionary.json");
int size = inputStream.available();
Log.d(LOG_TAG,"parsing json size: "+size);
byte[] buffer = new byte[size];
inputStream.read(buffer);
inputStream.close();
json = new String(buffer, "UTF-8");
// Log.d(LOG_TAG,"parsing json data: "+json);
JSONArray jsonArray = new JSONArray(json);
for (int i=0;i<jsonArray.length();i++){
notification.setProgress(countMax,i,false);
notificationManager.notify(1,notification.build());
JSONObject obj = jsonArray.getJSONObject(i);
word = obj.getString("word");
definition = obj.getString("definition");
audiourl = obj.getString("audiourl");
synonym = obj.getString("synonym");
antonym = obj.getString("antonym");
ContentValues values = new ContentValues();
values.put("word",word);
values.put("definition",definition);
values.put("audiourl",audiourl);
values.put("synonyms",synonym);
values.put("antonyms",antonym);
long id = mDatabase.insert(WordDictionaryContract.WordDictionaryEntry.TABLE_NAME,null,values);
if (id==-1){
Log.d(LOG_TAG,"insert failed: "+word);
} else{
Log.d(LOG_TAG,"insert success id: "+id);
}
}
Log.d(LOG_TAG,"Dictionary Loaded");
} catch (IOException e) {
e.printStackTrace();
Log.d(LOG_TAG,"Read json error: "+e);
} catch (JSONException e) {
Log.d(LOG_TAG,"Parse json error: "+e.getStackTrace());
e.printStackTrace();
}
stopSelf();
notification.setContentText("Dictionary Loaded").setProgress(0,0,false).setOngoing(false);
notificationManager.notify(1,notification.build());
}
}).start();
return START_NOT_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
App.java
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
public class App extends Application {
public static final String CHANNEL_ID = "loadDataServiceChannel";
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
private void createNotificationChannel(){
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,"Loading Dictionary",
NotificationManager.IMPORTANCE_HIGH
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
错误
2020-09-08 22:42:06.863 5076-5076/com.android.word E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.word, PID: 5076
android.app.RemoteServiceException: Bad notification for startForeground
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1738)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6692)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
我不知道是什么导致了这个错误。我还在 AndroidManifest.xml.
中提供了前台服务权限这就是我构建通知的方式:
//Helpers
fun updateKibiNotification(context: Context, channelID: String, icon : Int, message : String = this.message): Notification? {
notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(context,channelID)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(icon)
.build()
} else {
Notification.Builder(context)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(icon)
.build()
}
return notification
}
fun createNotificationChannel(
channelName: String,
channelDescription: String = "",
channelID: String,
context: Context
){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(channelID, channelName, importance)
mChannel.description = channelDescription
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
}
这就是我启动前台服务的方式:
private fun setupForeground(){
KibiNotificationBuilder.createNotificationChannel(kibiChannelID,channelID = kibiChannelID, context = this)
val initialMessage = getString(R.string.setup_foreground_text)
startForeground(KibiNotificationBuilder.notificationId,
KibiNotificationBuilder.updateKibiNotification(this, kibiChannelID,R.drawable.notification_icon_background,initialMessage))
Log.d(DEBUG_TAG, "setupForeground: started")
}
你不需要这条线
notificationManager.notify(1,notification.build());
尝试删除它并将 startForeground
放入 onCreate
方法中
@Override
public void onCreate() {
super.onCreate();
final NotificationCompat.Builder notification = new NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle("Loading Dictionary")
.setSmallIcon(R.drawable.ic_file_download_black_24dp)
.setOnlyAlertOnce(true)
.setOngoing(true)
.setProgress(countMax,0,false);
final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// notificationManager.notify(1,notification.build()); <-- remove this
startForeground(1,notification.build());
//... other stuff
}
请注意,与 onCreate
onStartCommand
方法将被多次调用