从MainActivity调用时服务无法启动

时间:2017-07-18 19:02:14

标签: java android

我编写了一个侦听UDP消息的服务,然后根据UDP消息中解析的消息更改TextView和ImageView。我在尝试使用服务中的公共getParsedMessage方法时获得NPE,这意味着该服务尚未启动。它在清单中被声明为拼写中的服务,所以我知道这不是问题。这是我的MainActivity的代码:

public class MainActivity extends Activity {

AlertAssignments mAlertAssignments;



Button startListeningButton;

boolean started;
int counter;
boolean mBound = false;
Context context;
ListenerService mListenerService;

TextView mTextView;
TextView mBlinkView;
ImageView mImageView;


@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    mImageView = (ImageView) findViewById(R.id.image_view);
    mTextView = (TextView) findViewById(R.id.alert_text);
    mBlinkView = (TextView) findViewById(R.id.blinking_text);
    Animation mAnimation = new AlphaAnimation(0.0f, 1.0f);
    mAnimation.setDuration(50);
    mAnimation.setStartOffset(20);
    mAnimation.setRepeatCount(Animation.INFINITE);
    mAnimation.setRepeatMode(Animation.REVERSE);
    mBlinkView.startAnimation(mAnimation); //animation value
    mAlertAssignments = new AlertAssignments();

    Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage()); //this is the cause of the NPE

    mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);

    if(parsedMessage >= 10 && parsedMessage <= 19 && parsedMessage != 0) {
        mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
    } else {
        mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
    }

}
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
        ListenerService.LocalBinder binder = (ListenerService.LocalBinder) service;
        mListenerService = binder.getService();
        mBound = true;
    }

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

@Override
protected void onStart() {
    super.onStart();
    //start listener service
    Intent listenerServiceIntent = new Intent(MainActivity.this, ListenerService.class);
    this.bindService(listenerServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
}


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

}

}

错误发生在第75行,标记在上面(Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());)。我在设置和启动服务时遵循了developer.android文档,但是我似乎在多个位置看到了冲突的信息。这是我的ListenerService:

public class ListenerService extends Service{
public String the_alarm_S;
public String parsedMessage = "";
private final IBinder mBinder = new LocalBinder();


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

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



static String UDP_BROADCAST = "UDPBroadcast";

//Boolean shouldListenForUDPBroadcast = false;
DatagramSocket socket;

private void listenAndWaitAndThrowIntent(InetAddress broadcastIP, Integer port) throws Exception {
    byte[] recvBuf = new byte[15000];
    if (socket == null || socket.isClosed()) {
        socket = new DatagramSocket(port, broadcastIP);
        socket.setBroadcast(true);
    }
    //socket.setSoTimeout(1000);
    DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
    Log.e("UDP", "Waiting for UDP broadcast");
    socket.receive(packet);

    String senderIP = packet.getAddress().getHostAddress();
    String message = new String(packet.getData()).trim();

    Log.e("UDP", "Got UDB broadcast from " + senderIP + ", message: " + message);


    broadcastIntent(senderIP, message);
    setParsedMessage(message);
    socket.close();

}

private void broadcastIntent(String senderIP, String message) {
    Intent intent = new Intent(ListenerService.UDP_BROADCAST);
    intent.putExtra("sender", senderIP);
    intent.putExtra("message", message);
    Log.e("UDP", "Message received containing" +message);
    sendBroadcast(intent);
}

Thread UDPBroadcastThread;

void startListenForUDPBroadcast() {
    UDPBroadcastThread = new Thread(new Runnable() {
        public void run() {
            try {
                InetAddress broadcastIP = InetAddress.getByName("172.16.238.255"); //172.16.238.42 //192.168.1.255
                Integer port = 12001;
                while (shouldRestartSocketListen) {
                    listenAndWaitAndThrowIntent(broadcastIP, port);
                }
                //if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
            } catch (Exception e) {
                Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
            }
        }
    });
    UDPBroadcastThread.start();
}

private Boolean shouldRestartSocketListen=true;

private void setParsedMessage(String messageContents) {
    the_alarm_S = messageContents;
    String parseMessage[] = the_alarm_S.split("!!!");
    Log.e("UDP", "Parsed message with value " + parseMessage[1]);
    parsedMessage = parseMessage[1];

}
public String getParsedMessage() {
    return parsedMessage;
}

void stopListen() {
    shouldRestartSocketListen = false;
    socket.close();
}

@Override
public void onCreate() {

};

@Override
public void onDestroy() {
    stopListen();
}


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    shouldRestartSocketListen = true;
    startListenForUDPBroadcast();
    Log.i("UDP", "Service started");
    return START_STICKY;
}




}

我以前一直在使用AsyncTask来获取这些数据,但是,我需要它不断地获取数据并更新TextViewImageView个对象,并且由于ping速度使用了执行此操作的while导致内存耗尽,因为我假设它在UI线程上运行。 AlertAssignments只是一个Enum,它将图像文件和字符串绑定到序数数组值,这样我就可以根据解析后的消息(TextView的整数值轻松更改ImageViewparsedMessage[1]。原始邮件xxx!!!n!!!xxx提供parsedMessage[1] = n

关于如何解决可能是我的疏忽的任何建议都会很棒,谢谢

1 个答案:

答案 0 :(得分:3)

了解Android活动生命周期:

enter image description here

您注意到的一件事是在onCreate()之前调用onStart()。这意味着您在实际启动之前尝试访问服务。

我解决这个问题的方法是在onCreate()中启动您的服务(因此它会在您的活动首次创建时启动),然后读取onResume()内的值,以便每个时间你的活动回到前台,它会根据服务更新。

您可能还会注意到,如果需要资源,您的应用可能会在onPause()之前被终止。在那里进行清理是一个好主意,而不是onDestroy()

修改

如果上述选项不起作用,我怀疑是否存在竞争条件。仅仅因为您在阅读之前启动了服务,并不意味着它已完全配置。值得庆幸的是,您有一个监听器可以告诉您绑定服务的时间。

您可以专门为更新用户界面编写一个单独的方法,并让您的活动仅在服务启动后调用它:

public class MyActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            ListenerService.LocalBinder binder = (ListenerService.LocalBinder) service;
            mListenerService = binder.getService();
            mBound = true;

            readFromService();
        }

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

    private void readFromService() {
        Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage()); //this is the cause of the NPE

        mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);

        if(parsedMessage >= 10 && parsedMessage <= 19 && parsedMessage != 0) {
            mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
        } else {
            mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
        }
    }
}