- PVSM.RU - https://www.pvsm.ru -
Продолжение той самой истории.
Первая часть тут [2], вторая тут [3], третья тут [4].
4,5 года назад я имел неосторожность начать писать свою криптовалюту на совсем неподходящем для этого дела языке — на PHP. В итоге, конечно, написал (я упрямый), но получился костыль на костыле и то, что оно вообще работало было просто какой-то магией.
Сразу хочу предупредить, программер я самоучка-недоучка и пишу код, мягко сказать, неидеально.
Началось всё с того, что я расстался с девушкой, по имени Катя и в этот же день (4 апреля 2015-го) решил изучить Go и переписать свою криптовалюту. Писать про Катю не под спойлерами не могу, т.к. хабр всё же для IT-шных статей, а не для любовных рассказов и суровые айтишники, которым интересна тема Go, могут просто не обращать внимание на спойлеры «про Катю».
Итог 8 месяцев: приложение работает на Win (64 [5]/32 [6]), OSX(64 [7]/32 [8]), Linux(64 [9]/32 [10]), FreeBSD(64 [11]/32 [12]), Android [13], IOS [14].
Общего кода ~73к строк, кода под разные ОС где-то несколько сотен строчек.
40к — обработка/генерация блоков/тр-ий, 17.5к — контроллеры для интерфейса, 15.5к — шаблоны
Поддерживаются PostgreSQL, SQLite, MySQL.
Тех, кто будет тестировать мое творение, предупреждаю — могут быть баги, и если у Вас есть время, черкните о них, пожалуйста, на darwin@dcoin.club [15] или в личку на хабре. Пожелания и советы тоже приветствуются.
В первых трех частях я рассказал про то, как в dcoin функционирует веб-сервер, про html/template [16], базы данных, плавное завершение приложения, шифрование и парсинг блоков.
В этой статье я расскажу про работу с Go на Android.
Смешно сказать, но мой первый андройд появился у меня этим летом. До этого я просто не находил причин, чтобы заменить свою Nokia 1200. Купил дешевый ZTE за 3000 руб с 512 памяти и 2-я ядрами. Для тестирования самое то. И звонить с него тоже можно
Хотелось сделать компиляцию через github.com/golang/mobile [17] в apk. Посмотрел мануалы, вроде всё просто. Почти сразу получилось скомпилировать бинарник и запустить его под рутом на андройде. Обрадовался, что всё идет как по маслу и казалось, что через пару дней у меня будет apk, запустив который я увижу Dcoin.
Собрать Apk оказалось не сложно. В принципе, всё что мне было нужно — это автоматически открыть в браузере 127.0.0.1 [18]:8089. Вот тут-то я забуксовал. Несколько дней гуглил и экспериментировал и всё чего смог добиться — это отрисовка картинки на которой я прошу пользователя открыть в браузере нужный хост.
Решил зайти через aar. Т.е. добавить его как библиотеку в андройд-студио. И средствами студии уже открыть браузер или webview. Но sqlite упорно не хотело компилиться, оказалось что ошибка в компиляторе C и решения проблемы на тот момент не было (сейчас, кстати, уже есть).
В какой-то момент стало очевидно, что без этого волшебного файлика ничего не выйдет. Начал экспериментировать, внес несколько изменений, генерирую apk и ничего не меняется. Через пару дней не выдержал и решил написать одному из разработчиков gomobile, ответ пришел довольно быстро. Оказывается, после изменений в GoNativeActivity надо вызывать go generate github.com/c-darwin/mobile/cmd/gomobile, чтобы сгенерировался .dex файл и только после этого go install github.com/c-darwin/mobile/cmd/gomobile
Научившись править GoNativeActivity я получил огромные возможности. Нужно было лишь уметь писать на Java. А я не умел, и сейчас не умею. Но кое-что всё же смог сделать. Тут [19] мой GoNativeActivity. Чуть позже понял, как создать свой AndroidManifest.xml, что дало еще больше возможностей, в итоге вместо работы в браузере я смог добиться работы в WebView, тут [20] моя реализация вебвьюхи.
Мне захотелось сделать уведомления, когда приходят деньги или поступает входящий запрос на обмен монет на фиат
После гугления и изучения stackoverflow получился такой код
public void notif(String title, String text) {
Intent intent = new Intent("org.golang.app.MainActivity");
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
// с этой иконкой пришлось повозиться, нижу расскажу
mBuilder.setSmallIcon(R.drawable.icon);
mBuilder.setContentTitle(title);
mBuilder.setContentText(text);
Intent resultIntent = new Intent(this, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// notificationID allows you to update the notification later on.
mNotificationManager.notify(2, mBuilder.build());
}
Осталось только понять, как этот notif дергать из Go. Вот тут я застрял еще на недельку. Оказалось, что надо использовать некого зверя под названием JNI [21]. Получается что-то вроде такой конструкции: Go вызывает C, который запускает Java-машину и дергает через неё мой notif. Ужас. Особенно учитывая, что в C, я также как и в Java, почти полный ноль.
Короче, после долгих мучений я смог таки написать рабочий код [22] и даже понять, что в нем происходит:
package notif
/*
#cgo LDFLAGS: -llog -landroid
#include <android/log.h>
#include <jni.h>
#include <stdlib.h>
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go/fatal", __VA_ARGS__)
void notif_manager_init(void* java_vm, void* ctx, char* title, char* text) {
JavaVM* vm = (JavaVM*)(java_vm);
JNIEnv* env;
int err;
int attached = 0;
err = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6);
if (err != JNI_OK) {
if (err == JNI_EDETACHED) {
// присоединяемся к JM
if ((*vm)->AttachCurrentThread(vm, &env, 0) != 0) {
LOG_FATAL("cannot attach JVM");
}
attached = 1;
} else {
LOG_FATAL("GetEnv unexpected error: %d", err);
}
}
// преобразуем в jstring наш title
jstring javaTitle = (jstring)(*env)->NewStringUTF(env, (const char *)title);
// преобразуем в jstring наш text
jstring javaText = (jstring)(*env)->NewStringUTF(env, (const char *)text);
//указатель на класс, к которому относится объект
jclass cls = (*env)->GetObjectClass(env, ctx);
// идентификатор метода.Ljava/lang/String;Ljava/lang/String - тип передаваемых данных и V(Void) - возвращаемых
jmethodID nJmethodID = (*env)->GetMethodID(env, cls, "notif", "(Ljava/lang/String;Ljava/lang/String;)V");
// вызываем сам метод
(jstring)(*env)->CallObjectMethod(env, ctx, nJmethodID, javaTitle, javaText);
if (attached) {
// отсоединяемся от JM
(*vm)->DetachCurrentThread(vm);
}
}
*/
import "C"
import (
"github.com/c-darwin/mobile/internal/mobileinit"
)
func SendNotif(title string, text string) {
ctx := mobileinit.Context{}
C.notif_manager_init(ctx.JavaVM(), ctx.AndroidContext(), C.CString(title), C.CString(text))
}
По аналогии у меня получился пакет get_files_dir.go [23] который получает рабочую директорию.
На Go получился вот такой код [24] для вызова уведомления
// +build android
package sendnotif
import (
"github.com/c-darwin/mobile/notif"
)
func SendMobileNotification(title, text string) {
notif.SendNotif(title, text)
}
Заметил, что веб-серер стал постоянно падать. Работать с кошельком было невозможно. Погуглил, понял, что надо делать сервис. На этот раз сложностей было уже не так много, исходник тут [25]. ShortcutIcon() создает иконку на рабочем столе.
При появлении уведомлений нужно указать иконку. Пришлось разбираться как в android устроена работа с ресурсами. Примерно понял, что нужно сгенерировать R.jar и подключить его при генерации dex-файла.
Погуглил, как генерить java файлы, получилась такая команда:
aapt package -v -f -J /home/z/go-projects/src/github.com/c-darwin/dcoin-go/ -S /home/z/go-projects/src/github.com/c-darwin/dcoin-go/res/ -M /home/z/go-projects/src/github.com/c-darwin/dcoin-go/AndroidManifest.xml -I /home/z/android-sdk-linux/platforms/android-22/android.jar
Полученный R.java помещаем в R/org/golang/app/
mv R.java /home/z/go-projects/src/github.com/c-darwin/dcoin-go/R/org/golang/app/
И генерируем R.jar
cd R && jar cfv /home/z/go-projects/src/github.com/c-darwin/dcoin-go/R.jar
Генерим неподписанный apk
aapt package -v -f -J /home/z/go-projects/src/github.com/c-darwin/dcoin-go/ -S /home/z/go-projects/src/github.com/c-darwin/dcoin-go/res/ -M /home/z/go-projects/src/github.com/c-darwin/dcoin-go/AndroidManifest.xml -I /home/z/android-sdk-linux/platforms/android-22/android.jar -F unsigned.apk
Вытаскиваем в корень resources.arsc
unzip unsigned.apk -d apk && mv apk/resources.arsc .
Дальше нужно подправить gendex.go [26] из gomobile:
cmd := exec.Command(
"javac",
"-source", "1.7",
"-target", "1.7",
"-bootclasspath", platform+"/android.jar",
"-classpath", "/home/z/go-projects/src/github.com/c-darwin/dcoin-go/R.jar:"+androidHome+"/extras/android/m2repository/com/android/support/support-v4/22.2.1/support-v4-22.2.1-sources.jar",
"-d", tmpdir+"/work",
)
Генерируем новый Dex
ANDROID_HOME=/home/z/android-sdk-linux go generate github.com/c-darwin/mobile/cmd/gomobile/
Затем бинарник самого gomobile
go install github.com/c-darwin/mobile/cmd/gomobile/
И наконец получаем наш apk
CGO_ENABLED=1 GOOS=android ANDROID_HOME=/home/z/android-sdk-linux gomobile build -v github.com/c-darwin/dcoin-go
В итоге, у меня получился вот такой bash скрипт для генерации apk:
./bindata.sh
echo "######## generate R.java ########"
aapt package -v -f -J /home/z/go-projects/src/github.com/c-darwin/dcoin-go/ -S /home/z/go-projects/src/github.com/c-darwin/dcoin-go/res/ -M /home/z/go-projects/src/github.com/c-darwin/dcoin-go/AndroidManifest.xml -I /home/z/android-sdk-linux/platforms/android-22/android.jar
mv R.java /home/z/go-projects/src/github.com/c-darwin/dcoin-go/R/org/golang/app/
echo "######## generate R.jar ########"
cd R
jar cfv /home/z/go-projects/src/github.com/c-darwin/dcoin-go/R.jar .
cd ../
echo "######## generate unsigned.apk ########"
aapt package -v -f -J /home/z/go-projects/src/github.com/c-darwin/dcoin-go/ -S /home/z/go-projects/src/github.com/c-darwin/dcoin-go/res/ -M /home/z/go-projects/src/github.com/c-darwin/dcoin-go/AndroidManifest.xml -I /home/z/android-sdk-linux/platforms/android-22/android.jar -F unsigned.apk
echo "######## extract resources.arsc ########"
unzip unsigned.apk -d apk
mv apk/resources.arsc .
rm -rf apk unsigned.apk
ANDROID_HOME=/home/z/android-sdk-linux go generate github.com/c-darwin/mobile/cmd/gomobile/
go install github.com/c-darwin/mobile/cmd/gomobile/
CGO_ENABLED=1 GOOS=android ANDROID_HOME=/home/z/android-sdk-linux gomobile build -v github.com/c-darwin/dcoin-go
В следующей, заключительной статье я расскажу про gomobile и IOS. И, наконец, будет финал про Катю.
Ищу Go-программеров в команду на хорошую з.п.
Автор: c-darwin
Источник [27]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/112122
Ссылки в тексте:
[1] Image: https://habrahabr.ru/post/277073/
[2] тут: http://habrahabr.ru/company/dcoin/blog/272695/
[3] тут: http://habrahabr.ru/post/273333/
[4] тут: https://habrahabr.ru/post/274885/
[5] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_win64.exe
[6] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_win32.exe
[7] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_osx64.dmg
[8] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_osx32.dmg
[9] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_linux64.deb
[10] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_linux32.deb
[11] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_freebsd64.zip
[12] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_freebsd32.zip
[13] Android: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin.apk
[14] IOS: http://dcoin.club/ru/ios.html
[15] darwin@dcoin.club: mailto:darwin@dcoin.club
[16] html/template: https://golang.org/pkg/html/template/
[17] github.com/golang/mobile: https://github.com/golang/mobile
[18] 127.0.0.1: http://127.0.0.1
[19] Тут: https://github.com/c-darwin/dcoin-go/blob/master/GoNativeActivity.java
[20] тут: https://github.com/c-darwin/dcoin-go/blob/master/XWalkActivity.java
[21] JNI: https://ru.wikipedia.org/wiki/Java_Native_Interface
[22] код: https://github.com/c-darwin/mobile/blob/master/notif/notif.go
[23] get_files_dir.go: https://github.com/c-darwin/mobile/blob/master/get_files_dir/get_files_dir.go
[24] вот такой код: https://github.com/c-darwin/dcoin-go/blob/master/packages/sendnotif/android.go
[25] тут: https://github.com/c-darwin/dcoin-go/blob/master/MyService.java
[26] gendex.go: https://github.com/c-darwin/mobile/blob/master/cmd/gomobile/gendex.go
[27] Источник: https://habrahabr.ru/post/277099/
Нажмите здесь для печати.