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 a 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.
An application does not always consist of one screen. For example, we have created very useful program and the user wants to know who its author is. He clicks on the “About the program” button and is taken to a new screen, where there is useful information about the program version, author, website address, how many cats the author has, etc. Think of the activity screen as a web page with a link to another page. If you look at the code in the file MainActivity.java from 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 new class, which may be similar to 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 we will create new uniform to display useful information. For example, let's show the user what a cat does when it goes left and right. Agree, this is very important information, which provides the key to the Universe.
We will create a new activity manually, although the studio has ready-made templates. But there is nothing complicated there and for a better understanding it is useful to do everything by hand.
Let's create a new XML markup file activity_about.xml in folder res/layout. Click right click mouse on folder layout and select from 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 it's there 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 full word to be entered:
You will get a corresponding blank into which we will insert the element TextView.
Information will be retrieved from resources, namely the string resource about_text. Now it is highlighted in red, signaling the absence of information. It was possible to 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/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 relate to the cat and the direction of movement. To translate text into new line use symbols \n. Let's add another string resource for the title of the new screen:
We figured out the markings. Next you need to create a class for the window AboutActivity.java. Select from the menu File | New | Java Class and fill in the required fields. At first, it is enough to indicate only the name. Then you'll deal with other fields.
Let's get the blank.
Now the class is almost empty. Let's add the code manually. The class must inherit from an abstract class Activity or his relatives like FragmentActivity, AppCompatActivity etc. Let's add extends Activity. The activity class must have a method onCreate(). Place the mouse cursor inside the class and select from the menu Code | Override Methods(Ctrl+O). In the dialog box we look for the required 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 have this option.
Package ru.alexanderklimov.helloworld; import android.app.Activity; import android.os.Bundle; /** * Created by Alexander Klimov on 12/01/2014. */ public class AboutActivity extends Activity ( @Override protected void onCreate(Bundle savedInstanceState) ( super.onCreate(savedInstanceState); setContentView(R.layout.activity_about); ) )
Now comes the most important part. Our task is to go to a new screen when we click a button on the first screen. Let's go back to class MainActivity. Let's write a button click handler:
Public void onClick(View view) ( Intent intent = new Intent(MainActivity.this, AboutActivity.class); startActivity(intent); )
Here I used the button click processing method described in the lesson.
To launch a new screen you need to create an instance of the class Intent and indicate the current class in the first parameter, and the class to go to in the second, we have this AboutActivity. After this 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 manifesto AndroidManifest.xml. Find this file in your project and double click on it. The file editing window will open. Add new tag
This is where the string resource comes in handy about_title. We launch the application, click on the button and get a window About the program. Thus, we learned how to create a new window and call it by clicking a button. And we have a mega-convenient program at our disposal - now we will always have at hand a hint of what the cat does when it goes left.
Once again, please note that the second activity class created must inherit from the class Activity or similar ones ( ListActivity etc.), have an XML markup file (if required) and be specified in the manifest.
After calling the method startActivity() a new activity will be launched (in this case AboutActivity), it will become visible and move to the top of the stack containing the running components. When calling a method finish() from a new activity (or when the hardware 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().
Creating a third screen - a method for the lazy
Programmers, like cats, are lazy creatures. Always remember that for an activity you need to create markup and a class that inherits from Activity, and then don’t forget to register the class in the manifest - oh well.
In this case, select from the menu File | New | Activity | Basic Activity(or other template). Next, the familiar window for creating a new activity will appear. Fill in the required fields.
Click on the button Finish and the activity will be ready. To verify this, open the manifest file and check for new entry. I’m not even talking about class and markup files, they will appear in front of you on their own.
Add it yourself new button on the main activity screen and write the code to switch to the created activity.
At first, I would advise you to manually create all the necessary components for the new activity so that you understand the relationship between the class, markup and manifest. And when you get the hang of it, you can use the Activity Creation Wizard to speed up your work.
Passing data between activities
We used simplest example to bring up another activity screen. Sometimes you not only need to call a new screen, but also transfer data to it. For example, username. In this case, you need to use a special area extraData, which the class has Intent.
Region extraData is a list of pairs key/value, which is transmitted along with the intention. Strings are used as keys, and any primitive data types, arrays of primitives, class objects can be used for values Bundle and etc.
To transfer data to another activity, use the method putExtra():
Intent.putExtra("Key", "Value");
The receiving activity must call some appropriate method: getIntExtra(), getStringExtra() etc.:
Int count = 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. Appearance could be as follows:
At the second activity SecondActivity set 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() at the second activity.
@Override protected void onCreate(Bundle savedInstanceState) ( super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); String user = "Animal"; String gift = "donut hole"; TextView infoTextView = (TextView)findViewById( R.id.textViewInfo); infoTextView.setText(user + " , you were given " + gift); )
If we now run the program and simply call up the second window, as was described in the first part of the article, then we will see the default inscription Animal, they handed you a donut hole. Agree, it’s quite annoying to receive such messages.
Let's correct the situation. Add the code to the first activity:
Public void onClick(View view) ( EditText userEditText = (EditText) findViewById(R.id.editTextUser); EditText giftEditText = (EditText) findViewById(R.id.editTextGift); Intent intent = new Intent(MainActivity.this, SecondActivity. class); // into the username key we push the text from the first text field intent.putExtra("username", userEditText.getText().toString()); // into the gift key we push the text from the second text field intent.putExtra("gift ", giftEditText.getText().toString()); startActivity(intent); )
We placed the object in a special container Intent two keys with values that are taken from text fields. When the user enters data into the text fields, it will go into this container and be passed to the second activity.
The second activity should be ready to warmly receive messages as follows (in bold).
// Default values String user = "Animal"; String gift = "donut hole"; user = getIntent().getExtras().getString("username"); gift = getIntent().getExtras().getString("gift"); TextView infoTextView = (TextView)findViewById(R.id.textViewInfo); infoTextView.setText(user + " , you were given " + gift);
Now the message looks less offensive, and even pleasant for some. In complex examples, it is advisable to add a check when processing data. There may be situations where you launch a second activity with empty data of type null, which may cause the application to crash.
In our case, we know that we are expecting a string value, so the code can be rewritten like this:
Intent intent = getIntent(); user = intent.getStringExtra("username");
User = getIntent().getStringExtra("username");
The program has a drawback - it is not clear from whom we receive greetings. Any well-bred monkey will not accept a gift from an anonymous source. So as homework, 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 of the uniqueness of the key when interacting with other applications. Something like this:
Public final static String USER = "ru.alexanderklimov.myapp.USER";
Who framed the cat Vaska - we get the result back
It's not always enough to simply pass data to another activity. Sometimes you need 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 additional parameter RequestCode. It's basically just an integer number that you can come up with 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 from 1 to 5, and by this code you will be able to 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 won't get any results.
If you use the method startActivityForResult(), then you need to override the method in your code to receive the result onActivityResult() and process the result. Confused? Let's look at an example.
Let's say you're a detective. Information was received 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 dog and Vaska the cat.
One of the visitors provided a series of photos from his show-off iPhone:
There is also testimony from another witness: And Vaska listens and eats.
Create a new project Sherlock with two activities. On the first screen there will be a button to switch to the second screen and a text label in which the thief's name will be displayed.
On the second screen there will be a group of switches:
Since we will expect a response from the second screen, we need to use the method startActivityForResult() on the first screen in which we will pass the variable CHOOSE_THIEF as a parameter RequestCode.
Static final private int CHOOSE_THIEF = 0; public void onClick(View v) ( Intent questionIntent = new Intent(MainActivity.this, ChooseActivity.class); startActivityForResult(questionIntent, CHOOSE_THIEF); )
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.
Let's go to the second screen and write the code for the second activity.
Public final static String THIEF = "ru.alexanderklimov.sherlock.THIEF"; public void onRadioClick(View v) ( Intent answerIntent = 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 selecting, we immediately close the second window and before closing we pass the value RESULT_OK, so that it is 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 resulting code and the result itself, represented as an intent. The resulting code tells you what the result of the activity was, as a rule, it is either Activity.RESULT_OK, or Activity.RESULT_CANCELED. In some cases, you need to use your own return code to handle variations specific to your application. Method setResult() supports any integer value.
If you will be passing data explicitly through a button, then it would be a good idea 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 by pressing the hardware return button or if the method finish() was called before the method setResult(), the resulting code will be set to RESULT_CANCELED, and 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 a method to the code onActivityResult().
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) ( super.onActivityResult(requestCode, resultCode, data); TextView infoTextView = (TextView) findViewById(R.id.textViewInfo); if (requestCode == CHOOSE_THIEF) ( if (resultCode == RESULT_OK) ( String thiefname = 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, it retrieves the value from the key ChooseActivity.THIEF using the method getStringExtra. We display the resulting value in TextView(variable infoTextView). If we returned to the screen through the Back button, then we simply erase the text.
When a child activity is closed inside the parent component, a handler is fired onActivityResult(). Handler onActivityResult() accepts several parameters.
- Request code. Code used to start the activity that returns the result
- Resulting code. A result code set by the child activity indicating how its work completed. This can be any integer value, but typically either Activity.RESULT_OK, or Activity.RESULT_CANCELED
- Data. The intent used to package the returned data. Depending on the purpose of the child activity, it may include a URI path that represents the selected piece of content. Alternatively (or complementarily), the child activity can return information as simple values wrapped in an intent parameter extras
If a child activity terminated 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 select one of the options. If you select the crow, the screen will close and the criminal's name will appear 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 criminal did not take into account one important detail. The restaurant was under surveillance from video cameras, and the recording showed who actually stole the sausage and framed the cat. Vaska, hold on!
P.S. If at first something seemed unclear, then with practice a lot will become clearer. Transferring data between screens is common in applications, and you will study the example more than once.
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 to be transferred are indicated. By the way, the activity class does not have to be part of your application. If you know the class name from another application, you can switch to it. But you can move to another activity in another way.
In practice it is less common, but can be useful. Let's say you already have a second activity. In the manifest we will add a special filter to it:
And we launch the second activity by clicking a 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 = "ru.alexanderklimov.testapplication.SecondActivity"; public void onClick(View view) ( startActivity(new Intent(ACTION_SECOND_ACTIVITY)); )
So what did we do. For the second activity, we registered 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 name of the package in it. Class constructor Intent has several overloaded versions. In one version, you can specify a string for the action. We indicated our created action, which is registered in the second activity. During operation, the system views the manifests of all installed applications. When searching for a match, the system finds our filter and launches the required activity.
You can launch other activities using the same principle. Look at the 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 a string constant public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLANE_MODE_SETTINGS". Compare with our code. You can assume that the settings activity for offline mode has this line in its filter.
Filter category name android.intent.category.DEFAULT tells the system to perform the default action, which is to start the activity. There are other names that do not interest us yet.
And now a final question. 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 and copy the block with the filter from the second activity into it.
Click on the button in the first activity. The system will ask you to select the desired option.
If you select ALWAYS, then next time you won’t have to choose. To reset the selection, go to the application properties in Settings and find the button Clear defaults.
Launch an activity by its name
In the constructor Intent the second parameter is the class. But suppose that there is some kind of database where the names of activities are indicated and we need to launch the desired activity by its name. We can get the class itself based on a string variable and launch the activity.
Try ( // Full name of the activity class String activityName = "ru.alexanderklimov.testapplication.SecondActivity"; // get the Class object>myClass = Class.forName(activityName); Intent intent = new Intent(this, myClass); startActivity(intent); ) catch (ClassNotFoundException e) ( e.printStackTrace(); )
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.
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:
Three text fields for data entry and a button are defined here.
In the MainActivity class we will 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 = findViewById(R.id .name); final EditText companyText = findViewById(R.id.company); final EditText priceText = findViewById(R.id.price); String name = nameText.getText().toString(); String company = companyText.getText( ).toString(); int price = Integer.parseInt(priceText.getText().toString()); Intent intent = 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 receive the data entered into the EditText text fields and pass it to the Intent object using the putExtra() method. Then we launch SecondActivity.
As a result, when you click on the button, SecondActivity will launch, which will receive some data entered into the text fields.
Transferring complex objects
In the example above, simple data was transmitted - numbers, strings. But we can also transmit more complex data. In this case, the serialization mechanism is used.
For example, let’s say we 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 = name; this.company = company; this.price = price; ) public String getName() ( return name; ) public void setName(String name) ( this.name = name; ) public String getCompany() ( return company; ) public void setCompany(String company) ( this.company = company; ) public int getPrice() ( return price; ) public void setPrice(int price) ( this.price = price; ) )
It is worth noting that this class implements the Serializable interface. Now let's change the MainActivity code:
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 = findViewById(R.id .name); final EditText companyText = findViewById(R.id.company); final EditText priceText = findViewById(R.id.price); String name = nameText.getText().toString(); String company = companyText.getText( ).toString(); int price = Integer.parseInt(priceText.getText().toString()); Product product = new Product(name, company, price); Intent intent = new Intent(this, SecondActivity.class); intent.putExtra(Product.class.getSimpleName(), product); startActivity(intent); ) )
Now, instead of three separate data, one Product object is transmitted. The key is the result of the Product.class.getSimpleName() method, which essentially returns the name of the class.
And 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 = new TextView(this); textView.setTextSize(20); textView.setPadding(16, 16, 16, 16 ); Bundle arguments = getIntent().getExtras(); final Product product; if(arguments!=null)( product = (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 the data because the Product class implements the Serializable interface. This way we can pass one single object instead of a bunch of disparate data.