当用户从最近的应用程序关闭应用程序时,Sticky Service 会被杀死

Sticky Service is getting killed when user closes app from recent apps

我对启动和使用基本服务有很好的概念。我的意思是不要复杂。在我的应用程序中,我想要一个在任何情况下都不应被终止的服务,并且应该从服务器下载一些文件然后它应该调用 stopSelf。我通过以下方式提供服务。但在分享它的整个代码之前,让我告诉你我在做什么

  1. 在服务中,我传递了一系列 url(字符串数组),它必须从服务器下载所有文件。
  2. 我正在使用异步任务从服务器下载。
  3. 在这整个过程中,我得到了第一个响应,它在 xml 中,然后我解析它,并得到 JSON 字符串(很抱歉,我的网络服务设计师和我一样麻木).所以在这两次转换之后,我将数据存储在数据库中,然后开始下载文件并将它们保存到设备并将它们的路径存储在数据库中。 (一切正常)
  4. 我正在通知栏中计算和更新进度。 (显示用户下载了多少文件)

我真正想要的

我希望当用户从最近的应用程序列表中删除我的服务时,它不应该被终止,以便它应该继续下载并继续更新通知栏中的状态。我正在使用通知管理器来更新进度。

到底发生了什么

当我从最近的应用程序托盘关闭我的应用程序时,我认为我的服务被终止并且下载过程停止,并且它也停止更新通知栏中的通知进度,我希望它继续运行 直到下载过程完成。

Here is my code it is simplified as some methods are really not worthy to be discussed here Such as Parsing the xml or JSON

这是代码

public class MyDemoService extends Service {
private static final String TAG = "MyDemoService";
private static final int NOTIFICATION_ID = 1;
private LocalBinder m_binder = new LocalBinder();
private NotificationManager mNotifyManager;
private NotificationCompat.Builder mBuilder;
myAsyncTask myWebFetch;
// Timer to update the ongoing notification
private final long mFrequency = 100;    // milliseconds
private final int TICK_WHAT = 2;

public class LocalBinder extends Binder {
    MyDemoService getService() {
        return MyDemoService.this;
    }
}

private Handler mHandler = new Handler() {
    public void handleMessage(Message m) {
        updateNotification();
        sendMessageDelayed(Message.obtain(this, TICK_WHAT), mFrequency);
    }
};

@Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "bound");

    return m_binder;
}

@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG, "created");
    mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return Service.START_STICKY;
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    super.onTaskRemoved(rootIntent);
    Log.d(TAG, "Removed");
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "Destroyed");
}


public void updateNotification() {
    // Log.d(TAG, "updating notification");

    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

}


public void hideNotification() {
    Log.d(TAG, "removing notification");
    mNotifyManager.cancel(NOTIFICATION_ID);
    mHandler.removeMessages(TICK_WHAT);
}

public void start() {
    Log.d(TAG, "start");
    mBuilder =
            new NotificationCompat.Builder(MyDemoService.this)
                    .setSmallIcon(R.drawable.download)
                    .setContentTitle("SMU")
                    .setContentText("Downloading Images");
    Intent targetIntent = new Intent(MyDemoService.this, MainActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(MyDemoService.this, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    mBuilder.setContentIntent(contentIntent);
    mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
    myWebFetch = new myAsyncTask();
    myWebFetch.execute();
}

class myAsyncTask extends AsyncTask<String, Integer, Void> {
    MyDB myDB;


    myAsyncTask() {
        myDB = new MyDB(MyDemoService.this);
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        mBuilder.setContentText("Download complete");
        // Removes the progress bar
        mBuilder.setProgress(0, 0, false);
        mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        mBuilder.setProgress(100, values[0], false);
        mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
    }

    @Override
    protected Void doInBackground(String... params) {
        //set the download URL, a url that points to a file on the internet
        getJSON("http://*****", 1000000);
        return null;
    }


    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mBuilder.setProgress(100, 0, false);
        mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
    }

    public void getJSON(String url, int timeout) {
        HttpURLConnection c = null;
        try {
            URL u = new URL(url);
            c = (HttpURLConnection) u.openConnection();
            c.setRequestMethod("GET");
            c.setUseCaches(false);
            c.setAllowUserInteraction(false);
            c.setConnectTimeout(timeout);
            c.setReadTimeout(timeout);
            c.setInstanceFollowRedirects(false);
            c.connect();
            int status = c.getResponseCode();

            if (status == 200) {
                String readStream = readStream(c.getInputStream());
                if (readStream != null) {
                    JsonParser mJsonParser = new JsonParser(MyDemoService.this);
                    mJsonParser.parseJaSon(readStream);
                    ArrayList<SuitDetails> mImageList = new ArrayList<>(myDB.GetAllData());

                    if (mImageList != null) {
                        //NOW HERE DOWNLOADING IMAGES FROM URL WE GOT SAVED IN DB AFTER PARSING
                        downloadImages(mImageList);

                    }
                }

            }

        } catch (MalformedURLException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (c != null) {
                try {
                    c.disconnect();
                } catch (Exception ex) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private String readStream(InputStream in) {
        //parsing my input stream and sending back string
        return jsonString.toString();
    }

    void downloadImages(ArrayList<SuitDetails> arrayList) {

        try {

            ArrayList<SuitDetails> imageUrl = arrayList;
            URL url;
            float progressImages = 0;
            HttpURLConnection urlConnection = null;
            for (int i = 0; i < imageUrl.size(); i++) {
                progressImages += 100 / imageUrl.size();
                publishProgress((int) progressImages);
                url = new URL(imageUrl.get(i).getPath().toString());
                //create the new connection
                urlConnection = (HttpURLConnection) url.openConnection();
                //set up some things on the connection
                urlConnection.setRequestMethod("GET");
                urlConnection.setDoOutput(false);
                urlConnection.setUseCaches(false);
                urlConnection.setAllowUserInteraction(false);
                urlConnection.setConnectTimeout(60000);
                urlConnection.setReadTimeout(60000);
                urlConnection.setInstanceFollowRedirects(false);
                //and connect!
                urlConnection.connect();
                File storagePath = new File(MyDemoService.this.getExternalFilesDir("TEST") + "/Mytest");
                storagePath.mkdirs();
                String finalName = imageUrl.get(i).getImageName();
                File myImage = new File(storagePath, finalName + ".png");
                FileOutputStream fileOutput = new FileOutputStream(myImage);
                InputStream inputStream = urlConnection.getInputStream();
                int totalSize = urlConnection.getContentLength();
                int downloadedSize = 0;
                byte[] buffer = new byte[1024];
                int bufferLength = 0;
                while ((bufferLength = inputStream.read(buffer)) > 0) {
                    //add the data in the buffer to the file in the file output stream (the file on the sd card
                    fileOutput.write(buffer, 0, bufferLength);
                    //add up the size so we know how much is downloaded
                    downloadedSize += bufferLength;
                    //this is where you would do something to report the prgress, like this maybe
                }
                //close the output stream when done
                ContentValues contentValues = new ContentValues();
                contentValues.put("Status", "1");
                contentValues.put("Path", myImage.getPath().toString());
                myDB.UpdateDownloadStatus(contentValues, imageUrl.get(i).getSImageID());
                fileOutput.close();
            }
            myDB.closeDb();
            //catch some possible errors...
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

}

I Know this is length code but sharing if You want to analyse it deeply.

如果您需要,我将在 MainActivity 中提供我如何使用和调用此服务

  1. 如果您想进行网络操作,为什么不使用 IntentService
  2. 您应该考虑在构造函数中添加 setIntentRedelivery(true);

来自 documentation

Sets intent redelivery preferences. Usually called from the constructor with your preferred semantics.

If enabled is true, onStartCommand(Intent, int, int) will return START_REDELIVER_INTENT, so if this process dies before onHandleIntent(Intent) returns, the process will be restarted and the intent redelivered. If multiple Intents have been sent, only the most recent one is guaranteed to be redelivered.

If enabled is false (the default), onStartCommand(Intent, int, int) will return START_NOT_STICKY, and if the process dies, the Intent dies along with it.