具有计时器问题的前台服务

时间:2011-06-01 12:06:31

标签: service timer notifications foreground

我正在开发一个应用程序,以给定的时间间隔检查与服务器的连接。 我使用带有通知的前台服务。问题是,只要手机处于唤醒状态(屏幕开启),一切正常。从我通过按下电源按钮转动屏幕的那一刻起,服务开始表现得很奇怪。 我制作了一个精简的服务版本给你看。
ForegroundService.java:

package service.App;
import android.R.drawable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.IBinder;
import android.util.Log;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

public class ForegroundService extends Service {
static final String ACTION_FOREGROUND = "com.example.android.apis.FOREGROUND";
static final String ACTION_BACKGROUND = "com.example.android.apis.BACKGROUND";
private static final Class<?>[] mStartForegroundSignature = new Class[] {int.class, Notification.class };
private static final Class<?>[] mStopForegroundSignature = new Class[] { boolean.class };
private Method mStartForeground;
private Method mStopForeground;
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
private Timer timer = new Timer();
long interv=10000;
int timeout=3000;
Intent notificationIntent;
PendingIntent contentIntent;
Notification notification;
CharSequence text;
SimpleDateFormat df;

void invokeMethod(Method method, Object[] args) {
    try {
        mStartForeground.invoke(this, mStartForegroundArgs);
    } catch (InvocationTargetException e) {
        Log.w("ApiDemos", "Unable to invoke method", e);
    } catch (IllegalAccessException e) {
        Log.w("ApiDemos", "Unable to invoke method", e);
    }
}

void startForegroundCompat(int id, Notification notification) {
    Scan();
    if (mStartForeground != null) {
        mStartForegroundArgs[0] = Integer.valueOf(id);
        mStartForegroundArgs[1] = notification;
        invokeMethod(mStartForeground, mStartForegroundArgs);
        return;
    }
}

void stopForegroundCompat(int id) {
    if (mStopForeground != null) {
        mStopForegroundArgs[0] = Boolean.TRUE;
        try {
            mStopForeground.invoke(this, mStopForegroundArgs);
        } catch (InvocationTargetException e) {
            Log.w("ApiDemos", "Unable to invoke stopForeground", e);
        } catch (IllegalAccessException e) {
            Log.w("ApiDemos", "Unable to invoke stopForeground", e);
        }
        return;
    }
}

@Override
public void onCreate() {

    notificationIntent = new Intent(this, ForegroundService.class);
    contentIntent = PendingIntent.getActivity(this, 0, notificationIntent,0);
    try {
        mStartForeground = getClass().getMethod("startForeground",mStartForegroundSignature);
        mStopForeground = getClass().getMethod("stopForeground",mStopForegroundSignature);
    } catch (NoSuchMethodException e) {
        return;
    }
}

@Override
public void onDestroy() {
    stopForegroundCompat(R.string.foreground_service_started);

    if (timer != null) {

        timer.cancel();
    }
}

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

void handleCommand(Intent intent) {
    if (ACTION_FOREGROUND.equals(intent.getAction())) {
        text = getText(R.string.foreground_service_started);

        notification = new Notification(R.drawable.icon, text, System
                .currentTimeMillis());

        contentIntent = PendingIntent.getActivity(this, 0, new Intent(this,
                Start.class), 0);

        notification.setLatestEventInfo(this,
                getText(R.string.local_service_label), text, contentIntent);

        startForegroundCompat(R.string.foreground_service_started,
                notification);

    } else if (ACTION_BACKGROUND.equals(intent.getAction())) {
        stopForegroundCompat(R.string.foreground_service_started);
    }

}

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


private void Scan() {
    timer.scheduleAtFixedRate(new TimerTask() {

        @Override
        public void run() {

            Socket s = null;
            int p = 81;
            long time;
            String url="www.google.com";
            SimpleDateFormat df;
            InetAddress ipaddress;
                try {

                    ipaddress = InetAddress.getByName(url);

                    try {
                        s = new Socket();
                        s.connect(new InetSocketAddress(url, p), timeout);
                        time = System.currentTimeMillis();
                        df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        Log.i("Server Monitor",
                                "A server is running on port " + p + " "
                                        + "@ IP address "
                                        + ipaddress.getHostAddress()
                                        + ". Date/Time: "
                                        + df.format(new Date(time)));
                        s.close();
                    } catch (IOException e) {
                        String ns = Context.NOTIFICATION_SERVICE;
                        NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
                        long when = System.currentTimeMillis();
                        Notification notification = new Notification(drawable.ic_dialog_alert,"Server Monitor", when);
                        Context context = getApplicationContext();
                        CharSequence contentTitle = "Server Monitor";
                        CharSequence contentText = "No server on port." + p;
                        notification.setLatestEventInfo(context,contentTitle, contentText, contentIntent);
                        notification.defaults |= Notification.DEFAULT_SOUND;
                        final int HELLO_ID = 1;
                        mNotificationManager.notify(HELLO_ID, notification);

                    }
                } catch (UnknownHostException e) {
                    String ns = Context.NOTIFICATION_SERVICE;
                    NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
                    long when = System.currentTimeMillis();
                    Notification notification = new Notification(drawable.ic_dialog_alert,"Server Monitor", when);
                    Context context = getApplicationContext();
                    CharSequence contentTitle = "Server Monitor";
                    CharSequence contentText = "Could not find host.";
                    notification.setLatestEventInfo(context,contentTitle, contentText, contentIntent);
                    notification.defaults |= Notification.DEFAULT_SOUND;
                    final int HELLO_ID = 1;
                    mNotificationManager.notify(HELLO_ID, notification);

                }

                if (s != null) {
                    try {
                        s.close();
                    } catch (IOException ioEx) {
                        Log.i("Server Monitor", "Unable to close socket "+ s);
                    }
                }
    }

    }, 0, interv);
}
}

因此,此服务每隔10秒检查81端口与www.google.com的连接。我把它放在端口81上使其失败并测试通知。失败时会显示通知。从我关闭屏幕的那一刻起,没有一分钟或更长时间的通知,然后它立即给我所有通知。以下是启动服务的活动。
Start.java:

package service.App;
import service.App.R;
import service.App.ForegroundService;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class Start extends Activity{
private Button ButtonStartService;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ButtonStartService = (Button)findViewById(R.id.ButtonStartService);

    ButtonStartService.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            Intent intent = new Intent(ForegroundService.ACTION_FOREGROUND);
            intent.setClass(Start.this, ForegroundService.class);
            startService(intent);
        }

    });
}
}

所以我在这里做错了什么?这是我的应用程序中非常重要的一部分,所以这个bug真的必须修复。有人可以帮帮我吗?

1 个答案:

答案 0 :(得分:1)

当处于睡眠模式(CPU关机)时,Android将不会执行应用程序代码。

为避免这种情况,您可以使用AlarmManager类来安排手机启动的意图,使其从睡眠状态减弱,使用自定义广播接收器捕获它: http://developer.android.com/reference/android/app/AlarmManager.html

然后,您应该使用PowerManager类获取一个WakeLock,以确保至少CPU将保持打开状态,以便允许执行您的应用程序代码,并在不再需要时释放它(这将允许手机在睡眠模式下返回,以避免无用的电池使用): http://developer.android.com/reference/android/os/PowerManager.html

当然,您也可以在服务开始后立即获取一个WakeLock,直到您的服务关闭,但这种方法不是非常适合电池使用。