Preview
- Caratteristiche principali =>
- Nato nel 2011 grazie a JetBrains (Praga – 2000)
- IntelliJ IDEA (IDE per Java)
- Statically-typed language
- Se definisco una variabile string non posso dargli un valore boolean (per esemipo). Infatti una volta lanciata la compilatore riceverei un errore.
- Kotlin permette in questo modo di scrivere programmi più affidabili.
- Compilazione
- Bytecode =>
- Eseguito in una JVM (Java virtual machine).
- Utilizzato quindi lato server (es: il backend di un sito web)
- Javascript =>
- Utilizzato quindi lato client
- Android
- Bytecode =>
- Differeneze con Java
- Più sicuro perché capta una serie di errori direttamente alla compilazione
- Più conciso
-
//Nuova classe data class Person(var name: String, var age: Int)
-
- Compatibile
- Con tutte le versioni di Android.
- Con quasi tutte le librerie Java esistenti.
- E’ possibile nella stessa applicazione scrivere delle pagine in Java e in Kotlin
- Ufficialmente supportato da Google.
- Supporta Android Studio.
- Nato nel 2011 grazie a JetBrains (Praga – 2000)
- Installazione (su Windows) =>
- Android Studio (developer.android.com)
- Android SDK
- Emulatore
- Intel x86 Emulator Accelerator (HAXM installer)
- USB Driver (per connettere il telefono al computer)
- Android Studio (developer.android.com)
- Interoperabilità con Java
- Da un file kotlin é possibile chiamare una classe Java et viceversa
IDE
- Shortcut
- Ctrl+Shift+A (Apre una finestra per trovare tutti i differenti shortcut offerti dall’IDE)
- Tab
- Logcat (equivale a output in Visual Studio)
- Profiler
- Mostra l’utilizzo di risorse nel device connesso dove la nostra applicazione é stata deploiata e eseguita.
- Emulatore Android
- E’ un programma che si esegue sulla macchina di sviluppo e replica un sistema android.
- Permette il deploy dell’applicazione da installare più rapida.
- L’emulatore permette di:
- Testare l’applicazione su differenti ‘form factors‘ (dimensioni del monitor).
- Testare l’applicazione su differenti versioni di Android.
- Creare nuovo emulatore
- Proprietà
- Nome
- Play Store (si o no)
- Tips:
- Ccegliere un’immagine che non contiene il Google ‘Play Store’ se si vuole avere un’ampia capacità di configurare il proprio emulatore.
- Nelle immagini con il Google ‘Play Store’ disponibile le configurazioni possibili sono più limitate.
- Tips:
- Dimensione del monitor
- Risoluzione
- Tips: puo’ essere utile selezionare una risoluzione simile a quella del computer di sviluppo per vedere meglio).
- Densità
- Immagine di sistema
- Scegliere quale versione di Android usare nell’emulatore che stiamo creando.
- Proprietà
- Nome
- API Level (nome tecnico della versione Android)
- ABI (Application Binary Interface)
- Architettura del processore sul quale l’emulatore viene eseguito.
- es: x86 o x64.
- Tips : puo’ essere utile usare la stessa architettura del computer di sviluppo
- Nome commerciale della versione Android target.
- Proprietà
- Connettere un device fisico
- 1 – Trovare la versione Android presente nel device fisico che si vuole usare
- 2 – Sbloccare la modalità sviluppatore
- 3 – Cliccare più volte sul ‘Parametri’ => ‘Sistema’ => ‘Informazione sul telefono’ => ‘Build Number’
- Voce di menuù ‘Opzioni di sviluppo‘
- Nel menù ‘Parametri’ ora deve esserci un’opzione menu’ in più
- 4- Attivare Debug USB
- 3 – Cliccare più volte sul ‘Parametri’ => ‘Sistema’ => ‘Informazione sul telefono’ => ‘Build Number’
Linguaggio Kotlin
- Dichiarazione variabili =>
-
var age: Int = 45 var name: String = "John" var height: Float = 1.60F
- Number (variabile generica di tipo numberico che puo’ essere una qualsiasi dei tipi seguenti)
- Byte (-128, +127)
- Short
- Int
- Long
- Float (es: 42.5F)
- Double (es: 42.5)
- Char (es: ‘a’)
- String (es: “Helllo”)
- String template simple
-
//scrivo nella console (tab 'Run' dell'IDE) println("String template simple : $name ha $age anni")
-
- String template complex
-
//!! Posso fare delle operazioni all'interno delle parentesi graffe !! println("String template complex : ${name.uppercase()} ha ${age*2} anni")
- Raw string (“””)
-
//E' possibile scrivere del testo su più linee //Devo stare attento agli spazi che metto perché saranno visibili nel mio testo println("""String template complex: Name : $name Age : $age""")
-
- Raw string tim (“”” |)
-
trimMargin() //Usando il simbolo pipe (|) per ogni nuova riga // risolvo il problema degli spazi vuoti usati solo per indentare meglio println("""String template complex trim: |Name : $name |Age : $age |""".trimMargin()) marginPrefix /posso customizzare il carattere che indica l'inizio della nuova riga della mia stringa raw println("""String template complex trim: >Name : $name >Age : $age >""".trimMargin(">"))
-
-
- String template simple
-
- Operatori logici
- Operatore di confronto valore (==)
-
//contronto tra valori //diverso da Java if(name == "bob'){ } else if (){ } else { }
- Operatore di confronto referenza (===)
-
//contronto tra indirizzi di memoria if(name === "bob'){ }
- Operatore ternario
-
val type = if (age < 18) "child" else "adult"
-
- When (lo switch di C#)
- Permette il pattern matching
-
when(age) { 5 -> 10 -> else -> } oppure when { age == 5 -> height >= 1.50f -> else -> } oppure when(age) { in 1..5 -> println("${name} é troppo giovane") in 6..10 -> println("${name} puo' giocare a basket") !in 1..18 -> println("${name} non puo' giocare con i giovani") else -> println("condizione non gestita") } var canPlayBasketball = when (age) { in 1..5 -> true else -> false }
- Mutabilità di una variabile
- var => variabile puo’ cambiare valore.
- val (final in Java) => la variabile NON puo’ cambiare valore.
- Permette di rendere il codice più robusto
- Nullabilità (?)
- Per poter dare il valore null ad una variabile é necessario dichiararla con il punto ? (come in C#)
-
var name: String? = "Bob" name = null //Come usare una variabile null //1 - Fare un test se la variabile é nulla if(name != null) { println(name.length); } //2 - Operatore safe call (come C#) //l'applicazione non dà errore nel caso name=null println(name?.length); //3 - Operatore assert not-null (simile C#) //posso forzare il compilatore a ignorare il caso NULL => la variabile avrà sicuramente un valore //l'applicazione dà errore nel caso invece name=null println(name!!.length);
- Funzioni
- E’ possibile dichiarare delle funzioni fuori di una classe
-
fun name(age : int, p2...): TypeRetour { } //senza ritorno fun name(age : int, p2...) { }
- Lambda expression (=)
-
//Se la funzione ha una sola linea é possibile utilizzare una sintassi simile alle labda expression di C# //Mettendo = al posto di => e senza usare la parola chiave return fun CanPlayBasket(age: Int) : Boolean = (age>15)
-
- Array
- L’indice di base é 0
-
Definire un array di taglia fissa //Metodo 1 - Uso il costruttore 'Array' val ages = Array<Int>(10) { 0 } //0, 0, 0, 0, 0, 0, 0, 0, 0, 0 //Metodo 2 - Uso la factory 'arrayOf' //Da usare se si conosco già tutti gli elementi dell'array //ho definito un array di interi lungo 3 val ages = arrayOf(4, 7, 15) //4, 7, 15 // Recuperare un valore var myage = ages.get(index) // Modificare un valore ages.set(index, value) oppure ages[index} = value //Display tutti gli elementi di un array Arrays.toString(myArray)
- For
-
//senza indicare esplicitamente step, il contatore avanza di 1 for (i in 1..5) { println(i) //1, 2, 3, 4, 5 } //step //nell'esempio il contatore avanza di 2 for (i in 1..5 step 2) { println(i) //1, 3, 5 } //downTo //nell'esempio il contatore indietreggia di 1 for (i in 5 downTo 1) { println(i) //5, 4, 3, 2, 1 } //downTo + step //nell'esempio il contatore indietreggia di 2 for (i in 5 downTo 1) { println(i) //5, 3, 1 }
-
- For each / continue (come C#)
-
for(item in colleciton) { if(condition) continue } var name = arrayOf("Bob", "John", "Rosy"); for((index,name) in names.withIndex()) { println("$name é all'indice $index") }
-
- While / Break (come C#)
-
while(condition) { .. } do { .. } while(condition)
-
Package
- Una serie di file contenuti in una stessa cartella
- Creare un nuovo package (verrà creata una cartella con il nome scelto)
- Aggiungere a tale package i file desiderati (i file verranno aggiunti fisicamente alla cartella constituente il package)
- File
- Class
- Dichiarazione
-
//aggiungo un file (package) con delle funzioni utility da usare negli altri package //questo file lo metto in una cartela 'common' package com.example.helloword.nico.common
-
- Visibilità
-
package com.example.helloword.nico.common //Caso 1 (default) //name é visibile in tutti gli altri package (file sorgente) public val name = value //Caso 2 //name é visibule in tutti gli altri package ma non puo' essere modificata public val name = value private set //Caso 3 //name é visibile solo nel file dove é dichiarata private val name = value
-
- Import
-
//Da un altro file devo importare la variabile definita nel file common //Import specifico Import package com.example.helloword.nico.common.name //Import global (importo tutti gli elementi public presenti nel package commmon) Import package com.example.helloword.nico.common.*
-
POO
- Class
-
class MyClass(val age: Int = 10, val name: String){ fun MyMethod(){ } } var myClass = MyClass(20, "John")
-
- Costruttore
-
//Costruttore primario class MyClass(val age: Int = 10, val name: String) //Blocco di inizializzazione class MyClass(val age: Int = 10, val name: String) { var myVar.. init { ... myVar } } //Costruttore secondario (deve chiamare il costruttore primario) class MyClass(val age: Int = 10, val name: String) { constructor(a: Int) : this(a, 10) }
-
- Ereditarietà
- Utile per estendere la funzionalità di una classe esistente
- Open (virtual in c#)
- Le classi sono di default chiuse alla ereditarietà
- Usare ‘open’ per rendere una classe ereditabile
- Usare ‘open’ per poter fare l’override di un metodo
- Una classe figlio puo’ derivare solo da una classe padre
-
//classe padre open class Parent(val var1: Int){ open fun myMethod(){ println("Hello from parent"); } } //classe figlio class Child : Parent(4){ override fun MyMethod(){ println("Hello from child"); } }
- Interfacce
- Utile per dare accesso a un comportamento predefinito.
- Possono definire dei metodi al loro interno (come nelle ultime versioni di C#)
-
interface IFeul { var fuelGauge: Double fun fillGasTank(){ println("Riempio il serbatorio") fuelGauge = 100.0 } } class Car: IFuel { override fuelGauge: Double = 0.0 } class Motorcycle: IFuel { override fuelGauge: Double = 0.0 override fillGasTank() { super.fillGasTank(); honk(); } fun honk(){ ... } }
- Verifica del tipo (come C#)
-
var vehicle: Vehicle = Car() //smart-cast if(vehicle is Car){ //il compilatore riconosce il controllo if che ho fatto //quindi la variabile vehicle si comportamento necessariamente e implicitamete come Car } //usafe-cast //se non é possibile fare il cast di vehicle alla classe Car //allora a runtime ricevero' un errore var jeep: Car = vehicle as Car //safe-cast //se non é possibile fare il cast di vehicle alla classe Car //allora a runtime NON ricevero' un errore ma jeep sarà null var jeep: Car? = vehicle as? Car
-
- Data class (non esiste in JAVA)
- Una classe che contiene solo dati
- Non accetta gli access modifier (open, abstract,…)
- Kotlin genera in automatico i metodi
- equals() => compara 2 oggi della stessa classe di tipo data verificando se sono uguali (compara ogni variabili della classe)
- hashCode() => genera un identificatore unico per la nostra istanza della classe di tipo data
- toString()
- copy() => fa una copia dell’oggetto partendo dai valori forniti
- componentN()
-
data class User(val name: string, val age: Int)f
- Nested class
- la classe ha NON accesso ai membri della classe contenitore
-
class Outer(val age: Int){ class Nested(){ fun foo() = 42 } } //non ho bisogno di inizializzare la classe Outer val temp = Outer.Nested().foo() //42
- Inner class
- la classe interna ha accesso ai membri della classe contenitore
-
class Outer(val age: Int){ inner class Inner(){ fun foo() = age } } //devo inizializzare la classe Outer val temp = Outer(42).Inner().foo() //42
- Generic
- Permette di applicare lo stesso comportamento a classi differenti
- es: stoccare degli elementi in una lista accettando diversi tipi di variabili (Int, String, MyClass…)
-
//metodi generici fun<T> myFunction(var1: T){ .. } //classi generiche class MyClass<T>(val var1: T) val instance = MyClass<Int>(5) val instance = MyClass<String>("my_text") oppure //posso limitare il tipo generico da usare class MyClass<T: string>(val var1: T) val instance = MyClass<Int>(5) val instance = MyClass<String>("my_text")
- lateinit (lazy loading)
- Permette di fare una inizializzazione tardiva di una variabile
- La variabile lateini deve essere membro di una classe.
- Non deve essere dichiarata nel costruttore della classe.
- La variabile non puo’ essere di un tipo primitivo.
-
class User(val name String, val age: Int){ lateinit var nickename: String } val bob = User("bob"; 10) bob.nickname = "MJ" println("l'utente ${bob.Name} é soprannominato ${bob.Nickname}"); Attenzione: le variabili lateinit DEVONO essere inizializzate prima che siano utilizzate per non avere un errore.
- Companion object (Singleton)
- Non esiste static in Kotlin
- Permette la retro compatibilità con Java
- Simplifica la dichiarazione di un singleton
- Ogni istanza della mia classe ha a che fare con lo stesso ‘comanion oggetto ‘
-
class MyClass { companion object { val MY_CONSTANT: Int = 42; } }
Android
- Componenti principali di un progetto Android
- Dossier java
- Cartella con i file sorgente veri e propri ((Java, Kotlin, C++).
- Cartella con i test da lanciare sul device.
- Cartella con i test unitari da eseguire sulla macchina di sviluppo.
- Dossier res
- Layout
- File di layout (descrive interfaccia di una schermata).
- Values
- File di traduzione.
- File di configurazione dello stile dell’applicazione.
- Cartella con le immagini.
- Layout
- AndroidManifest.xml (carta d’identità dell’applicazione)
- Dossier java
-
-
- Definisce il punto d’entrata dell’applicazione.
- Definisce le permssion richieste dalla nostra appli.
- Definisce le ‘activity’ che costituiscono la nostra applicazione.
- Build.gradle =>
- Alcuni parametri di configurazione che vanno a sovrascrivere certi presenti nel manifest.xml.
- Direttive di compilazione.
- Release
- Debug
- Lista delle dipendenze della nostra applicazione.
-
- Activity
- 1 attività = 1 schermo
- Divisa in 2 parti
- Comportamento (Kotlin)
- Creare una classe che definisce la mia ‘activity’. Tale classe deve
- Derivare da AppCompatActivity o Activity.
- Chiamare il metodo setContentView(<activity.xml>) per legare la classe all’interfaccia grafica indicata.
- Essere aggiunta al file manifest.xml.
-
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //indica quale file xml é legato a questo componente } val helloWorldTextViez : TextView = findViewById<TextView>(R.id.helloWorldText) oppure //per avere questa sintassi installare le Kotlin Android Extensions textView.setText("Hello world!); }
- Kotlin Android Extensions
- Permettono tra l’altro di richiamare i differenti componenti di una schermata con una sintassi semplificata.
- build.gradle (Module:app)
-
//per poter usare le Kotlin Android Extensions //aggiungere questo plugin al file build.gradle e rilanciare una sincronizzazione plugins { id 'kotlin-android-extensions' }
-
- Creare una classe che definisce la mia ‘activity’. Tale classe deve
- Interfaccia grafica (Xml)
- Comportamento (Kotlin)
- Ciclo di vita complesso
-
-
- Deve interagire con molte componenti
- Esempi
- Rotation d’ecran => Girando lo schermo, l’activity passa ad uno stato diverso ridisegnandosi.
- Bottone Home => L’activity deve essere messa in ‘pausa’ nel caso l’utente che ha cliccato su ‘home’ ritorni sulla nostra ‘activity’.
- Bottone Return => Quando si clicca sul bottone back si torna allo schermo precedente o si esce dall’applicazione.
- OS Android
- Crea le istanze delle activity.
- Avverte l’applicazione dei differenti cambi di stato e che gestisce il ciclo di vita di un ‘activity’.
-
- Backstack
-
- Sequenza delle ‘activity’ eseguiti in un’applicazione secondo un ordine FIFO.
- Ogni nuova ‘activity’ eseguita si pone in cima alla pila. E’ la sola visibile e quando l’utente uscirà da tale attività sarà la seconda ad essere visualizzata.
- Per ogni applicazione Android tiene in memoria il backstack relativo.
- logcat
-
- Esiste la possibilità di usare 6 livelli di log
- Intent
- Classe usata per fare comunicare moduli Android tra di loro
- Esprime un’intenzione
- Aprire un’activity
- Inviare un messaggio ad un’altra applicazione
- Azioni di Android
- action
- ACTION_VIEW
- ACTION_EDIT
- data (uri)
- numero di telefono (tel:123)
- contatto (content://contacts/people/1)
- category
- CATEGORY_LAUNCHER per eseguire l’attività principale dell’applicazione
- CATEGORY_BROWSABLE per aprire un link web
- extras (bundle)
- Int, Float; String
- Oggetti complessi
-
//classe chiamante val intent = Intent(this, MyActivity) intent.action = Intent.ACTION_VIEW intent.addCategory("UserViewer") intent.putExtra("name", "Bob") intent.putExtra("age", 10) //classe chiamata val action = intent.action //android.intent.action.VIEW val isUser = intent.hasCategory("UserViewer") //true val extras: Bundle = intent.extras val name = extras.getString("name") //Bob val age = extras.getInt("age") //10
- Parcelable =>
- Serializzazione/Deserializzazione
- Una volta creato un oggetto di tipo parcelable sarà Android che si incarica di gestirlo
- Invio di un oggetto parcelabe (tra 2 activity o 2 applicazioni):
- L’oggetto deve derivare l’interfaccia Parcelable
-
class MyClass : Parcelable { ... }
-
- Nella ‘activity’ del mittente é necessario metere l’oggetto da inviare come l’extra di un Intent
-
intent.putExtra("mykey", myClass)
-
- Nella ‘activity’ del destinatario l’ogetto inviato verrà letto dall’intent
-
intent.getParcelableExtra<MyClass>("myKey")
-
- L’oggetto deve derivare l’interfaccia Parcelable
-
//Esempio di classe parcelable data class User(val name: String?, val age:Int) : Parcelable { //usato del destinatario per deserializzare l'oggetto User constructor(parcel: Parcel) : this( //l'ordine é importante (uguale a writeToParcel) parcel.readString(), parcel.readInt() ) //usato del mittente per serializzare l'oggetto User override fun writeToParcel(parcel: Parcel, flags: Int) { //l'ordine é importante (uguale a constructor) parcel.writeString(name) parcel.writeInt(age) } override fun describeContents(): Int { return 0 } //Permette di creare gli oggetti User a partire dell'oggetto parcel ricevuto come parametro //companion = static companion object CREATOR : Parcelable.Creator<User> { //singolo User override fun createFromParcel(parcel: Parcel): User { return User(parcel) } //lista di User override fun newArray(size: Int): Array<User?> { return arrayOfNulls(size) } } } //classe mittente //Invio di User come parcelable val user = User("Bob", 10) findViewById(R.id.show_user_detail_button).setOnClickListener { val intent = Intent(this, DestinationActivity::class.java intent.putExtra("user", user) startActivity(intent) } //classe destinatario //Ricezione di User val user = Intent.getParcelableExtra<User>("user") val name = user.Name val age = user.Age
Gestione del layout
- Usare più unità di misura
- dp
- Usato per tutti gli elementi tranne che per i testi.
- E’ la scelta migliore per avere la massima coerenza possibile nel visualizzare lo schermo su più device differenti.
- sp
- Usato per la dimensione dei testi.
- La dimensione varia in base ai setting che l’utente ha impostato nel suo Android..
- dp
- Usare dimensioni dinamiche (larghezza, lunghezza)
- wrap_content = imposta la taglia minima affiché l’elemento sia visibilie
- match_parent = importa la taglia massima possibile par rapporto all’elemento parent
- LinearLayout
- Orizontal / Vertical
- Ogni elemento puo’ avere un peso che indica la sua dimensione in rapporto agli altri elementi.
-
- Non esagerare nell’annidare un LinearLayout in un altro => C’é un problema di performance.
- Layout_resource_file (creare un template)
- E’ possibile definire un template per un layout che si ripete e importarlo nel nostro file xml.
- Creare un layout_resource_file
- Assegnare un nome (es: “linear_orizontal” )
- Inserirgli il contenuto che si vuole rendere templace
- Dopodiché é sufficiente fare l’include come mostrato sotto:
-
<include layout="@layoute/linear_horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" />
- RelativeLayout
-
- Gli elementi si posizionato relativamente agli altri.
- La taglia di un elemento é limitata delle regole di posizionamento definite.
- Creare una finestra modale
- Definire una nuova classe che eredita da DialogFragment
-
- Chiamare la nostra classe dialog
- Menù =>
- Creare un nuovo menù
- Creare una nuova ‘Android Resource Directory’ di tipo menù
- Aggiungere a tale cartella un file di tipo ‘Menù resource file‘
- Creare un nuovo menù
-
- Aggiungere un menu nela propria classe
- Toolbar
- Si é chiamata ActionBar, AppBar e ora Toolbar
- Build.gradle
-
//nel file Build.gradle deve esserci questa dependency dependencies.compile 'com.android.support:appcompat-v7:25.3.1' //oppure dependences.compile 'androix.appcompat:appcompat:1.6.1
-
- Verificare di non avere una actionbar di default (res->values->styles.xml->parent=’Theme.AppCompat.NoActionBar)
- Definisco la toolbar customizzata nel file activity_main.xml
-
- Nell’activity_main aggiungola toolbar customizzata
-
- File manifest.xml
- In questo file é possibile definire una gerarchie tra ‘activity’
- Tale gerarchia sarà rispettata nella visualizzzione della nostra toolbar
- File manifest.xml
- RecyclerView
- Permette di visualizzare lunghe liste di dati senza perdere in fluidità
- Funziona trattando separatamente la visualizzazione e i dati visualizzati secondo lo schema seguente:
-
- CardView
Android Studio
- Gestione degli ambienti di build (es: dev o staging)
- BuildType
- Flavor
- BuildVariant
- Aprire la voce di menu Build -> BuildVariant
- Per poter scegliere quale BuildType tra quelli disponibili utilizzare (es: lanciare la varsione dev o staging)
- Emulatore
- Play Store (Enable/Disabled)
- Quando si sceglie un emulatore se esiste una piccola icona nella colonnna ‘Play Store’ significa che tale emulatore avrà a disposizione il google play store per poter installare dele applicazioni.
- E’ possibile modificare la configurazione per poter abilitare il google play store in un emulatore che originariamente non l’ha
- Installare applicazione su un emulatore
- E’ possibile installare un’applicazione su un emulatore semplicemente facendo il drag-and-drop del suo APK direttamente nella finestra dell’emulatore in esecuzione.
- SKD Tools – Android Debug Bridge (adb)
- E’ possibile usare adb.exe per potersi connettere via comando shell all’emulatore e installare un nuovo APK.
- Emulatore – Disinstallare un APK
-
//C:\Users\nicola.riccardi\AppData\Local\Android\Sdk\platform-tools //adb.exe //visualizzare i packages presenti nell'emulatore adb shell pm list packages oppure adb shell cmd package list packages //disinstallare il package desiderato adb uninstall fr.cdchabitat.adoma.rmt.dev.debug //Increasing Buffer Sizes adb logcat -g adb logcat -b main -G 16M //to get process and thread IDs in the logging statements. adb logcat -v threadtime ------------------------------------------------------------------------------------------- //C:\Users\nicola.riccardi\AppData\Local\Android\Sdk\emulator //emulator.exe //C:\Windows\System32\cmd.exe emulator -avd <device-name> emulator -list-avds -no-snapshot-load -gpu swiftshader_indirect
-
- Play Store (Enable/Disabled)
- Device File Manager
- Una volta lanciato l’emulatore, é possibile visualizzare il file SQLite a partire da Android Studio aprendo la finestra <Device File Manager>
- Navigare fino alla file in questone /data/data/<my-project-package-folder>/<my-SQLite>
- Il file é presente fisicamente nel file system del computer seguento il path
- C:\User\<my-user>\AppData\Local\Google\AndroidStudio2022.1\device-explorer\<my-emulateur>\data/data/<my-project-package-folder>/<my-SQLite>