Павел Шмыголь (Provectus): Несколько приемов разработки от...

14
Несколько приемов разработки от Google https://github.com/google/iosched.git

Transcript of Павел Шмыголь (Provectus): Несколько приемов разработки от...

Page 1: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

Несколько приемов разработки от Google

https://github.com/google/iosched.git

Page 2: Павел Шмыголь (Provectus): Несколько приемов разработки от Google
Page 3: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

SyncAdapter

<service android:name=".sync.SyncService" android:exported="true" tools:ignore="ExportedService"> <intent-filter> <action android:name="android.content.SyncAdapter" /> </intent-filter> <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" /></service>

Page 4: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

@xml/syncadapter

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="com.google.samples.apps.iosched" android:accountType="com.google" android:supportsUploading="false" android:userVisible="true" />

Page 5: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

public class SyncService extends Service { private static final Object sSyncAdapterLock = new Object(); private static SyncAdapter sSyncAdapter = null;

@Override public void onCreate() { synchronized (sSyncAdapterLock) { if (sSyncAdapter == null) { sSyncAdapter = new SyncAdapter(getApplicationContext(), false); } } }

@Override public IBinder onBind(Intent intent) { return sSyncAdapter.getSyncAdapterBinder(); }}

Page 6: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

public class SyncAdapter extends AbstractThreadedSyncAdapter { private static final String TAG = makeLogTag(SyncAdapter.class);

private static final Pattern sSanitizeAccountNamePattern = Pattern.compile("(.).*?(.?)@"); public static final String EXTRA_SYNC_USER_DATA_ONLY = "com.google.samples.apps.iosched.EXTRA_SYNC_USER_DATA_ONLY";;

private final Context mContext;

public SyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); mContext = context;

//noinspection ConstantConditions,PointlessBooleanExpression if (!BuildConfig.DEBUG) { Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable throwable) { LOGE(TAG, "Uncaught sync exception, suppressing UI in release build.", throwable); } }); } }

Page 7: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

@Override public void onPerformSync(final Account account, Bundle extras, String authority, final ContentProviderClient provider, final SyncResult syncResult) { final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); final boolean initialize = extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false); final boolean userDataOnly = extras.getBoolean(EXTRA_SYNC_USER_DATA_ONLY, false);

final String logSanitizedAccountName = sSanitizeAccountNamePattern .matcher(account.name).replaceAll("$1...$2@");

if (uploadOnly) { return; }

LOGI(TAG, "Beginning sync for account " + logSanitizedAccountName + "," + " uploadOnly=" + uploadOnly + " manualSync=" + manualSync + " userDataOnly =" + userDataOnly + " initialize=" + initialize);

// Sync from bootstrap and remote data, as needed new SyncHelper(mContext).performSync(syncResult, account, extras); }

}

Page 8: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

public static void updateSyncInterval(final Context context, final Account account) { LOGD(TAG, "Checking sync interval for " + account); long recommended = calculateRecommendedSyncInterval(context); long current = PrefUtils.getCurSyncInterval(context); LOGD(TAG, "Recommended sync interval " + recommended + ", current " + current); if (recommended != current) { LOGD(TAG, "Setting up sync for account " + account + ", interval " + recommended + "ms"); ContentResolver.setIsSyncable(account, ScheduleContract.CONTENT_AUTHORITY, 1); ContentResolver.setSyncAutomatically(account, ScheduleContract.CONTENT_AUTHORITY, true); ContentResolver.addPeriodicSync(account, ScheduleContract.CONTENT_AUTHORITY, new Bundle(), recommended / 1000L); PrefUtils.setCurSyncInterval(context, recommended); } else { LOGD(TAG, "No need to update sync interval."); }}public static void requestManualSync(Account chosenAccount) { if (chosenAccount != null) { Bundle b = new Bundle(); b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); ContentResolver.requestSync(chosenAccount, ScheduleContract.CONTENT_AUTHORITY, b); } else { LOGD(TAG, "Can't request manual sync -- no chosen account."); }}

Page 9: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

public class SyncUserCommand extends GCMCommand { private static final String TAG = makeLogTag("TestCommand"); private static final int DEFAULT_TRIGGER_SYNC_DELAY = 0; // default to immediately sync

@Override public void execute(Context context, String type, String extraData) { ... scheduleSync(context, syncJitter); }

private void scheduleSync(Context context, int syncDelay) {

final Intent intent = new Intent(context, TriggerSyncReceiver.class); intent.putExtra(TriggerSyncReceiver.EXTRA_USER_DATA_SYNC_ONLY, true); ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).set( AlarmManager.RTC, System.currentTimeMillis() + syncDelay, PendingIntent.getBroadcast( context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) );

}}

Page 10: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

SQLite FTS3 and FTS4 Extensions

FTS3 and FTS4 are SQLite virtual table modules that allows users to perform full-text searches on a set of documents. The most common (and effective) way to describe full-text searches is "what Google, Yahoo, and Bing do with documents placed on the World Wide Web". Users input a term, or series of terms, perhaps connected by a binary operator or grouped together into a phrase, and the full-text query system finds the set of documents that best matches those terms considering the operators and groupings the user has specified.

Then either of the two queries below may be executed to find the number of documents in the database that contain the word "linux" (351). Using one desktop PC hardware configuration, the query on the FTS3 table returns in approximately 0.03 seconds, versus 22.5 for querying the ordinary table.

Page 11: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

// Full-text search index. Update using updateSessionSearchIndex method.// Use the porter tokenizer for simple stemming, so that "frustration" matches "frustrated."db.execSQL("CREATE VIRTUAL TABLE " + Tables.SESSIONS_SEARCH + " USING fts3(" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + SessionsSearchColumns.BODY + " TEXT NOT NULL," + SessionsSearchColumns.SESSION_ID + " TEXT NOT NULL " + References.SESSION_ID + "," + "UNIQUE (" + SessionsSearchColumns.SESSION_ID + ") ON CONFLICT REPLACE," + "tokenize=porter)");

Page 12: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

static void updateSessionSearchIndex(SQLiteDatabase db) { db.execSQL("DELETE FROM " + Tables.SESSIONS_SEARCH);

db.execSQL("INSERT INTO " + Qualified.SESSIONS_SEARCH + " SELECT s." + Sessions.SESSION_ID + ",("

// Full text body + Sessions.SESSION_TITLE + "||'; '||" + Sessions.SESSION_ABSTRACT + "||'; '||" + "IFNULL(GROUP_CONCAT(t." + Speakers.SPEAKER_NAME + ",' '),'')||'; '||" + "'')"

+ " FROM " + Tables.SESSIONS + " s " + " LEFT OUTER JOIN"

// Subquery resulting in session_id, speaker_id, speaker_name + "(SELECT " + Sessions.SESSION_ID + "," + Qualified.SPEAKERS_SPEAKER_ID + "," + Speakers.SPEAKER_NAME + " FROM " + Tables.SESSIONS_SPEAKERS + " INNER JOIN " + Tables.SPEAKERS + " ON " + Qualified.SESSIONS_SPEAKERS_SPEAKER_ID + "=" + Qualified.SPEAKERS_SPEAKER_ID + ") t"

// Grand finale + " ON s." + Sessions.SESSION_ID + "=t." + Sessions.SESSION_ID + " GROUP BY s." + Sessions.SESSION_ID);}

Page 13: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

case SESSIONS_SEARCH: { //ScheduleProvider.query() final String query = Sessions.getSearchQuery(uri); return builder.table(Tables.SESSIONS_SEARCH_JOIN_SESSIONS_ROOMS, getCurrentAccountName(uri, true)) .map(Sessions.SEARCH_SNIPPET, Subquery.SESSIONS_SNIPPET) .mapToTable(Sessions._ID, Tables.SESSIONS) .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS) .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS) .map(Sessions.SESSION_IN_MY_SCHEDULE, "IFNULL(in_schedule, 0)") .where(SessionsSearchColumns.BODY + " MATCH ?", query);}//---------------------------Cursor cursor = builder .where(selection, selectionArgs) .query(db, distinct, projection, sortOrder, null);Context context = getContext();if (null != context) { cursor.setNotificationUri(context.getContentResolver(), uri);}return cursor;//---------------------------String SESSIONS_SEARCH_JOIN_SESSIONS_ROOMS = "sessions_search " + "LEFT OUTER JOIN sessions ON sessions_search.session_id=sessions.session_id " + "LEFT OUTER JOIN myschedule ON sessions.session_id=myschedule.session_id " + "AND myschedule.account_name=? " + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";

//Where the builder is instance of SelectionBuilder

Page 14: Павел Шмыголь (Provectus): Несколько приемов разработки от Google

Few interesting classes to look at: 1. CollectionView. Extention of ListView. Supports multi rows, headers, works with

cursors. In difference to GridView can display double sized cells. Has advanced customization abilities via Inventory and IneventoryGroup classes.

2. SelectionBuilder. Helper for building selection clauses for SQLiteDatabase.3. DebugActionRunnerFragment.4. ImageLoader has nothing to do with Volley. Used in pair with the Glide

framework which provide extremely smooth scrolling. https://github.com/bumptech/glide