Date post: | 22-Jan-2018 |
Category: |
Mobile |
Upload: | erik-hellman |
View: | 1,217 times |
Download: | 0 times |
Staying aliveOnline and offline
Erik Hellman, BonTouch@ErikHellman
How well does your app work offline?
When leaving my apartment
Why?4 Reduce glitches
4 Roaming users
4 Missing coverage
4 Improve performance
4 Reduce data costs
Offline examples
How?4 Connectivity detection
4 Request queing
4 Caching
4 Preloading
Challenges4 Captive Portals
4 Network handover
4 Temporary network loss
4 Timeouts
4 2G Voice and Data
4 Local Storage
4 Request Serialization
4 ...and more
Detecting connectivityboolean isNetworkAvailable() { ConnectivityManager mgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo networkInfo = mgr.getActiveNetworkInfo(); return networkInfo != null && networkInfo.isConnected();}
4 Checks network state right now
4 Captive portals detection (since API level 17)
4 Poor networks (since API level 16)
4 Must be called before every network operation
4 Will indicate DISCONNECTED if background data is disabled!
Listen for changesprivate void setupNetworkChangeListener() { IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); this.networkStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { ConnectivityManager mgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo networkInfo = mgr.getActiveNetworkInfo(); notifyNetworkState(networkInfo != null && networkInfo.isConnected()); } }; registerReceiver(networkStateReceiver, intentFilter);}
4 Continously listen for network state changes
4 Remember to unregister!
NetworkInfo.State
public enum State { CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN}
NetworkInfo.DetailedState
public enum DetailedState { IDLE, SCANNING, CONNECTING, AUTHENTICATING, OBTAINING_IPADDR, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, FAILED, BLOCKED, VERIFYING_POOR_LINK, CAPTIVE_PORTAL_CHECK}
private static final EnumMap<DetailedState, State> stateMap = new EnumMap<DetailedState, State>(DetailedState.class);
static { stateMap.put(DetailedState.IDLE, State.DISCONNECTED); stateMap.put(DetailedState.SCANNING, State.DISCONNECTED); stateMap.put(DetailedState.CONNECTING, State.CONNECTING); stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING); stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING); stateMap.put(DetailedState.VERIFYING_POOR_LINK, State.CONNECTING); stateMap.put(DetailedState.CAPTIVE_PORTAL_CHECK, State.CONNECTING); stateMap.put(DetailedState.CONNECTED, State.CONNECTED); stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED); stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING); stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED); stateMap.put(DetailedState.FAILED, State.DISCONNECTED); stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);}
Unexpected conditions4 2G Voice and Data
4 Flight Mode
public static boolean isAirplaneModeOn(Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) != 0; } else { return Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0; } }
15 minutes
socket timeout
Response Times:The 3 Important Limits4 0.1 seconds - system is reacting instantaneously
4 1.0 seconds - limit for user's flow of thought to stay uninterrupted
4 10 seconds - limit for keeping the user's attention
...response time guidelines for web-based applications are the same as for all other applications...
— Jakob Nielsen, http://www.nngroup.com/articles/response-times-3-important-limits/
Timeout for HttpURLConnectionURL url = new URL(url);HttpURLConnection urlConnection = url.openConnection();urlConnection.setConnectTimeout(CONNECTION_TIMEOUT);urlConnection.setReadTimeout(CONNECTION_TIMEOUT);
Timeout for OkHttpClientOkHttpClient client = new OkHttpClient();client.setConnectTimeout(CONNECTION_TIMEOUT);client.setReadTimeout(CONNECTION_TIMEOUT);
Request Serialzation
Queing outgoing requests// In your ActivitystartService(buildRequestIntent(url, data));
// IntentService.onHandleIntent()is(isConnected(this)) { performRequest(url, data);} else { serializeRequest(url, data);}
// BroadcastReceiver listening for network changesif(isConnected(context)) { context.startService(new Intent(ACTION_SEND_QUEUED_REQUESTS));}
Alternatives4 Firebase
4 Couchebase
Request serialization4 Query params?
4 Request body?
4 HTTP headers?
4 Timestamp?
Request serializationpublic void serializeRequest(url, data) { ContentValues values = new ContentValues(); values.put("url", url); values.put("data", data); getContentResolver.insert(...);}
Caching4 Not HTTP Response cache!
4 Custom cache implementations
4 Don't evict when offline?
4 Context.getCacheDir(), Context.getFilesDir() or Context.getExternalFilesDir()?
Caching examples
DiskLruCache. java
Load from cache or network?// Our sources (left as an exercise for the reader)Observable<Data> memory = ...; Observable<Data> disk = ...; Observable<Data> network = ...;
// Retrieve the first source with dataObservable<Data> source = Observable .concat(memory, disk, network) .first();
4 Dan Lew, http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/
Preload data for offline4 Assets or Raw resources (100 MB)
4 APK Expansion files (2 * 2GB)
4 Download at first startup (unlimited)
How much data?Street addresses
4 Sweden: 650 000 ~ 7 MB
4 US: 154 million ~ 1.6 GB
Preloading SQLite DBprivate SQLiteDatabase openPreloadedSQLiteDB() { File localCopy = new File(getFilesDir(), DB_NAME); if (localCopy.exists()) { // TODO Perform version check! InputStream inputStream = getResources().openRawResource(R.raw.preloaded_db); readStreamToFile(inputStream, localCopy); } return SQLiteDatabase.openOrCreateDatabase(localCopy, null);}
Local storage4 Micromax Canvas A1 (Android One) - 2GB internal
storage
4 Use external storagepublic static void saveFileToExternalStorage(Context context, String filename, byte[] data) { File appPrivateFile = new File(context.getExternalFilesDir(null), filename); writeBytesToFile(data, appPrivateFile);}
Mesh networking?4 Bluetooth
4 WiFi Direct
ConclusionsDon't ignore offline!
Thanks for listening!@ErikHellman