IntentService:如何正确入队?

Posted

技术标签:

【中文标题】IntentService:如何正确入队?【英文标题】:IntentService : How to enqueue correctly? 【发布时间】:2011-10-15 12:52:04 【问题描述】:

在我的代码中,我使用IntentService 来收听位置更新(GPS 或网络更新),当收到事件时会触发此IntentService,因此它从任何活动中以startService() 开始.

public class AddLocationService extends IntentService implements LocationListener 
    /*My code here*/


    @Override
protected void onHandleIntent(Intent intent) 
    if(getOldLoc() == null) 
     
        //Get a new location
        this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, TIME_INTERVAL_GPS, 0, this);
        this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, TIME_INTERVAL_GPS, 0, this);
        Log.d(AddLocationService.TAG, "Network listener started");

        this.time_start_listening = System.currentTimeMillis();
        mTimerThread mTimerRunnable = new mTimerThread();
        this.timerThread = new Thread(mTimerRunnable);
        this.timerThread.start();
    
    else    
        /*REUSE OLD LOCATION*/

现在我的问题是:当两个事件启动 IntentService 并且第二个事件启动它而第一个事件仍在请求更新时,我希望第二个事件等到第一个事件完全完成(找到位置或计时器线程完成)。 但是,每当第二次执行 IntentService 时(第一个实例仍在运行),它会打印日志并按照并行执行的方式执行。

但是我认为IntentService 的主要目标是它是连续的,所以第二个意图必须等到第一个意图完成...

我是不是误会了什么?

【问题讨论】:

【参考方案1】:

您的 onHandleIntent 方法似乎没有阻塞它正在执行的线程,因此它会快速返回并允许处理第二个意图。不仅如此,从 LocationManager 到该线程的任何回调都不太可能被处理,因为后台线程可能会在 onHandleIntent 完成时被杀死。

如果您真的想使用 IntentService 来管理您的意图队列,那么您需要在自己的线程上进行位置处理,并在等待位置回调时将 IntentService 线程加入到位置线程。

这里有一段代码演示了这个想法:

public class TestService extends IntentService 
    private static final String TAG = "TestService";

    private Location mLocation = null;

    public TestService() 
       super(TAG);
    

    @Override
    public void onHandleIntent(Intent intent) 
        Log.d(TAG, "onHandleIntent");

        if (mLocation == null) 
            Log.d(TAG, "launching location thread");
            LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

            LocationThread thread = new LocationThread(locationManager);
            thread.start();
            try 
                thread.join(10000);
             catch (InterruptedException e) 
                Log.d(TAG, "timeout");
                return;
            

            Log.d(TAG, "join finished, loc="+mLocation.toString());
         else 
             Log.d(TAG, "using existing loc="+mLocation.toString());
        
    

    private class LocationThread extends Thread implements LocationListener  
        private LocationManager locationManager = null;

        public LocationThread(LocationManager locationManager) 
            super("UploaderService-Uploader");
            this.locationManager = locationManager;
        

        @Override
        public void run() 
            Log.d(TAG, "Thread.run");
            Looper.prepare();
           this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
            this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);

            Looper.loop();
        

        @Override
        public void onLocationChanged(Location location) 
            // TODO Auto-generated method stub
            Log.d(TAG, "onLocationChanged("+location.toString()+")");
            mLocation = location;
            Looper.myLooper().quit();
         

         @Override
         public void onProviderDisabled(String arg0) 
         

         @Override
         public void onProviderEnabled(String arg0) 
         

         @Override
         public void onStatusChanged(String arg0, int arg1, Bundle arg2) 
         

   

有趣的是 Looper 在线程上运行消息循环(以允许处理回调)。

考虑到使用 IntentService 执行此操作所需的工作量,可能值得研究从 Service 派生并管理您自己的意图队列。

【讨论】:

嘿@Rob,感谢您的回复。当您说onHandleIntent 方法没有阻塞线程时,您能否更具体一点?我应该怎么做才能阻止它?从另一个活动创建意图时与标志有关还是我应该在 onHandleIntent() 方法中做的事情? 编辑:我想我明白你的意思:我的 IntentService 在仍然请求更新的同时返回。我怎样才能阻止它。两人wait() - notify() 会是个好主意吗? 有点,我已经更新了我的答案,提供了有关如何修复它的更多信息 也可以直接在 IntentService 后台线程上使用 Looper,但我有点紧张,因为我不确定它是否会对 IntentService 管理排队。【参考方案2】:

onHandleIntent 已经在它自己的线程中了。你不(不应该)在那里创作。这一切都由 IntentService 为您处理。

【讨论】:

【参考方案3】:

感谢一百万,这正是我处理位置请求所需要的。 谢谢你的解释,让我明白,我对所有的looper概念都不是很熟悉,现在我更好地理解了!

如果有人需要同样的东西,如果您的位置线程没有自然停止(join(millis) 的时间结束),请不要忘记停止线程循环器,方法是在onHandleIntent() 中添加:

                if(thread.isAlive())
                
                    thread.onThreadStop();
                    try
                        thread.interrupt();
                    catch (Exception e) 
                        Log.d(TAG, "Exception on interrupt: " + e.getMessage());
                    
                   

thread.join(yourTime) 之后,例如,如果您没有找到任何位置更新,您仍然会在一段时间后停止线程。以及方法onThreadStop()

                /*We remove location updates here and stop the looper*/
                public void onThreadStop()
                
                    this.locationManager1.removeUpdates(this);
                    handleLocationChange(AddLocationService.this.currentBestLocation);
                    Looper.myLooper().quit();
                

但是,我认为我在第一次运行此代码时看到了我的两个意图被处理,但现在当我有多个意图同时仍然请求位置更新时,只有第一个被处理。 我的方法onHandleIntent() 似乎执行正确,在指定的时间后停止线程,甚至显示最后一个日志(方法的最后一条语句),但第二个意图没有执行...... 你知道为什么吗?

【讨论】:

经过几次测试后,我注意到只有在某些情况下(当我调用thread.interrupt(),即时间到期时)线程仍在运行,这可能就是为什么下一个意图不是治疗。但是,当线程正常完成时,线程确实完全停止(thread.isAlive() 返回 false)并且我的下一个意图被发送。所以我的问题是:如何正确停止我的线程,和/或为什么在使用thread.interrupt() 时它没有停止??

以上是关于IntentService:如何正确入队?的主要内容,如果未能解决你的问题,请参考以下文章

LocalBroadcastReceiver + IntentService 是正确的做法吗?

在 IntentService 和 AsyncTask (Android) 之间使用共享代码时,领域“从不正确的线程访问”错误

如何使用 Intentservice 获取位置信息?

jQuery 未在 Wordpress 中定义,但我的脚本已正确入队

你能解释一下 IntentService 是如何工作的吗

位置侦听器从服务而不是 IntentService 工作