androidandroid-serviceandroid-notificationsforeground-service

Bad notification for startForeground error in Android


The error occurs when starting a foreground service with notification.

I am trying to load the data from the JSON file to my database. Since the file is large, I am using foreground service to show the loading progress in notification.

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);
        }
    }

}

Error

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)

I don't know what is causing this error. I also provided Foreground service permission in AndroidManifest.xml.


Solution

  • You do not need this line

    notificationManager.notify(1,notification.build());
    

    Try to remove it and place startForeground into onCreate method

    @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
    }
    

    Notice that onStartCommand method will be called multiple times unlike the onCreate