onSaveInstanceState to onRestoreInstanceState without serialization. It uses the writeStrongBinder(IBInder) method of the android.os.Parcel class.... system processes
( http://developer.android.com/intl/ru/reference/android/app/Activity.html )
Activity to update the displayed state of the application.onClick() ) to start an asynchronous operationActivityActivity activity displayed at the time of completion of the operation may beActivty from the application is displayed.Activity .TicketSubsystem )TicketActivity where the ticket will be displayed and the button "Take a ticket"TicketSubsystem component at TicketSubsystem . Ticket could be Ticket.currentTicket , or in the field in the heir class android.app.Application . from MVC — that is, generate notifications when a ticket appears (or is replaced).TicketSubsystem model in MVC terms, then the Activity will be able to subscribe to events and update the ticket display when it is loaded. In this case, the Activity will perform the role of View ( ) in terms of MVC .TicketSubsystem and not take care of anything else.TicketSubsystem . The ticket itself must also be presented somehow, let it be the Ticket class. Both of these classes must be able to fulfill the role of an active model.PropertyChangeSupport and PropertyChangeListener from the java.beans packageObservable and Observer from java.utilBaseObservable and Observable.OnPropertyChangedCallback from android.databindingandroid.databinding.Bindable annotation. But there are other ways, and they all fit.PropertyChangeSupport from java.beans ). @groovy.beans.Bindable class TicketSubsystem { Ticket ticket } @groovy.beans.Bindable class Ticket { String number int positionInQueue String tellerNumber } TicketActivity (as almost all objects related to the presentation) appears and disappears at the will of the user. The application only needs to correctly display the data at the time the Activity appears and when the data changes, while the Activity is displayed.TicketActivity you need:ticketTicketSubsytem (to refresh the view when the ticket appears)PropertyChangeListener from java.beans for the sake of demonstration android.databinding library,  PropertyChangeListener ticketListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { updateTicketView(); } }; void updateTicketView() { TextView queuePositionView = (TextView) findViewById(R.id.textQueuePosition); queuePositionView.setText(ticket != null ? "" + ticket.getQueuePosition() : ""); ... }  PropertyChangeListener ticketSubsystemListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { setTicket(ticketSubsystem.getTicket()); } }; void setTicket(Ticket newTicket) { if(ticket != null) { ticket.removePropertyChangeListener(ticketListener); } ticket = newTicket; if(ticket != null) { ticket.addPropertyChangeListener(ticketListener); } updateTicketView(); } setTicket method when replacing a ticket removes a subscription to events from an old ticket and subscribes to events from a new ticket. If you call setTicket(null) , then TicketActivity unsubscribe from ticket events. void setTicketSubsystem(TicketSubsystem newTicketSubsystem) { if(ticketSubsystem != null) { ticketSubsystem.removePropertyChangeListener(ticketSubsystemListener); setTicket(null); } ticketSubsystem = newTicketSubsystem; if(ticketSubsystem != null) { ticketSubsystem.addPropertyChangeListener(ticketSubsystemListener); setTicket(ticketSubsystem.getTicket()); } } @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); setTicketSubsystem(globalTicketSubsystem); } @Override protected void onStop() { super.onStop(); setTicketSubsystem(null); } upon completion of the asynchronous operation. A will be updated on the notification from the . public class GetNewTicket extends AsyncTask<Void, Void, Void> { private int queuePosition; private String ticketNumber; @Override protected Void doInBackground(Void... params) { SystemClock.sleep(TimeUnit.SECONDS.toMillis(2)); Random random = new Random(); queuePosition = random.nextInt(100); ticketNumber = "A" + queuePosition; // TODO     ,    //     . return null; } @Override protected void onPostExecute(Void aVoid) { Ticket ticket = new Ticket(); ticket.setNumber(ticketNumber); ticket.setQueuePosition(queuePosition); globalTicketSubsystem.setTicket(ticket); } } globalTicketSubsystem link (also referred to in TicketActivity ) depends on the way subsystems are built in your application. public class ReadTicketFromFileextends AsyncTask<File, Void, Void> { ... @Override protected Void doInBackground(File... files) { //     number, positionInQueue, tellerNumber } @Override protected void onPostExecute(Void aVoid) { Ticket ticket = new Ticket(); ticket.setNumber(number); ticket.setPositionInQueue(positionInQueue); ticket.setTellerNumber(tellerNumber); globalTicketSubsystem.setTicket(ticket); } } Layers levels, we’ll get something like this:
TicketActivity creates a GetNewTicket - down arrow. GetNewTicket creates a Ticket - down arrow. Anonymous ticketListener implements the PropertyChangeListener interface - the down arrow. Ticket notifies listeners PropertyChangeListener - down arrow. Etc.Domains level reflect business entities with which the application works. They should be independent of how our application is organized. For example, the presence of the positionInQueue field of a Ticket is due to business requirements (and not the way we wrote our application).Application level is the boundary of where the application logic can be located (except for the appearance formation). If you need to do some useful work, the code should be here (or below).Presentation level. So this class can contain only the mapping code, and no logic. For logic, he will have to access classes from the Application level.Layers is conditional. The class is at a given level as long as it fulfills its requirements. That is, as a result of the editing, the class may move to another level, or become unsuitable for one level.android.databinding and roboguice . Look at the code, and here I briefly explain what choice I made and for what reasons.com.android.support:appcompat-v7 dependency is added because commercial development relies on this library to support older android versions.com.android.support:support-annotations dependency is added to use the @UiThread annotation (there are many other useful annotations).org.roboguice:roboguice is a library for dependency injection. Used to compose an application from parts using Inject annotations. Also, this library allows you to embed resources, links to widgets and contains a mechanism for sending messages similar to CDI Events from JSR-299.TicketActivity using annotation @Inject receives a link to TicketSubsystem .ReadTicketFromFile task using the @InjectResource annotation @InjectResource file name from the resources from which the ticket is to be loaded.TicketSubsystem using @Inject gets a Provider that it uses to create a ReadTicketFromFile .org.roboguice:roboblender creates a database of all annotations for org.roboguice:roboguice at compile time, which is then used at run time.app/lint.xml with settings for suppressing warnings from the roboguice library.dataBinding option in app/build.gradle enables special syntax in layout files similar to Expression Language ( EL ) and includes the android.databinding package, which is used to make Ticket and TicketSubsystem an active model. As a result, the presentation code is greatly simplified and replaced with declarations in the layout file. For example: <TextView ... android:text="@{ts.ticket.number}" /> .idea folder is in .gitignore to use any version of Android Studio or IDEA . The project is perfectly imported and synchronized via the build.gradle files.gradlew , gradlew.bat and the folder gradle ). This is a very effective and convenient mechanism.unitTests.returnDefaultValues = true in app/build.gradle . This is a trade-off between protection against random errors in unit tests and the brevity of unit tests. Here I prefer the shortness of the unit tests.org.mockito:mockito-core library is used to create stubs in unit tests. In addition, this library allows you to describe “System Under Test” using the annotations @Mock and @InjectMocks . When using Dependency Injection, components “expect” that they will be injected dependencies before using them. Before the tests also need to implement all the dependencies. Mockito can create and implement stubs in the class under test. This greatly simplifies the test code, especially if the fields being injected have limited visibility. See GetNewTicketTest.Mockito , and not Robolectric ?org.powermock:powermock-module-junit library org.powermock:powermock-module-junit and org.powermock:powermock-api-mockito . Some things cannot be replaced with plugs. For example, replace the static method or suppress the call to the base class method. For these purposes, PowerMock replaces the class loader and corrects the byte code. In TicketActivityTest , using PowerMock suppresses the call to RoboActionBarActivity.onCreate(Bundle) and sets the return value from the call to the static method DataBindingUtil.setContentViewLogInterface log variable not static?LogInterface and LogImpl which are just descendants of similar classes from RoboGuice?@ImplementedBy(LogImpl.class) .@UiThread annotation for the Ticket and TicketSubsystem ?onPropertyChanged events that are used in UI components to update the display. It is necessary to ensure that calls will be made in the UI stream.TicketSubsystem constructor?TicketSubsystem is created (only one copy is created, TicketSubsystem it is marked with the @Singleton annotation). However, in the TicketSubsystem constructor, TicketSubsystem cannot create a ReadTicketFromFile , since it needs a link to a not yet created TicketSubsystem . Therefore, the creation of ReadTicketFromFile postponed to the next UI flow cycle.adb shell am kill ru.soldatenko.habr3Source: https://habr.com/ru/post/281290/
All Articles