【问题标题】:How to stop a START_STICKY Service in onResume() when user returns to activty用户返回活动时如何在 onResume() 中停止 START_STICKY 服务
【发布时间】:2016-09-05 01:50:14
【问题描述】:

我目前正在开发一个将文件上传到我的服务器的应用程序。我的应用程序旨在启动,使用直到任务完成,最后将通过按下按钮启动文件上传,并且该应用程序已被使用。所以一个非常线性的方法:开始,使用到最后,文件上传,完成。

如果由于某种原因决定直到最后才使用它,我现在还想将文件发送到我的服务器。所以当 onStop() 被调用时。

经过一些研究,我决定使用一个未绑定的服务,该服务在 onStop() 中使用 startService() 调用。在该服务中,会执行 while 循环,直到上传成功 (serverResponseCode == 200) 并在每次尝试不成功后休眠。

当用户再次开始使用停止的应用程序时,因此调用 onResume() 我想取消文件传输,因为用户无论如何都会通过单击不同活动中的上传按钮来上传文件。 因此,当我尝试在 onResume() 中停止服务时,我收到了服务已停止的 Toast,但我在 logcat 中看到它仍在运行:尝试连接到服务器并休眠。

到目前为止我所尝试的:

1) 尝试了以下答案中的建议:

automatically start up a service when the application is closed

stop service in android

2) 即使 onStartCommand 返回 START_NOT_STICKY 它也会发生。

这是我的代码:

服务代码:

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class UploadService extends Service {
    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;
    private static final String TAG = "UploadService";

    int serverResponseCode = 0;

    String upLoadServerUri = null;
    String uploadFilePath;
    String uploadFileName;

    // Handler that receives messages from the thread
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {

           //... have some Server connection parameters here

            while (serverResponseCode != 200) {

                // ... trying to upload a file to my server

                if (serverResponseCode != 200) {
                    try {
                        Log.d(TAG, "Thread slepps");
                        Thread.sleep(10000);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            stopSelf(msg.arg1);
        }
    }


    private class DisplayToast implements Runnable {
        String mText;

        public DisplayToast(String text) {
            mText = text;
        }

        public void run() {
            Toast.makeText(UploadService.this, mText, Toast.LENGTH_SHORT).show();
        }
    }


    @Override
    public void onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        mServiceHandler.sendMessage(msg);

        // If we get killed, after returning from here, restart
        return START_STICKY;
    }


    @Override
    public void onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

调用启动和停止服务的Activity:

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.LayerDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.PopupMenu;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RatingBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import java.text.SimpleDateFormat;
import java.util.Date;

public class AppSelectionActivity extends AppCompatActivity implements View.OnClickListener, OnQueryTextListener {

    private final static String TAG = "AppSelectionActivity";
    private boolean leftOnIntent = false;  // is set to true when starting one of my other activity with an intent, so that service will not be started
    private Intent serviceIntent;

    @Override
    protected void onStop() {
        Log.d(TAG,"onStop: app is stopped");
        if (!leftOnIntent) {
            serviceIntent = new Intent(AppSelectionActivity.this, UploadService.class);
            startService(serviceIntent);
            Log.d(TAG, "onStop: Intent is started");
        }
        super.onStop();
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"onResume: app is resumed");
        stopService(new Intent(AppSelectionActivity.this, UploadService.class));
        leftOnIntent = false;
    }

    @Override
    protected void onDestroy() {
        Log.d(TAG,"onDestroy: got called");
        super.onDestroy();
    }
}

对如何正确停止服务和文件上传有什么建议吗?

【问题讨论】:

    标签: android service upload


    【解决方案1】:

    您的Handler 需要一些标志,您可以设置它来停止上传循环(while (serverResponseCode != 200))。那么我们来介绍一个

    public boolean keepRunning;
    

    修改循环如下:

    keepRunning = true;
    while (serverResponseCode != 200 && keepRunning)
    {
          // ... trying to upload a file to my server
          if (serverResponseCode != 200) {
              try {
                  Log.d(TAG, "Thread slepps");
                  Thread.sleep(10000);
    
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
    }
    

    如果您想停止您的Service,您现在可以通过调用startService() 来执行某个Intent 操作...

    Intent intent = new Intent(this, UploadService.class);
    intent.setAction("your.package.name.STOP_UPLOAD");
    startService(intent);
    

    ...然后您在 onStartCommand() 中对其进行评估。

    String intentAction = (intent != null) ? intent.getAction() : "";
    if ( "your.package.name.STOP_UPLOAD".equals(intentAction) )
    {
        mServiceHandler.keepRunning = false;
        stopSelf();
    }
    else
    {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        mServiceHandler.sendMessage(msg);
    }
    return START_STICKY;
    

    这里涉及到一些方法,如果您的代码中的某些其他方法被执行,运行时会调用它们:

    • Service.onStartCommand() 由startService() 触发
    • Handler.handleMessage() 由sendMessage(); 触发

    在您的情况下,handleMessage() 在您调用 mServiceHandler.sendMessage(msg); 之后由运行时调用。这将开始上传,当用户返回Activity 时可能仍然很忙,因此会触发onResume()。所以在这个方法中,你调用startService() 来中止上传。

    这会触发onStartCommand()。在评估 Intent 操作后,boolean 将设置为 false。 Handler 可能仍在执行 while 循环。如果是这种情况,循环将立即停止,因为条件不再为真。

    【讨论】:

    • 非常感谢您的回答帮了很多忙,而且效果很好。我只需要添加'return START_NOT_STICKY;'在 stopSelf() 之后;在 if 语句中,否则即使看到活动,服务也会重新启动。
    • 还有一件事,请问您能否详细说明何时调用 onStartCommand 和 handleMessage ?似乎在 handleMessage() 开始之前布尔值 keepRunning 设置为 false ,因此上传甚至不会开始,但 keepRunning 在 while 循环之前设置为 true 。所以我的逻辑说它应该被覆盖为真。但它仍然有效,怎么会?因为当应用程序启动时,服务也启动了,服务也建立了与服务器的连接,但文件上传没有发生。你能告诉我这只是运气吗?非常感谢
    • 发现一个错误:如果我启动应用程序,然后离开通过 onStop() [例如转到主屏幕] 然后通过设置-> 应用程序-> 运行手动停止服务,线程不会被杀死(可能是因为 START_STICKY 但是当我然后恢复到我的应用程序/活动时,您的解决方案不会并且线程将继续睡眠和无论如何醒来。这种情况可能有点不太可能,但只是好奇。有什么想法吗?
    • @Savate - 感谢您的反馈。请看一下我编辑的答案。关于“手动”停止应用程序 - 好吧,警告用户:“如果您停止应用程序,它可能会出现异常”。我本来希望整个进程停止,包括处理程序,即使它在另一个线程上运行。垃圾收集器慢?关键是,在强制停止之后,您不会简单地返回。 Activity、Service、Handler 等的每一个实例都会从头开始创建。新的 mServiceHandler 对象与之前的不完全相同。旧的处理程序现在无法访问。它的标志将始终保持“真”。
    • @Savate - (继续)我必须承认我在这里有点超出我的深度。可能的解决方法:让处理程序有一个计数器和一个(非常大但有限的)循环重复限制,因此它不会在设备生命周期的剩余时间内运行。更复杂的解决方法:考虑使用在自己的线程上运行但具有 / 是一种上下文的 IntentService,因此您可以通过写入 SharedPreferences 进行通信(设置标志)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多