অ্যান্ড্রয়েড রুম - সাধারণ নির্বাচন জিজ্ঞাসা - মূল থ্রেডে ডাটাবেস অ্যাক্সেস করতে পারে না


126

আমি রুম পার্সোনেন্স লাইব্রেরি সহ একটি নমুনা চেষ্টা করছি । আমি একটি সত্তা তৈরি করেছি:

@Entity
public class Agent {
    @PrimaryKey
    public String guid;
    public String name;
    public String email;
    public String password;
    public String phone;
    public String licence;
}

একটি ডিএও ক্লাস তৈরি করেছে:

@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();
}

কোটলিনে নীচে সাবক্লাস ব্যবহার করে প্রকাশিত ডাটাবেস:

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) 

সমস্যাটি মনে হচ্ছে মূল থ্রেডে ডিবি অপারেশন কার্যকর করার সাথে সম্পর্কিত। তবে উপরের লিঙ্কে প্রদত্ত নমুনা পরীক্ষার কোডটি পৃথক থ্রেডে চলবে না:

@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));
    }

আমি কি এখানে কিছু মিস করছি? আমি কীভাবে এটি ক্রাশ ছাড়াই সম্পাদন করতে পারি? সুপারিশ করুন.


1
যদিও কোটলিনের জন্য লেখা, এই নিবন্ধটি অন্তর্নিহিত সমস্যাটি খুব ভাল ব্যাখ্যা করেছে!
পিটার লেহনহার্ড

দেখে নিন ব্যবহার করার stackoverflow.com/questions/58532832/...
nnyerges

এই উত্তর দেখুন। আমার জন্য এই উত্তরটি কাজ stackoverflow.com/a/51720501/7655085
সোমেন Tushir

উত্তর:


59

ডেল যেমন বলেছিল তেমনি ইউআইআই লক করে মূল থ্রেডে ডাটাবেস অ্যাক্সেস ত্রুটি।

আপনার ক্রিয়াকলাপ 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();
        }
    }
}

অথবা আপনি নিজের ফাইলটিতে একটি চূড়ান্ত শ্রেণি তৈরি করতে পারেন।

তারপরে এটি সাইনআপ (অ্যাকশন দেখুন) পদ্ধতিতে চালিত করুন:

new AgentAsyncTask(this, email, phone, license).execute();

কিছু ক্ষেত্রে আপনি আপনার ক্রিয়াকলাপে এজেন্টঅ্যাসেন্সটাস্কের একটি রেফারেন্সও রাখতে চাইতে পারেন যাতে কার্যকলাপটি নষ্ট হয়ে গেলে আপনি এটি বাতিল করতে পারেন। তবে আপনাকে কোনও লেনদেন নিজেই বাধা দিতে হবে।

এছাড়াও, গুগলের পরীক্ষার উদাহরণ সম্পর্কে আপনার প্রশ্ন ... তারা সেই ওয়েব পৃষ্ঠায় বর্ণনা করেছেন:

আপনার ডাটাবেস বাস্তবায়ন পরীক্ষার জন্য প্রস্তাবিত পদ্ধতির মধ্যে একটি অ্যান্ড্রয়েড ডিভাইসে চলমান একটি JUnit পরীক্ষা লেখা হচ্ছে। যেহেতু এই পরীক্ষাগুলির কোনও ক্রিয়াকলাপ তৈরি করার প্রয়োজন নেই, আপনার ইউআই পরীক্ষার চেয়ে এগুলি কার্যকর করা দ্রুত হওয়া উচিত।

কোনও কার্যকলাপ নেই, কোনও ইউআই নেই।

--EDIT--

ভাবছেন লোকেদের জন্য ... আপনার কাছে অন্য বিকল্প রয়েছে। আমি নতুন ভিউমোডেল এবং লাইভ ডেটা উপাদানগুলি একবার দেখে নেওয়ার পরামর্শ দিচ্ছি। লাইভডেটা রুম সহ দুর্দান্ত কাজ করে। https://developer.android.com/topic/libraries/architecture/livedata.html

আর একটি বিকল্প হ'ল RxJava / RxAndroid। লাইভডাটার চেয়ে আরও শক্তিশালী তবে জটিল। https://github.com/ReactiveX/RxJava

--ডিডি 2 -

যেহেতু অনেক লোক এই উত্তরটি জুড়ে আসতে পারে ... আজকাল সবচেয়ে ভাল বিকল্পটি, সাধারণত বললে, হ'ল কোটলিন কর্টিনেস। রুমটি এখন এটি সরাসরি সমর্থন করে (বর্তমানে বিটাতে)। https://kotlinlang.org/docs/references/coroutines-overview.html https://developer.android.com/jetpack/androidx/releases/room#2.1.0-beta01


31
প্রতিবার আমার ডাটাবেস অ্যাক্সেস করার জন্য কি এই বিশাল অ্যাসিঙ্ক টাস্কটি (যে আপনি কোডের নমুনায় প্রস্তাব করেছেন) করার কথা রয়েছে? ডিবি থেকে কিছু তথ্য পাওয়ার জন্য এটি একের পরিবর্তে কোডের এক ডজন বা তাই লাইন। আপনি নতুন ক্লাস তৈরির প্রস্তাব করেছিলেন, তবে এর অর্থ কি প্রতিটি সন্নিবেশ / সিলেক্ট ডাটাবেস কলের জন্য নতুন এসিঙ্কটাস্ক ক্লাস তৈরি করা দরকার?
পাইট্রেইক

7
হ্যাঁ, আপনার কাছে আরও কিছু বিকল্প রয়েছে। আপনি হয়ত নতুন ভিউমোডেল এবং লাইভ ডেটা উপাদানগুলি দেখতে চান। লাইভ ডেটা ব্যবহার করার সময় আপনার অ্যাসিঙ্কটাস্কের দরকার নেই, যখনই কিছু পরিবর্তন হবে তখন অবজেক্টটি অবহিত হবে। developer.android.com/topic/libraries/architecture/... developer.android.com/topic/libraries/architecture/... এর রয়েছে AndroidRx অঙ্গীকার (যদিও এটা প্রায় কাছাকাছি কি LiveData করে না), এবং। অ্যাসিঙ্কটাস্ক ব্যবহার করার সময় আপনি এমনভাবে আর্কিটেকচার করতে পারেন যাতে আপনি একটি অ্যাসিঙ্কটাস্কে একাধিক ক্রিয়াকলাপ অন্তর্ভুক্ত করতে পারেন বা প্রতিটি পৃথক করতে পারেন।
ম্যাসস্ট্রো

@ পাইওট্রিক - কোটলিনে এখন অ্যাসিঙ্ক বেকড রয়েছে (যদিও এটি পরীক্ষামূলকভাবে পতাকাযুক্ত)। আমার উত্তর দেখুন যা তুলনামূলকভাবে তুচ্ছ। স্যামুয়েল রবার্টের উত্তরটি রেক্সকে অন্তর্ভুক্ত করে। আমি এখানে লাইভ ডেটা উত্তর দেখতে পাচ্ছি না, তবে আপনি যদি পর্যবেক্ষণযোগ্য চান তবে এটি আরও ভাল পছন্দ হতে পারে।
অজাহানচার্লস

নিয়মিত অ্যাসিঙ্কটাস্ক ব্যবহার করাও এখন কাজ করবে বলে মনে হয় না, এখনও অবৈধ রাষ্ট্রীয় ব্যতিক্রম
পাচ্ছে

@ পাইওট্রেইক আপনি কি আমাকে বলছেন আপনি আপনার পুরো অভিজ্ঞতার মূল থ্রেডে ডাটাবেস অ্যাক্সেস কার্যকর করতে ব্যবহার করেছেন?
এমআর 5

142

এটি প্রস্তাবিত নয় তবে আপনি মূল থ্রেডে ডাটাবেসে অ্যাক্সেস করতে পারবেন allowMainThreadQueries()

MyApp.database =  Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").allowMainThreadQueries().build()

9
ঘরটি মূল থ্রেডে ডাটাবেস অ্যাক্সেসের অনুমতি দেয় না যদি না আপনি allowMainThreadQueries()বিল্ডারকে ডেকে থাকেন কারণ এটি সম্ভবত দীর্ঘ সময়ের জন্য ইউআই লক করে। অ্যাসিঙ্ক্রোনাস ক্যোয়ারী (যে LiveDataপ্রশ্নগুলি ফিরে আসে বা আরএক্সজাভা Flowable) এই নিয়ম থেকে অব্যাহতিপ্রাপ্ত কারণ তারা যখন প্রয়োজন ততক্ষণ পটভূমির থ্রেডে অবিচ্ছিন্নভাবে ক্যোয়ারি চালায়।
pRaNaY

4
ধন্যবাদ মাইগ্রেশনের জন্য এটি খুব কার্যকর, কারণ আমি পরীক্ষা করতে চাই রুম
লোডার্স

5
পছন্দ করুন কিছু ক্ষেত্রে এটি আপনার অ্যাপ্লিকেশনটিকে অনেক ধীর করতে পারে। অপারেশনগুলি অবিচ্ছিন্নভাবে করা উচিত।
অ্যালেক্স

@ অ্যালেক্স মূল থ্রেডে কোয়েরি করার ক্ষেত্রে কখনও মামলা হয় না?
জাস্টিন মাইনার্স

2
@ জাস্টিনমিনিয়ার্স এটি কেবল খারাপ অভ্যাস, আপনি যতক্ষণ ডাটাবেস ছোট থাকবেন ততক্ষণ আপনি এটি ঠিক রাখবেন।
lasec0203

53

কোটলিন কার্টাইনস (পরিষ্কার এবং সংক্ষিপ্ত)

অ্যাসিঙ্কটাস্ক আসলেই চতুর। করোটাইনগুলি একটি ক্লিনার বিকল্প (কেবল কয়েকটি কীওয়ার্ড ছিটিয়ে দিন এবং আপনার সিঙ্ক কোডটি অ্যাসিঙ্ক হয়ে যায়)।

// 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'

- আপডেটগুলি:
08-মে-2019: ঘর 2.1 এখন suspend
13-সেপ্টেম্বর -2018 সমর্থন করে : আর্কিটেকচার উপাদান স্কোপ ব্যবহার করার জন্য আপডেট হয়েছে


1
@Query abstract suspend fun count()সাসপেন্ড কীওয়ার্ড ব্যবহার করে আপনার কি কোনও সংকলন ত্রুটি আছে ? আপনি কি দয়া করে এই অনুরূপ প্রশ্নের সন্ধান করতে পারেন: স্ট্যাকওভারফ্লো
রবিন

@ রবিন - হ্যাঁ আমি করি। আমার ভুল; আমি একটি সর্বজনীন (অননোটযুক্ত) ডিএও পদ্ধতিতে সাসপেন্ড ব্যবহার করছিলাম যা একটি সুরক্ষিত অ-স্থগিত @Queryফাংশন বলে। আমি যখন অভ্যন্তরীণ @Queryপদ্ধতিতে সাসপেন্ড কীওয়ার্ডটিও যুক্ত করি তখন এটি সংকলন করতে ব্যর্থ হয়। এটি স্থগিতকরণ এবং কক্ষ সংঘর্ষের জন্য হুড স্টাফের নীচে চালাকের মতো দেখায় (যেমন আপনি আপনার অন্যান্য প্রশ্নে উল্লেখ করেছেন, সাসপেন্ডের সংকলিত সংস্করণটি ধারাবাহিকতা ফিরিয়ে দিচ্ছে যা ঘরটি পরিচালনা করতে পারে না)।
অজাহানচার্লস

প্রচুর বোধ করে। আমি পরিবর্তে কর্টিন ফাংশন দিয়ে এটি কল করতে যাচ্ছি।
রবিন

1
@ রবিন - এফওয়াইআই তারা রুম ২.১ এ স্থগিতের জন্য সমর্থন যোগ করেছেন :)
আজাহানচর্লেস

স্পষ্টতই launchআর কোনও কীওয়ার্ড নেই, আপনি একটি সুযোগ দিয়ে লঞ্চ করুন, যেমনGlobalScope.launch
ন্যাসাচ

48

সমস্ত RxJava বা RxAndroid বা RxKotlin প্রেমীদের জন্য

Observable.just(db)
          .subscribeOn(Schedulers.io())
          .subscribe { db -> // database operation }

3
যদি আমি এই কোডটি কোনও পদ্ধতির ভিতরে রাখি, তবে কীভাবে ফলাফলটি ডাটাবেস অপারেশন থেকে ফিরিয়ে আনব?
এগাকিন বেকনওয়ালকার

@ এগাকাকিন বাকনওয়ালার আমার override fun getTopScores(): Observable<List<PlayerScore>> { return Observable .fromCallable({ GameApplication.database .playerScoresDao().getTopScores() }) .applySchedulers() }যেখানে applySchedulers()সবেমাত্র আছেfun <T> Observable<T>.applySchedulers(): Observable<T> = this.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
নোলমান

এটি ইনটেন্স সার্ভিসের পক্ষে কাজ করবে না। কারণ থ্রেডটি শেষ হয়ে গেলে ইন্টেন্টসেসওয়ারটি করা হবে।
উমং কোঠারি

1
@ উমংকোঠারি আপনি যদি ব্যতিক্রম হন তবে আপনি এই ব্যতিক্রমটি পাবেন না IntentService#onHandleIntentকারণ এই পদ্ধতিটি কর্মী থ্রেডের উপর চালিত করে তাই ঘর ডাটাবেস ক্রিয়াকলাপ করার জন্য আপনার কোনও থ্রেডিং মেকানিজম প্রয়োজন হবে না
স্যামুয়েল রবার্ট

স্যামুয়েলরোবার্ট, হ্যাঁ আমার খারাপ বিষয়ে রাজি হন। এটা আমার মন পিছলে।
উমং কোঠারি

27

আপনি এটিকে হ্যান্ডলার, অ্যাসিঙ্ক বা ওয়ার্কিং থ্রেডের পরিবর্তে মূল থ্রেডে চালাতে পারবেন না। একটি নমুনা কোড এখানে উপলভ্য এবং রুম লাইব্রেরির উপর নিবন্ধটি এখানে পড়ুন: অ্যান্ড্রয়েডের রুম লাইব্রেরি

/**
 *  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()


যদি আমি এই পদ্ধতিটি ডেটা পেতে (শুধুমাত্র getAllUser () এক্ষেত্রে প্রাপ্ত) ব্যবহার করি, তবে এই পদ্ধতি থেকে ডেটা কীভাবে ফিরে আসবে? এটি একটি ত্রুটি হিসাবে উপস্থিত হয়, যদি আমি "রান" এর ভিতরে "রিটার্ন" শব্দটি রাখি।
এগগাকিন বেকনওয়ালকার

1
কোথাও একটি ইন্টারফেস পদ্ধতি তৈরি করুন এবং এখান থেকে ডেটা পেতে বেনাম শ্রেণি যুক্ত করুন।
রিজওয়ান

1
এটি সন্নিবেশ / আপডেট করার সহজ সমাধান।
বিয়ার মি

12

ল্যাম্বদা সহ এ্যাসিঙ্কটাস্ক দিয়ে চালানো সহজ

 AsyncTask.execute(() -> //run your query here );

2
ধন্যবাদ এটি সুবিধাজনক। যাইহোক, কোটলিন আরও সহজ: AsyncTask.execute {}
আলেক্স্রনভ

1
তবে আপনি এই পদ্ধতিটি ব্যবহার করে কীভাবে ফলাফল পাবেন?
leeCoder

11

জেটব্রেইনস আঙ্কো লাইব্রেরির মাধ্যমে, আপনি স্বয়ংক্রিয়ভাবে ডাটাবেস কলগুলি সম্পাদন করতে doAsync {..} পদ্ধতিটি ব্যবহার করতে পারেন। এটি আপনার মনে হয়েছে যে ম্যাসস্ট্রোর উত্তর দিয়ে গেছে বলে মনে হচ্ছে ভার্বোসিটি সমস্যাটির যত্ন নেয়।

ব্যবহারের উদাহরণ:

    doAsync { 
        Application.database.myDAO().insertUser(user) 
    }

আমি এটি প্রায়শই ইনসার্ট এবং আপডেটের জন্য ব্যবহার করি, তবে নির্বাচিত প্রশ্নগুলির জন্য আমি আরএক্স ওয়ার্কফ্লো ব্যবহার করে সুপারিশ করি।



6

আপনাকে পটভূমিতে অনুরোধটি কার্যকর করতে হবে। একটি সহজ উপায় একটি নির্বাহক ব্যবহার করা যেতে পারে :

Executors.newSingleThreadExecutor().execute { 
   yourDb.yourDao.yourRequest() //Replace this by your request
}

আপনি কীভাবে ফলাফল ফিরে পাবেন?
LeCoder

5

একটি মার্জিত আরএক্সজাভা / কোটলিন সমাধান ব্যবহার করা হয় Completable.fromCallable, যা আপনাকে একটি পর্যবেক্ষণযোগ্য দেয় যা কোনও মান দেয় না, তবে পর্যবেক্ষণ করতে এবং আলাদা থ্রেডে সাবস্ক্রাইব করতে পারে।

public Completable insert(Event event) {
    return Completable.fromCallable(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            return database.eventDao().insert(event)
        }
    }
}

বা কোটলিনে:

fun insert(event: Event) : Completable = Completable.fromCallable {
    database.eventDao().insert(event)
}

আপনি যেমনটি পছন্দ করেন তেমন পর্যবেক্ষণ ও সাবস্ক্রাইব করতে পারেন:

dataManager.insert(event)
    .subscribeOn(scheduler)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(...)

5

আপনি মূল থ্রেডে ডাটাবেস অ্যাক্সেসের অনুমতি দিতে পারবেন তবে কেবল ডিবাগিং উদ্দেশ্যে, আপনার উত্পাদনতে এটি করা উচিত নয়।

এখানে কারণ আছে।

দ্রষ্টব্য: ঘরটি মূল থ্রেডে ডাটাবেস অ্যাক্সেসকে সমর্থন করে না যতক্ষণ না আপনি বিল্ডারে অনুমতিমেনথ্রেডকুইয়ারিজ () কল না করে কারণ এটি দীর্ঘ সময়ের জন্য ইউআই লক করতে পারে। অ্যাসিঙ্ক্রোনাস ক্যোয়ারীস - লাইভডাটা বা ফ্লোবলের দৃষ্টান্তগুলি ফেরত পাওয়া ক্যুরিগুলি rule এই নিয়ম থেকে অব্যাহতিপ্রাপ্ত কারণ তারা যখন প্রয়োজন ততক্ষণ পটভূমির থ্রেডে অবিচ্ছিন্নভাবে ক্যোয়ারি চালায়।


4

এটি সমাধানের জন্য কেবল আপনি এই কোডটি ব্যবহার করতে পারেন:

Executors.newSingleThreadExecutor().execute(new Runnable() {
                    @Override
                    public void run() {
                        appDb.daoAccess().someJobes();//replace with your code
                    }
                });

বা ল্যাম্বডায় আপনি এই কোডটি ব্যবহার করতে পারেন:

Executors.newSingleThreadExecutor().execute(() -> appDb.daoAccess().someJobes());

আপনি appDb.daoAccess().someJobes()নিজের কোড দিয়ে প্রতিস্থাপন করতে পারেন ;


4

AsyncTask অবচিত হওয়ায় আমরা নির্বাহক পরিষেবাটি ব্যবহার করতে পারি। অথবা আপনি অন্যান্য উত্তরে বর্ণিত হিসাবে লাইভডাটা সহ ভিউমোডেলও ব্যবহার করতে পারেন ।

এক্সিকিউটর পরিষেবা ব্যবহারের জন্য, আপনি নীচের মতো কিছু ব্যবহার করতে পারেন।

public class DbHelper {

    private final Executor executor = Executors.newSingleThreadExecutor();

    public void fetchData(DataFetchListener dataListener){
        executor.execute(() -> {
                Object object = retrieveAgent(agentId);
                new Handler(Looper.getMainLooper()).post(() -> {
                        dataListener.onFetchDataSuccess(object);
                });
        });
    }
}

মেইন লুপার ব্যবহার করা হয়, যাতে আপনি onFetchDataSuccessকলব্যাক থেকে ইউআই উপাদানটি অ্যাক্সেস করতে পারেন ।


3

ত্রুটি বার্তা,

মূল থ্রেডে ডাটাবেস অ্যাক্সেস করতে পারে না কারণ এটি দীর্ঘ সময় ধরে ইউআইকে লক করতে পারে।

বেশ বর্ণনামূলক এবং নির্ভুল। প্রশ্নটি হল কীভাবে আপনার মূল থ্রেডে ডাটাবেস অ্যাক্সেস করা উচিত। এটি একটি বিশাল বিষয়, তবে শুরু করতে, অ্যাসিঙ্কটাস্ক সম্পর্কে পড়ুন (এখানে ক্লিক করুন)

----- সম্পাদনা ----------

আমি দেখতে পাচ্ছি যে আপনি যখন ইউনিট পরীক্ষা চালাচ্ছেন তখন আপনার সমস্যা হচ্ছে। এটি ঠিক করার জন্য আপনার কাছে কয়েকটি পছন্দ রয়েছে:

  1. অ্যান্ড্রয়েড ডিভাইস (বা এমুলেটর) না দিয়ে সরাসরি উন্নয়ন মেশিনে পরীক্ষা চালান। এটি এমন পরীক্ষাগুলির জন্য কাজ করে যা ডাটাবেস কেন্দ্রিক এবং সেগুলি কোনও ডিভাইসে চলছে কিনা তা সত্যিই চিন্তা করে না।

  2. @RunWith(AndroidJUnit4.class) অ্যান্ড্রয়েড ডিভাইসে পরীক্ষা চালানোর জন্য টিকাটি ব্যবহার করুন , তবে কোনও ইউআই সহ কোনও ক্রিয়াকলাপে নয়। এ সম্পর্কে আরও বিশদ এই টিউটোরিয়ালে পাওয়া যাবে


আমি আপনার বক্তব্যটি বুঝতে পারি, আমার ধারনা হ'ল আপনি JUnit এর মাধ্যমে কোনও ডিবি অপারেশন পরীক্ষা করার চেষ্টা করার সময় একই পয়েন্টটি কার্যকর হয়। তবে ডেভেলপার.এন্ড্রয়েড / টপিক / লাইব্রিজি / অর্কিটেকচার / রুম html এ নমুনা পরীক্ষার পদ্ধতি রাইটউইজারঅ্যান্ডআরইডআইএনলিস্ট ব্যাকগ্রাউন্ড থ্রেডে সন্নিবেশ ক্যোয়ারিকে অনুরোধ করে না। আমি কি এখানে কিছু মিস করছি? সুপারিশ করুন.
দেবর্ষি

দুঃখিত, আমি এই পরীক্ষাটি সমস্যা হচ্ছিল তা হারিয়েছি। আমি আরও কিছু তথ্য যুক্ত করতে আমার উত্তর সম্পাদনা করব।
ডেল উইলসন

3

আপনি যদি অ্যাসিঙ্ক কার্যে আরও স্বাচ্ছন্দ্য বোধ করেন :

  new AsyncTask<Void, Void, Integer>() {
                @Override
                protected Integer doInBackground(Void... voids) {
                    return Room.databaseBuilder(getApplicationContext(),
                            AppDatabase.class, DATABASE_NAME)
                            .fallbackToDestructiveMigration()
                            .build()
                            .getRecordingDAO()
                            .getAll()
                            .size();
                }

                @Override
                protected void onPostExecute(Integer integer) {
                    super.onPostExecute(integer);
                    Toast.makeText(HomeActivity.this, "Found " + integer, Toast.LENGTH_LONG).show();
                }
            }.execute();

2

আপডেট: আমি যখন ডিএওর ভিতরে @ রাউকিউয়ারি এবং সাপোর্টএসকিউএলাইটকিউয়ারি ব্যবহার করে একটি কোয়েরি তৈরির চেষ্টা করছিলাম তখন আমি এই বার্তাটি পেয়েছি।

@Transaction
public LiveData<List<MyEntity>> getList(MySettings mySettings) {
    //return getMyList(); -->this is ok

    return getMyList(new SimpleSQLiteQuery("select * from mytable")); --> this is an error

সমাধান: ভিউমোডেলের ভিতরে ক্যোয়ারী তৈরি করুন এবং এটি ডিএওতে দিন।

public MyViewModel(Application application) {
...
        list = Transformations.switchMap(searchParams, params -> {

            StringBuilder sql;
            sql = new StringBuilder("select  ... ");

            return appDatabase.rawDao().getList(new SimpleSQLiteQuery(sql.toString()));

        });
    }

অথবা ...

আপনার মূল থ্রেডে সরাসরি ডাটাবেস অ্যাক্সেস করা উচিত নয়, উদাহরণস্বরূপ:

 public void add(MyEntity item) {
     appDatabase.myDao().add(item); 
 }

অপারেশনগুলি আপডেট, সংযোজন এবং মুছতে আপনার অ্যাসিঙ্কটাস্ক ব্যবহার করা উচিত।

উদাহরণ:

public class MyViewModel extends AndroidViewModel {

    private LiveData<List<MyEntity>> list;

    private AppDatabase appDatabase;

    public MyViewModel(Application application) {
        super(application);

        appDatabase = AppDatabase.getDatabase(this.getApplication());
        list = appDatabase.myDao().getItems();
    }

    public LiveData<List<MyEntity>> getItems() {
        return list;
    }

    public void delete(Obj item) {
        new deleteAsyncTask(appDatabase).execute(item);
    }

    private static class deleteAsyncTask extends AsyncTask<MyEntity, Void, Void> {

        private AppDatabase db;

        deleteAsyncTask(AppDatabase appDatabase) {
            db = appDatabase;
        }

        @Override
        protected Void doInBackground(final MyEntity... params) {
            db.myDao().delete((params[0]));
            return null;
        }
    }

    public void add(final MyEntity item) {
        new addAsyncTask(appDatabase).execute(item);
    }

    private static class addAsyncTask extends AsyncTask<MyEntity, Void, Void> {

        private AppDatabase db;

        addAsyncTask(AppDatabase appDatabase) {
            db = appDatabase;
        }

        @Override
        protected Void doInBackground(final MyEntity... params) {
            db.myDao().add((params[0]));
            return null;
        }

    }
}

আপনি যদি নির্বাচিত ক্রিয়াকলাপের জন্য লাইভ ডেটা ব্যবহার করেন তবে আপনার অ্যাসিঙ্কটাস্কের দরকার নেই।


1

দ্রুত প্রশ্নের জন্য আপনি রুমটিকে ইউআই থ্রেডে চালিত করার অনুমতি দিতে পারবেন।

AppDatabase db = Room.databaseBuilder(context.getApplicationContext(),
        AppDatabase.class, DATABASE_NAME).allowMainThreadQueries().build();

আমার ক্ষেত্রে তালিকাভুক্ত ক্লিক করা ব্যবহারকারীদের তালিকা থেকে ডেটাবেজে উপস্থিত রয়েছে কিনা তা খুঁজে বের করতে হয়েছিল। তা না হলে ব্যবহারকারী তৈরি করুন এবং অন্য ক্রিয়াকলাপ শুরু করুন

       @Override
        public void onClick(View view) {



            int position = getAdapterPosition();

            User user = new User();
            String name = getName(position);
            user.setName(name);

            AppDatabase appDatabase = DatabaseCreator.getInstance(mContext).getDatabase();
            UserDao userDao = appDatabase.getUserDao();
            ArrayList<User> users = new ArrayList<User>();
            users.add(user);
            List<Long> ids = userDao.insertAll(users);

            Long id = ids.get(0);
            if(id == -1)
            {
                user = userDao.getUser(name);
                user.setId(user.getId());
            }
            else
            {
                user.setId(id);
            }

            Intent intent = new Intent(mContext, ChatActivity.class);
            intent.putExtra(ChatActivity.EXTRAS_USER, Parcels.wrap(user));
            mContext.startActivity(intent);
        }
    }

1

আপনি ভবিষ্যত এবং কলযোগ্য ব্যবহার করতে পারেন। সুতরাং আপনাকে দীর্ঘ অ্যাসিনটাস্ক লেখার প্রয়োজন হবে না এবং অনুমতিমেনথ্রেডকুইয়ারীস () যুক্ত না করেই আপনার অনুসন্ধানগুলি সম্পাদন করতে পারেন।

আমার দাও ক্যোয়ারী: -

@Query("SELECT * from user_data_table where SNO = 1")
UserData getDefaultData();

আমার সংগ্রহস্থল পদ্ধতি: -

public UserData getDefaultData() throws ExecutionException, InterruptedException {

    Callable<UserData> callable = new Callable<UserData>() {
        @Override
        public UserData call() throws Exception {
            return userDao.getDefaultData();
        }
    };

    Future<UserData> future = Executors.newSingleThreadExecutor().submit(callable);

    return future.get();
}

এ কারণেই আমরা কলযোগ্য / ভবিষ্যত ব্যবহার করছি কারণ অ্যান্ড্রয়েড কোয়েরিগুলিকে মূল থ্রেডে চালানোর অনুমতি দেয় না। উপরের প্রশ্নোত্তরে জিজ্ঞাসা করা হয়েছে
শিক্ষানবিস

1
আমার অর্থ, যদিও আপনার উত্তর থেকে কোড ব্যাকগ্রাউন্ড থ্রেডে ক্যোয়ারী তৈরি করে, মূল থ্রেডটি অবরুদ্ধ এবং কোয়েরি শেষ হওয়ার পরে অপেক্ষা করছে। সুতরাং শেষে এটির চেয়ে বেশি ভাল কিছু হয় নাallowMainThreadQueries() । মূল থ্রেড এখনও উভয় ক্ষেত্রেই অবরুদ্ধ
ইউজিনিক

0

আমার মতে সঠিক কাজটি হ'ল RxJava ব্যবহার করে কোনও আইও থ্রেডে ক্যোয়ারীটি অর্পণ করা।

আমি সবেমাত্র सामना করেছি এমন একটি সমতুল্য সমস্যার সমাধানের একটি উদাহরণ আমার কাছে রয়েছে।

((ProgressBar) view.findViewById(R.id.progressBar_home)).setVisibility(View.VISIBLE);//Always good to set some good feedback
        Completable.fromAction(() -> {
            //Creating view model requires DB access
            homeViewModel = new ViewModelProvider(this, factory).get(HomeViewModel.class);
        }).subscribeOn(Schedulers.io())//The DB access executes on a non-main-thread thread
        .observeOn(AndroidSchedulers.mainThread())//Upon completion of the DB-involved execution, the continuation runs on the main thread
        .subscribe(
                () ->
                {
                    mAdapter = new MyAdapter(homeViewModel.getExams());
                    recyclerView.setAdapter(mAdapter);
                    ((ProgressBar) view.findViewById(R.id.progressBar_home)).setVisibility(View.INVISIBLE);
                },
                error -> error.printStackTrace()
        );

এবং যদি আমরা সমাধানটি সাধারণীকরণ করতে চাই:

((ProgressBar) view.findViewById(R.id.progressBar_home)).setVisibility(View.VISIBLE);//Always good to set some good feedback
        Completable.fromAction(() -> {
            someTaskThatTakesTooMuchTime();
        }).subscribeOn(Schedulers.io())//The long task executes on a non-main-thread thread
        .observeOn(AndroidSchedulers.mainThread())//Upon completion of the DB-involved execution, the continuation runs on the main thread
        .subscribe(
                () ->
                {
                    taskIWantToDoOnTheMainThreadWhenTheLongTaskIsDone();
                },
                error -> error.printStackTrace()
        );
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.