Room Persistence Library 로 샘플을 시도하고 있습니다 . 엔티티를 만들었습니다.
@Entity
public class Agent {
@PrimaryKey
public String guid;
public String name;
public String email;
public String password;
public String phone;
public String licence;
}
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);
}
데이터베이스 클래스 생성 :
@Database(entities = {Agent.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract AgentDao agentDao();
}
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()
}
}
내 활동에서 아래 기능을 구현했습니다.
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();
}
}
불행히도 위의 방법을 실행하면 아래 스택 추적과 충돌합니다.
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)
그 문제는 메인 스레드에서 db 작업을 실행하는 것과 관련된 것 같습니다. 그러나 위 링크에 제공된 샘플 테스트 코드는 별도의 스레드에서 실행되지 않습니다.
@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));
}
여기에 빠진 게 있나요? 충돌없이 실행하려면 어떻게해야합니까? 제안 해주세요.
답변
Dale이 말한 것처럼 UI를 잠그는 주 스레드에 대한 데이터베이스 액세스는 오류입니다.
AsyncTask를 확장하는 활동에서 정적 중첩 클래스 (메모리 누수 방지)를 만듭니다.
private static class AgentAsyncTask extends AsyncTask<Void, Void, Integer> {
//Prevent leak
private WeakReference<Activity> weakActivity;
private String email;
private String phone;
private String license;
public AgentAsyncTask(Activity activity, String email, String phone, String license) {
weakActivity = new WeakReference<>(activity);
this.email = email;
this.phone = phone;
this.license = license;
}
@Override
protected Integer doInBackground(Void... params) {
AgentDao agentDao = MyApp.DatabaseSetup.getDatabase().agentDao();
return agentDao.agentsCount(email, phone, license);
}
@Override
protected void onPostExecute(Integer agentsCount) {
Activity activity = weakActivity.get();
if(activity == null) {
return;
}
if (agentsCount > 0) {
//2: If it already exists then prompt user
Toast.makeText(activity, "Agent already exists!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(activity, "Agent does not exist! Hurray :)", Toast.LENGTH_LONG).show();
activity.onBackPressed();
}
}
}
또는 자체 파일에 최종 클래스를 만들 수 있습니다.
그런 다음 signUpAction (View view) 메서드에서 실행합니다.
new AgentAsyncTask(this, email, phone, license).execute();
경우에 따라 활동이 삭제 될 때 취소 할 수 있도록 활동에 AgentAsyncTask에 대한 참조를 보유 할 수도 있습니다. 그러나 모든 거래를 스스로 중단해야합니다.
또한 Google의 테스트 예에 대한 질문 … 해당 웹 페이지에 다음과 같이 명시되어 있습니다.
데이터베이스 구현을 테스트하는 데 권장되는 접근 방식은 Android 기기에서 실행되는 JUnit 테스트를 작성하는 것입니다. 이러한 테스트는 활동을 만들 필요가 없기 때문에 UI 테스트보다 실행 속도가 더 빠릅니다.
활동 없음, UI 없음.
–편집하다–
궁금한 사람들을 위해 … 다른 옵션이 있습니다. 새로운 ViewModel 및 LiveData 구성 요소를 살펴 보는 것이 좋습니다. LiveData는 Room과 잘 작동합니다.
https://developer.android.com/topic/libraries/architecture/livedata.html
또 다른 옵션은 RxJava / RxAndroid입니다. LiveData보다 강력하지만 복잡합니다.
https://github.com/ReactiveX/RxJava
-편집 2–
많은 사람들이이 답변을 접할 수 있기 때문에 … 요즘 가장 좋은 옵션은 일반적으로 Kotlin Coroutines입니다. 이제 Room에서 직접 지원합니다 (현재 베타 버전).
https://kotlinlang.org/docs/reference/coroutines-overview.html
https://developer.android.com/jetpack/androidx/releases/room#2.1.0-beta01
답변
권장하지는 않지만 기본 스레드의 데이터베이스에 액세스 할 수 있습니다. allowMainThreadQueries()
MyApp.database = Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").allowMainThreadQueries().build()
답변
Kotlin 코 루틴 (명확하고 간결한)
AsyncTask는 정말 투박합니다. 코 루틴은 더 깨끗한 대안입니다 (키워드 몇 개만 뿌리면 동기화 코드가 비 동기화됩니다).
// Step 1: add `suspend` to your fun
suspend fun roomFun(...): Int
suspend fun notRoomFun(...) = withContext(Dispatchers.IO) { ... }
// Step 2: launch from coroutine scope
private fun myFun() {
lifecycleScope.launch { // coroutine on Main
val queryResult = roomFun(...) // coroutine on IO
doStuff() // ...back on Main
}
}
종속성 (아치 구성 요소에 대한 코 루틴 범위 추가) :
// lifecycleScope:
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha04'
// viewModelScope:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha04'
-업데이트 :
2019 년 5 월 8 일 : 이제 Room 2.1 suspend
에서 2019 년 9 월 13 일 지원 : 아키텍처 구성 요소 범위 를 사용하도록 업데이트 됨
답변
모든 RxJava 또는 RxAndroid 또는 RxKotlin 애호가를위한
Observable.just(db)
.subscribeOn(Schedulers.io())
.subscribe { db -> // database operation }
답변
대신 핸들러, 비동기 또는 작업 스레드를 사용하여 주 스레드에서 실행할 수 없습니다. 여기에서 샘플 코드를 사용할 수 있으며 여기에서 룸 라이브러리에 대한 기사를 읽으십시오. Android의 Room Library
/**
* Insert and get data using Database Async way
*/
AsyncTask.execute(new Runnable() {
@Override
public void run() {
// Insert Data
AppDatabase.getInstance(context).userDao().insert(new User(1,"James","Mathew"));
// Get Data
AppDatabase.getInstance(context).userDao().getAllUsers();
}
});
선호하는 방법이 아닌 메인 스레드에서 실행하려면.
이 방법을 사용하여 주 스레드에서 얻을 수 있습니다 Room.inMemoryDatabaseBuilder()
답변
람다를 사용하면 AsyncTask로 쉽게 실행할 수 있습니다.
AsyncTask.execute(() -> //run your query here );
답변
Jetbrains Anko 라이브러리를 사용하면 doAsync {..} 메서드를 사용하여 데이터베이스 호출을 자동으로 실행할 수 있습니다. 이것은 mcastro의 대답으로 겪었던 것처럼 보였던 자세한 문제를 처리합니다.
사용 예 :
doAsync {
Application.database.myDAO().insertUser(user)
}
삽입 및 업데이트에 자주 사용하지만 일부 쿼리의 경우 RX 워크 플로를 사용하는 것이 좋습니다.