后台运行的更新服务

5
我需要实现以下过程:
  1. 启动后台服务
  2. 使用参数更新服务(从UI - 用户输入)
  3. 在活动结束后,服务应该继续运行并每分钟向HTTP服务器发送请求。 在这个阶段,我仍然需要第二阶段更新的参数 - 我将它们发送到服务器。
  4. 服务应该存储服务器的最后响应并与最后一次进行比较。如果有变化,则通知用户。
  5. 最后,当活动再次开始时,服务应该使用最新的服务器响应更新UI。
我尝试过: BroadcastReciver - 问题是在onRecive结束后,所有未声明为final的参数都会被清除,而且我找不到一种自动每分钟更新发送的Intent的方法。
Service - 使用startService() - 问题是当活动结束时,服务会停止并重新开始,刷新所有参数。而且我还没想出如何在服务已经启动后更新参数。
那么如何处理这种情况呢?
谢谢。

这可能会有所帮助 - https://dev59.com/cOo6XIcBkEYKwwoYTS1D - coderplus
2个回答

3

听起来你需要做的是能够“绑定”到你的服务。 我下面发布的是如何实现的简单模板。 对于你的目的,你需要在Service类中存储变量并创建getter方法,以便在重新启动活动时可以获取最新的变量。 另外,请注意我在onResume和onPause中启动和停止下面的Service示例。 你无疑会希望以不同的方式实现。

//Activity

//Bind to Service Example

public class ExampleActivity extends Activity implements OnClickListener {

// UI
private Button binderButton;

// service
private MyService myService;
private Intent serviceIntent;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.your_layout);

    // binder button
    binderButton = (Button) findViewById(R.id.button1);
    binderButton.setOnClickListener(this);
    binderButton.setText("start");

    serviceIntent = new Intent(this, MyService.class);
}

private ServiceConnection serviceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        myService = ((MyService.MyBinder) service).getService();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        myService = null;
    }
};

@Override
protected void onResume() {
    super.onResume();
    // start the service
    startService(serviceIntent);
    // bind to the service
    bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.button1:
        // call method within the service
        myService.doServiceStuff();
        break;
    }
}

@Override
protected void onPause() {
    super.onPause();
    stopService(serviceIntent);
    unbindService(serviceConnection);
}
}

//Service

public class MyService extends Service {
private final IBinder binder = new MyBinder();

@Override
public IBinder onBind(Intent arg0) {
    return binder;
}

public void doServiceStuff() {
    task.execute();
}

// create an inner Binder class
public class MyBinder extends Binder {
    public MyService getService() {
        return MyService.this;
    }
}

AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {

    @Override
    protected Void doInBackground(Void... params) {
        Log.d("yourTag", "long running service task");
        return null;
    }       
};
}

谢谢,但是不幸的是,在解除绑定后,服务计时器任务仍然停止。 顺便说一下,也许问题出在计时器任务上: code private TimerTask updateTask = new TimerTask() { @Override public void run() { test++; Log.d("OREN", "Timer task doing work" + test ); } }; public void runOnTimer(double rad){ radius = rad; timer = new Timer("UpdateTimer"); timer.schedule(updateTask, 1000L, 10 * 1000L); } code - SMD
尝试使用重复的AlarmManager。它将通过挂起意图向操作系统注册,因此您可以确保它不会停止,除非您明确告诉它停止。 - Swifty McSwifterton

2

谢谢javaJoe,虽然你的回答没有解决我的问题,但是它给了我一些好的想法。

我的做法:

  1. 在Activity的onCreate中,检查我的服务是否正在运行,如果是,则绑定它;否则,创建一个新的并绑定它。

  2. 使用setter和getter在Service和Activity之间传递参数。

  3. 在Activity的onDestroy中(问题是服务调用了自身的销毁),Activity通过Intent将最终参数发送到Broadcastreciver。Broadcastreciver再次启动服务,并使用正确的参数进行初始化。

我不知道这种架构是否理想,我想得到一些反馈。

以下是代码:

Activity:

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //Set Service Intent
    serviceIntent = new Intent(this, UpdateService.class);
    if (isMyServiceRunning()) {
        //Bind to the service
        bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }else{
        updateService=new UpdateService();
        //Start the service
        startService(serviceIntent);
        //Bind to the service
        bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}

private boolean isMyServiceRunning() {
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (UpdateService.class.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        updateService = ((UpdateService.MyBinder) service).getService();
        //Set Initial Args
        updateService.setParams(int arg0);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        updateService = null;
    }
};

@Override
protected void onDestroy() {
    //UnBind from service
    unbindService(serviceConnection);
    //Stop Service
    stopService(serviceIntent);
    //Prepare intent to broadcast reciver
    Intent intent = new Intent(MainActivity.this,ServiceRunnerBCR.class);
    intent.setAction(ServiceRunnerBCR.ACTION_SET_UpdateService);
    intent.putExtra(ServiceRunnerBCR.keyVal_arg0, arg0);
    intent.putExtra(ServiceRunnerBCR.keyVal_arg1, arg1);
    //Send broadcast to start UpdateService after the activity ended
    sendBroadcast(intent);

    super.onStop();
}

广播接收器:
public class ServiceRunnerBCR extends BroadcastReceiver {


    public static final String ACTION_SET_UpdateService = "ACTION_ALARM";

    public static final String keyVal_arg0="ARG0";
    public static final String keyVal_arg1="ARG1";


    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(ACTION_SET_UpdateService)){   
             updateIntent(context, intent.getDoubleExtra(keyVal_arg0, 0.02), intent.getStringExtra(keyVal_arg1));
        }
    }

    private void updateIntent(Context context, double arg0, String arg1){
        Intent intent = new Intent(context,UpdateService.class);
        intent.setAction(ACTION_SET_UpdateService);
        intent.putExtra(keyVal_arg0, arg0);
        intent.putExtra(keyVal_arg1, arg1);
        synchronized (this){
            try {
                this.wait(6000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        context.startService(intent);
        Log.d("OREN","ServiceRunner");
    }
}

服务:

public class UpdateService extends Service {

    private final IBinder binder = new MyBinder();
    public static final String keyVal_arg0="ARG0";
        public static final String keyVal_arg1="ARG1";
    private Timer timer;
    private HTTPHandler http = new HTTPHandler();
    private int test=0;
    double arg0=0;
    String arg1= "";

    private TimerTask updateTask = new TimerTask() {
        @Override
        public void run() {
            test++;
            Log.d("OREN", "Timer task doing work " + test + " arg0: " + arg0);
                        //Do some work here
        }
    };


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent!=null){
            arg0=intent.getDoubleExtra(keyVal_arg0, 0.002);
                        arg1=intent.getStringExtra(keyVal_arg1);
            timer = new Timer("UpdateTimer");
            timer.schedule(updateTask, 1000L, 10 * 1000L);
            Log.d("OREN", "ServiceStarted" + test);
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d("OREN", "OnBind" + test);
        return binder;
    }

    public void setArg0(double d){
        arg0=d;
    }

    // create an inner Binder class
    public class MyBinder extends Binder {
        public UpdateService getService() {
            return UpdateService.this;
        }
    }

    @Override
    public void onDestroy() {
        Log.d("OREN", "OnDestroy" + test);
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("OREN", "OnUnBind" + test);
        return super.onUnbind(intent);
    }
}

不知道具体问题是什么,我无法确定这是否是最好的解决方法。但是,我可以说几个事情。1)您正在从onDestroy()中调用super.onStop()。那肯定会最终创建问题。2)您正在在UI线程上调用sleep(6000)。您还在其上进行同步,这是不必要的。最好使用处理程序来提供某种延迟。但是,这个硬编码的延迟可能表明您的架构需要更改。我建议尽量避免使用它。 :) - Swifty McSwifterton
谢谢,我把wait(6000)改成了while (isMyServiceRunning())。如果我在onStop事件中停止服务而不是在onDestroy事件中停止,这样做不会引起问题吗? - SMD
1
我认为把它留在onDestroy中是可以的。问题在于你正在覆盖onDestroy,但是却调用了onStop()。例如,应该是“public void onDestroy() { super.onDestroy(); }”。而你写的是“public void onDestroy() { super.onStop(); }”。此外,听起来你仍然在主UI线程上阻塞了“while { (isMyServiceRunning()) }”。 - Swifty McSwifterton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接