Hello.

It is necessary to transfer the data received via UART to the Activity. This can be done by creating a thread in the Activity in which to organize while loop(!isInterrupted()) and read data from the UART buffer. After that, by calling the UI thread Activity - MainActivity.this.runOnUiThread(new Runnable() , perform the necessary actions with this Activity. But if we call other Activities from the main Activity, then the organized thread does not allow passing data to the newly created Activities. If I I understand correctly, in order for data from a stream to be transferred to any Activity, the stream must be created not in the Activity, but in the Service.

Question: data has arrived via UART, in a stream (which is created in Servce) it is necessary to transfer data to the Activity, which is now active, how can this be done and is this even done?

1 answer

In each Activity you create a Handler. In the onResume() method of this Activity, bindService() is done. One of the parameters there is interface ServiceConnection. Implement it with at least the same Activity. Implement the onServiceConnected() method in it. In this callback, one of the parameters is Service itself. So call this Service’s own setHandler() method. Pass there the Handler that is in the current Activity. But send incoming data via UART to Service on this Handler. By the way, Handler traditionally runs on the main thread, so there will be no need to runOnUiThread.

Somehow I had a task to transfer data from a service to an activity. The search began for a solution in the standard SDK, but since there was no time, I created a bad solution in the form of using a database. But the question was open and after some time I figured out a more correct method, which is in the SDK - using the Message, Handler, Messenger classes.

Idea

We need to transfer data from the activity to the service and back. How do we do this? We already have everything we need to solve our problem. All you need is to bind the service to the activity using bindService, pass the necessary parameters and a little magic in the form of using the Message classes. And the magic is to use the Message instance variables and, in particular, replyTo. We need this variable so that we can access the Messanger service instance from the activity and in the service the Messanger instance of the activity. In fact, it's not that simple. At least to my less than gifted mind. In part, I'm just improving the documentation that already exists - Services Also, there is good example on StackOverflow. In any case, I hope the article will be useful to at least someone and my work was not in vain.

Example

As an example, we will implement a service that will increase and decrease the counter value and return the result in the activity, in the TextView. I’ll omit the layout code, because there are two buttons and a text field - everything is simple.

Implementation

Here's the complete activation code:

Public class MainActivity extends Activity ( public static final String TAG = "TestService"; TestServiceConnection testServConn; TextView testTxt; final Messenger messenger= new Messenger(new IncomingHandler()); Messenger toServiceMessenger; @Override public void onCreate(Bundle savedInstanceState) ( super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); testTxt = (TextView)findViewById(R.id.test_txt); bindService(new Intent(this, TestService.class ), (testServConn = new TestServiceConnection()), Context.BIND_AUTO_CREATE); ) @Override public void onDestroy() ( super.onDestroy(); unbindService(testServConn); ) public void countIncrClick(View button)( Message msg = Message. obtain(null, TestService.COUNT_PLUS); msg.replyTo = messenger; try ( toServiceMessenger.send(msg); ) catch (RemoteException e) ( e.printStackTrace(); ) ) public void countDecrClick(View button)( Message msg = Message.obtain(null, TestService.COUNT_MINUS); msg.replyTo = messenger; try ( toServiceMessenger.send(msg); ) catch (RemoteException e) ( e.printStackTrace(); ) ) private class IncomingHandler extends Handler ( @Override public void handleMessage(Message msg)( switch (msg.what) ( case TestService.GET_COUNT: Log.d(TAG, "(activity)...get count"); testTxt.setText(""+msg.arg1); break; ) ) ) private class TestServiceConnection implements ServiceConnection ( @Override public void onServiceConnected(ComponentName name, IBinder service) ( toServiceMessenger = new Messenger(service); //send the initial value of the counter Message msg = Message.obtain(null, TestService.SET_COUNT); msg.replyTo = messenger; msg.arg1 = 0; //our counter try ( toServiceMessenger.send(msg); ) catch (RemoteException e) ( e.printStackTrace(); ) ) @Override public void onServiceDisconnected(ComponentName name) ( ) ) )

Let me explain. When creating an activity, we immediately bind to the service, implementing the ServiceConnection interface and in it we send a message to the service “set the counter value”, passing zero and creating a toServiceMessanger, passing the IBinder interface to the constructor. By the way, this copy must be returned to the service, otherwise there will be an NPE. Using this class we send messages to the service. And here is the magic - in the replyTo variable we save our other Messenger instance - the one that receives a response from the server and it is through it that communication with the activity will be carried out.

To receive a message from the service, we use our Handler and simply look for the variables we need and take actions on them. By clicking on the buttons (methods countIncrClick, countDecrClick) we send requests to the service, indicating required action in the msg.what variable.

Package com.example.servicetest; import android.app.Service; import android.content.*; import android.os.*; import android.os.Process; import android.util.Log; public class TestService extends Service ( public static final int COUNT_PLUS = 1; public static final int COUNT_MINUS = 2; public static final int SET_COUNT = 0; public static final int GET_COUNT = 3; int count = 0; IncomingHandler inHandler; Messenger messanger; Messenger toActivityMessenger; @Override public void onCreate())( super.onCreate(); HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); inHandler = new IncomingHandler(thread.getLooper()); messanger = new Messenger(inHandler); ) @Override public IBinder onBind(Intent arg0) ( return messanger.getBinder(); ) @Override public int onStartCommand(Intent intent, int flags, int startId) ( return START_STICKY; ) //message handler activity private class IncomingHandler extends Handler ( public IncomingHandler(Looper looper)( super(looper); ) @Override public void handleMessage(Message msg)( //super.handleMessage(msg); toActivityMessenger = msg.replyTo; switch (msg.what) ( case SET_COUNT: count = msg.arg1; Log.d(MainActivity.TAG, "(service)...set count"); break; case COUNT_PLUS: count++; Log.d(MainActivity.TAG , "(service)...count plus"); break; case COUNT_MINUS: Log.d(MainActivity.TAG, "(service)...count minus"); count--; break; ) //send the counter value in activity Message outMsg = Message.obtain(inHandler, GET_COUNT); outMsg.arg1 = count; outMsg.replyTo = messanger; try ( if(toActivityMessenger != null) toActivityMessenger.send(outMsg); ) catch (RemoteException e) ( e.printStackTrace(); ) ) ) )

Everything is similar to the logic in Activiti. I don’t even know if I need to explain anything. The only point is that I immediately send the request back to the activity in handleMessage, using the magic replyTo variable and pulling out the desired Messenger above. And the second point I already talked about is:

@Override public IBinder onBind(Intent arg0) ( return messanger.getBinder(); )

without which everything will fall. It is this interface instance that will be passed to the ServiceConnection

Conclusion

All in all. This is a far-fetched example of interaction between an activity and a service. It seems to me that this is a rather non-trivial interaction, although others may think otherwise.

Questions, clarifications, etc. in PM. There may be inaccuracies regarding some aspects, so feel free to write and correct.
I hope the post was useful to readers.

Last update: 04/03/2018

An Intent object is used to transfer data between two Activities. Through its putExtra() method you can add a key and its associated value.

For example, passing the string "Hello World" with the key "hello" from the current activity to SecondActivity:

// creating an Intent object to launch SecondActivity Intent intent = new Intent(this, SecondActivity.class); // passing an object with the key "hello" and the value "Hello World" intent.putExtra("hello", "Hello World"); // start SecondActivity startActivity(intent);

To transfer data, the putExtra() method is used, which allows you to transfer data of the simplest types as a value - String, int, float, double, long, short, byte, char, arrays of these types, or a Serializable interface object.

To get the sent data when loading SecondActivity, you can use the get() method, which is passed the object key:

Bundle arguments = getIntent().getExtras(); String name = arguments.get("hello").toString(); // Hello World

Depending on the type of data we are sending, we can use a number of methods on the Bundle object when we receive it. All of them take the object key as a parameter. The main ones:

    get() : a generic method that returns a value of type Object. Accordingly, the receiving field given value needs to be converted to the desired type

    getString() : returns a String object

    getInt() : returns an int value

    getByte() : returns a byte value

    getChar() : returns a char value

    getShort() : returns a value of type short

    getLong() : returns a long value

    getFloat() : returns a float value

    getDouble() : returns a double

    getBoolean() : returns a boolean value

    getCharArray() : returns an array of char objects

    getIntArray() : returns an array of int objects

    getFloatArray() : returns an array of float objects

    getSerializable() : returns a Serializable interface object

Let us have two activities defined in our project: MainActivity and SecondActivity.

In the SecondActivity code, we will define receiving data:

Package com.example.eugene.serializeapp; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class SecondActivity extends AppCompatActivity ( @Override protected void onCreate(Bundle savedInstanceState) ( super.onCreate(savedInstanceState); TextView textView = new TextView(this); textView.setTextSize(20); textView.setPadding(16, 16, 16, 16 ); Bundle arguments = getIntent().getExtras(); if(arguments!=null)( String name = arguments.get("name").toString(); String company = arguments.getString("company"); int price = arguments.getInt("price"); textView.setText("Name: " + name + "\nCompany: " + company + "\nPrice: " + price); ) setContentView(textView); ) )

In this case, in SecondActivity we get all the data from the Bundle object and display it in the TextView text field. It is assumed that three elements will be passed to this activity - two strings with the keys name and company and a number with the key price.

Now let's define the transfer of data to SecondActivity. For example, let's define the following interface for MainActivity in the activity_main.xml file: