2011-04-21 4 views
12

यहां और वेब विस्तार के विभिन्न उत्तरों के अनुसार आवेदन और इसकी विरासत विधि getDatabasePath() मानक आंतरिक मेमोरी स्थान से डेटाबेस आकार को बड़े आकार के एक एसडी-कार्ड में सेट करने की अनुमति देगी।एसडी-कार्ड पर डेटाबेस के साथ SQLiteOpenHelper का उपयोग कैसे करें?

यह मेरे लिए काम नहीं कर रहा है। सुझाया गया निर्माण अभी भी आंतरिक मेमोरी पर डेटाबेस का उपयोग कर रहा है। असल में विधि getDatabasePath() को SQLiteOpenHelper द्वारा कभी नहीं बुलाया जाता है।

मैं इसे प्राप्त करना और चलाना चाहता हूं।

यहाँ मैं अब तक क्या किया है:

1.) का विस्तार आवेदन:

public class MyApplication extends Application { 

    @Override 
    public File getDatabasePath(String name) { 
    // Just a test 
    File file = super.getDatabasePath(name); 

    return file; 
    } 

    @Override 
    public void onCreate() { 
    // Just a test 
    super.onCreate(); 
    } 
} 

2.) प्रकट करने के लिए विस्तारित आवेदन जोड़ना:

<application 
    ... 
    android:name="MyApplication" 
    ... > 

3.) विस्तार और SQLiteOpenHelper का उपयोग करके:

public class MySqliteOpenHelper extends SQLiteOpenHelper { 

    public void onCreate(SQLiteDatabase sqliteDatabase) { 
    ... 
    } 

    @Override 
    public void onUpgrade(SQLiteDatabase sqliteDatabase, int oldVersion, int newVersion) { 
    ... 
    } 
} 

4.) हमेशा की तरह मेरी गतिविधियों में विस्तारित SQLiteOpenHelper का उपयोग करना:

public class MyActivity extends Activity { 

    private MySqliteOpenHelper mySqliteOpenHelper; 
    private SQLiteDatabase  sqliteDatabase; 

    @Override 
    public void onCreate(Bundle bundle) { 
    super.onCreate(bundle); 
    ... 
    mySqliteOpenHelper = new MySqliteOpenHelper(getApplicationContext()); 
    sqliteDatabase = mySqliteOpenHelper.getReadableDatabase(); 
    ... 
    } 

    @Override 
    protected void onDestroy() { 
    if (mySqliteOpenHelper != null) { 
     mySqliteOpenHelper.close(); 
     mySqliteOpenHelper = null; 
    } 

    super.onDestroy(); 
    } 
} 

मुझे लगता है कि विस्तारित आवेदन वर्ग सामान्य रूप में काम कर रहा है का कहना है करना चाहते हैं। मैं इसे देख सकता हूं क्योंकि MyAplication.onCreate() को कॉल किया जाता है। लेकिन MyApplication.getDatabasePath() को नहीं कहा जाता है।

किसी भी मदद की अत्यधिक सराहना की जाती है।

+0

एसडी कार्ड में एक सादा स्क्लाइट डेटाबेस फ़ाइल को सहेजना सुरक्षित नहीं है। एन्क्रिप्टेड एक कैसे प्राप्त करें के समाधान के साथ एक लिंक यहां दिया गया है: https://github.com/sqlcipher/android-database-sqlcipher/issues/67 –

उत्तर

0

इस कार्यप्रणाली को कॉल SqliteOpen सहायक वर्ग

public dbOperation open() throws SQLException 
    { 
     db = DBHelper.getWritableDatabase(); 
     return this; 
    } 

OnCreate विधि इस

 public void onCreate(SQLiteDatabase db) 
     { 
      try { 
       db.execSQL(DATABASE_CREATE); 

      } catch (Exception e) { 
        e.printStackTrace(); 
      } 
     } 

DATABASE_CREATE की तरह है में onCreate विधि लागू करेगा स्ट्रिंग जो डेटाबेस

बनाने के लिए क्वेरी शामिल है
+0

मेरे पास आंतरिक मेमोरी पर काम करने वाले बहुत सारे डेटाबेस हैं। मुद्दा यह है कि सिफारिशों के मुताबिक ऊपर वर्णित चरणों को बाहरी मेमोरी (एसडी-कार्ड) पर डेटाबेस-फाइल बनाना चाहिए - और यह काम नहीं कर रहा है। फिर भी धन्यवाद। –

0

आपका डेटाबेस अपनी आंतरिक मेमोरी में रखा जाता है ताकि अन्य एप्लिकेशन इसका उपयोग न कर सकें और डेटा को बदल/भ्रष्ट कर सकें।

एंड्रॉइड डेटाबेस का डिफ़ॉल्ट पथ/डेटा/डेटा/APPLICATIONPACKAGENAME/डेटाबेस/है। फ़ाइल में अपने डेटाबेस को स्टोर करने के तरीके के बारे में निम्नलिखित एक बहुत अच्छी मार्गदर्शिका है और फिर इसे रन टाइम पर पॉप्युलेट करें।

Article

+0

मेरा बुरा अंग्रेजी होना चाहिए कि हर कोई डेटाबेस का उपयोग कैसे करें या मुझे एसडी-कार्ड पर डेटाबेस फ़ाइलों को क्यों संग्रहीत नहीं करना चाहिए ;-) मैं बस एसडी-कार्ड पर डेटाबेस बनाने का तरीका जानना चाहता था। इस बीच मुझे समस्या मिली और मुझे ऐसा करने का एक तरीका मिला। मुझे तत्काल कारण देखने के लिए मूल SQLiteOpenHelper कोड के माध्यम से पढ़ना पड़ा और मैं इसके आसपास कैसे काम कर सकता हूं।फिर भी धन्यवाद। –

+0

जब आपको कोई समाधान मिला तो क्या आप इसे उत्तर के रूप में पोस्ट कर सकते थे। मुझे आपकी दिलचस्पी होगी कि आप कैसे समस्या हल करते हैं। Thx – Flo

+1

getWritableDatabase() getDatabasePath() को कॉल नहीं करता है। इसे केवल getReadableDatabase() के भीतर ही बुलाया जाता है। लेकिन getReadableDatabase() स्वयं getWriteableDatabase() को कॉल करता है और यदि वह उस भाग को सफल करता है जो getDatabasePath() को कभी भी उपयोग नहीं करता है। तो मैं वर्तमान में क्या कर रहा हूं अमूर्त वर्ग SQLiteOpenHelper को डुप्लिकेट करना और उस पंक्ति को बदलना है। हां, मुझे परिणामों के बारे में पता है, सुरक्षा जोखिमों के बारे में जिन्हें उपयोगकर्ताओं को ठीक क्लिक करने की आवश्यकता है और मैं सामान्य रूप से SQLiteOpenHelper के साथ रहना चाहता हूं। मैं इसे उत्तर के रूप में पोस्ट नहीं करता क्योंकि यह कोई समाधान नहीं है जिसे मैं किसी को भी सुझाव दूंगा। –

3

इस कोड मेरे समान समस्या तय, अपने आवेदन वर्ग:

@Override 
public File getDatabasePath(String name) { 
    File result = new File(getExternalFilesDir(null), name); 
    return result; 
} 

@Override 
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { 
    return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory); 
} 

आशा है कि यह आप में मदद मिलेगी।

3

अच्छा, मुझे लगता है कि आप ऐसा नहीं कर सकते हैं। अगर कोई रास्ता जानता है, तो कृपया हमें बताएं कि कैसे।

String path = mContext.getDatabasePath(mName).getPath(); 

सभी अच्छे:

तो जब आप

mySqliteOpenHelper.getReadableDatabase(); 

बुला रहे हैं यह सब के रूप में अगर हम implementation को देखो हम देखते हैं कि ठीक होना चाहिए। लेकिन अगर हम ऊपर एक नज़र कुछ लाइनों ले:

return getWritableDatabase(); 

तो यह वास्तव में एक और तरीका बुला रहा है, और अगर यह विफल रहता है, उसके बाद ही यह getDatabasePath() का उपयोग करने के procedes।
तो getWritableDatabase के कार्यान्वयन पर सुबह देखो - हम स्पष्ट रूप से देख सकते हैं कि यह getDatabasePath लेकिन इसके बजाय का उपयोग नहीं करता:

db = mContext.openOrCreateDatabase(mName, 0, mFactory); 

यह देखना चाहते हैं कि openOrCreateDatabase कार्यान्वित किया जाता है हमें लाता है के लिए हम ContextImpl.java पर उसकी समीक्षा करेंगे

if (name.charAt(0) == File.separatorChar) { 
      String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar)); 
      dir = new File(dirPath); 
      name = name.substring(name.lastIndexOf(File.separatorChar)); 
      f = new File(dir, name); 
     } else { 
      dir = getDatabasesDir(); 
      f = makeFilename(dir, name); 
     } 

तो हम देख सकते हैं कि यह सहायक विधि validateFilePath फ़ाइल रिटर्न अगर यह एक पूर्ण पथ (जैसे/कुछ/सही मायने में/पूर्ण/पथ) हो जाता है या फ़ाइल नाम के साथ getDatabasesDir() concat की कोशिश करता है। getDatabasesDir() कार्यान्वयन getDataDirFile() का उपयोग करता है जो सार्वजनिक है और शायद सिद्धांत में ओवरराइट किया जा सकता है .. लेकिन आपको जांच करनी होगी।

वर्तमान में मैं दो समाधान देखें:

1) आप लिखते हैं केवल पढ़ने के लिए मोड में पहुँच बल SQLite db की जरूरत नहीं है, getWritableDatabase असफल हो जायेगी और getDatabasePath बुलाया जाएगा
2) SQLiteOpenHelper निर्माता में पूर्ण पथ में दर्रा , और सुनिश्चित करें db लिखने योग्य है की तरह कुछ बनाने के लिए,:

public class MyDbOpenHelper extends SQLiteOpenHelper { 

    public MyDbOpenHelper(final Context context) { 
     super(context, Environment.getExternalStorageDirectory() 
       + "/path/to/database/on/sdcard/database.sqlite", null, 1); 
    } 

यह वास्तव में मेरे लिए कोई मतलब नहीं है, लेकिन एंड्रॉयड स्रोतों को देख (कम से कम 2.3.1) ऐसा लगता है इस तरह से इसे लागू किया जाता है।

+0

धन्यवाद। मैंने लिखा है कि एक टिप्पणी में कई उम्र –

+0

यहां से कई पेज (उम्र नहीं) नीचे से हैं। यह सही काम कर रहा है। –

+0

धन्यवाद आपने अपना समय बचाया .. आसानी से काम किया –

11

मैंने पाया कि मैं एंड्रॉइड 2.2 में एक पूर्ण पथ का उपयोग कर सकता हूं, लेकिन 2.1 में Context.openOrCreateDatabase() विधि ने अपवाद फेंक दिया। इस के आसपास काम करने के लिए मैंने SQLiteDatabase.openOrCreateDatabase() को सीधे कॉल करने के लिए उस विधि को लपेट लिया। यहाँ मेरी विस्तारित SQLOpenHelper

public class Database extends SQLiteOpenHelper { 
    public Database(Context context) { 
    super(new ContextWrapper(context) { 
     @Override public SQLiteDatabase openOrCreateDatabase(String name, 
       int mode, SQLiteDatabase.CursorFactory factory) { 

      // allow database directory to be specified 
      File dir = new File(DIR); 
      if(!dir.exists()) { 
       dir.mkdirs(); 
      } 
      return SQLiteDatabase.openDatabase(DIR + "/" + NAME, null, 
       SQLiteDatabase.CREATE_IF_NECESSARY); 
     } 
    }, NAME, null, VERSION); 
    this.context = context; 
    } 
} 
+0

धन्यवाद! यह मेरे लिए काम किया। +1 क्योंकि मुझे इस उत्कृष्ट उत्तर को देखने के लिए अब तक नीचे स्क्रॉल करना पड़ा था। (+1 इसे ऊपर ले जाना चाहिए)। – braden

+0

मैंने इस समाधान का उपयोग किया और यह तब तक काम किया जब तक डिवाइस को एंड्रॉइड 4.0.3 में अपग्रेड नहीं किया गया। इसके बाद उसने आंतरिक भंडारण का उपयोग शुरू किया, एसडी कार्ड नहीं। ट्रेसिंग में, ऊपर openOrCreateDatabase विधि _not_ कहा गया था, और SQLiteDatabaseHelper में ट्रेसिंग में यह गलत रेखा पर था (2.3.3 और 4.0.3 दोनों में, जिसमें अलग कोड है) यह देखने में मुश्किल हो रही थी कि क्या हो रहा था। F5 को बार-बार टाइप करके, मैं ContextImpl.openOrCreateDatabase() पर पहुंचने में सक्षम था, लेकिन स्रोत कोड उपलब्ध नहीं था। एक बग की तरह लग रहा है। नीचे की रेखा यह विधि अब काम नहीं करती है। मैंने इसे ठीक करने के लिए अपना स्वयं का SQLiteOpenHelper लिखा था। –

+0

बाद में: मैंने अपने SQLiteOpenHelper के लिए एक उत्तर के रूप में कोड जोड़ा। –

5

SQLOpenHelper नए सिरे से लिखना संदर्भ एसडी कार्ड निर्देशिका के बजाय का उपयोग करने के लिए और फिर विस्तार है कि मेरे लिए काम करने लगता है के लिए निर्माता है।

import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteDatabase.CursorFactory; 
import android.database.sqlite.SQLiteException; 
import android.util.Log; 

/** 
* SDCardSQLiteOpenhelper is a class that is based on SQLiteOpenHelper except 
* that it does not use the context to get the database. It was written owing to 
* a bug in Android 4.0.3 so that using a ContextWrapper to override 
* openOrCreateDatabase, as was done with Android 2.3.3, no longer worked. <br> 
* <br> 
* The mContext field has been replaced by mDir. It does not use lock on the 
* database as that method is package private to 
* android.database.sqlite.SQLiteDatabase. Otherwise the implementation is 
* similar.<br> 
* <br> 
* 
* @see android.database.sqlite.SQLiteOpenHelper 
*/ 
public abstract class SDCardSQLiteOpenHelper { 
    private static final String TAG = SDCardSQLiteOpenHelper.class 
      .getSimpleName(); 

    // private final Context mContext; 
    private final String mName; 
    private final String mDir; 
    private final CursorFactory mFactory; 
    private final int mNewVersion; 

    private SQLiteDatabase mDatabase = null; 
    private boolean mIsInitializing = false; 

    /** 
    * Create a helper object to create, open, and/or manage a database. This 
    * method always returns very quickly. The database is not actually created 
    * or opened until one of {@link #getWritableDatabase} or 
    * {@link #getReadableDatabase} is called. 
    * 
    * @param dir 
    *   the directory on the SD card. It must exist and the SD card 
    *   must be available. The caller should check this. 
    * @param name 
    *   of the database file, or null for an in-memory database 
    * @param factory 
    *   to use for creating cursor objects, or null for the default 
    * @param version 
    *   number of the database (starting at 1); if the database is 
    *   older, {@link #onUpgrade} will be used to upgrade the 
    *   database; if the database is newer, {@link #onDowngrade} will 
    *   be used to downgrade the database 
    */ 
    public SDCardSQLiteOpenHelper(String dir, String name, 
      CursorFactory factory, int version) { 
     if (version < 1) 
      throw new IllegalArgumentException("Version must be >= 1, was " 
        + version); 
     // mContext = context; 
     mDir = dir; 
     mName = name; 
     mFactory = factory; 
     mNewVersion = version; 
    } 

    /** 
    * Return the name of the SQLite database being opened, as given to the 
    * constructor. 
    */ 
    public String getDatabaseName() { 
     return mName; 
    } 

    /** 
    * Create and/or open a database that will be used for reading and writing. 
    * The first time this is called, the database will be opened and 
    * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be 
    * called. 
    * 
    * <p> 
    * Once opened successfully, the database is cached, so you can call this 
    * method every time you need to write to the database. (Make sure to call 
    * {@link #close} when you no longer need the database.) Errors such as bad 
    * permissions or a full disk may cause this method to fail, but future 
    * attempts may succeed if the problem is fixed. 
    * </p> 
    * 
    * <p class="caution"> 
    * Database upgrade may take a long time, you should not call this method 
    * from the application main thread, including from 
    * {@link android.content.ContentProvider#onCreate 
    * ContentProvider.onCreate()}. 
    * 
    * @throws SQLiteException 
    *    if the database cannot be opened for writing 
    * @return a read/write database object valid until {@link #close} is called 
    */ 
    public synchronized SQLiteDatabase getWritableDatabase() { 
     if (mDatabase != null) { 
      if (!mDatabase.isOpen()) { 
       // darn! the user closed the database by calling 
       // mDatabase.close() 
       mDatabase = null; 
      } else if (!mDatabase.isReadOnly()) { 
       return mDatabase; // The database is already open for business 
      } 
     } 

     if (mIsInitializing) { 
      throw new IllegalStateException(
        "getWritableDatabase called recursively"); 
     } 

     // If we have a read-only database open, someone could be using it 
     // (though they shouldn't), which would cause a lock to be held on 
     // the file, and our attempts to open the database read-write would 
     // fail waiting for the file lock. To prevent that, we acquire the 
     // lock on the read-only database, which shuts out other users. 

     boolean success = false; 
     SQLiteDatabase db = null; 
     // NOT AVAILABLE 
     // if (mDatabase != null) { 
     // mDatabase.lock(); 
     // } 
     try { 
      mIsInitializing = true; 
      if (mName == null) { 
       db = SQLiteDatabase.create(null); 
      } else { 
       String path = mDir + "/" + mName; 
       // db = mContext.openOrCreateDatabase(mName, 0, mFactory, 
       // mErrorHandler); 
       db = SQLiteDatabase.openDatabase(path, null, 
         SQLiteDatabase.CREATE_IF_NECESSARY); 
      } 

      int version = db.getVersion(); 
      if (version != mNewVersion) { 
       db.beginTransaction(); 
       try { 
        if (version == 0) { 
         onCreate(db); 
        } else { 
         if (version > mNewVersion) { 
          onDowngrade(db, version, mNewVersion); 
         } else { 
          onUpgrade(db, version, mNewVersion); 
         } 
        } 
        db.setVersion(mNewVersion); 
        db.setTransactionSuccessful(); 
       } finally { 
        db.endTransaction(); 
       } 
      } 

      onOpen(db); 
      success = true; 
      return db; 
     } finally { 
      mIsInitializing = false; 
      if (success) { 
       if (mDatabase != null) { 
        try { 
         mDatabase.close(); 
        } catch (Exception e) { 
         // Do nothing 
        } 
        // NOT AVAILABLE 
        // mDatabase.unlock(); 
       } 
       mDatabase = db; 
      } else { 
       // NOT AVAILABLE 
       // if (mDatabase != null) { 
       // mDatabase.unlock(); 
       // } 
       if (db != null) 
        db.close(); 
      } 
     } 
    } 

    /** 
    * Create and/or open a database. This will be the same object returned by 
    * {@link #getWritableDatabase} unless some problem, such as a full disk, 
    * requires the database to be opened read-only. In that case, a read-only 
    * database object will be returned. If the problem is fixed, a future call 
    * to {@link #getWritableDatabase} may succeed, in which case the read-only 
    * database object will be closed and the read/write object will be returned 
    * in the future. 
    * 
    * <p class="caution"> 
    * Like {@link #getWritableDatabase}, this method may take a long time to 
    * return, so you should not call it from the application main thread, 
    * including from {@link android.content.ContentProvider#onCreate 
    * ContentProvider.onCreate()}. 
    * 
    * @throws SQLiteException 
    *    if the database cannot be opened 
    * @return a database object valid until {@link #getWritableDatabase} or 
    *   {@link #close} is called. 
    */ 
    public synchronized SQLiteDatabase getReadableDatabase() { 
     if (mDatabase != null) { 
      if (!mDatabase.isOpen()) { 
       // darn! the user closed the database by calling 
       // mDatabase.close() 
       mDatabase = null; 
      } else { 
       return mDatabase; // The database is already open for business 
      } 
     } 

     if (mIsInitializing) { 
      throw new IllegalStateException(
        "getReadableDatabase called recursively"); 
     } 

     try { 
      return getWritableDatabase(); 
     } catch (SQLiteException e) { 
      if (mName == null) 
       throw e; // Can't open a temp database read-only! 
      Log.e(TAG, "Couldn't open " + mName 
        + " for writing (will try read-only):", e); 
     } 

     SQLiteDatabase db = null; 
     try { 
      mIsInitializing = true; 
      // String path = mContext.getDatabasePath(mName).getPath(); 
      String path = mDir + "/" + mName; 

      db = SQLiteDatabase.openDatabase(path, mFactory, 
        SQLiteDatabase.OPEN_READONLY); 
      if (db.getVersion() != mNewVersion) { 
       throw new SQLiteException(
         "Can't upgrade read-only database from version " 
           + db.getVersion() + " to " + mNewVersion + ": " 
           + path); 
      } 

      onOpen(db); 
      Log.w(TAG, "Opened " + mName + " in read-only mode"); 
      mDatabase = db; 
      return mDatabase; 
     } finally { 
      mIsInitializing = false; 
      if (db != null && db != mDatabase) 
       db.close(); 
     } 
    } 

    /** 
    * Close any open database object. 
    */ 
    public synchronized void close() { 
     if (mIsInitializing) 
      throw new IllegalStateException("Closed during initialization"); 

     if (mDatabase != null && mDatabase.isOpen()) { 
      mDatabase.close(); 
      mDatabase = null; 
     } 
    } 

    /** 
    * Called when the database is created for the first time. This is where the 
    * creation of tables and the initial population of the tables should 
    * happen. 
    * 
    * @param db 
    *   The database. 
    */ 
    public abstract void onCreate(SQLiteDatabase db); 

    /** 
    * Called when the database needs to be upgraded. The implementation should 
    * use this method to drop tables, add tables, or do anything else it needs 
    * to upgrade to the new schema version. 
    * 
    * <p> 
    * The SQLite ALTER TABLE documentation can be found <a 
    * href="http://sqlite.org/lang_altertable.html">here</a>. If you add new 
    * columns you can use ALTER TABLE to insert them into a live table. If you 
    * rename or remove columns you can use ALTER TABLE to rename the old table, 
    * then create the new table and then populate the new table with the 
    * contents of the old table. 
    * 
    * @param db 
    *   The database. 
    * @param oldVersion 
    *   The old database version. 
    * @param newVersion 
    *   The new database version. 
    */ 
    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, 
      int newVersion); 

    /** 
    * Called when the database needs to be downgraded. This is stricly similar 
    * to onUpgrade() method, but is called whenever current version is newer 
    * than requested one. However, this method is not abstract, so it is not 
    * mandatory for a customer to implement it. If not overridden, default 
    * implementation will reject downgrade and throws SQLiteException 
    * 
    * @param db 
    *   The database. 
    * @param oldVersion 
    *   The old database version. 
    * @param newVersion 
    *   The new database version. 
    */ 
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
     throw new SQLiteException("Can't downgrade database from version " 
       + oldVersion + " to " + newVersion); 
    } 

    /** 
    * Called when the database has been opened. The implementation should check 
    * {@link SQLiteDatabase#isReadOnly} before updating the database. 
    * 
    * @param db 
    *   The database. 
    */ 
    public void onOpen(SQLiteDatabase db) { 
    } 
} 

यह तब किया गया जब रोजर केज़ द्वारा ऊपर वर्णित विधि एंड्रॉइड 4.0.3 पर काम करना बंद कर दिया गया।

+0

योगदान के लिए धन्यवाद लेकिन यदि आप एक एन्क्रिप्टेड डेटाबेस (https://github.com/sqlcipher/android-database-sqlcipher/issues/67) का उपयोग करना चाहते हैं, तो यदि आप एसडी कार्ड में बचत कर रहे हैं तो अधिक समझ में आता है, तो आप नहीं कर सकते अपने समाधान का प्रयोग करें। क्या आपको एक और अधिक सुरुचिपूर्ण समाधान मिला है? –

+0

धन्यवाद। इससे बहुत मदद मिली। :) – Sufian

+0

@ जॉर्ज लॉजिस्ट यदि आप अपने डीबी को फोन मेमोरी में रखते हैं, तो भी उपयोगकर्ता इसे रूट डिवाइस से खोल सकता है। मुझे लगता है कि आप एन्क्रिप्शन के लिए [SecurePreferences] (https://github.com/sveinungkb/encrypted-userprefs) जैसे कुछ उपयोग कर सकते हैं। – Sufian