Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Клиент-серверное
взаимодействие под Android
        в деталях


                    Кирилл Зотин
                       Android dev lead
                            23.04.2011
Введение
• Преимущества родных приложений над
  мобильными версиями сайтов

• Специфика мобильных приложений:
      Document-ориентированая модель
                  VS
      Database-ориентированная модель
Преимущества над мобильными
          версиями сайтов.
•   Тесная интеграция с платформой
•   Улучшение поведения платформы
•   Фоновая работа
•   Более высокая скорость работы
•   "родной" пользовательский интерфейс
Document-ориентированая
     модель данных
Database-ориентированная
     модель данных
Типичная реализация
О проблеме
• OS в любой момент может
  остановить процесс
• Данные не всегда
  сохранены
Правильная реализация1:
      Service API
Правильная реализация2:
  Content Provider API
Network-MVC
Правильная реализация3:
Content Provider + SyncAdapter
Tips & Tricks
•   Выполняйте запросы к БД не в UI потоке
•   Используйте транзакции
•   Делайте код хорошо читаемым
•   Используйте Gzip сжатие
•   Android 3.0: Loaders
•   Удобный класс IntentService
Выполняйте запросы к БД
                 не в UI потоке
• Используйте (Notifying)AsyncQueryHandler
public class YourActivity extends Activity implements AsyncQueryListener {

    @Override
    public void onQueryComplete(int token, Object cookie,
            final Cursor cursor) {
        startManagingCursor(cursor);
        . . .
    }
}
Используйте транзакции
@Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
        throws OperationApplicationException {
    final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        final int numOperations = operations.size();
        final ContentProviderResult[] results = new ContentProviderResult[numOperations];
        for (int i = 0; i < numOperations; i++) {
            results[i] = operations.get(i).apply(this, results, i);
        }
        db.setTransactionSuccessful();
        return results;
    } finally {
        db.endTransaction();
    }
Группируйте детали запросов
                  в одном месте
Плохо                                                     Хорошо
Cursor mCursor = query(true,                              interface ReviewsQuery {
    DATABASE_TABLE_EMAIL,                                     final String[] PROJECTION = new String[] {
                                                                      Reviews._ID,
    new String[] {                                                    Reviews.TITLE,
        KEY_ROWID, KEY_EMAIL_NAME                                     Reviews.SUBTITLE,
    },                                                                Reviews.IMG_URL_SMALL,
    KEY_ROWID + "=" + rowId,                                          Reviews.ARTICLE_ID
    null, null, null, null, null);                                };
                                                              int ID = 0;
                                                              int TITLE = 1;
if (mCursor.moveToFirst()) {                                  int SUBTITLE = 2;
    long id = mCursor.getLong(                                int IMG_URL = 3;
        mCursor.getColumnIndexOrThrow(KEY_ROWID));            int ARTICLE_ID = 4;
    String name = mCursor.getString(
                                                              String DEFAULT_SORT = Reviews.DATE + " desc";
        mCursor.getColumnIndexOrThrow(KEY_EMAIL_NAME));   }
}                                                         ...
                                                          startAsyncQuery(QUERY_REVIEWS_ITEMS, null,
                                                                  Reviews.CONTENT_URI,
                                                                  ReviewsQuery.PROJECTION,
                                                                  selection, null,
                                                                  ReviewsQuery.DEFAULT_SORT);
                                                          …
                                                          final String url =
                                                               getCursor().getString(ReviewsQuery.IMG_URL);
Используйте сжатие Gzip
HttpGet request = new HttpGet(uri);
request.addHeader("Accept-Encoding", "gzip");

HttpResponse response = getHttpClient().execute(request);
Header contentEncoding = response.getFirstHeader("Content-Encoding");

if (contentEncoding != null &&
    contentEncoding.getValue().equalsIgnoreCase("gzip")) {
    is = new GZIPInputStream(is);
}
Honeycomb API: CursorLoader
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    getLoaderManager().initLoader(0, null, this);
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    final String eventId = getArguments().getString(EXTRA_EVENT_ID);
    return new CursorLoader(getActivity(),
            Events.CONTENT_URI,
            EventQuery.PROJECTION,
            EventQuery.SELECTION, new String[] {eventId}, null);
}
Использование IntentService
• Для обработки Intent стартует Service
• Запрос обрабатывается в отдельном потоке
• Параллельные запросы кладутся в одну
  очередь.
Выводы
• Не обрабатывайте сетевые операции в
  Activity, используйте Service
• Сохраняйте данные часто
• Все запросы к БД – в отдельный поток
• Минимизируйте трафик сетевых операций
• Используйте локальный кэш данных
Спасибо за внимание

          • kzotin@gmail.com
          • Skype: kzotin

More Related Content

Клиент-серверное взаимодействие под android в деталях

  • 1. Клиент-серверное взаимодействие под Android в деталях Кирилл Зотин Android dev lead 23.04.2011
  • 2. Введение • Преимущества родных приложений над мобильными версиями сайтов • Специфика мобильных приложений: Document-ориентированая модель VS Database-ориентированная модель
  • 3. Преимущества над мобильными версиями сайтов. • Тесная интеграция с платформой • Улучшение поведения платформы • Фоновая работа • Более высокая скорость работы • "родной" пользовательский интерфейс
  • 4. Document-ориентированая модель данных
  • 5. Database-ориентированная модель данных
  • 7. О проблеме • OS в любой момент может остановить процесс • Данные не всегда сохранены
  • 12. Tips & Tricks • Выполняйте запросы к БД не в UI потоке • Используйте транзакции • Делайте код хорошо читаемым • Используйте Gzip сжатие • Android 3.0: Loaders • Удобный класс IntentService
  • 13. Выполняйте запросы к БД не в UI потоке • Используйте (Notifying)AsyncQueryHandler public class YourActivity extends Activity implements AsyncQueryListener { @Override public void onQueryComplete(int token, Object cookie, final Cursor cursor) { startManagingCursor(cursor); . . . } }
  • 14. Используйте транзакции @Override public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); db.beginTransaction(); try { final int numOperations = operations.size(); final ContentProviderResult[] results = new ContentProviderResult[numOperations]; for (int i = 0; i < numOperations; i++) { results[i] = operations.get(i).apply(this, results, i); } db.setTransactionSuccessful(); return results; } finally { db.endTransaction(); }
  • 15. Группируйте детали запросов в одном месте Плохо Хорошо Cursor mCursor = query(true, interface ReviewsQuery { DATABASE_TABLE_EMAIL, final String[] PROJECTION = new String[] { Reviews._ID, new String[] { Reviews.TITLE, KEY_ROWID, KEY_EMAIL_NAME Reviews.SUBTITLE, }, Reviews.IMG_URL_SMALL, KEY_ROWID + "=" + rowId, Reviews.ARTICLE_ID null, null, null, null, null); }; int ID = 0; int TITLE = 1; if (mCursor.moveToFirst()) { int SUBTITLE = 2; long id = mCursor.getLong( int IMG_URL = 3; mCursor.getColumnIndexOrThrow(KEY_ROWID)); int ARTICLE_ID = 4; String name = mCursor.getString( String DEFAULT_SORT = Reviews.DATE + " desc"; mCursor.getColumnIndexOrThrow(KEY_EMAIL_NAME)); } } ... startAsyncQuery(QUERY_REVIEWS_ITEMS, null, Reviews.CONTENT_URI, ReviewsQuery.PROJECTION, selection, null, ReviewsQuery.DEFAULT_SORT); … final String url = getCursor().getString(ReviewsQuery.IMG_URL);
  • 16. Используйте сжатие Gzip HttpGet request = new HttpGet(uri); request.addHeader("Accept-Encoding", "gzip"); HttpResponse response = getHttpClient().execute(request); Header contentEncoding = response.getFirstHeader("Content-Encoding"); if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) { is = new GZIPInputStream(is); }
  • 17. Honeycomb API: CursorLoader @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getLoaderManager().initLoader(0, null, this); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { final String eventId = getArguments().getString(EXTRA_EVENT_ID); return new CursorLoader(getActivity(), Events.CONTENT_URI, EventQuery.PROJECTION, EventQuery.SELECTION, new String[] {eventId}, null); }
  • 18. Использование IntentService • Для обработки Intent стартует Service • Запрос обрабатывается в отдельном потоке • Параллельные запросы кладутся в одну очередь.
  • 19. Выводы • Не обрабатывайте сетевые операции в Activity, используйте Service • Сохраняйте данные часто • Все запросы к БД – в отдельный поток • Минимизируйте трафик сетевых операций • Используйте локальный кэш данных
  • 20. Спасибо за внимание • kzotin@gmail.com • Skype: kzotin