活动销毁时自动调用的服务

14

我遇到了Activity + Service的问题,我的Activities和Services如下:

Activities:

LoginActivity => OrderListActivity => AddOrderActivity => ConfirmOrderActivity

Services:

  1. ReceivingOrderService - 接收来自服务器的新数据
  2. SendingOrderService - 发送新数据到服务器

上述两个服务都在另一个独立的服务中以某些间隔调用。

  1. CheckAutoSyncReceivingOrder - 每15分钟调用ReceivingOrderService
  2. CheckAutoSyncSendingOrder - 每3分钟调用SendingOrderService

CheckAutoSyncReceivingOrder:

public class CheckAutoSyncReceivingOrder extends Service {

    Timer timer;

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        // TODO Auto-generated method stub

        if(timer != null) {
            timer.cancel();
            Log.i(TAG, "RECEIVING OLD TIMER CANCELLED>>>");
        }

        timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if(InternetConnection.checkConnection(getApplicationContext())) {
                    if(getDatabasePath(DatabaseHelper.DATABASE_NAME).exists())
                        startService(new Intent(CheckAutoSyncReceivingOrder.this, ReceivingOrderService.class));
                } else {
                    Log.d(TAG, "Connection not available");
                }
            }
        }, 0, 60000); // 1000*60*15 = 9,00,000 = 15 minutes
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();

        if(timer != null)
            timer.cancel();

        Log.d(TAG, "Stopping Receiving...");
    }
}

检查自动同步发送订单:

public class CheckAutoSyncSendingOrder extends Service {

    Timer timer;

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        // TODO Auto-generated method stub

        if(timer != null) {
            timer.cancel();
            Log.i(TAG, "OLD TIMER CANCELLED>>>");
        }

        timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                Log.i(TAG, ">>>>>>>> SENDING AUTO SYNC SERVICE >>>>>>>>");
                if(InternetConnection.checkConnection(getApplicationContext())) {
                    if(getDatabasePath(DatabaseHelper.DATABASE_NAME).exists())
                        startService(new Intent(CheckAutoSyncSendingOrder.this, SendingOrderService.class));
                } else {
                    Log.d(TAG, "connection not available");
                }
            }
        }, 0, 120000); //  1000*120*15 = 1,800,000 = 15 minutes
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();

        if(timer != null)
            timer.cancel();

        Log.d(TAG, "Stopping Sending...");
    }
}

确认订单活动(ConfirmOrderActivity)#最终任务,我已经调用以插入数据:

new AsyncTask<Void, Void, Integer>() {

    ProgressDialog progressDialog;

    @Override
    protected void onPreExecute() {
        // TODO Auto-generated method stub
        super.onPreExecute();

        progressDialog = new ProgressDialog(
                ConfirmOrderProductActivity.this);
        progressDialog.setMessage("Inserting "
                + (isInquiry ? "Inquiry" : "Order") + "...");
        progressDialog.setCancelable(false);
        progressDialog
                .setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.show();
    }

    @Override
    protected Integer doInBackground(Void... params) {
        // TODO Auto-generated method stub
        int account_id = context.getSharedPreferences(PREF_DATA,
                MODE_APPEND).getInt(DATA_ACCOUNT_ID, 0);

        /**
         * Check Whether isInquiry or not...
         */
        product_type = isWeight ? 1 : 0;
        if (isInquiry) {
            /*
             * INSERTING DATA IN INQUIRY TABLE
             */
            return m_inquiry_id;
        } else {
            /*
             * INSERTING DATA IN ORDER TABLE
             */
            return m_order_id;
        }
    }

    @Override
    protected void onPostExecute(Integer m_order_id) {
        // TODO Auto-generated method stub
        super.onPostExecute(m_order_id);

        progressDialog.dismiss();

        if (dbHelper.db.isOpen())
            dbHelper.close();

        String title = "Retry";
        String message = "There is some problem, Go Back and Try Again";

        AlertDialog.Builder alert = new AlertDialog.Builder(
                ConfirmOrderProductActivity.this);

        if (m_order_id != -1) {
            title = isInquiry ? "New Inquiry" : "New Order";
            message = isInquiry ? "Your Inquiry Send Successfully." : "Your Order Saved Successfully.";
            alert.setIcon(R.drawable.success).setCancelable(false);
        } else {
            alert.setIcon(R.drawable.fail).setCancelable(false);
        }

        alert.setTitle(title).setMessage(message)
                .setPositiveButton("OK", new OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog,
                            int which) {
                        // TODO Auto-generated method stub
                        dialog.dismiss();
                        startActivity(new Intent(
                                ConfirmOrderProductActivity.this,
                                FragmentChangeActivity.class)
                                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));

                        /* Opening Left to Right Animation */
                        overridePendingTransition(R.anim.right_out,
                                R.anim.right_in);
                    }
                });

        AlertDialog alertDialog = alert.create();
        alertDialog.show();

    }
}.execute();

插入记录到数据库的流程完全正常。

添加询问后:

enter image description here

销毁活动并获取以下Logcat:

enter image description here

主要问题:

当我从ConfirmOrderActivity成功下订单时,它会显示AlertDialog成功消息,该消息不可取消false。 当我停止此活动时,它会自动调用CheckAutoSyncReceivingOrderCheckAutoSyncSendingOrder

编辑:

我只有在LoginActivity中调用这两个服务, 然后按照给定的间隔时间自动调用,但问题是当我在显示对话框时关闭ConfirmOrderActivity时会出现问题。

我不知道为什么会发生这种情况,为什么直接停止活动时它会自动运行。

我已尝试在Service中使用onStartCommand()START_NON_STICKY,但无效。(因为START_STICKY是默认值。)

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

有解决方案吗?


2
当活动被销毁时,您是否已停止服务? - ajantha
不,@ajantha,我没有使用onDestroyonStop方法。它是自动调用的,没有停止。 - Pratik Butani
在您的活动被销毁时尝试解除绑定并销毁服务。 - ajantha
@ajantha 我想要在给定的时间间隔内保持它的调用,所以我不能这样做。但是我不知道为什么当我销毁活动时它还在调用。 - Pratik Butani
如果服务的生命周期与活动一样长,那么您使用服务的方式是错误的,而且并不需要它们。这将简化您的代码。 - Blundell
@MsYvette,我已经发布了一个任务,其中涉及将记录插入数据库。 - Pratik Butani
4个回答

4
你需要将你的服务放在前台,这样当活动被销毁时,服务也会被销毁;或者使用绑定服务并通过活动生命周期管理绑定,这样当活动被销毁时,服务不会被持续重启。请参考绑定服务教程中的说明。你需要为每个服务都做出相应的调整。
public class CheckAutoSyncReceivingOrder extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();

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

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

从创建并调用服务的活动中,当活动被销毁时,您希望服务也被销毁。

public class BindingActivity extends Activity {
    CheckAutoSyncReceivingOr mService;
    boolean mBound = false;


    @Override
    protected void onStart() {
        super.onStart();
        // Bind to CheckAutoSyncReceivingOr
        Intent intent = new Intent(this, CheckAutoSyncReceivingOr.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
            IBinder service) {
            // We've bound to CheckAutoSyncReceivingOr, cast the IBinder and get CheckAutoSyncReceivingOr instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}   

管理服务生命周期。使用定时器重新启动同一服务,不要创建新服务。

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

注意,START_NOT_STICKY仅在设备内存不足时防止服务重新启动。

请注意,在启动服务时需要谨慎,只需要启动一次并允许该服务维护其自己的生命周期,直到您使用活动将其销毁。

这是针对您最初未编辑的问题的回复,当应用程序神秘崩溃时:

您需要在附加对话框的上下文窗口之前销毁对话框。这会导致问题。因此,程序流程和关闭和清理资源的顺序非常重要。如果它们依赖于父窗口(通常以特定活动的形式),则通常必须按创建顺序的相反顺序进行销毁。

很难跟踪你的代码,因此这是一个通用的答案。

在您的活动中利用onPause和onDestroy。

在所有活动中,管理您在该活动中创建的任何资源,并通过空值检查将其关闭。就像您在服务类中所做的那样。如果您想覆盖父onDestroy,请在super.onDestroy之前放置您的自定义代码。

protected void onDestroy() {

    if(timer != null)
        timer.cancel();

    Log.d(TAG, "Stopping Sending...");

    super.onDestroy();
}

你能进一步解释一下运行实例可能需要在整个应用程序中传递单个应用上下文吗? - Pratik Butani
我已经尝试了START_NOT_STICKY和其他选项,但都没有起作用。 - Pratik Butani
亲爱的,不是这样的。我已经给了那个第一个回答我的用户赏金,他的回答对我有50%的帮助。你在他之后才提到服务,所以我给了他。感谢你的努力,你真的很可贵。 - Pratik Butani

1

(1)关于对话框:

解决方案是在退出Activity之前调用Dialogdismiss()方法,例如在onDestroy()中。在离开一个Activity之前,应该关闭所有窗口和对话框

(2)关于自启动服务:

您需要查看服务从其onStartCommand方法返回的值。默认值为START_STICKY,它将在服务被销毁后重新启动。请查看onStartCommand文档以获取更多详细信息:


第二个解决方案对我不起作用。还有其他的解决方案吗? - Pratik Butani

1
当应用程序被杀死时,服务也会被杀死(在服务的onStartCommand()onDestroy()函数中添加日志,并尝试从最近列表中清除应用程序,你将看到onDestroy()被调用。如果在onStartCommand()中返回了START_STICKY意图,则Android将重新启动服务)。
有两种方法可以解决您的问题:
  1. 将您的两个服务都设置为前台服务

  2. 不要使用CheckAutoSyncReceivingOrderCheckAutoSyncSendingOrder来安排启动另一个服务,而应该使用AlarmManager来安排您的任务。


1
如果运行您的服务的进程被杀死,Android系统将自动重新启动它,这是默认行为。
此行为由您的Service实现中onStartCommand()的返回值定义。常量START_NOT_STICKY告诉Android在进程“被杀死”时不要重新启动服务。
您需要在服务类中覆盖方法onStartCommand()并将所有代码从onStart()方法移动到onStartCommand()方法中。
根据Android文档:
对于启动的服务,它们可以决定以两种额外的主要模式运行,具体取决于它们从onStartCommand()返回的值:START_STICKY用于需要明确启动和停止的服务,而START_NOT_STICKYSTART_REDELIVER_INTENT用于只在处理发送到它们的任何命令时保持运行的服务。

onStart() 方法会在每次服务重新启动时调用,但如果你返回 START_NON_STICKY,则不会调用 onStartCommand() 方法。

不要再使用 onStart() 方法了,它已经过时。

希望这能帮到你。


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