在基于服务的类中设置监听器

时间:2011-10-05 10:33:42

标签: android android-service

我在服务中设置ServiceUpdateUIListener以更新UI时遇到问题。制作一个新的Service对象并在那里设置监听器并将其置于意图中是错误的。

代码源位于http://developerlife.com/tutorials/?p=356,我无法找到如何设置监听器并正确启动服务。

通话:

TimerService service = new TimerService();
                TimerService.setUpdateListener(new ServiceUpdateUIListener() {

                    @Override
                    public void updateUI(String time) {
                        clock.setText(time);

                    }
                });

                Intent i  = new Intent(Timer.this,service.class); //service cannot be resolved to a type
                i.putExtra("ms", ms);
                startService(i);  

服务:

 public class TimerService extends Service{

        CountDownTimer timer;
        Chronometer clock;
        public static ServiceUpdateUIListener UI_UPDATE_LISTENER;

        @Override
        public IBinder onBind(Intent intent) {

            return null;
        }
        @Override
        public void onStart(Intent intent, int startId) {
            // TODO Auto-generated method stub
            int ms = intent.getIntExtra("ms", 0);

            timer = new  CountDownTimer(ms,1000){
                @Override
                public void onTick(long millisUntilFinished) {

                    int seconds = (int) (millisUntilFinished / 1000) % 60 ;
                    int minutes = (int) ((millisUntilFinished / (1000*60)) % 60);
                    int hours   = (int) ((millisUntilFinished / (1000*60*60)) % 24);

                    clock.setText( String.format("%02d:%02d:%02d", hours,minutes,seconds));
                    Log.e("Timer", String.valueOf(millisUntilFinished));

                }

                @Override
                public void onFinish() {
                    // TODO Auto-generated method stub

                }
            }.start();
            super.onStart(intent, startId);
        }
        public static void setUpdateListener(ServiceUpdateUIListener l) {
             UI_UPDATE_LISTENER = l;

        }

3 个答案:

答案 0 :(得分:24)

服务文档包含相当完整的示例代码,用于在您的应用中实现服务的另一部分可以绑定并拨打电话:

http://developer.android.com/reference/android/app/Service.html#LocalServiceSample

只需将您的setUpdateListener()方法放在Service上,并在使用该服务获得ServiceConnected()后调用它。

所以你的代码会是这样的:

public interface UpdateListener {
    public void onUpdate(long value);
}

class LocalService {
    // Like in the Service sample code, plus:

    public static String ACTION_START = "com.mypackage.START";

    private final ArrayList<UpdateListener> mListeners
            = new ArrayList<UpdateListener>();
    private final Handler mHandler = new Handler();

    private long mTick = 0;

    private final Runnable mTickRunnable = new Runnable() {
        public void run() {
            mTick++;
            sendUpdate(mTick);
            mHandler.postDelayed(mTickRunnable, 1000);
        }
    }

    public void registerListener(UpdateListener listener) {
        mListeners.add(listener);
    }

    public void unregisterListener(UpdateListener listener) {
        mListeners.remove(listener);
    }

    private void sendUpdate(long value) {
        for (int i=mListeners.size()-1; i>=0; i--) {
            mListeners.get(i).onUpdate(value);
        }
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ACTION_START.equals(intent.getAction()) {
            mTick = 0;
            mHandler.removeCallbacks(mTickRunnable);
            mHandler.post(mTickRunnable);
        }
        return START_STICKY;
    }

    public void onDestroy() {
        mHandler.removeCallbacks(mTickRunnable);
    }

现在你可以启动服务让它开始计数,任何人都可以绑定它来注册一个监听器来接收回调计数。

很难回答你的问题,因为你并没有真正说出你真正想要完成的事情。有很多方法可以使用服务,无论是启动还是绑定或将两者混合在一起,具体取决于您想要完成的任务。

现在,您可以根据示例再次实现客户端代码:

public class SomeActivity extends Activity implements UpdateListener {
    private LocalService mBoundService;

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mBoundService = ((LocalService.LocalBinder)service).getService();
            mBoundService.registerListener(this);
        }

        public void onServiceDisconnected(ComponentName className) {
            mBoundService = null;
        }
    };

    void doBindService() {
        bindService(new Intent(Binding.this, 
                LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    void doUnbindService() {
        if (mIsBound) {
            if (mBoundService != null) {
                mBoundService.unregisterListener(this);
            }
            unbindService(mConnection);
            mIsBound = false;
        }
    }

    protected void onDestroy() {
        super.onDestroy();
        doUnbindService();
    }

答案 1 :(得分:1)

我不知道你想要什么,但这不是这样做的方法。看来你混淆了很多东西。

本教程本身就是一个不好的例子,在服务中保持对活动的静态引用似乎是不好的做法;你会使用绑定将你的服务绑定到一个活动,或者如果你不想,你可以传递Intents。

据我所知,像你一样实例化服务并在其上设置一个监听器就好了。你在startService()调用中得到一个错误,因为服务实例显然不是一个类;你应该使用TimerService.class代替。在您的服务中,您有一个onStart(); onStart() is a deprecated function,你应该改用onStartCommand()。

现在,如果你有一个活动,你想要显示一个你不需要的时钟,也不希望服务直接更新它的UI,但如果你想让服务计算一个新的时钟滴答你,只需调用startService();只要您的服务处于活动状态,发送新的启动服务意图只会使用您发送的意图调用onStartCommand()。

如果您的时钟处于某项活动中,请在您的活动中设置一个广播接收器,并让您的服务广播您设置的广播接收器可以接收的意图,并传递新的时钟值。

答案 2 :(得分:-1)

MrJre是正确的,onStart是折旧的,你应该使用onStartCommand()。

如果你想让它发挥作用,有更好的方法。

我正在做类似的事情,比如想要从服务中发生的结果更新UI。这不是特别容易。 (在我看来)

以下是如何操作:(首先废弃现有代码)

在UI类中添加:

public Intent service;
service = new Intent(thisContext, TimerService.class);
service.putExtra("ms", ms);
startService(service);

//bind service to the UI **Important**
bindService();

IntentFilter timerFilter = new IntentFilter("TimerIntent"); // Filter that gets stuff from the service
registerReceiver(myReceiver, timerFilter);

void bindService() {
    Intent newIntent = new Intent(this, TimerService.class);
    bindService(newIntent, mConnection, Context.BIND_AUTO_CREATE);
    mIsBound = true;
}

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName className, IBinder binder) {
        s = ((TimerService.MyBinder) binder).getService();
    }

    @Override
    public void onServiceDisconnected(ComponentName className) {
        s = null;
    }
};

public void releaseBind() {
    if (mIsBound) {
        unbindService(mConnection);
        mIsBound = false;
    }
}

// Now in this class we need to add in the listener that will update the UI (the receiver registered above) 
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        //Get Bundles
        Bundle extras = intent.getExtras();
        /* DO ANY UI UPDATING YOU WANT HERE (set text boxes, etc.) TAKING INFO FROM THE "extras" Bundle ie: setting the clock*/
        //ie: int timerTest = extras.getInt("0");
        // Now update screen with value from timerTest
    }
};

服务档案:

public class TimerService extends Service {

    public TimerService () {
        super();
    }

    private final IBinder mBinder = new MyBinder();
    public Timer clockTimer = new Timer();  
    public int timer = 0;   

    // We return the binder class upon a call of bindService
    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // After service starts this executes
        Bundle extras; 
        extras = intent.getExtras(); 
        /* Call a function to do stuff here. Like if you are a clock call a timer function updates every second */ 
        // Here's an example, modify to fit your needs. 
        clock();

        return START_STICKY;
    }

    public class MyBinder extends Binder {
        TimerService getService() {
            return TimerService.this;
        }
    }

    public void clock() {
        clockTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    // Some function ie: Time = Time + 1 // 
                    /* MAKE SURE YOU BROADCAST THE RECEIVER HERE. This is what you send back to the UI.  IE:*/
                    timer = timer+ 1; // increment counter
                    Intent intent = new 
                    //Bundle the timervalue with Intent
                    intent.putExtra("0", timer);
                    intent.setAction("TimerIntent");
                    sendBroadcast(intent); // finally broadcast to the UI
                } catch(Exception ie) {
                }
            }   
        },
        0, // Delay to start timer
        1000); // how often this loop iterates in ms (so look runs every second)
    }   

此代码中可能存在一些语法错误,因为我刚刚修改了现有代码和工作代码以尝试满足您的需求。显然,根据您的要求,还需要进行一些修改。但是按照这个框架,你将能够做你想做的事情。

这对我有用,所以希望你可以修改它以适合你。 (我唯一遗漏的是进口,但你应该能够轻松搞清楚)

关键点:

  • 将服务绑定到UI
  • 在UI文件中注册侦听器以响应服务内部的广播。

干杯。