Интегрируем Rust в Android-приложение (Финальная часть)
Это вторая и заключительная часть серии, в которой объясняется, как добавить код Rust в ваш Android-проект без необходимости изучать или использовать JNI или C в вашем проекте. В первой части серии мы создали простой проект Android Studio и простую библиотеку, написанную на Rust, для регистрации информации. Во второй и заключительной части мы рассмотрим практический пример применения функций Rust в нашей работе.
В этом пошаговом руководстве мы создадим простое приложение-калькулятор, которое вычисляет результат, когда пользователь нажимает кнопку “равно”. Приложение будет принимать 2 входных сигнала, а затем выполнять 3 операции над ними: сложение, вычитание и умножение. Эти простые знания были бы полезны в реальных случаях, когда вам, возможно, придется выполнять трудоемкие или низкоуровневые операции на Android. Не мудрствуя лукаво, давайте создадим серверную часть нашего приложения (изначально вы могли бы создать U.I, но для этого пошагового руководства я считаю, что сначала удобнее написать серверную часть Rust).
Проектирование Серверной части ⚙️
Как уже говорилось ранее, мы бы создали структуру, содержащую 2 значения, а также функции, которые возвращали бы соответствующие значения сложения, вычитания и умножения. Мы бы добавили эту структуру в lib.rs
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
pub struct Inputs {
first: i64,
second: i64,
}
impl Inputs {
#[generate_interface(constructor)]
pub fn new(first: i64, second: i64) -> Inputs {
Self {
first,
second,
}
}
#[generate_interface]
pub fn addition(&self) -> i64 {
self.first + self.second
}
#[generate_interface]
pub fn subtraction(&self) -> i64 {
self.first - self.second
}
#[generate_interface]
pub fn multiplication(&self) -> i64 {
self.first * self.second
}
}
Self
в аргументах метода - это синтаксический сахар для принимающего типа метода, то есть типа, в котором используется этот метод, и в данном случае UIit - это Inputs
. Вы могли бы заменить Self
на Inputs
, и код работал бы нормально.
Помните, что #[generate_interface]
происходит от rifgen, как описано в первой части серии.
Теперь давайте реализуем U.I.
Проектирование U.I. 📱
Как было описано ранее, U.I. будет иметь 2 текстовых поля для ввода и 3 текстовых поля для вывода. Это будет простой макет для иллюстрации:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package com.example.rustapplication
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.example.rustapplication.lib.Inputs // note the imports
import com.example.rustapplication.lib.RustLog
import com.example.rustapplication.ui.theme.RustApplicationTheme
class MainActivity : ComponentActivity() {
companion object {
init {
System.loadLibrary("rust_lib")
RustLog.initialiseLogging()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
RustApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Homepage()
}
}
}
}
}
@Composable
fun Homepage() {
var firstInput by remember { mutableStateOf("") }
var secondInput by remember { mutableStateOf("") }
var addition by remember { mutableStateOf("Add") }
var subtraction by remember { mutableStateOf("Subtract") }
var multiplication by remember { mutableStateOf("Multiply") }
var showError by remember { mutableStateOf(false) } // Indicate error if the wrong input is received
Column {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Row {
OutlinedTextField(
modifier = Modifier.weight(1f),
value = firstInput,
onValueChange = { firstInput = it },
label = { Text("Enter any number") },
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number // accept only numbers
),
isError = showError
)
OutlinedTextField(
modifier = Modifier.weight(1f),
value = secondInput,
onValueChange = { secondInput = it },
label = { Text("Enter any number") },
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number // accept only numbers
),
isError = showError
)
}
Spacer(modifier = Modifier.size(14.dp))
Button(onClick = {
val first = firstInput.toLongOrNull()
val second = secondInput.toLongOrNull()
if (first == null || second == null) {
showError = true
return@Button
}
val inputs = Inputs(first, second)
addition = "${inputs.addition()}"
subtraction = "${inputs.subtraction()}"
multiplication = "${inputs.multiplication()}"
showError = false
}) {
Text(text = "=")
}
Spacer(modifier = Modifier.size(14.dp))
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Row {
OutlinedTextField(
modifier = Modifier.weight(1f),
value = addition,
onValueChange = { },
label = { Text("Addition") },
readOnly = true
)
OutlinedTextField(
modifier = Modifier.weight(1f),
value = subtraction,
onValueChange = { },
label = { Text("Subtraction") },
readOnly = true
)
}
OutlinedTextField(
value = multiplication,
onValueChange = { },
label = { Text("Multiplication") },
readOnly = true
)
}
}
}
И это все! Вот как должно выглядеть приложение.
Всякий раз, когда пользователь нажимает кнопку “равно”, числа из текстовых полей передаются в структуру, написанную на Rust
. Затем значения вычисляются, и результат отображается в текстовом поле. Не стесняйтесь изменять код и пробовать другие способы добавления разнообразия Rust
в ваш Android-проект.
Дальнейшее чтение 📖
Узнайте больше о зависимостях, используемых в этом пошаговом руководстве:
- Dushistov/flapigen-rs: Инструмент для подключения программ или библиотек, написанных на
Rust
, к другим языкам - Kofituo/rifken: Программа для перевода библиотек, написанных на
Rust
, в файлы интерфейса. Работает сflapigen
. - sfackler/rust-jni-sys