Класс Nothing имеет уникальное и очень специфическое назначение. Он представляет тип, который не имеет значений и используется для обозначения операций, которые никогда не завершаются нормально. Вот несколько ключевых причин, почему данный тип полезен:
Nothing ясно демонстрирует этот намеренный аспект поведения функции.Nothing, для вывода о том, что последующий код недостижим. Это может помочь в оптимизации кода и предотвращении ошибок.fun throwError(message: String): Nothing {
throw IllegalArgumentException(message)
}В этом случае он указывает, что после вызова
throwError выполнение текущей функции не будет продолжено. Это позволяет компилятору правильно анализировать поток выполнения программы и разработчикам легче понять поведение кода.Nothing — это тип без значений, который используется для обозначения операций, не имеющих нормального завершения. Это предоставляет дополнительные возможности для управления потоком выполнения программы, статического анализа и улучшения понимания кода.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
Anonymous Quiz
12%
convert
15%
transform
58%
map
16%
cast
🤯2
Сериализация – это процесс преобразования объекта в поток байтов для сохранения его состояния или передачи его через сеть. Это нужно, чтобы можно было хранить объекты в файлы, базы данных или передавать их между разными компонентами приложения или даже разными приложениями.
Когда мы работаем с объектами в приложении, они находятся в оперативной памяти. Но, если нужно сохранить состояние объекта между запусками программы или передать его через сеть, нам нужно как-то его "упаковать". Вот тут и приходит на помощь сериализация. Например, вы можете сериализовать объект, сохранить его в файл, а потом прочитать этот файл и десериализовать объект обратно.
В Android для сериализации часто используется интерфейс
Serializable или Parcelable. Вот пример использования Serializable:import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// геттеры и сеттеры
}
Чтобы сериализовать объект
User и сохранить его в файл, можно сделать так:User user = new User("John", 30);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}Для десериализации объекта из файла:
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User user = (User) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}Parcelable предпочтительнее в Android, так как он быстрее работает по сравнению с Serializable.import android.os.Parcel;
import android.os.Parcelable;
public class User implements Parcelable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
// геттеры и сеттеры
}
Теперь можно передавать объект
User между активити:Intent intent = new Intent(this, AnotherActivity.class);
User user = new User("John", 30);
intent.putExtra("user", user);
startActivity(intent);
И получить его в
AnotherActivity:User user = getIntent().getParcelableExtra("user");Сериализация – это способ сохранения или передачи объектов, преобразовывая их в поток байтов. В Android можно использовать
Serializable или Parcelable для этих целей.Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤1
Anonymous Quiz
70%
private constructor
8%
private init
16%
private class
6%
private fun
Подключение
BroadcastReceiver в Android состоит из двух основных шагов: создание самого ресивера и его регистрация. Ресивер можно зарегистрировать как статически в манифесте, так и динамически в коде.Создадим простой
BroadcastReceiver, который будет реагировать на определённое событие, например, на получение SMS.import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Сообщение получено!", Toast.LENGTH_SHORT).show();
}
}
Можно зарегистрировать ресивер в файле
AndroidManifest.xml. Это удобно, когда вы хотите, чтобы ресивер всегда был активен и слушал определённые системные события, например, перезагрузку устройства или получение SMS.<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
</manifest>
Иногда нужно регистрировать ресивер только на время выполнения определённой активности или службы. В этом случае лучше использовать динамическую регистрацию.
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myBroadcastReceiver = new MyBroadcastReceiver();
}
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(myBroadcastReceiver, filter);
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(myBroadcastReceiver);
}
}
В этом примере ресивер регистрируется в методе
onStart() и отписывается в методе onStop(). Это позволяет ресиверу быть активным только тогда, когда активность видима.Чтобы подключить
BroadcastReceiver, создайте его и зарегистрируйте либо в манифесте, либо в коде. Статическая регистрация через манифест делает ресивер активным всегда, а динамическая регистрация в коде позволяет контролировать время его активности.Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥4
Anonymous Quiz
5%
Int
28%
String
53%
Boolean
14%
Long
🤯3
В Android-приложениях существует несколько типов хранилищ данных, каждый из которых подходит для различных сценариев и требований к данным. Основные типы хранилищ данных в Android включают:
String filename = "myfile";
String fileContents = "Hello, World!";
FileOutputStream fos = openFileOutput(filename, Context.MODE_PRIVATE);
fos.write(fileContents.getBytes());
fos.close();
// Чтение данных
FileInputStream fis = openFileInput(filename);
int c;
StringBuilder temp = new StringBuilder();
while( (c = fis.read()) != -1) {
temp.append((char)c);
}
fis.close();
String filename = "myfile.txt";
String fileContents = "Hello, World!";
File file = new File(getExternalFilesDir(null), filename);
FileOutputStream fos = new FileOutputStream(file);
fos.write(fileContents.getBytes());
fos.close();
// Чтение данных
FileInputStream fis = new FileInputStream(file);
int c;
StringBuilder temp = new StringBuilder();
while( (c = fis.read()) != -1) {
temp.append((char)c);
}
fis.close();
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
}
cursor.close();
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Anonymous Quiz
50%
abstract
19%
open
31%
override
0%
final
StateFlow – это специальный тип потока данных, используемый в Kotlin и библиотеке Kotlin Coroutines. Он предназначен для управления состоянием и передачи данных в реальном времени в реактивных приложениях. StateFlow основан на концепции потоков данных, аналогичной LiveData в Android, но с улучшенной поддержкой сопрограмм.StateFlow всегда активен и хранит последнее значение состояния. Подписчики получают текущее состояние немедленно после подписки.StateFlow поддерживает изменение состояния через методы value или emit.StateFlow помогает создавать реактивные и асинхронные приложения, где состояние может изменяться динамически и пользователи получают обновления в реальном времени.LiveData, StateFlow требует начальное состояние, что делает его состояние всегда предсказуемым и никогда не null.LiveData.Для создания
StateFlow используется MutableStateFlow, который позволяет изменять его значение:import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow("Initial State")
val state: StateFlow<String> get() = _state
fun updateState(newState: String) {
_state.value = newState
}
}
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
class CounterActivity : AppCompatActivity() {
private val viewModel: CounterViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_counter)
// Подписка на StateFlow
lifecycleScope.launch {
viewModel.count.collect { count ->
// Обновляем UI
textView.text = count.toString()
}
}
// Пример использования методов инкремента и декремента
incrementButton.setOnClickListener {
viewModel.increment()
}
decrementButton.setOnClickListener {
viewModel.decrement()
}
}
}
StateFlow – это поток данных для управления состоянием в Kotlin, который всегда содержит последнее состояние и работает с сопрограммами. Он используется для реактивного программирования, обеспечивая предсказуемое и синхронизированное обновление состояния в приложении.Please open Telegram to view this post
VIEW IN TELEGRAM
👍6👀1
Anonymous Quiz
14%
Ошибка компиляции
3%
Ошибка выполнения
6%
Параметр принимает значение null
77%
Используется значение по умолчанию
Для работы с сетью в Android используются различные библиотеки и инструменты, обеспечивающие выполнение сетевых запросов, обработку ответов, и управление асинхронными операциями. Наиболее популярные и широко используемые инструменты включают:
new Thread(() -> {
try {
URL url = new URL("https://api.example.com/data");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
// Обработка ответа
} finally {
urlConnection.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseData = response.body().string();
// Обработка ответа
}
}
});
Для работы с сетью в Android можно использовать
HttpURLConnection для простых задач, а также сторонние библиотеки, такие как OkHttp, Retrofit, Volley и Ktor, для более сложных и мощных сетевых операций. Эти инструменты помогают управлять HTTP-запросами, обрабатывать ответы и поддерживать асинхронные операции.Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Anonymous Quiz
52%
open
23%
override
0%
final
25%
abstract
Чтобы делать сетевые запросы с помощью Retrofit, следуйте этим шагам:
Сначала добавьте зависимости для Retrofit и конвертера JSON в файл
build.gradle вашего проекта:dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
}Создайте класс, который будет представлять данные, полученные от API. Например, если ваш API возвращает информацию о пользователях, создайте класс
User:public class User {
private String name;
private String email;
// Геттеры и сеттеры
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}Создайте интерфейс, который описывает HTTP-запросы. Используйте аннотации Retrofit для указания типа запроса и конечной точки:
import retrofit2.Call;
import retrofit2.http.GET;
import java.util.List;
public interface ApiService {
@GET("users")
Call<List<User>> getUsers();
}
Создайте экземпляр Retrofit в вашем приложении. Обычно это делается в классе
Application или в отдельном синглтоне:import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
private static final String BASE_URL = "https://api.example.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Теперь вы можете использовать созданный
ApiService для выполнения сетевых запросов. Это обычно делается в активити или во ViewModel:import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ApiService apiService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiService = ApiClient.getClient().create(ApiService.class);
fetchUsers();
}
private void fetchUsers() {
Call<List<User>> call = apiService.getUsers();
call.enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful()) {
List<User> users = response.body();
// Обработка полученных данных
for (User user : users) {
Toast.makeText(MainActivity.this, "User: " + user.getName(), Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(MainActivity.this, "Response Error " + response.code(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
Toast.makeText(MainActivity.this, "Network Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Anonymous Quiz
52%
G1
21%
CMS
9%
Serial
18%
ZGC
👀8
Runtime (время выполнения) в контексте Android — это среда, в которой исполняется приложение. Она отвечает за выполнение кода, управление памятью и взаимодействие с операционной системой. В Android runtime включает такие компоненты, как:
Рассмотрим простой пример создания и использования объекта в Java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Создание нового объекта
Person person = new Person("John", 30);
// Вызов метода объекта
String details = person.getDetails();
Log.d("MainActivity", details);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getDetails() {
return "Name: " + name + ", Age: " + age;
}
}В этом примере:
MainActivity и Person.onCreate, создавая объект Person и вызывая его метод getDetails.Person и освобождает её, когда объект больше не нужен (например, при завершении активности или сборке мусора).NullPointerException), Runtime обработал бы его и вызвал соответствующий обработчик исключений.Runtime в Android отвечает за выполнение кода приложения, управление памятью, загрузку классов, обеспечение безопасности и обработку исключений. ART (Android Runtime) является текущей реализацией runtime в Android и предлагает улучшенную производительность и управление памятью по сравнению с предыдущей версией (Dalvik).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Anonymous Quiz
28%
CoroutineDispatcher
13%
ContinuationInterceptor
47%
Continuation
13%
SuspendFunction
В Java лямбда-выражения имеют тип, который называется функциональным интерфейсом. Функциональный интерфейс — это интерфейс, который содержит только один абстрактный метод. Этот интерфейс может также содержать статические и по умолчанию (default) методы, но он должен иметь только один абстрактный метод, который определяет тип лямбда-выражения.
Java предоставляет несколько стандартных функциональных интерфейсов в пакете
java.util.function, таких как Function, Consumer, Supplier, и Predicate. Вы также можете создать свой собственный функциональный интерфейс.R apply(T t)Function<String, Integer> stringLength = s -> s.length();
void accept(T t)Consumer<String> print = s -> System.out.println(s);
T get()Supplier<Double> randomValue = () -> Math.random();
boolean test(T t)Predicate<Integer> isEven = i -> i % 2 == 0;
Вы можете создать свой собственный функциональный интерфейс, используя аннотацию
@FunctionalInterface (необязательно, но рекомендуется):@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
public class Main {
public static void main(String[] args) {
MyFunctionalInterface myFunction = () -> System.out.println("Hello, World!");
myFunction.myMethod(); // Вывод: Hello, World!
}
}
Когда вы создаете лямбда-выражение, Java автоматически сопоставляет его с абстрактным методом функционального интерфейса. Лямбда-выражение должно соответствовать параметрам и возвращаемому типу этого метода.
Например, в следующем коде лямбда-выражение соответствует методу
apply интерфейса Function:Function<String, Integer> stringLength = s -> s.length();
Рассмотрим более полный пример с использованием функционального интерфейса
Function:import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Function<String, Integer> stringLength = s -> s.length();
String testString = "Hello, World!";
Integer length = stringLength.apply(testString);
System.out.println("Length of the string: " + length); // Вывод: Length of the string: 13
}
}
Лямбда-выражения в Java имеют тип, называемый функциональным интерфейсом, который содержит только один абстрактный метод. Функциональные интерфейсы предоставляют способ описания сигнатуры лямбда-выражений и являются основой для работы с лямбдами в Java.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1👀1
Anonymous Quiz
61%
Unit
15%
Any
11%
Nothing
13%
Lambda
При передаче лямбда-выражений из Java в методы, написанные на Kotlin, существуют несколько потенциальных опасностей и проблем, которые необходимо учитывать. Некоторые из них включают:
В Kotlin строгая система типов, которая различает nullable и non-nullable типы. Java, в свою очередь, не имеет этой особенности, что может привести к проблемам при передаче лямбда-выражений.
Kotlin:
fun performAction(action: () -> Unit) {
action()
}Java:
public class Main {
public static void main(String[] args) {
MainKt.performAction(null); // NullPointerException
}
}В этом примере, если передать
null в метод performAction, это вызовет NullPointerException, так как Kotlin ожидает, что параметр action не будет null.Java компилятор может не поймать все ошибки, которые Kotlin компилятор обнаружил бы при использовании лямбда-выражений.
Kotlin:
fun transformData(transform: (String) -> Int): Int {
return transform("Kotlin")
}Java:
public class Main {
public static void main(String[] args) {
// Ошибка не будет обнаружена компилятором Java
int result = MainKt.transformData(str -> str.length()); // Ошибка при выполнении, если str будет null
}
}При использовании функциональных интерфейсов необходимо убедиться, что типы параметров и возвращаемые типы лямбда-выражений совпадают с ожидаемыми типами в Kotlin.
Kotlin:
fun executeOperation(operation: (Int, Int) -> Int): Int {
return operation(3, 4)
}Java:
public class Main {
public static void main(String[] args) {
// Типы должны совпадать с ожидаемыми в Kotlin
int result = MainKt.executeOperation((a, b) -> a + b);
}
}Кotlin предоставляет более строгую систему проверок типов, и отсутствие этих проверок в Java может привести к потенциальным ошибкам времени выполнения.
Kotlin:
fun processData(processor: (String) -> String) {
val result = processor("Kotlin")
println(result)
}Java:
public class Main {
public static void main(String[] args) {
// Неверное приведение типов не будет обнаружено компилятором Java
MainKt.processData(str -> (String) str.toUpperCase()); // ClassCastException при выполнении, если str будет null
}
}В Java отсутствуют аннотации на уровне типов, указывающие, может ли параметр или возвращаемое значение быть
null. Это может привести к ошибкам в Kotlin, где такие соглашения строго соблюдаются.Kotlin:
fun processString(processor: (String) -> String?) {
val result = processor("Kotlin")
println(result?.length)
}Java:
public class Main {
public static void main(String[] args) {
MainKt.processString(str -> null); // Возможный NPE, если Kotlin не учтет возможность null
}
}При передаче лямбда-выражений из Java в Kotlin нужно быть внимательным к:
null.При передаче лямбда-выражений из Java в Kotlin могут возникать проблемы с
null, несовпадением типов и отсутствием строгих проверок типов на стороне Java. Будьте осторожны с типами и проверками на null, чтобы избежать ошибок.Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Anonymous Quiz
12%
3.33
13%
3.0
73%
3
2%
Исключение
В архитектурных компонентах Android для сохранения состояния ViewModel используется специальный класс
SavedStateHandle. Этот класс позволяет сохранить и восстановить состояние ViewModel при изменении конфигурации (например, при повороте экрана) или при пересоздании активности или фрагмента.Убедитесь, что в вашем проекте добавлена зависимость на архитектурные компоненты Android:
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1"
Для использования
SavedStateHandle в вашей ViewModel, убедитесь, что ваш ViewModel принимает его в конструкторе. Это можно сделать, используя фабрику ViewModel при создании ViewModel.import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
class MyViewModel(private val state: SavedStateHandle) : ViewModel() {
// Ключ для сохранения и восстановления состояния
private val MY_STATE_KEY = "my_state_key"
// Получение значения из SavedStateHandle
var myValue: String?
get() = state.get(MY_STATE_KEY)
set(value) {
state.set(MY_STATE_KEY, value)
}
// Инициализация состояния
init {
if (!state.contains(MY_STATE_KEY)) {
myValue = "Initial Value"
}
}
}
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class MainActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels {
object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MyViewModel(defaultSavedStateHandle) as T
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myViewModel.myLiveData.observe(this, { value ->
println("Observed value: $value")
})
// Установка нового значения
myViewModel.updateValue("New LiveData Value")
}
}
Для сохранения состояния ViewModel в архитектурных компонентах Android используется класс
SavedStateHandle. Он позволяет сохранять и восстанавливать состояние при изменениях конфигурации. Создайте ViewModel с SavedStateHandle и используйте его для сохранения данных. Используйте ViewModelProvider.Factory для правильного создания ViewModel с SavedStateHandle.Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🤔1