2012-02-16 3 views
0

Я не очень уверен, что этот вопрос для вас здесь, но я хочу спросить всех вас, ребята, которые действительно могут дать мне несколько советов о том, как лучше оптимизировать этот кусок кода , чтобы работать лучше и быстрее. То, что я делаю, это то, что я загружаю данные через Интернет как JSON, разбору и вставке в базу данных sqlite. Если строка json невелика, для меня нет большой проблемы, но когда мои json содержат много массивов и объектов в некоторых ситуациях, я жду, как 10-13 минут, для загрузки/разбора/вставки всех данных в базу данных , что слишком много времени.Android JSON парсер/вставка оптимизации базы данных

Код, который я показываю, является своего рода тестовым кодом, потому что я пытался реализовать InsertHelper, чтобы увидеть, будет ли разница в скорости, но результат будет таким же на данный момент. Вот код:

UserDatabaseHelper userDbHelper = RPCCommunicator.rpcUserDbHelper; 

    SQLiteDatabase db = userDbHelper.getWritableDatabase(); 
    InsertHelper ih = new InsertHelper(db, "cards"); 

    ih.prepareForInsert(); 
    //ContentValues values = new ContentValues(); 
    ContentValues valuess = new ContentValues(); 
    try { 
     int objectid = ih.getColumnIndex("objectId"); 
     ih.bind(objectid, objectId); 
     //values.put("objectId", objectId); 
     Log.d("", "ObjectId: " + objectId); 
     int objectoid = ih.getColumnIndex("objectOid"); 
     ih.bind(objectoid, objectOid); 
     //values.put("objectOid", objectOid); 

     String jsonData = new String(cardBuffer, "UTF-8"); 
     Log.d("JSONDATA", "JSONDATA VALID OR NOT : " + jsonData); 
     json = new JSONObject(jsonData); 
     JSONObject jsonObj = (JSONObject) new JSONTokener(jsonData).nextValue(); 

     int collectionID = ih.getColumnIndex("collectionId"); 
     int collectionId = Integer.parseInt(jsonObj.optString("collection_id","0")); 
     Log.d("Collection Id ", "Show Collection Id : " + collectionId); 
     if(collectionId!=0) 
      ih.bind(collectionID, collectionId); 

     //values.put("collectionId", collectionId); 

     int categoryID = ih.getColumnIndex("categoryId"); 
     int categoryId = Integer.parseInt(jsonObj.optString("category_id", "0")); 
     Log.d("Category Id ", "Show Category Id : " + categoryId); 
     if(categoryId!=0) 
      ih.bind(categoryID, categoryId); 
     //values.put("categoryId", categoryId); 

     int dateCreated = ih.getColumnIndex("dateCreated"); 
     String date = jsonObj.optString("date_created"); 
     if(date!=null) 
      ih.bind(dateCreated, date); 
     //values.put("dateCreated", date); 

     int titlee = ih.getColumnIndex("title"); 
     String title = jsonObj.optString("title"); 
     Log.d("Title", "Show Title : " + title); 
     if(title!=null) 
      ih.bind(titlee, title); 
     //values.put("title", title); 

     // ... some other variables to get from JSON 

     JSONObject stats = jsonObj.optJSONObject("statistics"); 

     if (jsonObj.has("statistics")) { 
      ContentValues values2 = new ContentValues(); 
      InsertHelper ihr = new InsertHelper(db, "cardstats"); 


      Iterator<Object> keys = stats.keys(); 
      while (keys.hasNext()) { 

       ihr.prepareForInsert(); 
       String key = (String) keys.next(); 
       JSONObject obj = new JSONObject(); 
       obj = stats.getJSONObject(key); 

       int paramId = Integer.parseInt(obj.optString("param_id")); 

       int cardIdTable = ihr.getColumnIndex("cardId"); 
       ihr.bind(cardIdTable, objectId); 

       values2.put("cardId", objectId); 

       int statKey = ihr.getColumnIndex("statKeyId"); 
       ihr.bind(statKey, paramId); 

       values2.put("statKeyId", paramId); 

       int catIdTable = ihr.getColumnIndex("catId"); 
       int catId = Integer.parseInt(obj.optString("cat_id")); 
       ihr.bind(catIdTable, catId); 

       values2.put("catId", catId); 

       int paramtitle = ihr.getColumnIndex("title"); 
       String paramTitle = obj.optString("param_title"); 
       ihr.bind(paramtitle, paramTitle); 

       values2.put("title", paramTitle); 

       String cardstats = "SELECT cardId , statKeyId FROM cardstats WHERE cardId="+objectId+" AND statKeyId="+catId; 
       Cursor cardStats = userDbHelper.executeSQLQuery(cardstats); 
       if(cardStats.getCount()==0){ 
        //userDbHelper.executeQuery("cardstats", values2); 
        ihr.execute(); 
       } else { 
        for(cardStats.moveToFirst(); cardStats.moveToNext(); cardStats.isAfterLast()){ 
         //int card = Integer.parseInt(cardStats.getString(cardStats.getColumnIndex("cardId"))); 
         int statId = Integer.parseInt(cardStats.getString(cardStats.getColumnIndex("statKeyId"))); 

         if(paramId != statId){ 
          ihr.execute(); 
          //userDbHelper.executeQuery("cardstats", values2); 
         } else { 
          userDbHelper.updateSQL("cardstats", values2, "cardId=?", new String[]{Integer.toString(objectId)}); 
         } 
        } 
       } 
       cardStats.close(); 

       //userDbHelper.executeQuery("cardstats", values2); 
      } 
     }// end if 

     String sql = "SELECT objectId FROM cards WHERE objectId = " + objectId; 
     Cursor cursor = userDbHelper.executeSQLQuery(sql); 
     if (cursor.getCount() == 0) { 
      ih.execute(); 
      //userDbHelper.executeQuery("cards", values); 
     } else { 
      for (cursor.move(0); cursor.moveToNext(); cursor.isAfterLast()) { 
       int objectID = Integer.parseInt(cursor.getString(cursor.getColumnIndex("objectId"))); 
       Log.d("","objectId : objectID - "+objectId+" "+objectID); 
       if (objectId != objectID) { 
        ih.execute(); 
        //userDbHelper.executeQuery("cards", values); 
       } else if(objectId == objectID){ 
        userDbHelper.updateSQL("cards", valuess, "objectId=?", new String[] {Integer.toString(objectId)}); 
       } 
      } 
     } 
     cursor.close(); 

    } catch (Exception e) { 
     e.printStackTrace(); 
     Log.d("Error", ": " + e); 
    } 
    db.close(); 
    return true; 
} 

* Edit: *

А вот как я сохранить двоичные данные (изображения), которые я получаю из интернета:

public static void saveToExternalStorage(String servername, int userId, String filename, byte[] buffer){ 
    try { 
     File myDir=new File("/sdcard/.Stampii/Users/"+servername+"/"+userId+"/Storage"); 
     myDir.mkdirs(); 

     File file = new File(myDir, filename); 
     FileOutputStream fos = new FileOutputStream(file); 
     fos.write(buffer); 
     fos.flush(); 
     fos.close(); 

    } catch (FileNotFoundException e){ 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

Таким образом, любой вид приветствуются предложения/советы, которые помогут мне улучшить этот фрагмент кода и заставить его работать быстрее.

Заранее благодарен!

+0

Какова производительность, если вы пытаетесь сделать большие куски в транзакциях ('SQLiteDatabase # beginTransaction()'), скажем, 100-300 вставок/обновлений поп-музыки? – Jens

+0

На самом деле в одном пакете, который я загружаю, у меня есть 250 вставок, и я получаю как 200 пакетов из них. Поэтому, если я использую ContentValues ​​или InsertHelper, разница составляет 10-15 секунд. –

+0

Ну, запуск SQL-транзакции повлияет как на «нормальные» вставки, так и на оптимизированную InsertHelper. Если это влияет на скорость, то, вероятно, требует тестирования. Вы приурочили, сколько времени вы тратите на SQLite, а не просто на загрузку и анализ данных? – Jens

ответ

1

В вашем случае задействованы две трудоемкие операции.

a. Загрузка данных в пакеты (при условии, что это HTTP). Для одного пакета это займет около 1-3 секунд в зависимости от латентности сети. Для 200 = 2X100 = 200 секунд ~ 3 минуты Вы можете сэкономить много секунд, если вы загрузите все данные, скажем, не более 3-5 обращений в оба конца.

b. Вставка базы данных Вам необходимо выполнить операцию с файлом, в частности, операцию записи файла, которая требует времени. Честно говоря, вы не можете много оптимизации здесь

Проверьте мой другой ответ here

+0

Фактически последние несколько пакетов представляют собой двоичные данные, которые я сохраняю на SD-карте, любые полезные советы о наилучшем способе их сохранения. Я отредактирую свой ответ с помощью функции, которую я пишу на SDCard. –

+0

Ваш метод сохранения изображения выглядит хорошо. Существует не так много, чтобы их улучшить, но, не считая этого в AsyncTask и работая в фоновом режиме. – PravinCG

+1

@Bombastic: вы никогда не должны жестко указывать «/ sdcard» на свои пути при записи на внешнее хранилище. Попробуйте 'Environment # getExternalStorageDirectory()' вместо этого и проверьте его состояние раньше. – Jens

2

Даже если у вас есть много HTTP-трафика (который вы, кажется, есть) вы можете оптимизировать использование базы данных.

Этот наивный пример, который делает 10000 вставок покажет вам масштаб улучшения мы говорим здесь:

public class BombasticActivity extends Activity { 
    DBHelper mHelper; 
    SQLiteDatabase mDb; 
    InsertHelper mInsertHelper; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     mHelper = new DBHelper(this); 
     mDb = mHelper.getWritableDatabase(); 
     mInsertHelper = new InsertHelper(mDb, "table1"); 
    } 
    @Override 
    protected void onStart() { 
     super.onStart(); 
     AsyncTask.SERIAL_EXECUTOR.execute(new MeasureTime(new Insert(10000, mInsertHelper))); 
     AsyncTask.SERIAL_EXECUTOR.execute(new MeasureTime(new DoInTransaction(mDb, new Insert(10000, mInsertHelper)))); 
    } 
    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     mInsertHelper.close(); 
     mDb.close(); 
     mHelper.close(); 
    } 
    static class MeasureTime implements Runnable { 
     final Runnable mAction; 
     MeasureTime(Runnable action) { 
      mAction = action; 
     } 
     public void run() { 
      final String name = mAction.getClass().getSimpleName(); 
      System.out.println("Starting action (" + name + ")"); 
      long t0 = System.currentTimeMillis(); 
      try { 
       mAction.run(); 
      } finally { 
       t0 = System.currentTimeMillis() - t0; 
       System.out.println("Time to complete action (" + name + "): " + t0 + "ms"); 
      } 
     } 
    } 
    static class DoInTransaction implements Runnable { 
     final Runnable mAction; 
     final SQLiteDatabase mDb; 
     DoInTransaction(SQLiteDatabase db, Runnable action) { 
      mAction = action; 
      mDb = db; 
     } 
     public void run() { 
      mDb.beginTransaction(); 
      try { 
       mAction.run(); 
       mDb.setTransactionSuccessful(); 
      } finally { 
       mDb.endTransaction(); 
      } 
     } 
    } 
    static class Insert implements Runnable { 
     final int mNumberOfInserts; 
     final InsertHelper mInsertHelper; 
     Insert(int numberOfInserts, InsertHelper insertHelper) { 
      mNumberOfInserts = numberOfInserts; 
      mInsertHelper = insertHelper; 
     } 
     public void run() { 
      Random rnd = new Random(0xDEADBEEF); 
      ContentValues values = new ContentValues(); 
      for (int i = 0; i < mNumberOfInserts; i++) { 
       values.put("text1", String.valueOf(rnd.nextDouble())); 
       values.put("text2", String.valueOf(rnd.nextFloat())); 
       values.put("text3", String.valueOf(rnd.nextLong())); 
       values.put("int1", rnd.nextInt()); 
       mInsertHelper.insert(values); 
       if (i % 200 == 0) { 
        System.out.println("Done " + i + " inserts"); 
       } 
      } 
     } 
    } 
} 

class DBHelper extends SQLiteOpenHelper { 
    DBHelper(Context context) { 
     super(context.getApplicationContext(), "bombastic", null, 1); 
    } 
    @Override 
    public void onCreate(SQLiteDatabase db) { 
     db.execSQL("CREATE TABLE table1 (_id INTEGER PRIMARY KEY AUTOINCREMENT, text1 TEXT, text2 TEXT, text3 TEXT, int1 INTEGER)"); 
    } 
    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    } 
} 

На ICS устройства (вы можете запустить его на Gingerbread, если начать нить или ThreadPool вместо того, чтобы злоупотреблять AsyncTask.SERIAL_EXECUTOR) не-транзакция занимает всего 4 минуты (229484 мс), а версия, выполняемая в транзакции, занимает около 3 секунд (2975 мс).

Итак, поставьте его в ближайшее время, сделайте много обновлений - сделайте это в транзакции.

Для оптимизации вашего HTTP вы должны убедиться, что вы поддерживаете HTTP-соединение в живых (сохраняете) и загружаете большие куски. Намного больше, чем те, которые вы сейчас делаете - если возможно, переключитесь на парсер JSON, который поддерживает чтение из потока вместо того, чтобы загружать всю вещь в String перед ее разбором.

Смежные вопросы