Hello.
It is necessary to transfer data received via UART to Activity. This can be done by creating a thread in Activity, in which to organize a while (! IsInterrupted ()) loop and read data from the UART buffer. After that, by calling the UI thread of the 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 transferring data to the newly created Activity. If I I understand correctly that in order for the data from the stream to be transferred to any Activity, the stream must be created not in the Activity, but in the Service.
Question: data came 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 actually done?
1 answer
Create a Handler in each Activity. In the onResume () method of this Activity does bindService (). There one of the parameters is interface ServiceConnection. Implement it at least with the same Activity. Implement the onServiceConnected () method in it. In this callback, the Service itself comes as one of the parameters. So call this Service your own setHandler () method. Pass the Handler that is in the current Activity there. But send the incoming data via UART to Service on this Handler. By the way, Handler traditionally runs on the main thread, so you won't need to runOnUiThread to execute.
The application does not always consist of a single screen. For example, we have created a very useful program and the user wants to know who the author is. He clicks on the button "About the program" and gets to a new screen, where there is useful information about the version of the program, author, site address, how many cats the author has, etc. Think of an activity screen as a web page with a link to another page. If you look at the code in the file MainActivity.java from the previous lessons, you will see that our class MainActivity also applies to Activity (or his heirs) or, more precisely, inherited from him.
Public class MainActivity extends AppCompatActivity
As you might guess, we should create a new class, which may look like MainActivity and then somehow switch to it when the button is pressed.
For the experiment, we will take the program from the first lesson and use a button for experiments (or create a new project with one button on the screen). Next, let's create a new form to display useful information. For example, let's show the user what the cat does when he walks left and right. Agree, this is very important information that gives a clue to the Universe.
We will create a new activity manually, although the studio has ready-made templates. But there is nothing complicated and for better understanding it is useful to do everything by hand.
Create a new XML markup file activity_about.xml in folder res / layout... Right click on the folder layout and select from the context menu New | Layout resource file... A dialog box will appear. In the first field, enter the file name activity_about... In the second, you need to enter the root element. By default there is ConstraintLayout... Erase the text and enter ScrollView... Entering a few characters is enough for the studio to suggest ready-made options, you can immediately press Enter, without waiting for the complete input of the word:
We will get the corresponding blank, into which we will insert the element TextView.
Information will be fetched from resources, namely from a string resource about_text... Now it is highlighted in red, signaling the lack of information. You could press Alt + Enter and enter text in the dialog box. But for our example, this method will not work, since our text will be multi-line, using control characters. So let's do it differently. Let's open the file res / values \u200b\u200b/ strings.xml and enter the following text manually:
We used the simplest HTML text formatting tags like , , ... For our example, it is enough to highlight in bold the words that refer to the cat and the direction of movement. To transfer text to a new line, use the symbols \\ n... Let's add one more string resource for the title of the new screen:
We figured out the markup. Next, you need to create a class for the window AboutActivity.java... Choose from the menu File | New | Java Class and fill in the required fields. At first, it is enough to indicate only the name. Then deal with other fields.
Let's get a blank.
The class is now almost empty. Let's add the code manually. The class must inherit from the abstract class Activity or his relatives like FragmentActivity, AppCompatActivity etc. We add extends Activity... Activity class must have a method onCreate ()... We put the mouse cursor inside the class and select from the menu Code | Override methods (Ctrl + O). In the dialog box, we are looking for the desired class, you can type the first characters on the keyboard for quick search... In the created method, you need to call the method setContentView ()which will load the prepared markup onto the screen. We will get this option.
Package ru.alexanderklimov.helloworld; import android.app.Activity; import android.os.Bundle; / ** * Created by Alexander Klimov on 01.12.2014. * / public class AboutActivity extends Activity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_about);))
Now the most important thing begins. Our task is to go to a new screen when the button is clicked on the first screen. Go back to class MainActivity... Let's write a button click handler:
Public void onClick (View view) (Intent intent \u003d new Intent (MainActivity.this, AboutActivity.class); startActivity (intent);)
Here I used the method for handling button clicks that I described in the lesson.
To start a new screen, you need to create an instance of the class Intent and specify the current class in the first parameter, and the class for the transition in the second, we have this AboutActivity... After that, the method is called startActivity ()which launches a new screen.
If you now try to test the application in the emulator, you will receive an error message. What did we do wrong? We missed one important step. You need to register a new one Activity in the manifest AndroidManifest.xml... Find this file in your project and double click on it. The file editing window will open. Add a new tag
So the string resource came in handy about_title... Run the application, click on the button and get the window About the program... Thus, we learned how to create a new window and call it on the click of a button. And we have at our disposal a mega-convenient program - now there will always be a hint at hand what the cat does when he goes to the left.
Once again, I draw your attention to the fact that the second created activity class must inherit from the class Activity or similar ( ListActivity and others), have an XML markup file (if required) and be written in the manifest.
After calling the method startActivity () a new activity will start (in this case AboutActivity), it will become visible and move to the top of the stack containing the running components. When calling the method finish () from a new activity (or when a hard return key is pressed) it will be closed and removed from the stack. The developer can also navigate to the previous (or any other) activity using the same method. startActivity ().
How to create a third screen - a way for the lazy
Programmers, like cats, are lazy creatures. Always remember that for activity you need to create markup and a class that inherits from Activity, and then do not forget to register the class in the manifest - well, nafig.
In this case, select from the menu File | New | Activity | Basic Activity (or another template). Next, the familiar window for creating a new activity will appear. We fill in the required fields.
Click on the button Finish and the activity will be ready. To verify this, open your manifest file and check for a new entry. I'm not talking about class and markup files, they themselves will appear in front of you.
Add a new button on the main activity screen yourself and write the code to go to the created activity.
At first, I would advise you to manually create all the necessary components for a new activity in order to understand the relationship between class, markup and manifest. And when you get your hands on it, you can use the activity creation wizard to speed up your work.
Transferring data between activities
We used the simplest example to call another activity screen. Sometimes it is necessary not only to call a new screen, but also to transfer data to it. For example, username. In this case, you need to use a special area extraDatathat the class has Intent.
Region extraData is a list of pairs key / valuethat is passed along with the intent. Strings are used as keys, and for values \u200b\u200byou can use any primitive data types, arrays of primitives, class objects Bundle and etc.
To transfer data to another activity, use the method putExtra ():
Intent.putExtra ("Key", "Value");
The receiving activity should call some suitable method: getIntExtra (), getStringExtra () etc.:
Int count \u003d getIntent (). GetIntExtra ("name", 0);
Let's redo the previous example. We already have three activities. The first activity will have two text fields and a button. The appearance can be as follows:
The second activity SecondActivity install the element TextView, in which we will display the text received from the first activity. Let's write the following code for the method onCreate () the second activity.
@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_second); String user \u003d "Live"; String gift \u003d "donut hole"; TextView infoTextView \u003d (TextView) findViewById R.id.textViewInfo); infoTextView.setText (user + ", you were given" + gift);)
If we run the program now and just call the second window, as described in the first part of the article, we will see the default label ZHYVotnoe, you were given a donut hole... Agree, it is rather annoying to receive such messages.
We fix the situation. Add the code for the first activity:
Public void onClick (View view) (EditText userEditText \u003d (EditText) findViewById (R.id.editTextUser); EditText giftEditText \u003d (EditText) findViewById (R.id.editTextGift); Intent intent \u003d new Intent (MainActivity) class); // push the text from the first text field into the username key intent.putExtra ("username", userEditText.getText (). toString ()); // push the text from the second text field into the gift key intent.putExtra ("gift ", giftEditText.getText (). toString ()); startActivity (intent);)
We placed an object in a special container Intent two keys with values \u200b\u200btaken from text fields. When the user enters data into the text fields, they will go to this container and will be transferred to the second activity.
The second activity should be ready to receive messages warm as follows (in bold).
// Default values \u200b\u200bString user \u003d "Live"; String gift \u003d "donut hole"; user \u003d getIntent (). getExtras (). getString ("username"); gift \u003d getIntent (). getExtras (). getString ("gift"); TextView infoTextView \u003d (TextView) findViewById (R.id.textViewInfo); infoTextView.setText (user + ", you were given" + gift);
Now the message looks not so offensive, but even pleasant for some. In complex examples, it is desirable to add validation when processing data. There are situations when you start the second activity with empty data like nullwhich can crash the application.
In our case, we know that we are waiting for a string value, so the code can be rewritten like this:
Intent intent \u003d getIntent (); user \u003d intent.getStringExtra ("username");
User \u003d getIntent (). GetStringExtra ("username");
The program has a drawback - it is not clear from whom we receive greetings. Any well-mannered monkey will not accept a gift from an anonymous source. So as a homework assignment, add another text field to enter the name of the user who is sending the message.
Google recommends using the following format for keys: your package name as a prefix, followed by the key itself. In this case, you can be sure that the key is unique when interacting with other applications. Something like this:
Public final static String USER \u003d "ru.alexanderklimov.myapp.USER";
Who framed the cat Vaska - we get the result back
It is not always sufficient to simply pass data to another activity. Sometimes you want to get information back from another activity when it is closed. If earlier we used the method startActivity (Intent intent), then there is a related method startActivityForResult (Intent intent, int RequestCode)... The difference between the methods is the extra parameter RequestCode... It's basically just an integer that you can make up yourself. It is needed in order to distinguish from whom the result came. Let's say you have five additional screens and you assign them values \u200b\u200bfrom 1 to 5, and from this code you can determine whose result you need to process. You can use the value -1, then it will be the same as calling the method startActivity (), i.e. we get no result.
If you use the method startActivityForResult (), then you need to override the method in the code to receive the result onActivityResult () and process the result. Confused? Let's take an example.
Let's say you're a detective. There was information that two pieces of sausage and other products were stolen from the table of an influential person in a restaurant. Suspicion fell on three suspects - a crow, a fucking doggie and a cat Vaska.
One of the visitors provided a series of photos from his pontoon iPhone:
There is also testimony from another witness: And Vaska listens, but eats.
Create a new project Sherlock with two activities. The first screen will have a button to switch to the second screen and a text label that will display the name of the thief.
The second screen will have a group of radio buttons:
Since we will expect a response from the second screen, we need to use the method startActivityForResult () on the first screen in which we pass the variable CHOOSE_THIEF as parameter RequestCode.
Static final private int CHOOSE_THIEF \u003d 0; public void onClick (View v) (Intent questionIntent \u003d new Intent (MainActivity.this, ChooseActivity.class); startActivityForResult (questionIntent, CHOOSE_THIEF);)
Take a look at the code. When we click on the button, we are going to work with the second screen ChooseActivity and launch the second screen waiting for the result.
Go to the second screen and write the code for the second activity.
Public final static String THIEF \u003d "ru.alexanderklimov.sherlock.THIEF"; public void onRadioClick (View v) (Intent answerIntent \u003d new Intent (); switch (v.getId ()) (case R.id.radioDog: answerIntent.putExtra (THIEF, "Fucking doggie"); break; case R.id .radioCrow: answerIntent.putExtra (THIEF, "Crow"); break; case R.id.radioCat: answerIntent.putExtra (THIEF, "Przewalski's horse"); break; default: break;) setResult (RESULT_OK, answerIntent); finish ();)
Everything is simple here, when the detective chooses the name of the criminal, then through the method putExtra () we pass the name of the key and its value.
For convenience, after selection, we immediately close the second window and pass the value before closing RESULT_OKto make it clear that the choice has been made. If the user closes the screen via the Back button, the value will be passed RESULT_CANCELED.
Method setResult () takes two parameters: the result code and the result itself, represented as an intent. The resulting code tells you what result the activity ended with, as a rule, it is either Activity.RESULT_OKor Activity.RESULT_CANCELED... In some cases, you may need to use your own return code to handle variations specific to your application. Method setResult () supports any integer value.
If you will pass data explicitly through a button, then it would be nice to add a method finish ()to close the second activity as unnecessary. If the transition occurs through the Back button, then this is not necessary.
If the activity was closed by the user when the hardware return button was pressed, or if the method finish () was called before the method setResult (), the resulting code will be set to RESULT_CANCELEDand the returned intent will show the value null.
We return to the first screen. The first screen is waiting for a response from the second screen, so you need to add the method to the code onActivityResult ().
@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) (super.onActivityResult (requestCode, resultCode, data); TextView infoTextView \u003d (TextView) findViewById (R.id.textViewInfo); if (requestCode \u003d\u003d CHOOSE_THIEF) ( if (resultCode \u003d\u003d RESULT_OK) (String thiefname \u003d data.getStringExtra (ChooseActivity.THIEF); infoTextView.setText (thiefname);) else (infoTextView.setText (""); // erase the text)))
The method expects incoming data with code CHOOSE_THIEF, and if such data arrives, then extracts the value from the key ChooseActivity.THIEF using the method getStringExtra... We output the resulting value in TextView (variable infoTextView). If we returned to the screen through the Back button, then we simply erase the text.
When the child activity is closed inside the parent component, the handler is triggered onActivityResult ()... Handler onActivityResult () takes several parameters.
- Request code. Code that was used to start an activity that returns a result
- Result code. The result code set by the child activity that indicates how the activity ended. It can be any integer value, but usually either Activity.RESULT_OKor Activity.RESULT_CANCELED
- Data. The intent used to package the returned data. Depending on the purpose of the child activity, it can include a URI path representing the selected piece of content. Alternatively (or additionally) the child activity can return information as simple values, packed into an intent parameter extras
If the child activity ended unexpectedly or if no result code was specified before closing it, this parameter will become Activity.RESULT_CANCELED.
We launch the project, click on the button and go to the second screen. There we choose one of the options. If you select the crow, the screen will close and the criminal's name will be displayed on the first screen. If you select a dog, its name will be displayed.
By the way, if you select a cat, its name will not be displayed! Check it out and see for yourself. You will ask why? Elementary Watson! The perpetrator did not take into account one important detail. The restaurant was monitored with video cameras, and the recording showed who actually stole the sausage and set the cat up. Vaska, hold on!
P.S. If at first something seemed incomprehensible, then with practice a lot will become clear. Transferring data between screens is common in applications and you will study the example again and again.
P.P.S. The best fish is sausage. Knowing this weakness, it was not difficult to frame the cat.
Using filters
In the article, I showed a common way to switch to another activity, when in the method startActivity () the current class and the class for the transition are indicated. By the way, the activity class doesn't have to be part of your application. If you know the name of the class from another application, you can switch to it. But you can go to another activity in a different way.
In practice, it is less common, but it can be useful. Let's say you already have a second activity. Add a special filter to it in the manifest:
And we launch the second activity by clicking the button in this way.
Public void onClick (View view) (startActivity (new Intent ("ru.alexanderklimov.testapplication.SecondActivity"));)
Let's replace the long string with a constant.
Public static final String ACTION_SECOND_ACTIVITY \u003d "ru.alexanderklimov.testapplication.SecondActivity"; public void onClick (View view) (startActivity (new Intent (ACTION_SECOND_ACTIVITY));)
So what have we done. For the second activity, we added a filter and specified a name for action in attribute android: name... For convenience, I just put the full name of the activity with the package name in it. Class constructor Intent has several overloaded versions. In one version, you can specify a string for the action. We have indicated our created action, which is registered in the second activity. The system looks at the manifests of all installed applications during operation. When searching for a match, the system finds our filter and launches the desired activity.
Other activities can be launched using the same principle. Take a look at an example. If you copy the example to yourself and look at the documentation for android.provider.Settings.ACTION_AIRPLANE_MODE_SETTINGS, you will see that this code corresponds to the string constant public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS \u003d "android.settings.AIRPLANE_MODE_SETTINGS"... Compare with our code. You can assume that the activity of the settings for offline mode has this line written in the filter.
Filter category name android.intent.category.DEFAULT tells the system to take the default action, which is to start the activity. There are other names that do not interest us yet.
And now a question for filling. What happens if you create another activity and specify the same filter as the second activity? Let's check it out. Create a third activity in yourself and copy the block with the filter from the second activity to it.
We click on the button in the first activity. The system will ask you to select the desired option.
If you select the item ALWAYS, you won't have to choose next time. To reset the selection, go to the application properties in Settings and find the button Clear defaults.
Running an activity by its name
In the constructor Intent the second parameter is the class. But suppose there is some kind of database where the names of the activities are specified and we need to launch the necessary activity by its name. We can get the class itself based on the string variable and start the activity.
Try (// Full name of the activity class String activityName \u003d "ru.alexanderklimov.testapplication.SecondActivity"; // get the Class Class object> myClass \u003d Class.forName (activityName); Intent intent \u003d new Intent (this, myClass); startActivity (intent); ) catch (ClassNotFoundException e) (e.printStackTrace ();)
Last update: 03.04.2018
An intent is used to transfer data between two Activities. Through its putExtra () method, you can add a key and its associated value.
For example, passing from the current activity to SecondActivity the string "Hello World" with the key "hello":
// create an Intent to launch SecondActivity Intent intent \u003d 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 an object of the Serializable interface.
To get the sent data when loading SecondActivity, you can use the get () method, which is passed the object key:
Bundle arguments \u003d getIntent (). GetExtras (); String name \u003d arguments.get ("hello"). ToString (); // Hello World
Depending on the type of data being sent, when we receive it, we can use a number of methods on the Bundle object. All of them take the object key as a parameter. The main ones are:
get (): a generic method that returns a value of type Object. Accordingly, the field of obtaining this value must 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 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 define the receipt of 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 \u003d new TextView (this); textView.setTextSize (20); textView.setPadding (16, 16, 16, 16 ); Bundle arguments \u003d getIntent (). GetExtras (); if (arguments! \u003d Null) (String name \u003d arguments.get ("name"). ToString (); String company \u003d arguments.getString ("company"); int price \u003d 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 this activity will be passed three elements - 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:
It defines three text fields for data entry and a button.
In the MainActivity class, we define the following content:
Package com.example.eugene.serializeapp; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class MainActivity extends AppCompatActivity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main);) public void onClick (View v) (final EditText nameText \u003d findViewById (R. .name); final EditText companyText \u003d findViewById (R.id.company); final EditText priceText \u003d findViewById (R.id.price); String name \u003d nameText.getText (). toString (); String company \u003d companyText.getText ( ) .toString (); int price \u003d Integer.parseInt (priceText.getText (). toString ()); Intent intent \u003d new Intent (this, SecondActivity.class); intent.putExtra ("name", name); intent. putExtra ("company", company); intent.putExtra ("price", price); startActivity (intent);))
In the button click handler, we retrieve the data entered into the EditText text fields and pass it to the intent using the putExtra () method. Then we launch SecondActivity.
As a result, when you click on the button, SecondActivity will be launched, which will receive some data entered into the text fields.
Transferring complex objects
In the example above, simple data was transferred - numbers, strings. But we can also transfer more complex data. In this case, the serialization mechanism is used.
For example, let us have a Product class defined in our project:
Package com.example.eugene.serializeapp; import java.io.Serializable; public class Product implements Serializable (private String name; private String company; private int price; public Product (String name, String company, int price) (this.name \u003d name; this.company \u003d company; this.price \u003d price;) public String getName () (return name;) public void setName (String name) (this.name \u003d name;) public String getCompany () (return company;) public void setCompany (String company) (this.company \u003d company;) public int getPrice () (return price;) public void setPrice (int price) (this.price \u003d price;))
It should be noted that this class implements the Serializable interface. Now let's change the code for MainActivity:
Package com.example.eugene.serializeapp; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class MainActivity extends AppCompatActivity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main);) public void onClick (View v) (final EditText nameText \u003d findViewById (R. .name); final EditText companyText \u003d findViewById (R.id.company); final EditText priceText \u003d findViewById (R.id.price); String name \u003d nameText.getText (). toString (); String company \u003d companyText.getText ( ) .toString (); int price \u003d Integer.parseInt (priceText.getText (). toString ()); Product product \u003d new Product (name, company, price); Intent intent \u003d new Intent (this, SecondActivity.class); intent.putExtra (Product.class.getSimpleName (), product); startActivity (intent);))
Now, instead of three scattered data, a single Product object is passed. The result of the Product.class.getSimpleName () method is used as the key, which essentially returns the name of the class.
And let's change the SecondActivity class:
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 \u003d new TextView (this); textView.setTextSize (20); textView.setPadding (16, 16, 16, 16 ); Bundle arguments \u003d getIntent (). GetExtras (); final Product product; if (arguments! \u003d Null) (product \u003d (Product) arguments.getSerializable (Product.class.getSimpleName ()); textView.setText ("Name: "+ product.getName () +" \\ nCompany: "+ product.getCompany () +" \\ nPrice: "+ String.valueOf (product.getPrice ()));) setContentView (textView);))
The getSerializable () method is used to retrieve data because the Product class implements the Serializable interface. This way, we can pass one single object instead of a collection of disparate data.
Somehow I had a task to transfer data from the service to the activity. They started looking for a solution in the standard SDK, but since there was no time, I made a bad solution in the form of using a database. But the question was open and after a while I figured out a more correct way, which is in the SDK - using the Message, Handler, Messenger classes.
Idea
We need to transfer data from activity to service and vice versa. 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 variables of the Message instance and, in particular, replyTo. We need this variable so that we can refer to the Messanger instance of the service from the activation and in the service to the Messanger instance of the activation. In fact, it's not that simple. At least for my not the most gifted mind. In part, I'm just improving the documentation that is already there - Services. Also, there is a good example on StackOverflow. In any case, I hope the article will be useful to at least someone, and I have not worked in vain.
Example
As an example, we will implement a service that will increase and decrease the counter value and return the result to the activity, to the TextView. I will omit the layout code, because there are two buttons and a text field - everything is simple.
Implementation
I will give the full activation code:
Public class MainActivity extends Activity (public static final String TAG \u003d "TestService"; TestServiceConnection testServConn; TextView testTxt; final Messenger messenger \u003d new Messenger (new IncomingHandler ()); Messenger toServiceMessenger; @Override public void onCreate (Bundle savedInstanceState) (super onCreate (savedInstanceState); setContentView (R.layout.activity_main); testTxt \u003d (TextView) findViewById (R.id.test_txt); bindService (new Intent (this, TestService.class), (testServConn \u003d new TestServiceConnection ()), Context .BIND_AUTO_CREATE);) @Override public void onDestroy () (super.onDestroy (); unbindService (testServConn);) public void countIncrClick (View button) (Message msg \u003d Message.obtain (, TestService.COUNT_PLUS); msg.reply \u003d messenger; try (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) public void countDecrClick (View button) (Message msg \u003d Message.obtain (, TestService.COUNT_MINUS); msg .replyTo \u003d 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 \u003d new Messenger (service); // send the initial value of the counter Message msg \u003d Message.obtain (, TestService.SET_COUNT); msg.replyTo \u003d messenger; msg.arg1 \u003d 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 toServiceMessanger, passing the IBinder interface to the constructor. By the way, this instance must be returned in the service, otherwise it will be NPE. We use this class to send messages to the service. And here it is 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 just look for the variables we need and do actions on them. By clicks on buttons (methods countIncrClick, countDecrClick), we send requests to the service, indicating the desired 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 \u003d 1; public static final int COUNT_MINUS \u003d 2; public static final int SET_COUNT \u003d 0; public static final int GET_COUNT \u003d 3; int count \u003d 0; IncomingHandler inHandler; Messenger messanger; Messenger toActivityMessenger; @Override public void onCreate () (super.onCreate (); HandlerThread thread \u003d new HandlerThread ("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start (); inHandler \u003d new IncomingHandler (thread.getLooper () \u003d 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 private class IncomingHandler extends Handler (public IncomingHandler (Looper looper) (super (looper);) @Override public void handleMessage (Message msg) (//super.handleMessage(msg); toActivityMess enger \u003d msg.replyTo; switch (msg.what) (case SET_COUNT: count \u003d 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 the activity Message outMsg \u003d Message.obtain (inHandler, GET_COUNT); outMsg.arg1 \u003d count; outMsg.replyTo \u003d messanger; try (if (toActivityMessenger! \u003d null) toActivityMessenger.send (outMsg);) catch (RemoteException e) (e.printStackTrace ();))))
All by analogy with the logic in the activity. I don't even know if I need to explain something. The only point is that I immediately send a request back to the activity in handleMessage, using the magic replyTo variable and pulling the desired Messenger above. And the second point about which I already spoke is:
@Override public IBinder onBind (Intent arg0) (return messanger.getBinder ();)
without which everything will fall. It is this instance of the interface that will be passed to the ServiceConnection
Conclusion
All in all. Such is the contrived example of the interaction between activites and services. It seems to me that this is a rather non-trivial interaction, although it may seem different to someone.
Questions, clarifications and so on in a personal. There may be inaccuracies about any aspects, so feel free to write and correct.
I hope the post was helpful to the readers.