poniedziałek, 17 lutego 2014

Baza SQLite w androidzie


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.


Android zapewnia wbudowaną obsługę baz SQLite. Nie potrzebujemy żadnych dodatkowych bibliotek, wszystko co niezbędne jest dla Androida natywne. SQLite jest łatwą w obsłudze lekką bazą danych i wykorzystywana jest nie tylko w systemie Android. Również w zwykłej Javie czy JEE możemy tę bazę wykorzystać. Jest jednak kilka różnic w obsłudze. Po pierwsze w zwykłej Javie/JEE potrzebujemy dodawać biblioteki SQLite, po drugie z samą bazą łączymy się z użyciem JDBC czy jakiegoś ORMa np. Hibernate. Tutaj cała baza zawiera się w jednym pliku który na dodatek jest generowany automatycznie :)
W niniejszym przykładzie stworzymy prostą bazę kontaktów. W pierwszej kolejności muszę stworzyć klasę dziedziczącą po SQLiteOpenHelper i przesłonić metody onCreate i onUpgrade, oraz stworzyć konstruktor sparametryzowany. Klasa ta będzie służyła do tworznia bazy danych i niezbędnych tabel.

Konstruktor jako taki przyjmuje tylko jeden parametr – kontekst, który zresztą przekazuje następnie do konstruktora klasy po której dziedziczy. „Kontakty.db” to nazwa pliku bazy danych który zostanie utworzony. Jedynka w konstruktorze to wersja bazy danych. Możemy tę wartość zwiększać przy kolejnych zmianach w strukturze bazy.
Podczas pierwszego skorzystania z obiektu naszej klasy ZarzadcaBazy, SQLiteOpenHelper zauważy brak pliku bazy (tutaj kontakty.db) i go utworzy a następnie wywoła metodę onCreate. W mojej metodzie onCreate, posługując się metodą execSQL obiektu klasy SQLiteDatabase tworzę tabelę w której będę przechowywał dane. Nic nadzwyczajnego – zwykły SQL. Metoda onUpgrade zostanie wywołana jeśli będziemy się odnosić do przestarzałej wersji bazy danych (silnik bazy będzie to wiedział na podstawie numeru wersji którą podajemy jako parametr konstruktora). Możemy tutaj np. zaimplementować wywołanie jakichś poleceń rozbudowujących czy zmieniających struktury bazy.


Teraz dodamy funkcjonalność odpowiedzialną za dodawanie, modyfikowanie, kasowanie i wyświetlanie danych. Na razie jest bardzo ubogo :) Kolejne dwie metody również dodaję do klasy ZarządcaBazy. Oczywiście schemat ten jest bardzo uprowadzony, raczej powinniśmy stosować oddzielne klasy DAO do zadań tego typu. Taka uproszczona forma ułatwi zrozumienie samej zasady działania, potem każdy może sobie to przerobić wg własnego uznania. Dodałem metodę dodajKontakt która będzie służyła do dodawania nowych kontaktów. Metoda ta przyjmuje 3 parametry które stanową dane dodawane później do tabeli tj imię, nazwisko, telefon.
W linii 34 uzyskuję uchwyt do bazy. Korzystam tutaj z metody getWritableDatabase, ponieważ będę wprowadzał zmiany w bazie. W sytuacji gdybym chciał jedynie odczytywać dane, wywołałbym metodę getReadableDatabase. Linie 36-38 służą podstawieniu wartości do poszczególnych kolumn. Pierwszy parametr metody put to nazwa kolumny do której wstawiamy dane, drugi to wartość która ma do niej trafić. Nie muszę uzupełniać wszystkich. Swoją drogą przykładowo kolumny nr nie uzupełniam wcale. To jest kolumna typu „autoincrement”, a więc wartości w niej będą generowane automagicznie. W linii 16 wywołuję metodę insertOrThrow której przekazuję jako parametry nazwę tabeli do której wstawiane są dane, oraz listę wartości klasy ContentValues którą uzupełniałem wcześniej. Metoda ta służy do wstawienia wiersza do tabeli. W razie problemów wyrzuci wyjątek typu SQLExcepion, ale możemy go wyłapać obejmując to wywołanie blokiem try catch.
Do klasy ZarzadcaBazy dodalem tez metodę dajWszystkie. Sluzy ona pobieraniu danych z bazy.
W linii 43 zdeklarowałem kolumny jakie chcę odczytać, oraz ich kolejność. Nie muszę odczytywać wszystkich kolumn, ani też nie muszę wyciągać ich w takiej kolejności w jakiej są w tabeli. W linii 44 pobieram uchwyt do bazy. Tym razem wywołuję jednak metodę getReadableDatabase, ponieważ ie będę w bazie nic modyfikował, a jedynie czytał. Zasadniczo pobranie danych jest realizowane metodą query w linii 45. Podaję tutaj nazwę tabeli z której będę czytał dane, oraz jako parametr listę kolumn które będę pobierał. Pozostałe parametry (które są tutaj nullami) służą do stsowania warunków WHERE, Having, grupowania, sortowania etc.
Przyszedł czas na dorobienie warstwy prezentacji. Przyklejam więc na swojej głównej aktywności komponent klasy TextView:


W ramach tego przykładu, do tabeli telefony dodam z użyciem dotychczas stworzonych funkcji 3 nowe kontakty, a następnie je wyświetlę. W metodzie onCreate aktywności głównej zasadniczy najważniejszy dla nas element mieści się w liniach 18-28. Linia 18 to stworzenie obiektu zarządcy bazy danych – a więc tej klasy która jest odpowiedzialna za łączenie się z bazą i operacje na niej. Konstruktor tej klasy wymaga podania konktekstu. Tutaj jako kontekt wskazuję „this”, a więc bieżącą aktywność. Z użyciem metody „dodajKontakt” zawartej w owej klasie, dodaję trzy nowe kontakty. W dalszej kolejności chcę je pobrać z bazy i wyświetlić na ekranie. Korzystam z metody dajWszystkie, która jednak zwraca obiekt klasy Cursor. Przetwarzanie takiego kursora nie jest najwygodniejsze więc za chwilę zajmiemy się przerobieniem wszystkiego w taki sposób by metoda dajWszystkie zwracała listę obiektów np. klasy Kontakt. Krok po kroku. Na razie mamy spartańskie warunki.
W pętli while (linia 23) widzimy metodę moveToNext(), wykorzystanie tej metody pozwala nam na przesuwanie się po wyniku zapytania po linii w dół tak długo jak długo są jeszcze jakieś dane do przeczytania. W liniach 24-27 do definiowanych w pętli zmiennych przypisuję zawartość kolumn z kolejnych wierszy wg pozycji tej kolumny od lewej – stąd wywołania typu getInt(0), getString(3) – chodzi o numer kolumny od lewej strony wg kolejności jaką wymieniliśmy w metodzie dajWszystkie klasy ZarzadcaBazy. Zauważ że posługuję się też różnymi metodami w zależności od spodziewanego typu danych zwracanych z zapytania – mam na myśli metody getString i getInt. Pilnuj tego, ponieważ jeśli spróbujesz zrobić getInt lub np. getLong a w wyniku dostaniesz ciąg tekstowy , program wyrzuci wyjątek. W linii 28 zwyczajnie konkatenuję uzyskane informacje i wyświetlam je w kompononecie klasy TextView który przykleiłem tam przed momentem. Aby nie posklejało mi się to w jeden długi pozawijany ciąg – przed każdym kolejnym kontaktem doklejam „\n” czyli znak nowej linii.


Efekt :



Pokażę teraz ciekawą właściwość na bazie pewnego przykładu. Zawartość tej bazy danych będzie trwała pomiędzy kolejnymi uruchomieniami programu. Jeśli więc teraz uruchomię jeszcze raz ten program, a co za tym idzie po raz kolejny dodam trzy te same nowe kontakty, powinienem teraz zobaczyć zdublowane rekordy. Jedyne co powinno je odróżniać to identyfikatory rekordów tj. zawartość kolumny nr będącej kluczem głównym tabeli „telefony” a mającej własność „autoincrement”. Dokonuję więc drobnej kosmetycznej zmiany w kodzie:


W linii 28 dodaję jedynie wyświetlanie kolumny nr Żadnych innych zmian nie nanoszę. Efekt jest więc taki jak przewidywaliśmy:


Aby program był w jakikolwiek sposób użyteczny, oraz aby kod był przejrzysty, trzeba będzie dokonać kilku kolejnych zmian:
Pozbyć się tego paskudnego „TextView” na samym początku listy.
Dodać możliwość kasowania kontaktów
Dodać możliwość aktualizowania kontaktów
Stworzyć klasę „Kontakt” i posługiwać się obiektami tej klasy a nie listą elementów typu String.
Dorobić metody pozwalające wyciągać dane kontakty wg wskazanych kryteriów (np. numeru identyfikacyjnego, numeru telefonu czy nazwiska).
Dodać jakieś okienka umożliwiające wygodne dodawanianie, akatualizację i kasowanie kontaktów przez użytkownika.
Odseparować kod odpowiedzialny za odczyt, zapis, aktualizację i kasowanie kontaktów do osobnej klasy.
Rozwiązanie problemu nr 1. sprowadza się tylko do wyczyszczenia wyświetlanego w komponencie klasy TextView tekstu przed jego uzupełnianiem w pętli. Widzimy to w linii nr 22 :


Przy okazji widzimy że nasze 3 kontakty zostały dodane po raz kolejny ;) Z tym też musimy coś zrobić. In plus że mamy różne numery identyfikacyjne.


Dodam więc prostą metodę służącą do kasowania zbędnych rekordów.



Oczywiście bazę podpinamy w trybie do zapisu, co widzimy w linii 50. W linii 51 definiuję listę elementów argumentów dla warunków where. Musi to być lista argumentów klasy String. Myślę że najlepiej będzie to wyjaśnić na przykładzie. Przyjrzyjmy się linii 52. Pierwszy warunek to nazwa tabeli z której chcemy kasować wiersze. Drugi to warunki które mają trafić po klauzuli where. Samego where już tutaj nie wymieniamy. Zamiast wartości liczbowych czy tekstowych które miałyby się pojawić w warunkach, stawiamy znak zapytania. Gdybyśmy mięli więcej niż jeden parametr, to ten ciąg tekstowy mógłby wyglądać np. tak: „department_id=? and manager_id=? and salary>?”. System oczekiwałby w takiej sytuacji podania mu trzech wartości które trafią pod nasze znaki zapytania. Listę tychże właśnie wartości podaję w postaci kolekcji String zdefiniowanej w linii 51 poprzez trzeci parametr metody delete. W moim przypadku lista składa się z całego jednego elementu który na dodatek w sposób mało elegancki, aczkolwiek popularny rzutuję na typ String. Czyli podsumowując:
Argument nr 1 to nazwa tabeli z której kasujemy
Argument 2 to warunki dla klauzuli where
Argument 3 to lista wartości które mają trafić do parametrów warunków where określonych jako argument nr 2 metody delete :)
Przetestujmy teraz działanie naszej nowej metody.


Troszkę przerobiłem kod naszej głównej aktywności. Linie od 19 do 21 odpowiedzialne za dodawanie nwych wierszy po prostu wykomentowałem, za to 2 liniach 25-27 dodałem kasowanie wszystkich kontaktów o identyfikatorach od 4 do 9. Wyświetlanie kontaktów pozostało takie jakie było na początku. Aktualny stan :


Przydałaby się teraz metoda pozwalająca na modyfikowanie wpisów w bazie.


Troszkę przemieściłem metody w klasie „ZarzadcaBazy”, ponieważ nowo dodana metoda „aktualizujKontakt” ma parę elementów wspólnych z wcześniej napisanymi metodami „kasujKontakt” i „dodajKontakt”. W linii 49 określiłem jakie parametry przyjmuje metoda. Pierwszy to identyfikator wiersza (unikalny numer z kolumny nr) wg którego będę wybierał wiersz do aktualizacji. Pozostałe to wartości do aktualizacji. Przyjąłem że nie będę rozgraniczał aktualizji poszczególnych kolumn , robił osobnych metod ani skomplikowanych bloków warunkowych żeby aktualizować tylko te faktycznie zmienione kolumny. Po najmniejszej linii oporu. Zakładam że po prostu zczytam wiersz, zmienię jedno czy więcej pól i przekażę do tej metody tak by zaktualizowała wszystkie kolumny, niezależnie od tego czy faktycznie coś się w nich zmieniło czy nie.
Podobnie jak działo się to przy dodawaniu nowego wiersza, tak i tutaj w liniach 51-54 podaję listę wartości które mają trafić do poszczególnych kolumn. Są to wartości które ulegają zmianie – te które podaję przez parametry metody. Tak jak i wcześniej metoda put przyjmuje jako argumenty nazwę kolumny w której aktualizujemy wartość, oraz wartość dla tej kolumny. Podobnie jak w przypadku metody kasujKontakt, tak i tutaj w linii 55 wartości które zostaną podane jako argumenty warunku where. W linii 56 to już wywołanie metody update która przyjmje jako parametry kolejno: nazwę aktualizowanej tabeli,zaktualizowane wartości kolumn, warunek where, wartość lub wartości które mają trafić do warunku where w miejsce znaków zapytania.
Przerabiam teraz główną aktywność tak, aby przed wyświetleniem wywołać metodę aktualizującą i zmienić numer telefonu Dziadka Mroza:




Efekt:



Oczywiście programistyczni puryści podniosą zarzut, że jakto tak z palca podawać wszystkie wartości włącznei z tymi nie zmienianymi, że te dane powinny być pobrane z bazy , zwrócone przez jakąś metodę w postaci obiektu, a następnie po zmianie wartości wybranego pola przekazane jako obiekt do metody aktualizującej w bazie. I jak najbardziej będą mięli rację :) Z tym, że aby to osiągnąć potrzebujemy najpierw przerobić to na troszkę bardziej obiektową formę, oraz stworzyć metodę zwracającą wybrany wiersz wg identyfikatora :). Zacznijmy więc od zdefiniowania klasy która będzie reprezentować pojedynczy wiersz. Starałem się tak definiować żeby pola w klasie odpowiadały nazwom kolumn w tabeli:



Jest taka dobra praktyka programistyczna, by pola w klasach typu POJO nie były publiczne, a prywatne, natomiast dostęp do nich uzyskiwać poprzez tak zwane gettery i settery. Dzięki temu mamy większą kontrolę nad zawartością tych pól. W środowisku Eclipse jest nawet specjalna funkcja do automatycznego generowania tego typu metod. Wybieramy z menu dostępnego pod prawym przyciskiem myszki:



Gdy pojawi się okno, zaznaczamy wszystkie opcje:


i klikamy dostępny na dole przycisk „OK”. W tej chwili nasza klasa powinna wyglądać mniej więcej tak:



Stworzę teraz w naszym ZarzadcyBazy metodę odpowiedzialną za pobieranie z bazy i oddawanie wiersza w postaci obiektu nowo stworzonej klasy Kontakt. Dodaję do klasy ZarzadcaBazy metodę dajKontakt. W linii 67 tworzę na razie pusty obiekt klasy kontakt, który po uzupełnieniu zostanie z metody zwrócony. W linii 68, tak jak i w każdym dotychczasowym przypadku podpinam zaczep do bazy. W linii 69 definiuję kolumny które będę czytał. Widzimy że analogiczny fragment pojawia się po raz kolejny, trzeba więc będzie go odseparować w przyszłości. Linia 70 to tak jak i wcześniej podstawienie wartości która trafi pod znak „?” w warunku where. Linia 71 to zasadnicze wykonanie zapytania. Argumenty odpowiedzialne za grupowanie, warunek having, sortowanie i limitowanie ilości zwracanych wierszy (wg wymienionej kolejności) pozostawiam nullowe, ponieważ żadnej z tych operacji nie będę wykonywał. Jeśli w wyniku zapytania zostanie zwrócony przynajmniej jeden wiersz (co sprawdzam w linii 72), przechodzę do czytania danych z pól wiersza i uzupełniania obiektu. W linii 73 przechodzę do pierwszego (i w tym przypadku jedynego) wiersza. Linie 74-77 to znane już z wcześniejszych przypadków czytanie danych, tym razem jednak nie przypisuję ich do zmiennych, a przy użyciu setterów uzupełniam obiekt klasy kontakt. W linii 79 uzupełniony już (miejmy nadzieję – mogliśmy przecież poszukiwać kontaktu po numerze który w tabeli nie występuje) obiekt klasy kontakt zwracam z metody. Cała metoda:



Aby przetestować nową metodę przerabiam ponownie główną aktywność:



Efekt:



Przerobię teraz pozostałe metody z klasy ZarzadcaBazy, tak by całość była jako tako obiektowa :)
Zacząłem od zmiany metody dodajKontakt. Zamiast przyjmować wartości pojedynczych pól jako osobne argumenty metody, przekazuję po prostu jeden obiekt klasy Kontakt, który zostaje dodany do bazy. Jeszcze jedna drobna różnica jest widaczna w liniach 36-39. Zamiast korzystać z osobnych zmiennych reprezentujących wartości do poszczególnych pól, po prostu wydobywam niezbędne dane z obiektu, z użyciem zdefiniowanych w klasie Kontakt getterów.




W podobny sposób przerobiłem metodę aktualizującą wiersze:


Bardzo nie podoba mi się również dotychczasowe rozwiązanie pobierania wszystkich kontaktów:


Metoda zwraca obiekt klasy Cursor, którego przetwarzanie nie należy do najwygodniejszych. Tą metodę przerobię też tak, by zwracała listę (klasy LinkedList) obiektów.
Po paru poprawkach metoda dajWszystkie działa tak jak sobie tego życzyliśmy. Sposób pobierania danych jest taki sam, jedynie dodałem w liniach 67-73 przetwarzanie kursora i ładowanie danych do przygotowanej wcześniej (w linii 63) listy kontaktów.


Przetwarzanie takiej listy jest później dużo wygodniejsze. Poniżej wykorzystanie nowej wersji metody dajWszystkie w głównej aktywności:



Przy okazji pojawia się nam tutaj nowa metoda: d klasy Log. Wywołuje się ją w taki sposób:
Log.d(„tag”,”text”); a efekt wygląda tak:

Sprawdza się to zwłaszcza w zastępstwie System.out.println którego może nieco brakować programistom Javy ;)

Taki jeszcze drobny, acz istotny element. W tych przykładach tego nie wykorzystamy, ale może nastąpić konieczność pogrupowania, zastosowania klauzuli having, sortowania, czy limitowania ilości wierszy w zapytaniu. Trzeba odpowiednio podać parametry w wywołaniu metody query. Najlepiej będzie pokazać na przykładzie. Gdybym zechciał np. odebrać wynik takiego zapytania:

SELECT AVG(SALARY) , DEPARTMENT_ID FROM EMPLOYEES GROUP BY DEPARTMENT_ID ORDER BY DEPARTMENT_ID DESC;

To wywołanie metody query musiałoby wyglądać tak:
SQLiteDatabase db = getReadableDatabase();
String[] kolumny={"avg(salary) as srednia","department_id"};
Cursor kursor = db.query("telefony",kolumny,null,null,”department_id”,null,
”department_id desc”);

Parametry wg kolejności :
1. Nazwa tabeli z której czytamy
2. Kolumny które czytamy
3. Warunki where
4. Wartości do warunków where
5. Kolumna/kolumny po których grupuję
6. Warunek do klauzuli having
7. Sposób sortowania

Tutaj już każdy może uznać czy chce taką postać wykorzystywać czy nie. Moim zdaniem to średnio wygodne i mało intuicyjne. Poza tym, co jeśli chciałbym stosować złączenia między tabelami, podzapytania etc? Zamiast takiej nieco abstrakcyjnej formy wywoływania zapytań z użyciem metody query, możemy wykorzystać metodę rawQuery. Do naszego zarządcy bazy dopisałem metodę wyciągającą kontakty o nazwisku które przekazuję jako parametr.


W przypadku metody rawQuery, nie używamy żadnych dodatkowych parametrów do określania grupowania, czy sortowania. Wszyskie tego typu rzeczy określamy w samym zapytaniu SQL bez żadnych udziwnień. Jak widać w liniach 98-100 potrzebny parametr zwyczajnie dołączyłem na zasadzie sklejania tekstu, warunek WHERE i sortowanie określone jest w samym SQL. Zdecydowanie preferuję tę formę, jako bardziej przyjazną :) W liniach 101-105 wstawiłem też zakomentowaną alternatywną postać tego samego wywołania. W tym przypadku zamiast sklejać zapytanie, posłużyłem się znakami zapytania i podstawiam wartości które pod te znaki zapytania mają trafić w trakcie wykonania z listy podanej jako drugi parametr metody rawQuery (tutaj lista jest po prostu jednym elementem typu String). Samo pobieranie danych z takiego kursora przebiega dokładnie tak jak wcześniej. Ponieważ wartości z kolejnych kolumn pobieram podając jej numer zaczynac liczyć od 0 od lewej strony, nie muszę nawet robić żadnych aliasów dla kolumn które wynikają np. z działania funkcji.

31 komentarzy:

  1. Fajnie, ale jak już piszesz taki kurs, to przydałoby się uczyć początkujących dobrych praktyk. Chodzi mi o rawQuery. To "alternatywne" wywołanie powinno być na pierwszym miejscu, a pierwsze opisane jako błędne.

    OdpowiedzUsuń
    Odpowiedzi
    1. Niby czemu błędne. RawQuery jest tylko używane w szczególnych przypadkach, bo jest wrażliwe na różne injectiony. W Query masz juz zaimplementowane zabezpieczenia.

      Usuń
    2. Ponieważ elementy są autoincementowane przez baze danych która zwyczajnie zwiększa swoją wartość przy każdym dodawaniu nowego id. Jest to głupia metoda sql'a

      Usuń
  2. Dlaczego nie jest wołany close() na kursorze?

    OdpowiedzUsuń
    Odpowiedzi
    1. O to samo chcialem zapytać

      Usuń
    2. To może jakaś mała modyfikacja kursu albo dopisek jak ten kursor zamknąć i po co to robić ?

      Usuń
  3. Ja tam początkujący androidowiec jestem, a tak wogóle to nie początkujący nieandroidowiec... ale nasuwa mnie się pytanie, czy android dba o gubione obiekty w pamięci lub te nadpisywane referencje?hmmm... nic o tym nie było na razie...

    OdpowiedzUsuń
  4. Gdzie ta nowa klase mam stworzyc w projekcie? W osobnym pliku? Jak to zrobic w android studio lub eclipse?

    OdpowiedzUsuń
    Odpowiedzi
    1. Klikasz prawym klawiszem myszy na nazwie projektu i wybierasz New -> Class i potem wpisujesz nazwę klasy i już możesz działać ;)

      Usuń
  5. A jak przetestować tą metodę dajWszystkie() albo dajPoNazwisku() ? Tak żeby nie używać opcji Log.d tylko żeby nam to wyświetliło normalnie w aplikacji po uruchomieniu?

    OdpowiedzUsuń
    Odpowiedzi
    1. umieszczasz np TextView podczas wywolania przechwytujesz go
      TextView tv1 = (TextView) findViewById( R.id.textView1 );
      a potem tv1.setText( tv1.getText() + "\n" + zmienna)

      Usuń
    2. Dzięki za odpowiedz, ale dalej ona nie jest wystarczająca ;) mamy metodę dajWszystkie() która zwracam nam obiekt kontakty który nie wiem jak obsłużyć w mainie. Zrobiłem jak piszesz, ale nie wiem co wpisać w "słowo" zmienna. Mam takie coś:
      TextView tv1 = (TextView) findViewById( R.id.textView1 );
      tv1.setText( tv1.getText() + "\n" + zb.dajWszystkie());
      Ale to zwraca mi tylko te znaki []

      Usuń
    3. Mam ten sam problem. A ten sposób który został podany w tutorialu nie wyświetla danych :C

      Usuń
    4. @Anonimowy18 lipca 2014 10:03
      Bo konkatenujesz aktualnie wyświetlany tekst i listę. W dodatku wygląda na to że pustą. Jeśli bierzesz wszystkie to musisz to puścić w pętli for i wykonać doklejenie dla każdego z wpisów.
      Jak dostać się do wartości z tych obietków? Są 2 rozwiązania.
      1. Do każdej z własności za pomocą getera.
      2. Napiszcie sobie metodę toString w swojej klasie i wywołujcie xxx.toString()

      @Override
      public String toString() {
      return "co tam chcesz";
      }

      Usuń
  6. A mam takie pytanko: Czy jest takie rozwiązanie aby android pracował jako klient bazy firebirdowej lub innej? (Oracle, PostgreSQL; np. podłączał się do bazy na serwerze po wifi?)

    OdpowiedzUsuń
  7. Przy wersji 3.
    Jak wygląda dodawanie kontaktu w klasie MainActivity?
    Jakiś prosty przykład: np dodać wiersz: Jan Kowalski 58-99-99-99
    Dzięki!

    OdpowiedzUsuń
  8. Jestem na etapie dodawania klasy "konkakty" i co bym nie zrobił zawsze wywala mi błąd tutaj:
    public Kontakt dajKontakt(int nr){
    Kontakt kontakt = new Kontakt();
    SQLiteDatabase db = getWritableDatabase();


    i nie widzi mi nic konkretnego przy getWritableDatabase(); jedyne co mogę zrobić, to stworzyć taką metodę, ale to chyba nie o to chodzi.
    Druga sprawa to nie rozumiem tego:
    Kontakt k = zb.dajKontakt(3);
    jak to ma działać, skoro klasa ZarządcaBazy nie ma takiej metody jak dajKontakt?
    (tutaj oczywiście android też mi wywala błąd)

    OdpowiedzUsuń
  9. jednak źle przeczytałem, ale dodałem już tą metody do ZarzadcyBazy

    OdpowiedzUsuń
  10. mam problem podczas debugowania wyrzuca mi info że source not found co z tym zrobić

    OdpowiedzUsuń
  11. Jak Dodać Kontakt ? co trzeba napisać w main ?

    OdpowiedzUsuń
  12. dlaczego gdy mam 5 elementów i usunę 2 elementy i gdy dodam 1 to przypisuje mu wartość 6 a nie 4 ?

    OdpowiedzUsuń
  13. Pytanie jak jeszcze zrobić wyszukiwarkę, aby można było wyszukać dany obiekt po jednym z atrybutów?

    OdpowiedzUsuń
  14. Mam pytanie, w jaki sposób po każdym kliknięciu czyścić wcześniejszą zawartość. Załóżmy, że chciałbym przy następnym kliknięciu przycisku, otrzymać te same kontakty ale w losowej kolejności.

    OdpowiedzUsuń
  15. Proszę poprawić wording - "Klasa ta będzie służyła do tworznia bazy danych"

    OdpowiedzUsuń
  16. Witam jestem poczatkujacym w jaki sposob moge dodac kontakt??

    OdpowiedzUsuń
  17. po użyciu metody do usuwania elementu nie resetuje się numer po autoinkrementacji, przykładowo dodam nowy wiersz który dostanie numer 10, usunę go i ponownie dodam nowy wiersz i zamiast 10 otrzymuje 11. Jak to obejść? jest jakaś metoda usuwania ?

    OdpowiedzUsuń
  18. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń
  19. A da rade podpiąc dajWszystko() aby wyswietlalo sie w ListView przez arrayAdapter albo jakis Cursor adapter ? Jeżeli tak to czy mógłbym prosic o pomoc przy tym, ewentualnie jakias literaturę? bo na przykladach internetowych polegam...

    OdpowiedzUsuń
  20. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń
  21. Witam, moją aplikacje uruchamiam na fizycznym urządzeniu, jest możliwość podejrzenia mojej bazy danych?
    Widziałem, ze jeżeli ktoś uruchamia apkę na urządzeniu wirtualnym, to można pobrać plik bazy poprzez Android Device Monitor i tam odnaleźć go w folderze data...
    Ja używając fizycznego urządzania nie mam takiej możliwości.
    Jest jakiś sposób aby podejrzeć moją bazę używając fizycznego urządzenia.
    Pozdrawiam
    Adrian

    OdpowiedzUsuń
    Odpowiedzi
    1. Nie masz bo nie masz roota na telefonie. Bazę możesz podejrzeć za pomocą SQLiteOnWeb (https://github.com/skyhacker2/SQLiteOnWeb-Android) lub za pomocą ADB Shell (https://stackoverflow.com/questions/18370219/how-to-use-adb-in-android-studio-to-view-an-sqlite-db)

      Usuń