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

发布于 2024-12-08 16:06:11 字数 2147 浏览 1 评论 0原文

我在服务中设置 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;

        }

Hy i have a problem to set the ServiceUpdateUIListener in the service to update the UI. It's wrong to make a new Service object and set there the listener and put it in an intent.

Code source is at http://developerlife.com/tutorials/?p=356 there i can't find how the set the listener and start the service right.

Calling:

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);  

Service:

 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;

        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟沫凡尘 2024-12-15 16:06:11

服务文档具有相当完整的示例代码,用于在应用程序中实现服务,应用程序的另一部分可以绑定到该服务并进行调用:

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

只需将 setUpdateListener() 方法放在 Service 上,并在 onServiceConnected 后调用它() 与服务。

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

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();
    }

The Service documentation has fairly complete sample code for implementing a service in your app that another part of your app can bind to and make calls on:

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

Just put your setUpdateListener() method on the Service, and call it once you get onServiceConnected() with the service.

So your code would be something like this:

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);
    }

Now you can start the service to get it to start counting, and anyone can bind to it to register a listener to receive callbacks as it counts.

It is really hard though to answer your question very well because you aren't really saying what you actually want to accomplish. There are a lot of ways to use services, either starting or binding or mixing the two together, depending on exactly what you want to accomplish.

Now you can implement your client code again based on the sample:

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();
    }
吻泪 2024-12-15 16:06:11

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

在我看来,教程本身就是一个坏例子,在服务中保留对活动的静态引用在我看来是不好的做法;您可以使用绑定将您的服务绑定到活动,或者如果您不想,您可以传递意图。

据我所知,像您一样实例化服务并在其上设置侦听器是行不通的。您在 startService() 调用中收到错误,因为服务实例显然不是一个类;您应该使用 TimerService.class 来代替。在您的服务中,您有一个 onStart(); onStart() 已弃用函数,您应该使用 onStartCommand() 代替。

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

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

I don't know exactly what you want, but this is not the way to do it. It seems you're mixing up a lot of things.

The tutorial itself is a bad example to my opinion, keeping a static reference to an activity in a service seems to me bad practice; you would use binding to bind your service to an activity, or if you don't want to you can pass Intents around.

As far as I know instantiating a service like you do and setting a listener on it like that doesn't work. You get an error in the startService() call because the service instance isn't a class obviously; you should use TimerService.class instead. In your service you have an onStart(); onStart() is a deprecated function, you should use onStartCommand() instead.

Now, if you have an activity in which you want to show a clock you don't need nor want the service to update its UI directly of course, but if you'd want the service to calculate a new clock tick for you, just call startService(); As long as your service is alive, sending a new start service intent will just call the onStartCommand() with the intent you're sending along.

If your clock is in an activity, setup a broadcast receiver inside your activity that and let your service broadcast an intent that can be received by the broadcast receiver you setup, with your new clock value passed along.

℉絮湮 2024-12-15 16:06:11

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 文件中注册侦听器以响应服务内部的广播。

干杯。

MrJre is correct that onStart is depreciated and that you should be using onStartCommand().

If you want to get this to work, there is a better way.

I am doing something similar, as in wanting to update a UI from results happening in a service. This was not particularly easy. (In my opinion)

Here's how to do it: (First off scrap your existing code)

In UI class add:

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
    }
};

Service File:

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)
    }   

There might be some syntax errors in this code as I've just modified my existing and working code to try and fit your needs. There will obviously need to also be some modifications depending on what you want to do. But follow this framework and you will be able to do what you are trying to do.

This works for me, so hopefully you can modify this to work for you. (Only thing I've left out are the imports, but you should be able to easily figure that out)

Key points:

  • Bind service to UI
  • Register listener in UI file to respond to the broadcast from inside the service.

Cheers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文