Periodically send data to server

2019-04-16 18:56发布

问题:

I'm working on an app that receives data from Arduino through Bluetooth and sends that data to server. I've got this part working - once a user presses a button, I initiate BT connection, receive data and send it to server via Async Task:

  1. Thread for receiving data:

    private class ConnectedThread extends Thread {
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;
    
    public ConnectedThread(BluetoothSocket socket) {
    
        InputStream tmpIn = null;
        OutputStream tmpOut = null;
    
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }
    
        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }
    
    public void run() {
        Log.i("test", "Connected thread RUN");
        byte[] buffer = new byte[256];
        int bytes;
    
        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                Log.i("test", "Trying....");
                bytes = mmInStream.read(buffer);
                handler.obtainMessage(RECIEVE_MESSAGE, bytes, -1, buffer).sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }
    
    public void write(String message) {
        Log.d(TAG, "...Data to send: " + message + "...");
        byte[] msgBuffer = message.getBytes();
        try {
            mmOutStream.write(msgBuffer);
        } catch (IOException e) {
            Log.d(TAG, "...Error data send: " + e.getMessage() + "...");
        }
    }
    

    }

  2. ConnectedThread handler (receives data, starts AsyncTask):

    private final Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case RECIEVE_MESSAGE:
                    byte[] readBuf = (byte[]) msg.obj;
                    //String readMessage = new String(readBuf, 0, msg.arg1);
                    String strIncom = new String(readBuf, 0, msg.arg1);
                    sb.append(strIncom);
                    int endOfLineIndex = sb.indexOf("\r\n");
                    if (endOfLineIndex > 0) {
                        String sbprint = sb.substring(0, endOfLineIndex);
                        sb.delete(0, sb.length());
                        Log.i("test", "sbprint: " + sbprint);
                        sendDataToServer(sbprint);
                        txtData.setText("Data from Arduino: " + sbprint);
                    }
                    break;
            }
        };
    

    };

  3. sendDataToServer method:

    private void sendDataToServer(String dataToSend){
        if(dataToSend != null && !dataToSend.equals("")) {
            sendDataAsyncTask = new SendDataAsyncTask(MyActivity.this, dataToSend);
            sendDataAsyncTask.setSendDataListener(MyActivity.this);
            sendDataAsyncTask.execute();
        }
    }
    

This, more or less works. However, now I need to repeat the process every two minutes. I've got it working with TimerTask, but of course the task stops running when device goes to sleep.

I'm guessing I need a Service and / or an AlarmManager to wake the device every two minutes and run the above code? Any tips for this?

回答1:

Yes, that's right. You need to implement a Service to do the background job, that is to execute sending data to server, even when your app is not on foreground or device display turning off.

You can use one of my example code here: https://xjaphx.wordpress.com/2012/07/07/create-a-service-that-does-a-schedule-task/

Additionally, if you want to make the Service running in the best condition (that is, make sure it is not killed by system), you need to check it frequently by

  • Implement a BOOT_RECEIVED Intent, a BroadcastReceiver, to launch your app when device is started.

  • Register for an AlarmManager to schedule check whether the Service is running or not; if it dies already, you can re-start (wake) it up.



回答2:

For the off-chance someone needs it:

MainActivity.class:

public class MainActivity extends Activity implements OnClickListener {

private AlarmManager alarmManager;

private static final long INTERVAL = 10*1000;

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

    final Button btn = (Button) findViewById(R.id.btn);
    final Button btn2 = (Button) findViewById(R.id.btn2);
    btn.setOnClickListener(this);
    btn2.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.btn:
        setServiceAlarm();
        break;
    case R.id.btn2:
        cancelServiceAlarm();
        break;

    default:
        break;
    }
}

private void setServiceAlarm(){
    if(alarmManager == null){
        Log.i("test", "***STARTING SERVICE***");
        showToast("Starting...");
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.SECOND, 10);
    Intent intent = new Intent(MainActivity.this, AlarmReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0);
    alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), INTERVAL, pendingIntent);
    }
}

private void cancelServiceAlarm(){
    if(alarmManager != null){
        Log.i("test", "***STOPPING SERVICE***");
        showToast("Stopping...");
        Intent intent = new Intent(MainActivity.this, AlarmReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0);
    alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
    alarmManager.cancel(pendingIntent);
    }
}

private void showToast(String message){
    Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}

AlarmReciever.class:

public class AlarmReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    // TODO Auto-generated method stub
    Log.i("test", "Alarm received...");
    Intent serviceIntent = new Intent(context, MyIntentService.class);
    serviceIntent.putExtra("date", Utils.getDateTime());
    context.startService(serviceIntent);
    //Log.i("test", "onReceive: " + Utils.getDateTime());
}
}

MyIntentService.class:

public class MyIntentService extends IntentService {

public MyIntentService() {
    super("MyIntentService");
    // TODO Auto-generated constructor stub
}

@Override
protected void onHandleIntent(Intent intent) {
    // TODO Auto-generated method stub
    final String str = intent.getStringExtra("date");
    Log.i("test", "MyIntentService " + str);
}

}

Manifest declaration inside application tags:

<service android:name=".MyIntentService"/>
<receiver android:name=".AlarmReceiver"></receiver>