Italian Come si può accedere ad oggetti di Activity dalla routine Process_Globals?

Mattiaf

Active Member
Licensed User
Ciao ragazzi, sto usando il codice postato da Luca nel topic Creare un semplice price alert senza utilizzare server
su main:
B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Private btnStop As Button
    Private btnStart As Button
    Private price As EditText ' fatto da me
End Sub
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("layMain")
  price.Text= File.ReadString(File.DirInternal,"1.txt") ' fatto da me
End Sub
Private Sub btnStart_Click
    btnStart.Enabled = False
    CallSubDelayed(MyService, "Start")
    btnStop.Enabled = True
End Sub
Private Sub btnStop_Click
    btnStop.Enabled = False
    CallSubDelayed(MyService, "Stop")
    btnStart.Enabled = True
End Sub
Private Sub Price_TextChanged (Old As String, New As String)  ' fatto da me
   File.WriteString(File.DirInternal, "1.txt", price.Text) ' fatto da me
'End Sub

sul modulo di servizio MyService

B4X:
Sub Process_Globals
    Private nid As Int = 1
    Private lock As PhoneWakeState
    Private  timer1 As Timer
    'Private price As EditText ' fatto da me ... ?!?!
End Sub

Sub Service_Create
    lock.PartialLock
    timer1.Initialize("Timer1", 3000)
End Sub


Sub Service_Start (StartingIntent As Intent)
    Service.StopAutomaticForeground
End Sub

Public Sub Start
    Service.StartForeground(nid, CreateNotification("HotBit attiva"))
    timer1.Enabled = True
End Sub

Public Sub Stop
    timer1.Enabled = False
    Service.StopForeground(nid)
End Sub


Sub CreateNotification (Body As String) As Notification
    Dim notification As Notification
    notification.Initialize2(notification.IMPORTANCE_HIGH)
    notification.Icon = "icon"
    notification.SetInfo("App attiva", Body, Main)
    Return notification
End Sub

Sub Service_Destroy
    lock.ReleasePartialLock
End Sub

Sub timer1_tick
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download("https://api.hotbit.io/api/v1/market.last?market=KIBA/USDT")
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Log(j.GetString)
        Dim parser As JSONParser
        parser.Initialize(j.GetString)
        Dim root As Map = parser.NextObject
        Dim result As String = root.Get("result")
        Log(result)
    End If
    If result > "0.0002884"  Then
        Dim n As Notification = CreateNotification($"Prezzo superiore a "$ + result)
        n.Notify(nid)
    End If
    j.Release
End Sub

Le righe commentate con 'fatte da me sono appunto scritte da me ( ma vah??!) e quello che sto cercando di fare è usare la edittext Price su "If result > "0.00002884" del modulo di servizio MyService tipo If result > Price.text
Ho provato a dichiarare Private Price As EditText in MyService su process globals ma dice "MyService - 9: Non è possibile accedere ad oggetti di Activity dalla routine Process_Globals." e se provo a creare l'evento textchanged dall'editor di MyService, mi dice che il modulo corrente non supporta eventi di layouts.
Come posso risolvere?
Vi ringrazio anticipatamente
 

Sagenut

Expert
Licensed User
Longtime User
In Process Globals del Main puoi dichiarare una variabile stringa Public
B4X:
Public targetprice as String
Quando confermi il valore nella EditText, o sfruttando l'evento TextChanged, assegna sempre il contenuto della EditText alla stringa
B4X:
targetprice = EditText.Text
Dal Servizio richiami
B4X:
Main.targetprice
 

LucaMs

Expert
Licensed User
Longtime User
Premesso che la risposta di @Sagenut è giustissima...


Questo è uno dei non pochi motivi per creare sempre progetti di tipo B4XPages, anche se poi si userà solo la versione B4A.
Dato che le B4XPage sono classi e non Activity, puoi dichiarare le View nelle loro Class_Globals senza problemi e saranno accessibili da dovunque.

**************************************

In realtà, anche con le B4XPage, il modo più "corretto" sarebbe leggere una proprietà della pagina-classe B4XMainPage, anziché direttamente la View (EditText), ma solo per "pignoleria", diciamo, per non accedere direttamente a un componente visuale da un punto diverso.

Cioè la B4XMainPage dovrebbe avere:
B4X:
Sub Class_Globals
    Private mPrice As Double
End Sub

Public Sub getPrice As Double
    Return mPrice
End Sub

Private Sub edtPrice_TextChanged (Old As String, New As String)
    mPrice = "0" & New ' Lo zero per evitare problemi nel caso in cui New sia una stringa vuota.
End Sub

e il modulo di servizio (MyService)
B4X:
If result > B4XPages.MainPage.Price Then
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Dato che, in questo momento, ho qualche motivo in più per essere logorroico 😄, spiego perché si dovrebbe essere "pignoli" come ho scritto nel post preDecente.

Mettiamo che in futuro tu debba/voglia fare delle modifiche al progetto (immagina un progetto più ampio) e che il confronto tra il prezzo scaricato dal web debba essere effettuato sì con il dato che stia nel Main (Activity o B4XMainPage che sia) ma che quest'ultimo nella nuova versione dipenda anche da altri fattori, non solo dall'input dell'utente. Ad esempio, certamenente non "pertinente", da una percentuale salvata in qualche parte (ad esempio un file locale).

Cioè, l'utente digita 1000 ma questa cifra dovrà essere aumentata del 20%. A questo punto, se nel modulo di servizio di accedessi direttamente alla EditText, otterresti quel 1000. Invece, accedendo alla proprietà B4XPages.MainPage.Price, farai la modifica in quella classe-pagina e via.

Insomma, una vecchissima e sempre validissima regola generale è tentare di mantenere separate l'interfaccia utente dai dati e dalla logica del progetto (anche se, pur estremamente comode e migliori delle Activity, le B4XPage non seguono questa regola, direi, ma in B4X almeno sono multipiattaforma, ovvero per B4A, B4j e B4i).
 

Xfood

Expert
Licensed User
Scu
Premesso che la risposta di @Sagenut è giustissima...


Questo è uno dei non pochi motivi per creare sempre progetti di tipo B4XPages, anche se poi si userà solo la versione B4A.
Dato che le B4XPage sono classi e non Activity, puoi dichiarare le View nelle loro Class_Globals senza problemi e saranno accessibili da dovunque.

**************************************

In realtà, anche con le B4XPage, il modo più "corretto" sarebbe leggere una proprietà della pagina-classe B4XMainPage, anziché direttamente la View (EditText), ma solo per "pignoleria", diciamo, per non accedere direttamente a un componente visuale da un punto diverso.

Cioè la B4XMainPage dovrebbe avere:
B4X:
Sub Class_Globals
    Private mPrice As Double
End Sub

Public Sub getPrice As Double
    Return mPrice
End Sub

Private Sub edtPrice_TextChanged (Old As String, New As String)
    mPrice = "0" & New ' Lo zero per evitare problemi nel caso in cui New sia una stringa vuota.
End Sub

e il modulo di servizio (MyService)
B4X:
If result > B4XPages.MainPage.Price Then
Scusa @LucaMs, solo per capire meglio io,
Se la variabile dichiarata e' mPrice, come fai a prendere il valore con Price ????

B4X:
If result > B4XPages.MainPage.Price Then
Non dovrebbe essere

B4X:
If result > B4XPages.MainPage.mPrice Then
 

Mattiaf

Active Member
Licensed User
In Process Globals del Main puoi dichiarare una variabile stringa Public
B4X:
Public targetprice as String
Quando confermi il valore nella EditText, o sfruttando l'evento TextChanged, assegna sempre il contenuto della EditText alla stringa
B4X:
targetprice = EditText.Text
Dal Servizio richiami
B4X:
Main.targetprice
Ciao, Ti ringrazio per la risposta.. Ho provato a fare quello che mi hai detto, ma non mi funziona :\
Ho creato la variabile pubblica in process global del main, ho assegnato il valore della edittext( Price) alla stringa su activity create e sull'evento price textchanged e infine ho usato Main.targetprice dal modulo di servizio, ma durante il debug, quando clicco su start, si chiude automaticamente sensa lasciarmi nessun log.
questo è il codice del main:
B4X:
#BridgeLogger: true
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Public targetprice As String

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Private btnStop As Button
    Private btnStart As Button
    Private price As EditText
   
End Sub
Sub Activity_Create(FirstTime As Boolean)
   
    Activity.LoadLayout("layMain")
    targetprice= price.text
    If FirstTime Then
        If File.Exists(File.DirInternal, "1.txt") Then
            price.Text = File.ReadString(File.DirInternal, "1.txt")
        End If
    End If
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
Private Sub btnStart_Click
    btnStart.Enabled = False
    CallSubDelayed(MyService, "Start")
    btnStop.Enabled = True
End Sub
Private Sub btnStop_Click
    btnStop.Enabled = False
    CallSubDelayed(MyService, "Stop")
    btnStart.Enabled = True
End Sub
Private Sub Price_TextChanged (Old As String, New As String)
    targetprice= price.text
    File.WriteString(File.DirInternal, "1.txt", price.Text)  
End Sub

e nel modulo di servizio ho usato Main.targetprice.As(Double) in quanto sto trattando numeri decimali
B4X:
If result > Main.targetprice.As(Double)  Then

questo è il log
Logger connesso a: samsung SM-G985F
--------- beginning of main
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
{"error":null,"result":"0.00002933","id":468823727}
0.00002933
myservice$ResumableSub_timer1_tickresume (java line: 276)
java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at lm.hotbit.myservice$ResumableSub_timer1_tick.resume(myservice.java:276)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:267)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:207)
at anywheresoftware.b4a.keywords.Common$11.run(Common.java:1178)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8641)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)

Spero di non essere poi così lontano :D, Vi ringrazio tutti
 

LucaMs

Expert
Licensed User
Longtime User
Scu
Scusa @LucaMs, solo per capire meglio io,
Se la variabile dichiarata e' mPrice, come fai a prendere il valore con Price ????

B4X:
If result > B4XPages.MainPage.Price Then
Non dovrebbe essere

B4X:
If result > B4XPages.MainPage.mPrice Then
No, prendo il valore dalla proprietà (getNomeProprietà) della B4XMainPage, che è pubblica.
mPrice è privata; viene detta "variabile membro".

Uno dei motivi per cui si usano le proprietà è che essendo una routine (sub), puoi scriverci codice, che normalmente serve a controllare il valore che gli passi (o magari a modificarlo). In effetti sarebbe la parte di proprietà setPrice, altra routine, che non ho scritto, quella in cui impostare il valore di mPrice.

In effetti, anziché scrivere, più correttamente:
B4X:
Private Sub edtPrice_TextChanged (Old As String, New As String)
    mPrice = "0" & New ' Lo zero per evitare problemi nel caso in cui New sia una stringa vuota.
End Sub
avrei potuto scrivere:
B4X:
Private Sub edtPrice_TextChanged (Old As String, New As String)
   setPrice("0" & New) ' Lo zero per evitare problemi nel caso in cui New sia una stringa vuota.
End Sub

Public Sub setPrice(Price As Double)
   mPrice = Price
End Sub
Così sarebbe più evidente che chiamo-uso la proprietà Price della B4XMainPage (ma dall'interno della classe stessa, per questo ho impostato direttamente il valore di mPrice)
 
Last edited:

Mattiaf

Active Member
Licensed User
scusa @LucaMs dalla spiegazione di @Sagenut il quale hai detto che è giusta, cosa ho sbagliato? Ho postato il codice nel post precedente.
p.s. Grazie per il consiglio di usare b4x pages, proverò a darci un'occhiata per sviluppi futuri.
 

Sagenut

Expert
Licensed User
Longtime User
@Mattiaf
Allora nel Main puoi dichiarare già
B4X:
Public targetprice as Double
e assegnare il valore alla variabile con l'accortezza che ti ha indicato @LucaMs
B4X:
targetprice = "0" & price.Text
per non avere mai un campo vuoto che ti manda in errore.
Dichiarando già la tua variabile come Double poi non è necessario usare As(Double) quando la richiami dal tuo Servizio.

Tra l altro vedo che salvi anche il tuo valore della EditText in un file.
Forse è conveniente leggere il valore dal file anche dal Servizio e ti eviti la variabile di scambio.
Quindi ti basterebbe cambiare la tua Sub in
B4X:
Private Sub Price_TextChanged (Old As String, New As String)
    File.WriteString(File.DirInternal, "1.txt", "0" & price.Text) 
End Sub
 
Last edited:

Mattiaf

Active Member
Licensed User
@Sagenut ti ringrazio ma non funziona ancora... :\ da lo stesso log error.
Pensi che li sia mettendo nel posto sbagliato?
ti posto il codice completo
main:

B4X:
#Region  Project Attributes
    #ApplicationLabel: HotBit
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region
#BridgeLogger: true
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    'Public targetprice As String
    Public targetprice As Double
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Private btnStop As Button
    Private btnStart As Button
    Private price As EditText
  
End Sub
Sub Activity_Create(FirstTime As Boolean)
  
    Activity.LoadLayout("layMain")
    If FirstTime Then
        If File.Exists(File.DirInternal, "1.txt") Then
            price.Text = File.ReadString(File.DirInternal, "1.txt")
            targetprice = "0" & price.Text
        End If
    End If
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
Private Sub btnStart_Click
    btnStart.Enabled = False
    CallSubDelayed(MyService, "Start")
    btnStop.Enabled = True

End Sub
Private Sub btnStop_Click
    btnStop.Enabled = False
    CallSubDelayed(MyService, "Stop")
    btnStart.Enabled = True
End Sub
Private Sub Price_TextChanged (Old As String, New As String)

      File.WriteString(File.DirInternal, "1.txt", "0" & price.Text)
    targetprice = price.Text
End Sub

myservice
B4X:
#Region  Service Attributes
    #StartAtBoot: False
#End Region

Sub Process_Globals
    Private nid As Int = 1
    Private lock As PhoneWakeState
    Private  timer1 As Timer
  
End Sub

Sub Service_Create
    lock.PartialLock
    timer1.Initialize("Timer1", 3000)
End Sub

Sub Service_Start (StartingIntent As Intent)
    Service.StopAutomaticForeground
End Sub

Public Sub Start
    Service.StartForeground(nid, CreateNotification("HotBit attiva"))
    timer1.Enabled = True
End Sub

Public Sub Stop
    timer1.Enabled = False
    Service.StopForeground(nid)
End Sub

Sub CreateNotification (Body As String) As Notification
    Dim notification As Notification
    notification.Initialize2(notification.IMPORTANCE_HIGH)
    notification.Icon = "icon"
    notification.SetInfo("App attiva", Body, Main)
    Return notification
End Sub

Sub Service_Destroy
    lock.ReleasePartialLock
End Sub

Sub timer1_tick
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download("https://api.hotbit.io/api/v1/market.last?market=KIBA/USDT")
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Log(j.GetString)
        Dim parser As JSONParser
        parser.Initialize(j.GetString)
        Dim root As Map = parser.NextObject
        Dim result As String = root.Get("result")
        Log(result)
    End If
    If result > Main.targetprice  Then
        Dim n As Notification = CreateNotification($"Prezzo superiore a "$ + result)
        n.Notify(nid)
    End If
    j.Release
End Sub

Vi ringrazio in anticipo
p.s. Scusate i messaggi sono moderati ed è probabile che vengano accettati solo quando voi avete già risposto e si accavallano.
 

Mattiaf

Active Member
Licensed User
Ok nevermind, il problema non era quello.. per un motivo che non capisco, la concatenazione di
B4X:
Dim n As Notification = CreateNotification($"Prezzo superiore a "$ + result)
crea il problema, nonostante result sia già string.
 

udg

Expert
Licensed User
Longtime User
B4X:
Dim n As Notification = CreateNotification($"Prezzo superiore a ${result}"$)
Non hai bisogno di concatenare le due stringhe e ,comunque, l'operatore di concatenazone è & non +
Le Smart String Literals costituiscono uno strumento molto utile e potente. Dai un'occhiata qui.
 

LucaMs

Expert
Licensed User
Longtime User
Mattia, "spreca" qualche eurino per ottenere la licenza, ne vale la pena! Che figura fai fare, oltretutto, a noi italiani? 🤔:)
(Che poi B4X-Anywhere Software merita più di qualche eurino; se fossi Erel rimetterei il tutto a pagamento, come un tempo!)
 

Sagenut

Expert
Licensed User
Longtime User
Nella Sub Text_Changed al momento hai
B4X:
targetprice = price.Text
Quando la EditText è completamente vuota potresti avere l'errore perchè targetprice (che adesso è un Double) non potrebbe ricevere l'assegnazione.
Modifica in
B4X:
targetprice = "0" & price.Text
anche lì in modo che targetprice sia sempre almeno impostata a 0.
 
Top