Android Chambre simple requête select - Ne peut pas accéder à la base de données sur le thread principal
Je suis en train d'essayer un échantillon avec Salle De La Persistance De La Bibliothèque.
J'ai créé une Entité:
@Entity
public class Agent {
@PrimaryKey
public String guid;
public String name;
public String email;
public String password;
public String phone;
public String licence;
}
Créé une classe DAO:
@Dao
public interface AgentDao {
@Query("SELECT COUNT(*) FROM Agent where email = :email OR phone = :phone OR licence = :licence")
int agentsCount(String email, String phone, String licence);
@Insert
void insertAgent(Agent agent);
}
Créé la Base de données de la classe:
@Database(entities = {Agent.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract AgentDao agentDao();
}
Exposé de la base de données à l'aide de la sous-classe ci-dessous dans Kotlin:
class MyApp : Application() {
companion object DatabaseSetup {
var database: AppDatabase? = null
}
override fun onCreate() {
super.onCreate()
MyApp.database = Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").build()
}
}
Mis en œuvre ci-dessous en fonction de mon activité:
void signUpAction(View view) {
String email = editTextEmail.getText().toString();
String phone = editTextPhone.getText().toString();
String license = editTextLicence.getText().toString();
AgentDao agentDao = MyApp.DatabaseSetup.getDatabase().agentDao();
//1: Check if agent already exists
int agentsCount = agentDao.agentsCount(email, phone, license);
if (agentsCount > 0) {
//2: If it already exists then prompt user
Toast.makeText(this, "Agent already exists!", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(this, "Agent does not exist! Hurray :)", Toast.LENGTH_LONG).show();
onBackPressed();
}
}
Malheureusement sur l'exécution de la méthode ci-dessus, il se bloque avec ci-dessous trace de la pile:
FATAL EXCEPTION: main
Process: com.example.me.MyApp, PID: 31592
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22288)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22288)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long periods of time.
at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:137)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:165)
at com.example.me.MyApp.RoomDb.Dao.AgentDao_Impl.agentsCount(AgentDao_Impl.java:94)
at com.example.me.MyApp.View.SignUpActivity.signUpAction(SignUpActivity.java:58)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22288)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Semble que ce problème est lié à l'exécution de la db opération sur le thread principal. Cependant, le test de l'échantillon de code fourni dans le lien ci-dessus ne fonctionne pas sur un thread séparé:
@Test
public void writeUserAndReadInList() throws Exception {
User user = TestUtil.createUser(3);
user.setName("george");
mUserDao.insert(user);
List<User> byName = mUserDao.findUsersByName("george");
assertThat(byName.get(0), equalTo(user));
}
Ai-je raté quelque chose ici? Comment puis-je faire exécuter sans crash? S'il vous plaît suggérer.
Vous devez vous connecter pour publier un commentaire.
Base de données d'accès sur le thread principal de verrouillage de l'INTERFACE utilisateur est l'erreur, comme Dale a dit.
Créer une statique de la classe imbriquée (pour empêcher la fuite de mémoire) dans votre Activité s'étendant AsyncTask.
Ou vous pouvez créer une classe finale sur son propre fichier.
Puis l'exécuter dans le signUpAction(View vue) méthode:
Dans certains cas, vous pouvez également maintenir une référence à l'AgentAsyncTask dans votre activité, de sorte que vous pouvez l'annuler lorsque l'Activité est détruite. Mais vous devez interrompre les opérations vous-même.
Aussi, à votre question sur le Google test exemple...
Dans cette page web:
Aucune Activité, aucune INTERFACE utilisateur.
--EDIT--
Pour des gens qui se demandent... Vous avez d'autres options.
Je vous recommande de prendre un coup d'oeil dans la nouvelle ViewModel et LiveData composants. LiveData fonctionne très bien avec de la place.
https://developer.android.com/topic/libraries/architecture/livedata.html
Une autre option est le RxJava/RxAndroid. Plus puissant, mais plus complexe que LiveData.
https://github.com/ReactiveX/RxJava
--EDIT 2--
Depuis de nombreuses personnes peuvent trouver cette réponse...
La meilleure option aujourd'hui, d'une manière générale, est Kotlin Coroutines. La chambre prend désormais en charge directement (actuellement en bêta).
https://kotlinlang.org/docs/reference/coroutines-overview.html
https://developer.android.com/jetpack/androidx/releases/room#2.1.0-beta01
Il n'est pas recommandé, mais vous pouvez accéder à la base de données sur le thread principal avec
allowMainThreadQueries()
allowMainThreadQueries()
sur le constructeur, car il peut potentiellement bloquer l'INTERFACE utilisateur pour une longue période de temps. Les requêtes asynchrones (les requêtes qui retournentLiveData
ou RxJavaFlowable
) sont exemptés de cette règle puisqu'ils asynchrone exécuter la requête sur un thread d'arrière-plan en cas de besoin.Pour tous les RxJava ou RxAndroid ou RxKotlin amateurs de il
override fun getTopScores(): Observable<List<PlayerScore>> { return Observable .fromCallable({ GameApplication.database .playerScoresDao().getTopScores() }) .applySchedulers() }
oùapplySchedulers()
je viens de fairefun <T> Observable<T>.applySchedulers(): Observable<T> = this.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
IntentService#onHandleIntent
parce que cette méthode s'exécute sur le thread de travail de sorte que vous n'ont pas besoin de tout mécanisme de threading là pour faire de la Salle d'opération de base de donnéesKotlin Coroutines (Clair & Concise)
AsyncTask est vraiment maladroit. Kotlin coroutines est une alternative plus propre (essentiellement du code synchrone, plus un couple de mots-clés).
Et c'est tout!!
Bonus: Activité CoroutineScope
À utiliser asynchrone à partir d'une Activité, vous avez besoin d'un CoroutineScope. Vous pouvez utiliser votre Activité comme ceci:
Bonus: Android Chambre & Suspendre
08-Mai-2019: Chambre 2.1 prend désormais en charge
suspend
(https://youtu.be/Qxj2eBmXLHg?t=1662)La
suspend
mot-clé assure méthodes asynchrones sont seulement appelés dans async blocs, cependant (comme indiqué par @Robin), cela ne jouent pas bien avec de la place (<2.1) annoté méthodes.@Query abstract suspend fun count()
par l'aide de suspendre le mot-clé? Pouvez vous de bien vouloir examiner cette question similaire: stackoverflow.com/questions/48694449/...@Query
fonction. Lorsque j'ajoute le suspendre mot-clé à l'intérieur de la@Query
méthode trop il n'a, en effet, ne parviennent pas à compiler. Il ressemble à l'astucieux sous le capot de trucs pour suspendre & Salle de clash (comme vous le mentionnez dans votre question, la version compilée de la suspension est de retour d'une poursuite qui ne peut pas gérer).launch
mot-clé, vous lancer avec un champ d'application, tels queGlobalScope.launch
Vous ne pouvez pas l'exécuter sur le thread principal, au lieu d'utiliser des gestionnaires, asynchrone ou de travail threads . Un exemple de code est disponible ici et de lire l'article sur la salle de la bibliothèque ici : Android la Salle de la Bibliothèque
Si vous voulez l'exécuter sur le thread principal qui n'est pas la méthode préférée .
Vous pouvez utiliser cette méthode pour réaliser sur le thread principal
Room.inMemoryDatabaseBuilder()
Avec le Jetbrains Anko bibliothèque, vous pouvez utiliser le doAsync{..} la méthode à exécuter automatiquement des appels de base de données. Cela prend en charge les verbosité problème semblait avoir été avoir avec mcastro de réponse.
Exemple d'utilisation:
J'utilise fréquemment pour les insertions et mises à jour, cependant pour les requêtes select j'ai recommandons à l'aide de la RX de flux de travail.
Un élégant RxJava/Kotlin solution est d'utiliser
Complétées.fromCallable
, ce qui vous donnera une Observable qui ne retourne pas de valeur, mais peut observés et souscrit sur un thread différent.Ou dans Kotlin:
Vous pouvez l'observer et abonnez-vous comme vous le feriez habituellement:
Avec lambda, il est facile de courir avec AsyncTask
Le message d'erreur,
Est assez précis et descriptif. La question est de savoir comment devez-vous éviter de consulter la base de données sur le thread principal. C'est un vaste sujet, mais pour commencer, lisez à propos de AsyncTask (cliquez ici)
-----EDIT----------
Je vois que vous rencontrez des problèmes lorsque vous exécutez un test unitaire. Vous avez un couple de choix pour résoudre ce problème:
Exécuter le test directement sur la machine de développement plutôt que sur un appareil Android (ou émulateur). Cela fonctionne pour les tests qui sont à la base de données centrée sur et ne me soucie pas vraiment de savoir s'ils sont en cours d'exécution sur un appareil.
Utiliser l'annotation
@RunWith(AndroidJUnit4.class)
pour exécuter le test sur l'appareil android, mais pas dans une activité avec une INTERFACE utilisateur.
Plus de détails à ce sujet peuvent être trouvées dans ce tutoriel
Si vous êtes plus à l'aise avec Async task:
Vous avez à l'exécution de la demande au fond.
Un moyen simple pourrait être l'aide d'un Les exécuteurs :
Viens de faire les opérations de base de données dans un Thread séparé. Comme ceci (Kotlin):
Rapide de requêtes vous pouvez vous permettre de l'exécuter sur le thread de l'INTERFACE utilisateur.
Dans mon cas, j'ai eu de la figure de l'utilisateur a cliqué dans la liste existe dans la base de données ou non. Si non, alors de créer l'utilisateur et démarrer une autre activité
Mise à jour: j'ai aussi eu ce message quand j'ai essayé de créer une requête à l'aide de @RawQuery et SupportSQLiteQuery à l'intérieur de la DAO.
Solution: construire la requête à l'intérieur de ce Dernier et de le passer à la DAO.
Ou...
Vous ne devriez pas accéder à la base de données directement sur le thread principal, par exemple:
Vous devez utiliser AsyncTask pour la mise à jour, d'ajouter et de supprimer des opérations.
Exemple:
Si vous utilisez LiveData pour sélectionner les opérations, vous n'avez pas besoin d'AsyncTask.
Simplement, vous pouvez utiliser ce code pour le résoudre:
Ou lambda, vous pouvez utiliser ce code:
Vous pouvez remplacer
appDb.daoAccess().someJobes()
avec votre propre code;Vous pouvez autoriser l'accès de base de données sur le thread principal, mais uniquement pour le débogage, vous ne devriez pas faire cela sur la production.
Voici la raison.
Remarque: cette Chambre n'a pas de soutien de la base de données d'accès sur le thread principal, sauf si vous avez appelé allowMainThreadQueries() dans le constructeur, car il peut bloquer l'INTERFACE utilisateur pour une longue période de temps. Asynchrone des requêtes—requêtes qui retournent des instances de LiveData ou Granulée—sont exemptés de cette règle parce qu'ils asynchrone exécuter la requête sur un thread d'arrière-plan en cas de besoin.
Vous pouvez utiliser Avenir et Exigible. Si vous n'avez pas à écrire une longue asynctask et d'effectuer vos requêtes sans ajouter allowMainThreadQueries().
Ma requête dao:-
Mon référentiel méthode:-
allowMainThreadQueries()
. Thread principal toujours bloquée dans les deux cas