Appcompatactivity android studio что это
Перейти к содержимому

Appcompatactivity android studio что это

  • автор:

Activity

Ключевым компонентом для создания визуального интерфейса в приложении Android является activity (активность). Нередко activity ассоциируется с отдельным экраном или окном приложения, а переключение между окнами будет происходить как перемещение от одной activity к другой. Приложение может иметь одну или несколько activity. Например, при создании проекта с пустой Activity в проект по умолчанию добавляется один класс Activity — MainActivity, с которого и начинается работа приложения:

Все объекты activity представляют собой объекты класса android.app.Activity , которая содержит базовую функциональность для всех activity. В приложении из прошлой темы мы напрямую с этим классом не работали, а MainActivity наследовалась от класса AppCompatActivity . Однако сам класс AppCompatActivity, хоть и не напрямую, наследуется от базового класса Activity.

Жизненный цикл приложения

Все приложения Android имеют строго определенный системой жизненный цикл. При запуске пользователем приложения система дает этому приложению высокий приоритет. Каждое приложение запускается в виде отдельного процесса, что позволяет системе давать одним процессам более высокой приоритет, в отличие от других. Благодаря этому, например, при работе с одними приложениями Android позволяет не блокировать входящие звонки. После прекращения работы с приложением, система освобождает все связанные ресурсы и переводит приложение в разряд низкоприоритетного и закрывает его.

Все объекты activity, которые есть в приложении, управляются системой в виде стека activity, который называется back stack . При запуске новой activity она помещается поверх стека и выводится на экран устройства, пока не появится новая activity. Когда текущая activity заканчивает свою работу (например, пользователь уходит из приложения), то она удаляется из стека, и возобновляет работу та activity, которая ранее была второй в стеке.

После запуска activity проходит через ряд событий, которые обрабатываются системой и для обработки которых существует ряд обратных вызовов:

Схематично взаимосвязь между всеми этими обратными вызовами можно представить следующим образом

Жизненный цикл приложения Android

onCreate()

onCreate — первый метод, с которого начинается выполнение activity. В этом методе activity переходит в состояние Created. Этот метод обязательно должен быть определен в классе activity. В нем производится первоначальная настройка activity. В частности, создаются объекты визуального интерфейса. Этот метод получает объект Bundle , который содержит прежнее состояние activity, если оно было сохранено. Если activity заново создается, то данный объект имеет значение null. Если же activity уже ранее была создана, но находилась в приостановленном состоянии, то bundle содержит связанную с activity информацию.

После того, как метод onCreate() завершил выполнение, activity переходит в состояние Started , и и система вызывает метод onStart()

onStart

В методе onStart() осуществляется подготовка к выводу activity на экран устройства. Как правило, этот метод не требует переопределения, а всю работу производит встроенный код. После завершения работы метода activity отображается на экране, вызывается метод onResume , а activity переходит в состояние Resumed.

onResume

При вызове метода onResume activity переходит в состояние Resumed и отображается на экране устройства, и пользователь может с ней взаимодействовать. И собственно activity остается в этом состоянии, пока она не потеряет фокус, например, вследствии переключения на другую activity или просто из-за выключения экрана устройства.

onPause

Если пользователь решит перейти к другой activity, то система вызывает метод onPause , а activity переходит в состояние Paused . В этом методе можно освобождать используемые ресурсы, приостанавливать процессы, например, воспроизведение аудио, анимаций, останавливать работу камеры (если она используется) и т.д., чтобы они меньше сказывались на производительность системы.

Но надо учитывать, что в этот состоянии activity по прежнему остается видимой на экране, и на работу данного метода отводится очень мало времени, поэтому не стоит здесь сохранять какие-то данные, особенно если при этом требуется обращение к сети, например, отправка данных по интернету, или обращение к базе данных — подобные действия лучше выполнять в методе onStop() .

После выполнения этого метода activity становится невидимой, не отображается на экране, но она все еще активна. И если пользователь решит вернуться к этой activity, то система вызовет снова метод onResume , и activity снова появится на экране.

Другой вариант работы может возникнуть, если вдруг система видит, что для работы активных приложений необходимо больше памяти. И система может сама завершить полностью работу activity, которая невидима и находится в фоне. Либо пользователь может нажать на кнопку Back (Назад). В этом случае у activity вызывается метод onStop .

onStop

В этом методе activity переходит в состояние Stopped. В этом состоянии activity полностью невидима. В методе onStop следует особождать используемые ресурсы, которые не нужны пользователю, когда он не взаимодействует с activity. Здесь также можно сохранять данные, например, в базу данных.

При этом во время состояния Stopped activity остается в памяти устройства, сохраняется состояние всех элементов интерфейса. К примеру, если в текстовое поле EditText был введен какой-то текст, то после возобновления работы activity и перехода ее в состояние Resumed мы вновь увидим в текстовом поле ранее введенный текст.

Если после вызова метода onStop пользователь решит вернуться к прежней activity, тогда система вызовет метод onRestart . Если же activity вовсе завершила свою работу, например, из-за закрытия приложения, то вызывается метод onDestroy() .

onDestroy

Ну и завершается работа activity вызовом метода onDestroy , который возникает либо, если система решит убить activity в силу конфигурационных причин (например, поворот экрана или при многоконном режиме), либо при вызове метода finish() .

Также следует отметить, что при изменении ориентации экрана система завершает activity и затем создает ее заново, вызывая метод onCreate .

В целом переход между состояниями activity можно выразить следующей схемой:

Состояния Activity в Android

Расмотрим несколько ситуаций. Если мы работаем с Activity и затем переключаемся на другое приложение, либо нажимаем на кнопку Home, то у Activity вызывается следующая цепочка методов: onPause -> onStop . Activity оказывается в состоянии Stopped. Если пользователь решит вернуться к Activity, то вызывается следующая цепочка методов: onRestart -> onStart -> onResume .

Другая ситуация, если пользователь нажимает на кнопку Back (Назад), то вызывается следующая цепочка onPause -> onStop -> onDestroy . В результате Activity уничтожается. Если мы вдруг захотим вернуться к Activity через диспетчер задач или заново открыв приложение, то activity будет заново пересоздаваться через методы onCreate -> onStart -> onResume

Управление жизненным циклом

Мы можем управлять этими событиями жизненного цикла, переопределив соответствующие методы. Для этого возьмем из прошлой главы класс MainActivity и изменим его следующим образом:

Для логгирования событий здесь используется класс android.util.Log .

В данном случае обрабатываются все ключевые методы жизненного цикла. Вся обработка сведена к вызову метода Log.d() , в который передается TAG — случайное строковое значение и строка, которая выводится в консоли Logcat в нижней части Android Studio, выполняя роль отладочной информации. Если эта консоль по умолчанию скрыта, то мы можем перейти к ней через пункт меню View -> Tool Windows -> Logcat .

И при запуске приложения мы сможем увидеть в окне Logcat отладочную информацию, которая определяется в методах жизненного цикла activity:

2.1: Activities and intents

In this chapter you learn about the Activity class, the major building block of your app's user interface (UI). You also learn about using an Intent to communicate from one activity to another.

About activities

An activity represents a single screen in your app with an interface the user can interact with. For example, an email app might have one activity that shows a list of new emails, another activity to compose an email, and another activity for reading individual messages. Your app is probably a collection of activities that you create yourself, or that you reuse from other apps.

 Your app can start an Activity in another app

Although the activities in your app work with each other to form a cohesive user experience, each activity is independent of the others. This enables your app to start an activity in another app, and it enables other apps to start activities in your app (if your app allows this). For example, a messaging app could start an activity in a camera app to take a picture, then start an activity in an email app to let the user share the picture in email.

Typically, one Activity in an app is specified as the "main" activity, for example MainActivity . The user sees the main activity when they launch the app for the first time. Each activity can start other activities to perform different actions.

Each time a new activity starts, the previous activity is stopped, but the system preserves the activity in a stack (the "back stack"). When the user is done with the current activity and presses the Back button, the activity is popped from the stack and destroyed, and the previous activity resumes.

When an activity is stopped because a new activity starts, the first activity is notified by way of the activity lifecycle callback methods. The activity lifecycle is the set of states an Activity can be in: when the activity is first created, when it's stopped or resumed, and when the system destroys it. You learn more about the activity lifecycle in a later chapter.

Creating an Activity

To implement an Activity in your app, do the following:

  • Create an Activity Java class.
  • Implement a basic UI for the Activity in an XML layout file.
  • Declare the new Activity in the AndroidManifest.xml file.

When you create a new project for your app, or add a new Activity to your app by choosing File > New > Activity, the template automatically performs the steps listed above.

Create the Activity

When you create a new project in Android Studio and choose the Backwards Compatibility (AppCompat) option, the MainActivity is, by default, a subclass of the AppCompatActivity class. The AppCompatActivity class lets you use up-to-date Android app features such as the app bar and Material Design, while still enabling your app to be compatible with devices running older versions of Android.

Here is a skeleton subclass of AppCompatActivity :

The first task for you in your Activity subclass is to implement the standard Activity lifecycle callback methods (such as onCreate() ) to handle the state changes for your Activity . These state changes include things such as when the Activity is created, stopped, resumed, or destroyed. You learn more about the Activity lifecycle and lifecycle callbacks in a different chapter.

The one required callback your app must implement is the onCreate() method. The system calls this method when it creates your Activity , and all the essential components of your Activity should be initialized here. Most importantly, the onCreate() method calls setContentView() to create the primary layout for the Activity .

You typically define the UI for your Activity in one or more XML layout files. When the setContentView() method is called with the path to a layout file, the system creates all the initial views from the specified layout and adds them to your Activity . This is often referred to as inflating the layout.

You may often also want to implement the onPause() method in your Activity . The system calls this method as the first indication that the user is leaving your Activity (though it does not always mean that the Activity is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back). You learn more about onPause() and all the other lifecycle callbacks in a later chapter.

In addition to lifecycle callbacks, you may also implement methods in your Activity to handle other behavior such as user input or button clicks.

Implement the activity's UI

The UI for an activity is provided by a hierarchy of View elements, which controls a particular space within the activity window and can respond to user interaction.

The most common way to define a UI using View elements is with an XML layout file stored as part of your app's resources. Defining your layout in XML enables you to maintain the design of your UI separately from the source code that defines the activity behavior.

You can also create new View elements directly in your activity code by inserting new View objects into a ViewGroup , and then passing the root ViewGroup to setContentView() . After your layout has been inflated—regardless of its source—you can add more View elements anywhere in the View hierarchy.

Declare the Activity in AndroidManifest.xml

Each Activity in your app must be declared in the AndroidManifest.xml file with the <activity> element, inside the <application> section. When you create a new project or add a new Activity to your project in Android Studio, the AndroidManifest.xml file is created or updated to include skeleton declarations for each Activity . Here's the declaration for MainActivity :

The <activity> element includes a number of attributes to define properties of the Activity such as its label, icon, or theme. The only required attribute is android:name , which specifies the class name for the Activity (such as MainActivity ). See the <activity> element reference for more information on Activity declarations.

The <activity> element can also include declarations for Intent filters. The Intent filters specify the kind of Intent your Activity will accept.

Intent filters must include at least one <action> element, and can also include a <category> and optional <data> . The MainActivity for your app needs an Intent filter that defines the "main" action and the "launcher" category so that the system can launch your app. Android Studio creates this Intent filter for the MainActivity in your project.

The <action> element specifies that this is the "main" entry point to the app. The <category> element specifies that this Activity should be listed in the system's app launcher (to allow users to launch this Activity ).

Each Activity in your app can also declare Intent filters, but only your MainActivity should include the "main" action. You learn more about how to use an implicit Intent and Intent filters in a later section.

Add another Activity to your project

 Activity Gallery in Android Studio

The MainActivity for your app and its associated layout file is supplied by an Activity template in Android Studio such as Empty Activity or Basic Activity. You can add a new Activity to your project by choosing File > New > Activity. Choose the Activity template you want to use, or open the Gallery to see all the available templates.

When you choose an Activity template, you see the same set of screens for creating the new activity that you did when you created the project. Android Studio provides three things for each new activity in your app:

  • A Java file for the new Activity with a skeleton class definition and onCreate() method. The new Activity , like MainActivity , is a subclass of AppCompatActivity .
  • An XML file containing the layout for the new activity. Note that the setContentView() method in the Activity class inflates this new layout.
  • An additional <activity> element in the AndroidManifest.xml file that specifies the new activity. The second Activity definition does not include any Intent filters. If you plan to use this activity only within your app (and not enable that activity to be started by any other app), you do not need to add filters.

About intents

Each activity is started or activated with an Intent , which is a message object that makes a request to the Android runtime to start an activity or other app component in your app or in some other app.

When your app is first started from the device home screen, the Android runtime sends an Intent to your app to start your app's main activity (the one defined with the MAIN action and the LAUNCHER category in the AndroidManifest.xml file). To start another activity in your app, or to request that some other activity available on the device perform an action, you build your own intent and call the startActivity() method to send the intent.

In addition to starting an activity, an intent can also be used to pass data between one activity and another. When you create an intent to start a new activity, you can include information about the data you want that new activity to operate on. So, for example, an email Activity that displays a list of messages can send an Intent to the Activity that displays that message. The display activity needs data about the message to display, and you can include that data in the intent.

In this chapter you learn about using intents with activities, but intents can also be used to start services or broadcast receivers. You learn how to use those app components in another practical.

Intent types

Intents can be explicit or implicit:

  • Explicit intent: You specify the receiving activity (or other component) using the activity's fully qualified class name. You use explicit intents to start components in your own app (for example, to move between screens in the UI), because you already know the package and class name of that component.
  • Implicit intent: You do not specify a specific activity or other component to receive the intent. Instead, you declare a general action to perform, and the Android system matches your request to an activity or other component that can handle the requested action. You learn more about using implicit intents in another practical.

Intent objects and fields

For an explicit Intent , the key fields include the following:

  • The Activity class (for an explicit Intent ). This is the class name of the Activity or other component that should receive the Intent ; for example, com.example.SampleActivity.class . Use the Intent constructor or the setComponent() , setComponentName() , or setClassName() methods to specify the class.
  • The Intent data. The Intent data field contains a reference to the data you want the receiving Activity to operate on as a Uri object.
  • Intent extras. These are key-value pairs that carry information the receiving Activity requires to accomplish the requested action.
  • Intent flags. These are additional bits of metadata, defined by the Intent class. The flags may instruct the Android system how to launch an Activity or how to treat it after it's launched.

For an implicit Intent , you may need to also define the Intent action and category. You learn more about Intent actions and categories in another chapter.

Starting an Activity with an explicit Intent

To start a specific Activity from another Activity , use an explicit Intent and the startActivity() method. An explicit Intent includes the fully qualified class name for the Activity or other component in the Intent object. All the other Intent fields are optional, and null by default.

For example, if you want to start the ShowMessageActivity to show a specific message in an email app, use code like this:

The intent constructor takes two arguments for an explicit Intent :

  • An application context. In this example, the Activity class provides the context ( this ).
  • The specific component to start ( ShowMessageActivity.class ).

Use the startActivity() method with the new Intent object as the only argument. The startActivity() method sends the Intent to the Android system, which launches the ShowMessageActivity class on behalf of your app. The new Activity appears on the screen, and the originating Activity is paused.

The started Activity remains on the screen until the user taps the Back button on the device, at which time that Activity closes and is reclaimed by the system, and the originating Activity is resumed. You can also manually close the started Activity in response to a user action (such as a Button click) with the finish() method:

Passing data from one Activity to another

In addition to simply starting one Activity from another Activity , you also use an Intent to pass information from one Activity to another. The Intent object you use to start an Activity can include Intent data (the URI of an object to act on), or Intent extras, which are bits of additional data the Activity might need.

In the first (sending) Activity , you:

  1. Create the Intent object.
  2. Put data or extras into that Intent .
  3. Start the new Activity with startActivity() .

In the second (receiving) Activity , you:

  1. Get the Intent object the Activity was started with.
  2. Retrieve the data or extras from the Intent object.

When to use Intent data or Intent extras

You can use either Intent data or Intent extras to pass data from one Activity to another. There are several key differences between data and extras that determine which you should use.

The Intent data can hold only one piece of information: a URI representing the location of the data you want to operate on. That URI could be a web page URL ( http:// ), a telephone number ( tel:// ), a geographic location ( geo:// ) or any other custom URI you define.

Use the Intent data field:

  • When you only have one piece of information you need to send to the started Activity .
  • When that information is a data location that can be represented by a URI.

Intent extras are for any other arbitrary data you want to pass to the started Activity . Intent extras are stored in a Bundle object as key and value pairs. A Bundle is a map, optimized for Android, in which a key is a string, and a value can be any primitive or object type (objects must implement the Parcelable interface). To put data into the Intent extras you can use any of the Intent class putExtra() methods, or create your own Bundle and put it into the Intent with putExtras() .

Use the Intent extras:

  • If you want to pass more than one piece of information to the started Activity .
  • If any of the information you want to pass is not expressible by a URI.

Intent data and extras are not exclusive; you can use data for a URI and extras for any additional information the started Activity needs to process the data in that URI.

Add data to the Intent

To add data to an explicit Intent from the originating Activity , create the Intent object as you did before:

Use the setData() method with a Uri object to add that URI to the Intent . Some examples of using setData() with URIs:

Keep in mind that the data field can only contain a single URI; if you call setData() multiple times only the last value is used. Use Intent extras to include additional information (including URIs.)

After you've added the data, you can start the Activity with the Intent as usual:

Add extras to the Intent

To add Intent extras to an explicit Intent from the originating Activity :

  1. Determine the keys to use for the information you want to put into the extras, or define your own. Each piece of information needs its own unique key.
  2. Use the putExtra() methods to add your key/value pairs to the Intent extras. Optionally you can create a Bundle object, add your data to the Bundle , and then add the Bundle to the Intent .

The Intent class includes extra keys you can use, defined as constants that begin with the word EXTRA_ . For example, you could use Intent.EXTRA_EMAIL to indicate an array of email addresses (as strings), or Intent.EXTRA_REFERRER to specify information about the originating Activity that sent the Intent .

You can also define your own Intent extra keys. Conventionally you define Intent extra keys as static variables with names that begin with EXTRA_ . To guarantee that the key is unique, the string value for the key itself should be prefixed with your app's fully qualified class name. For example:

Create an Intent object (if one does not already exist):

Use a putExtra() method with a key to put data into the Intent extras. The Intent class defines many putExtra() methods for different kinds of data:

Alternately, you can create a new Bundle and populate that Bundle with your Intent extras. Bundle defines many "put" methods for different kinds of primitive data as well as objects that implement Android's Parcelable interface or Java's Serializable .

After you've populated the Bundle , add it to the Intent with the putExtras() method (note the "s" in Extras ):

Start the Activity with the Intent as usual:

Retrieve the data from the Intent in the started Activity

When you start an Activity with an Intent , the started Activity has access to the Intent and the data it contains.

To retrieve the Intent the Activity (or other component) was started with, use the getIntent() method:

Use getData() to get the URI from that Intent :

To get the extras out of the Intent , you need to know the keys for the key/value pairs. You can use the standard Intent extras if you used those, or you can use the keys you defined in the originating Activity (if they were defined as public.)

Use one of the getExtra() methods to extract extra data out of the Intent object:

Or you can get the entire extras Bundle from the Intent and extract the values with the various Bundle methods:

Getting data back from an Activity

When you start an Activity with an Intent , the originating Activity is paused, and the new Activity remains on the screen until the user clicks the Back button, or you call the finish() method in a click handler or other function that ends the user's involvement with this Activity .

Sometimes when you send data to an Activity with an Intent , you would like to also get data back from that Intent . For example, you might start a photo gallery Activity that lets the user pick a photo. In this case your original Activity needs to receive information about the photo the user chose back from the launched Activity .

To launch a new Activity and get a result back, do the following steps in your originating Activity :

  1. Instead of launching the Activity with startActivity() , call startActivityForResult() with the Intent and a request code.
  2. Create a new Intent in the launched Activity and add the return data to that Intent .
  3. Implement onActivityResult() in the originating Activity to process the returned data.

You learn about each of these steps in the following sections.

Use startActivityForResult() to launch the Activity

To get data back from a launched Activity , start that Activity with the startActivityForResult() method instead of startActivity() .

The startActivityForResult() method, like startActivity() , takes an Intent argument that contains information about the Activity to be launched and any data to send to that Activity . The startActivityForResult() method, however, also needs a request code.

The request code is an integer that identifies the request and can be used to differentiate between results when you process the return data. For example, if you launch one Activity to take a photo and another to pick a photo from a gallery, you need different request codes to identify which request the returned data belongs to.

Conventionally you define request codes as static integer variables with names that include REQUEST . Use a different integer for each code. For example:

Return a response from the launched Activity

The response data from the launched Activity back to the originating Activity is sent in an Intent , either in the data or the extras. You construct this return Intent and put the data into it in much the same way you do for the sending Intent . Typically your launched Activity will have an onClick() or other user input callback method in which you process the user's action and close the Activity . This is also where you construct the response.

To return data from the launched Activity , create a new empty Intent object.

A return result Intent does not need a class or component name to end up in the right place. The Android system directs the response back to the originating Activity for you.

Add data or extras to the Intent the same way you did with the original Intent . You may need to define keys for the return Intent extras at the start of your class.

Then put your return data into the Intent as usual. In the following, the return message is an Intent extra with the key EXTRA_RETURN_MESSAGE .

Use the setResult() method with a response code and the Intent with the response data:

The response codes are defined by the Activity class, and can be

  • RESULT_OK : The request was successful.
  • RESULT_CANCELED : The user canceled the operation.
  • RESULT_FIRST_USER : For defining your own result codes.

You use the result code in the originating Activity .

Finally, call finish() to close the Activity and resume the originating Activity :

Read response data in onActivityResult()

Now that the launched Activity has sent data back to the originating Activity with an Intent , that first Activity must handle that data. To handle returned data in the originating Activity , implement the onActivityResult() callback method. Here is a simple example.

The three arguments to onActivityResult() contain all the information you need to handle the return data.

  • Request code: The request code you set when you launched the Activity with startActivityForResult() . If you launch a different Activity to accomplish different operations, use this code to identify the specific data you're getting back.
  • Result code: the result code set in the launched Activity , usually one of RESULT_OK or RESULT_CANCELED .
  • Intent data: the Intent that contains the data returned from the launch Activity .

The example method shown above shows the typical logic for handling the request and response codes. The first test is for the TEXT_REQUEST request, and that the result was successful. Inside the body of those tests you extract the return information out of the Intent . Use getData() to get the Intent data, or getExtra() to retrieve values out of the Intent extras with a specific key.

Activity navigation

Any app of any complexity that you build will include more than one Activity . As your users move around your app and from one Activity to another, consistent navigation becomes more important to the app's user experience. Few things frustrate users more than basic navigation that behaves in inconsistent and unexpected ways. Thoughtfully designing your app's navigation will make using your app predictable and reliable for your users.

Android system supports two different forms of navigation strategies for your app.

  • Back (temporal) navigation, provided by the device Back button, and the back stack.
  • Up (ancestral) navigation, provided by you as an option in the app bar.

Back navigation, tasks, and the back stack

Back navigation allows your users to return to the previous Activity by tapping the device back button . Back navigation is also called temporal navigation because the back button navigates the history of recently viewed screens, in reverse chronological order.

The back stack is the set of each Activity that the user has visited and that can be returned to by the user with the back button. Each time a new Activity starts, it is pushed onto the back stack and takes user focus. The previous Activity is stopped but is still available in the back stack. The back stack operates on a "last in, first out" mechanism, so when the user is done with the current Activity and presses the Back button, that Activity is popped from the stack (and destroyed) and the previous Activity resumes.

 The Activity back stack

Because an app can start an Activity both inside and outside a single app, the back stack contains each Activity that has been launched by the user in reverse order. Each time the user presses the Back button, each Activity in the stack is popped off to reveal the previous one, until the user returns to the Home screen.

 Recent Tasks Screen

Android provides a back stack for each task. A task is an organizing concept for each Activity the user interacts with when performing an operation, whether they are inside your app or across multiple apps. Most tasks start from the Android home screen, and tapping an app icon starts a task (and a new back stack) for that app. If the user uses an app for a while, taps home, and starts a new app, that new app launches in its own task and has its own back stack. If the user returns to the first app, that first task's back stack returns. Navigating with the Back button returns only to the Activity in the current task, not for all tasks running on the device. Android enables the user to navigate between tasks with the overview or recent tasks screen, accessible with the square button on lower right corner of the device .

In most cases you don't have to worry about managing either tasks or the back stack for your app—the system keeps track of these things for you, and the back button is always available on the device.

There may, however, be times where you may want to override the default behavior for tasks or for the back stack. For example, if your screen contains an embedded web browser where users can navigate between web pages, you may wish to use the browser's default back behavior when users press the device's Back button, rather than returning to the previous Activity . You may also need to change the default behavior for your app in other special cases such as with notifications or widgets, where an Activity deep within your app may be launched as its own task, with no back stack at all. You learn more about managing tasks and the back stack in a later section.

Up navigation

 Up button for up navigation

Up navigation, sometimes referred to as ancestral or logical navigation, is used to navigate within an app based on the explicit hierarchical relationships between screens. With Up navigation, each Activity is arranged in a hierarchy, and each "child" Activity shows a left-facing arrow in the app bar that returns the user to the "parent" Activity . The topmost Activity in the hierarchy is usually MainActivity , and the user cannot go up from there.

For instance, if the main Activity in an email app is a list of all messages, selecting a message launches a second Activity to display that single email. In this case the message Activity would provide an Up button that returns to the list of messages.

The behavior of the Up button is defined by you in each Activity based on how you design your app's navigation. In many cases, Up and Back navigation may provide the same behavior: to just return to the previous Activity . For example, a Settings Activity may be available from any Activity in your app, so "up" is the same as back—just return the user to their previous place in the hierarchy.

Providing Up behavior for your app is optional, but a good design practice, to provide consistent navigation for your app.

Implement Up navigation with a parent Activity

With the standard template projects in Android Studio, it's straightforward to implement Up navigation. If one Activity is a child of another Activity in your app's Activity hierarchy, specify the parent of that other Activity in the AndroidManifest.xml file.

Beginning in Android 4.1 (API level 16), declare the logical parent of each Activity by specifying the android:parentActivityName attribute in the <activity> element. To support older versions of Android, include <meta-data> information to define the parent Activity explicitly. Use both methods to be backwards-compatible with all versions of Android.

The following are the skeleton definitions in AndroidManifest.xml for both a main (parent) Activity ( MainActivity ) and a second (child) Activity ( SecondActivity ):

You learn more about Up navigation and other user navigation features in another practical.

Необходимо ли использовать AppCompatActivity

Для чего Android Studio по умолчанию генерирует AppCompatActivity , когда я создаю любую активность через окно создания активностей?

На сколько я понимаю, AppCompatActivity используется для того, чтобы внедрить новые «плюшки» андроида в старые версии (будь то фрагменты в версии ниже 3-тей или материальный дизайн ниже 5-ой) и ToolBar (если я правильно понял, это тот же ActionBar ).

Может есть еще какая-то задумка в генерации, даже если я ничего такого не использую, неспроста же она?

AppCompatActivity применяется для обратной совместимости в плане дизайна. Также ActionbarActivity с API 21 deprecated , чем подталкивают на использование AppCompatActivity . Плюс некоторые изменения «под капотом», как то использование Toolbar и пр.

Многие помешались на этом Material design , поэтому и инструмент для разработки приложений также не отстает от модных тенденций.

Если вам не нужны все те возможности, которые представлены библиотекой поддержки AppCompat (а их не так уж и много: Toolbar , виджеты в стиле Material Design и темы Material Design, в основном) вы можете не использовать AppCompatActivity .

При этом вы можете использовать другие библиотеки поддержки из секции v7, вроде RecyclerView , CardView , GridLayout , Pallete и пр., так как они подключаются отдельно.

Так же вы можете использовать фрагменты в версиях ниже API 10, так как они входят в библиотеку поддержки v4 Support, AppCompat просто включает в себя зависимость к этой библиотеке, поэтому подключив первую не требуется отдельно подключать вторую.

Виджет Toolbar это очень далеко не тот же ActionBar , сходство только в назначении — инструмент намного гибче и «приятнее» в использовании (с точки зрения разработчика)

Современная Android разработка на Kotlin. Часть 1

Очень сложно найти один проект, который охватывал бы всё новое в разработке под Android в Android Studio 3.0, поэтому я решил написать его. В этой статье мы разберём следующее:

  1. Android Studio 3
  2. Язык программирования Kotlin
  3. Варианты сборки
  4. ConstraintLayout
  5. Библиотека привязки данных Data Binding
  6. Архитектура MVVM + паттерн repository (с mapper’ами) + Android Manager Wrappers
  7. RxJava2 и как это помогает нам в архитектуре
  8. Dagger 2.11, что такое внедрение зависимости, почему вы должны использовать это.
  9. Retrofit (Rx Java2)
  10. Room (Rx Java2)

Каким будет наше приложение?

Наше приложение будет самым простым, которое охватывает все перечисленные выше вещи: у него будет только одна функция, которая извлекает все репозитории пользователя googlesamples из GitHub, сохраняет эти данные в локальной базе данных и показывает их пользователю.

Я попытаюсь объяснить как можно больше строк кода. Вы всегда можете посмотреть код, который я опубликовал на GitHub.

Android Studio

Чтобы установить Android Studio 3, перейдите на эту страницу

Android Studio 3 поддерживает Kotlin. Откройте Create Android Project. Там вы увидите новый флажок с меткой Include Kotlin support. Он выбран по умолчанию. Дважды нажмите кнопку Далее и выберите Empty Activity, затем нажмите Finish.

Поздравляю! Вы сделали первое приложение для Android на Котлине 🙂

Kotlin

Вы можете видеть MainActivity.kt:

Расширение .kt означает, что файл является файлом Kotlin.

MainActivity: AppCompatActivity() означает, что мы расширяем AppCompatActivity.

Кроме того, все методы должны иметь ключевое слово fun и в Котлине вам не нужно использовать ;, но вы можете, если хотите. Вы должны использовать ключевое слово override, а не аннотацию, как в Java.

Так что же означает ? в savedInstanceState: Bundle?? Это означает, что savedInstanceState может быть типа Bundle или типа null. Kotlin null безопасный язык. Если у вас есть:

вы получите ошибку компиляции, потому что a должна быть инициализированна и это не может быть null. Это означает, что вы должны написать:

Кроме того, вы получите ошибку компиляции, если вы это сделаете:

Чтобы сделать a nullable, вы должны написать:

Почему эта важная особенность языка Котлина? Это помогает нам избежать NPE. Разработчики Android уже устали от NPE. Даже создатель null, сэр Тони Хоар, извинился за изобретение. Предположим, что мы имеем nullable nameTextView. Если переменная равна null, то в следующем коде мы получим NPE:

Но Котлин, на самом деле, хорош, он не позволят нам делать даже такое. Он заставляет нас использовать оператор ? или оператор !!. Если мы используем оператор ?:

Строка будет исполнена только если nameTextView не null. В ином случае, если вы используете оператор !!:

Мы получим NPE если nameTextView null. Это для авантюристов :).
Это было небольшое введение в Kotlin. Когда мы продолжим, я остановлюсь, чтобы описать другой специфический код на Котлине.

2. Build Variants

В разработке часто вы имеете различные окружения. Наиболее стандартным является тестовое и производственное окружение. Эти среды могут отличаться в URL-адресах сервера, иконке, имени, целевом API и т.д. На fleka в каждом проекте у вас есть:

  • finalProduction, который отправляется в Google Play Store.
  • demoProduction, то есть версия с URL-адресом production сервера с новыми функциями, которые всё ещё не находятся в Google Play Store. Наши клиенты могут установить эту версию рядом с Google Play, чтобы они могли протестировать ее и дать нам обратную связь.
  • demoTesting, то же самое, что и demoProduction с тестовым URL-адресом сервера.
  • mock, полезен для меня как для разработчика и дизайнера. Иногда у нас есть готовый дизайн, и наш API ещё не готов. Ожидание API, чтобы быть начать разработку — не решение. Этот вариант сборки снабжён поддельными данными, поэтому команда дизайнеров может проверить его и дать нам обратную связь. Очень полезно это не откладывать. Когда API уже готов, мы перемещаем нашу разработку в окружение demoTesting.

В этом приложении мы будем использовать всех их. У них будут отличаться applicationId и имена. В gradle 3.0.0 есть новый API flavorDimension, который позволяет смешивать разновидности продукта, так, например, вы можете смешать разновидности demo и minApi23. В нашем приложении мы будем использовать только «default» flavorDimension. Перейдите в build.gradle для приложения и вставьте этот код внутри android <>

Перейдите в strings.xml и удалите строку app_name, чтобы у нас не было конфликтов. Затем нажмите Sync Now. Если вы перейдете в Build Variants, расположенным слева от экрана, вы увидите 4 варианта сборки, каждый из которых имеет два типа сборки: Debug и Release. Перейдите к варианту сборки demoProduction и запустите его. Затем переключитесь на другой и запустите его. Вы должны увидеть два приложения с разными именами.

3. ConstraintLayout

Если вы откроете activity_main.xml, вы увидите, что этот layout — ConstrainLayout. Если вы когда-либо писали приложение под iOS, вы знаете об AutoLayout. ConstraintLayout действительно похож на него. Они даже используют один и тот же алгоритм Cassowary.

Constraint помогает нам описать связи между View. Для каждого View у вас должно быть 4 Constraint, один для каждой стороны. В данном случае наш View ограничен родителем с каждой стороны.

Если вы передвинете TextView «Hello World» немного вверх во вкладке Design, во вкладке Text появится новая линия:

Вкладки Design и Text синхронизируются. Наши изменения во вкладке Design влияют на xml во вкладке Text и наоборот. Vertical_bias описывает вертикальную тенденцию view его Constraint. Если вы хотите центровать вертикально, используйте:

Давайте сделаем чтобы наш Activity показал только один репозиторий. В нём будут имя репозитория, количество звезд, владелец, и он будет показывать, есть ли у репозитория issues, или нет.

Чтобы получить такой layout, xml должен выглядеть так:

Пусть tools:text вас не смущает. Он просто помогает нам видеть хороший предварительный просмотр макета (layout’а).

Вы можете заметить, что наш макет плоский, ровный. Вложенных макетов нет. Вы должны использовать вложенные макеты как можно реже, поскольку это может повлиять на производительность. Более подробную информацию об этом вы можете найти здесь. Кроме того, ConstraintLayout отлично работает с разными размерами экрана:

и мне кажется, что я могу добиться желаемого результата очень быстро.
Это было небольшое введение в ConstraintLayout. Вы можете найти Google code lab здесь, и документацию о ConstraintLayout на GitHub.

4. Библиотека привязки данных Data Binding

Когда я услышал о библиотеке привязки данных, первое вопрос, который я задал себе: «ButterKnife работает очень хорошо для меня. Кроме того, я использую плагин, который помогает мне получать View из xml. Зачем мне это менять?». Как только я узнал больше о привязке данных, у меня было такое же чувство, какое у меня было, когда я впервые использовал ButterKnife.

Как ButterKnife помогает нам?

ButterKnife помогает нам избавиться от скучного findViewById. Итак, если у вас 5 View, без Butterknife у вас есть 5 + 5 строк, чтобы привязать ваши View. С ButterKnife у вас есть 5 строк. Вот и всё.

Что плохо в ButterKnife?

ButterKnife по-прежнему не решает проблему поддержки кода. Когда я использовал ButterKnife, я часто получал исключение во время выполнения, потому что я удалял View в xml, и не удалял код привязки в классе Activity / Fragment. Кроме того, если вы хотите добавить View в xml, вам нужно снова сделать привязку. Это очень скучно. Вы теряете время на поддерживание связей.

Что насчёт библиотеки привязки данных?

Есть много преимуществ! С помощью библиотеки привязки данных вы можете привязать свои View всего одной строкой кода! Позвольте мне показать вам, как это работает. Давайте добавим библиотеку Data Binding в наш проект:

Обратите внимание, что версия компилятора Data Binding должна совпадать с версией gradle в файле build.gradle проекта:

Нажмите Sync Now. Перейдите в activity_main.xml и оберните ConstraintLayout тегом layout:

Обратите внимание, что вам нужно переместить все xmlns в тег layout. Затем нажмите иконку Build или используйте сочетание клавиш Ctrl + F9 (Cmd + F9 на Mac). Нам нужно собрать проект, чтобы библиотека Data Binding могла сгенерировать класс ActivityMainBinding, который мы будем использовать в нашем классе MainActivity.

Если вы не выполните сборку проекта, вы не увидите класс ActivityMainBinding, потому что он генерируется во время компиляции. Мы все еще не закончили связывание, мы просто сказали, что у нас есть ненулевая переменная типа ActivityMainBinding. Кроме того, как вы можете заметить, я не указал ? в конце типа ActivityMainBinding, и я не инициализировал его. Как это возможно? Модификатор lateinit позволяет нам иметь ненулевые переменные, ожидающие инициализации. Подобно ButterKnife, инициализация привязки должна выполняться в методе onCreate, когда ваш Activity будет готов. Кроме того, вы не должны объявлять привязку в методе onCreate, потому что вы, вероятно, используете его вне области видимости метода onCreate. Наша привязка не должна быть нулевой, поэтому мы используем lateinit. Используя модификатор lateinit, нам не нужно проверять привязку переменной каждый раз, когда мы обращаемся к ней.

Давайте инициализируем нашу переменную binding. Вы должны заменить:

Вот и всё! Вы успешно привязали свои View. Теперь вы можете получить к ним доступ и применить изменения. Например, давайте изменим имя репозитория на «Modern Android Habrahabr Article»:

Как вы можете видеть, мы можем получить доступ ко всем View (у которых есть id, конечно) из activity_main.xml через переменную binding. Вот почему Data Binding лучше, чем ButterKnife.

Getter’ы и Setter’ы в Котлине

Возможно, вы уже заметили, что у нас нет метода .setText (), как в Java. Я хотел бы остановиться здесь, чтобы объяснить, как геттеры и сеттеры работают в Kotlin по сравнению с Java.

Во-первых, вы должны знать, почему мы используем сеттеры и геттеры. Мы используем их, чтобы скрыть переменные класса и разрешить доступ только с помощью методов, чтобы мы могли скрыть элементы класса от клиентов класса и запретить тем же клиентам напрямую изменять наш класс. Предположим, что у нас есть класс Square в Java:

Используя метод setA (), мы запрещаем клиентам класса устанавливать отрицательное значение стороне квадрата, оно не должно быть отрицательным. Используя этот подход, мы должны сделать a приватным, поэтому его нельзя установить напрямую. Это также означает, что клиент нашего класса не может получить a напрямую, поэтому мы должны предоставить getter. Этот getter возвращает a. Если у вас есть 10 переменных с аналогичными требованиями, вам необходимо предоставить 10 геттеров. Написание таких строк — это скучная вещь, в которой мы обычно не используем наш разум.

Kotlin облегчает жизнь нашего разработчика. Если вы вызываете

это не означает, что вы получаете доступ к a непосредственно. Это то же самое, что

в Java. Причина заключается в том, что Kotlin автоматически генерирует геттеры и сеттеры по умолчанию. В Котлине, вы должны указать специальный сеттер или геттер, только если он у вас есть. В противном случае, Kotlin автогенерирует его для вас:

field? Что это? Чтобы было ясно, давайте посмотрим на этот код:

Это означает, что вы вызываете метод set внутри метода set, потому что нет прямого доступа к свойству в мире Kotlin. Это создаст бесконечную рекурсию. Когда вы вызываете a = что-то, он автоматически вызывает метод set.
Надеюсь, теперь понятно, почему вы должны использовать ключевое слово field и как работают сеттеры и геттеры.

Вернемся к нашему коду. Я хотел бы показать вам ещё одну замечательную особенность языка Kotlin, apply:

apply позволяет вам вызывать несколько методов на одном экземпляре.

Мы все еще не закончили привязку данных, есть ещё много дел. Давайте создадим класс модели пользовательского интерфейса для репозитория (этот класс модели пользовательского интерфейса для репозитория GitHub хранит данные, которые должны отображаться, не путайте их с паттерном Repository). Чтобы сделать класс Kotlin, вы должны перейти в New -> Kotlin File / Class:

В Kotlin первичный конструктор является частью заголовка класса. Если вы не хотите предоставлять второй конструктор, это всё! Ваша работа по созданию класса завершена здесь. Нет параметров конструктора для назначений полей, нет геттеров и сеттеров. Целый класс в одной строке!

Вернитесь в класс MainActivity.kt и создайте экземпляр класса Repository:

Как вы можете заметить, для построения объекта не нужно ключевого слова new.

Теперь перейдем к activity_main.xml и добавим тег data:

Мы можем получить доступ к переменной repository, которая является типом Repository в нашем макете. Например, мы можем сделать следующее в TextView с идентификатором repository_name:

В TextView repository_name будет отображаться текст, полученный из свойства repositoryName переменной repository. Остается только связать переменную репозитория от xml до repository из MainActivity.kt.
Нажмите Build, чтобы сгенерировать библиотеку привязки данных для создания необходимых классов, вернитесь в MainActivity и добавить две строки:

Если вы запустите приложение, вы увидите, что в TextView появится «Habrahabr Android Repository Article». Хорошая функция, да? 🙂

Но что произойдёт, если мы сделаем следующее:

Отобразится ли новый текст через 2 секунды? Нет, не отобразится. Вы должны заново установить значение repository. Что-то вроде этого будет работать:

Но это скучно, если нужно будет делать это каждый раз, когда мы меняем какое-то свойство. Существует лучшее решение, называемое Property Observer.
Давайте сначала опишем, что такое паттерн Observer, нам понадобится это в разделе rxJava:

Возможно, вы уже слышали об androidweekly.net. Это еженедельный информационный бюллетень об Android разработке. Если вы хотите его получить, вам необходимо подписаться на него, указав свой адрес электронной почты. Позже, если вы захотите, вы можете остановить отказаться от подписки на своем сайте.

Это один из примеров паттерна Observer / Observable. В данном случае, Android Weekly — наблюдаемый (Observable), он выпускает информационные бюллетени каждую неделю. Читатели — это наблюдатели (Observers), они подписываются на него, ждут новых выпусков, и, как только они получают её, они читают её, и если некоторые из них решат, что им это не нравится, он / она может прекратить следить.

Property Observer, в нашем случае, представляет собой XML-макет, который будет прослушивать изменения в экземпляре Repository. Таким образом, Repository является наблюдаемым. Например, как только свойство name класса Repository изменяется в экземпляре класса, xml должен обновится без вызова:

Как сделать это с помощью библиотеки привязки данных? Библиотека привязки данных предоставляет нам класс BaseObservable, который должен быть реализован в классе Repository:

BR — это класс, который автоматически генерируется один раз, когда используется аннотация Bindable. Как вы можете видеть, как только новое значение установлено, мы узнаём об этом. Теперь вы можете запустить приложение, и вы увидите, что имя репозитория будет изменено через 2 секунды без повторного вызова функции executePendingBindings ().

Для этой части это всё. В следующей части я напишу о паттерне MVVM, паттерне Repository и об Android Wrapper Managers. Вы можете найти весь код здесь. Эта статья охватывает код до этого коммита.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *