Co nowego na forum?

Smok Zygmunta dodał post w Przejscie ze stażu na umowę zarobki

4 sekundy temu veneficus napisał(a): zarabiam mniej niz pracownik na produkcji co o tym myslicie? M...

afromarian dodał komentarz w Ile zarabiacie?

5 minut temu Dobrze wygląda.

AnyKtokolwiek dodał komentarz w JSON parse error: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token;

7 minut temu albo JSON jest błędny, albo kod (ewentualnie oba). Nie mogą być oba prawidłowe

P4TRYK dodał post w Jak wyjechać do USA?

7 minut temu Sam myślę do emigracji USA/Zurych, natomiast stawki o których tu piszecie w Szwajcar...

karsa dodał komentarz w Rynek pracy IT w Poznaniu

10 minut temu Chociaż mi się podoba charakter firmy jaka software mill. Ale nie mam pojęcia jak si...

karsa dodał komentarz w Rynek pracy IT w Poznaniu

11 minut temu Full stack mi nie straszny , chociaż ja bardziej w kierunku ops

kixe52 dodał post w Przejscie ze stażu na umowę zarobki

11 minut temu veneficus napisał(a): Obecnie jak mi placa 2400 to jest ich caly koszt wiec wychdozi...

SzyszekOgromny dodał post w Co mogę zmienić w sprzęcie + lekkie eksperymenty pod względem wizualnym + Windows dla firm.

17 minut temu @mr_jaro Programowanie webowe, gry AAA, Linux (różne dystrybucje, w tym Kali) i codz...

kixe52 dodał komentarz w 1000 brutto na stażu

19 minut temu entry lvl, osoba z 0 wiedzą? Pokaż bo aż jestem ciekaw.

molek1 dodał post w Crystal Reports - błąd przy instalacji mojego programu - CrystalDecisions.Shared.SharedUtils

23 minuty temu Witam. Mam programik w którym użytkownik wypełnia formularz, a następnie generuje si...

Delor dodał komentarz w Nie moge sprawdzić, czy użytkownik istnieje.

28 minut temu W wolnej chwili poczytaj o sql injection. Ale teraz działa i to się liczy :)

matjas87 dodał post w SQL Developer - defaultowy insert nowych wierszy

32 minuty temu Zdaję. :-) Jeśli to nie będzie kwestia triggera, powiem że chodzi o coś innego. Jutr...

Kamil Żabiński dodał komentarz w zapytanie do serwera SQL

32 minuty temu do tych drugich jest executeUpdate. To jest porządny jezyk ze statycznym typowaniem,...

ccwrc dodał komentarz w trochę humoru... :-)

41 minut temu kurna, piękne :D

Lucekzz dodał komentarz w JSON parse error: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token;

47 minut temu @AnyKtokolwiek: nie miałem nic narzuconego, bardziej moja potrzeba. Chce dodać do je...

Shalom dodał komentarz w zapytanie do serwera SQL

48 minut temu nope, jest executeUpdate do tych pierwszych ;)

kixe52 dodał komentarz w Java aplikacje webowe - jak to się wszystko łączy? [TEORIA]

49 minut temu Wygoogluj sobie "REST API". Takie coś tworzysz właśnie w Springu. Potem pobierasz te...

danek dodał komentarz w Rynek pracy IT w Poznaniu

50 minut temu constadata z tego co wiem ma raczej samych fullstacków

TerazOdpowiemNaKomcie dodał komentarz w Rynek pracy IT w Poznaniu

57 minut temu Consdata? Ale to też zależy jak trafisz. Jakbyś myślał, to się odezwij, możemy się p...

abrakadaber dodał post w zapytanie do serwera SQL

57 minut temu podejrzewam, że jak w większości języków executeQuery jest do zapytań modyfikujących...

Popularne wpisy na mikroblogu

GregoryI
dziś, 00:27

Odc 3. Apka w google play store

https://play.google.com/store[...]=com.grzegorziwanek.stopwatch

Znalazłem w końcu trochę czasu i zabrałem się za automatyczne zapisywanie stanu trwającej sesji przy ręcznym ubiciu aplikacji.Taki niby cache.

Sama apka zbudowana jest wokół pomysłu braku wykonywanych obliczeń będąc w background oraz obsłużenia wszystkich kombinacji przypadków wejścia w stan background ( liczniki w pauzie, liczniki działające, liczniki wyzerowane itp).

O ile ta implementacja sprawdzała się to bez zarzutów, o tyle nie załatwiała w żaden sposób przypadku, gdy niechący wciśniemy back telefonu ( i ubijemy Activity - co kończy się wyczyszczeniem danych), albo gdy ubije się apkę ręcznie.
Do obsłużenia takiego przypadku trzeba dodać obsługę zapisywania stanu sesji przy końcu lifecycle ekranu oraz odtworzenia z cache przy starcie.
Jak na razie tylko dla seksji "Stopwatch". Sekcja "Countdown" work-in-progress a'la refactor kodu.

Aplikację zbudowałem wokół patternu MVI oraz operowania jednym globalnym stanem ( dokładniej po jednym dla ekranu), co bardzo ułatwia dorobienie takiej funkcjonalności, ponieważ mamy gotowe obiekty ( XXXViewState) które są na bieżąco aktualizowane o zmiany w obrębie swoich ekranów, które posiadają wszystkie potrzebne nam dane na temat ekranu, i które musimy tylko serializaować do JSON i zapisać przed zamknięciem apki, oraz deserializować przy starcie aplikacji i wyrenderować z nich ekrany.
Serializacja oraz lokalne zapisywanie historii pomiarów do JSON było już w aplikacji od samego początku jako jedna z funkcjonalności. Zrobione za pomocą biblioteki moshi.
Dotychczas miałem do czynienia tylko z Gson oraz jackson, a informacja o kotlinx-serialization jakoś mnie ominęła, wybór padł więc na tę bibliotekę.

O ile do tego momentu nie miałem żadnych problemów z serializacją danych ( dodanie wyjątków obfuskacji w Proguard, płaskie struktury danych, bez polimorfiii, zagnieżdżeń oraz kotlinowych obiektów singletonów) o tyle w tym przypadku natrafiłem na ścianę w postaci polimorficznej kotlinowej Sealed Class której serializację musiałem przeprowadzić.

EDIT po komentarzu @Michał Sikora
Da się serializować object z sealed class jako jej supertypem, jednak jest to wiedza bardzo niepowszechna i trzeba dopisać sporo kodu do serializacji.
EDIT end

Spędzenie 3 wieczorów nad tą ścianą, sprawdzenie innych bibliotek oraz przeczytania mnóstwa tematów dotyczących serializacji kotlinowych sealed class, albo inaczej, klas polimorficznych, zaowocowało sieczka w głowie oraz nową wiedzą:

  • jeśli potencjalnie będziemy potrzebowali serializować/deserializować obiekty danej klasy, idealnie byłoby nie używać obiektów polimorficznych
  • broń boże używać kotlinowych object ( na zasadzie object ObjectSubclass : SealedSuperClass()) - można próbować jakiejkolwiek z bibliotek do serializacji a i tak nie zadziała nam próba odtworzenia typu ObjectSubclass ( nie ma tam informacji identyfikującej oraz jest to singleton) - najlepiej sobie odpuścić i już lepiej mieć zamiast tego pustą klasę class ObjectSubclass() : SealedSuperClass() - z tym po kombinacjach poniżej można działać
  • dla sealed class rodzic możemy użyć class albo data class jako dziedziczące, jednak żeby nasze czary zadziałały musimy w przypadku Moshi dodać identyfikator w postacji enuma ( lub jakiegoś stringa) dla każdego podtypu superklasy ( dodać to pole w superklasie oraz w definicji podklas) oraz spiąć to z PolymorphicJsonAdapter; dla bibliotek kotlinx oraz jackson też trzeba było dorabiać dodatkowe struktury danych żeby można było przeprowadzić "identyfikację" typu danego obiektu do i z JSON.
  • a już najlepiej byłoby nie mieć polimorfii i posiadać płaską strukturę danych bo strasznie to brzydkie :)

.Dodatkowo poprawiłem w końcu ikonę aplikacji oraz dodałem Changelog.
W tym tygodniu jeśli będę mieć czas, cache dla sekcji Countdown ( które w zasadzie jest już gotowe, ale potrzbuje refactoringu).

Tak na marginesie: zauważył ktoś, że proces akceptacji zmian ( czy to zmiany grafik sklepowych, czy to nowe wersje apki) baaardzo się wydłużył w google play store?
Natknąłem się na kilka tematów na reddit, zarówno od developerów jak i ogłoszeń od googla, ale dotychczas nie dotknęło mnie to w ogóle.
A teraz ni z gruchy, ni z pietruchy, akceptacja update ikony aplikacji ( wyświetlanej w sklepie!) trwała gdzieś prawie 3 dni.

Zauważyłem też, że po ustawieniu tagów ( od kilku dni w konsoli deweloperskiej aplikacji można przypisać jej do 5 kategorii/tagów) oraz ostatniej aktualizacji apka skoczyła do pierwszej dziesiątki wyników dla "stopwatch timer", mimo mniej niż 50 instalacji, hehe.

GregoryI

@Michał Sikora: Ty, wiesz ile ja szukałem czegoś takiego? :) Używam moshi.
Z tego co rzuciłem okiem, to na serio sporo trzeba dopisać, tylko że moshi dobrze rozumiem, że dla każdego object trzeba dodać osobny adapter (w tym przykładzie jest jeden object) ? Na wklejonym przykładzie QuxJsonAdapter.
Dziękuję bardzo za podesłane sample. Nawet nie wiesz ile spędziłem na stronie bugów moshi/kotlinx/gson żeby znaleźć dokładnie to co wstawiłeś. Jakby nikomu nie była potrzebna serializacja object, wszędzie, we wszystkich samplach mamy class, data class, a jak widać sam object przychodzi z nowymi problemami.
Na pewno użyję w następnym update, żeby przeczyścić trochę tamto miejsce.
Może trochę zredaguję ten wpis, bo chciało mi się spać jak pisałem:)

Michał Sikora

Tak, dla każdego object musisz napisać osobny adapter. Procesor adnotacyjny nie jest w stanie go wygenerować, a refleksja sobie z tym nie radzi. No i o ile serializacja object ma jakiś sens, bo nie wiemy co i jak to skonsumuje, to deserializacja jest trochę dziwna. Niby nie ma czego tu deserializować, bo i tak istnieje tylko jeden object, więc fromJson w adapterze bardziej służy do sprawdzenia poprawności danych niż do ich faktycznego wyciągnięcia.

WeiXiao
wczoraj, 23:15

Może komuś to kiedyś uratuje tyłek

W HTTP/2 Content-Length jest opcjonalny, więc takie coś może nie przejść var buffer = new byte[Convert.ToInt32(request.ContentLength)]; - będzie 0 ;)

Szybki myk (C#) (request.ContentLength ?? (request.Body?.Length ?? 0)) i "u mnie działa", ale radzę zapytać experta / sprawdzić doca libki od obsługi HTTP :D

https://svn.tools.ietf.org/sv[...]30.html#header.content-length

https://tools.ietf.org/html/rfc7540#section-8.1.2.6

edit

W HTTP/1.1 też jest opcjonalny, więc nvm.

Wibowit

Content-Length zawsze był opcjonalny i to świetna sprawa jeśli chce się przestrumieniować dane. Mogę mieć np 2 GiB RAMu na serwerze, a przestrumieniować 100 GiB danych z bazy jednym żądaniem HTTP. Albo np mogę zrobić endpoint HTTP który generuje losowe dane w nieskończoność.

szweszwe
2019-08-16 10:17

Bez tego ani rusz!

wiciu

Nawet jakby to coś dawało, to takie mikro-optymalizacje w obecnych czasach nie są warte tego, aby tak zmniejszać czytelność kodu.

PerlMonk

@wiciu: Bo to często jest przedwczesna optymalizacja. Dopóki nie mamy nawet zarysu projektu, nie można powiedzieć, że kawałek kodu trzeba zoptymalizować w określony sposób. Można na przykład zostawić funkcję w określonym formacie, ale za to spowodować, że będzie ona używana rzadziej.