Upload
droidcontlv
View
236
Download
3
Embed Size (px)
Citation preview
Yonatan LevinGoogle Developer Expert
levin.yonatanparahall
59 Cities > 20M usersRuby, Go, Python, Microservices
> 1300 members Largest Android Active Community
:)
The story about the shy guy
Doze Mode
Doze, the bro :)
No network access
No jobs/ No syncs
No wakelocks
No alarms
No GPS / Wifi Scans
Doze Mode on Marshmallow
Not shy anymore
Doze Mode on Nougat
When?
All intervals are configurable by Google/Manufacturer.
Statistics on OnePlus 3 (Marshmallow)
Statistics on Nexus 6P (Nougat)
Doze, sweet, Doze,Let me go...
Charge it...
AlarmManager
setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
Could be triggered only once in every 15 minutes
But...
No network access
GCM/FCM
Use FCM with High priority - but treat it with special care
{
"to" : "...",
"priority" : "high",
"notification" : {
...
},
"data" : {
...
}
}
WhiteList
● An app can fire the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent to take the user directly to the Battery Optimization, where they can add the app.
Note: Google Play policies prohibit apps from requesting direct exemption from Power
Management features in Android 6.0+ (Doze and App Standby) unless the core function of
the app is adversely affected.
And it’s not all, folks
CONNECTIVITY_CHANGES
My shiny new app
git clone [email protected]:parahall/final_battle_doze.git
public class FinalBattleActivity
public void onClick(View v) {
switch (v.getId()) {
case R.id.kill_button:
StarWarsUtils.makeDecision(
LukeDecision.LUKE_KILL_DARTH_VADER, this);
break;
case R.id.light_side_button:
StarWarsUtils.makeDecision(
LukeDecision.STAY_ON_LIGHT_SIDE, this);
Break;
}
}
public class StarWarsUtils {
public static void makeDecision(LukeDecision decision,Context
context) {
Intent nowIntent = new Intent(context, NowIntentService.class);
nowIntent.putExtra(..., decision);
context.startService(nowIntent);
}
public class NowIntentService extends IntentService {
protected void onHandleIntent(Intent intent) {
LukeDecision decision =
(LukeDecision)intent.getSerializableExtra(...);
if (decision != null) {
makeNetworkCall(decision);
}
...
}
public class NowIntentService extends IntentService {
private void makeNetworkCall(LukeDecision decision) {
boolean isCompleted = false;
if(StarWarsUtils.isNetworkActive()) {
isCompleted = doingNetworkCommunication();
}
...
}
public class NowIntentService extends IntentService {
private void makeNetworkCall(LukeDecision decision) {
...
if (!isCompleted) {
StarWarsUtils.addRetryTask(decision, this);
return;
}
...
}
How we will retry the request if it’s failed?
Connectivity Manager
AndroidManifest.xml
<receiver android:name=".ConnectivityChangeReceiver">
<intent-filter>
<action
android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
public void onReceive(Context context, Intent intent) {
LukeDecision decisionToRetry =
StarWarsUtils.getDecisionToRetry(context);
StarWarsUtils.makeDecision(decisionToRetry, context);
}
public class ConnectivityChangeReceiver extends WakefulBroadcastReceiver
Han Solo reporting to Rebels
public class FinalBattleActivity extends AppCompatActivity
protected void onCreate(Bundle savedInstanceState) {
...
scheduleHanSoloReport();
...
}
public class FinalBattleActivity extends AppCompatActivity
private void scheduleHanSoloReport() {
AlarmManager alarmManager = (AlarmManager)
getSystemService(ALARM_SERVICE);
PendingIntent broadcast = getPendingIntent();
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(),
ONE_MINUTE,
broadcast);
}
public class HanSoloReceiver
public void onReceive(Context context, Intent intent) {
LocationManager locationService = (LocationManager)
context.getSystemService(Context.LOCATION_SERVICE);
PendingIntent pendingIntent = getPendingIntent(context);
String provider = getLocationProvider(locationService);
locationService.requestSingleUpdate(provider, pendingIntent);
}
public class NowIntentService extends IntentService {
@Override protected void onHandleIntent(Intent intent) {
...
Location HanSoloLocation =
intent.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED);
if (HanSoloLocation != null) {
checkIfRebelsReady(HanSoloLocation);
}
}
RebelService
public class RebelService extends Service implements Handler.Callback
public void onCreate() {
handlerThread = new HandlerThread("RebelServiceHandlerThread");
handlerThread.start();
Looper looper = handlerThread.getLooper();
handler = new Handler(looper, this);
handler.sendEmptyMessage(WHAT_MAKE_NETWORK_REQUEST);
}
public class RebelService extends Service implements Handler.Callback
@Override public boolean handleMessage(Message msg) {
StarWarsUtils.doingNetworkCommunication();
handler.sendEmptyMessageDelayed(
WHAT_MAKE_NETWORK_REQUEST,
DELAY_MILLIS);
return true;
}
So what is affected by Doze my app?
What is affected
- Pending network transactions will never be fired...- Han Solo Alarms will be postponed.- No location updates
But...
There is still some place for...
JobScheduler/GCMNetworkManager
What?
Schedule the task to execute it when certain conditions met.
(charging, idle, connected to a network or connected to an unmetered network)
Why two?
JobScheduler was introduced in API >= 21 (Lollipop).
GCMNetworkManager - is part of GCM package. When using on devices >= 21, use JobScheduler underneath.
build.gradledependencies {
...
compile 'com.google.android.gms:play-services-gcm:9.4.0'
...
}
public class StarWarsUtilities
void addRetryTask(LukeDecision decision,Context context){
GcmNetworkManager gcmNetworkManager =
GcmNetworkManager.getInstance(context);
…
}
public class StarWarsUtilities
Task task = new
OneoffTask.Builder().setService(BestTimeService.class)
.setExecutionWindow(0, 30)
.setTag(BestTimeService.LUKE_DECISION)
.setUpdateCurrent(false)
.setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
.setRequiresCharging(false)
.setExtras(bundle)
.build();
public class StarWarsUtilities
void addRetryTask(LukeDecision decision,Context context){
...
gcmNetworkManager.schedule(task);
}
AndroidManifest.xml<service
android:name=".BestTimeService" android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE"android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
</intent-filter>
</service>
BestTimeService.java/**
* Task run by GcmNetworkManager when all the
requirements of the scheduled
* task are met.
*/
public class BestTimeService extends GcmTaskService {
...
}
BestTimeService.java@Override public int onRunTask(TaskParams taskParams) {
switch (taskParams.getTag()) {
case LUKE_DECISION:
...
return GcmNetworkManager.RESULT_SUCCESS;
case HAN_SOLO_LOCATION:
...
return GcmNetworkManager.RESULT_RESCHEDULE;
default:
return GcmNetworkManager.RESULT_FAILURE;
}
}
BestTimeService.java
public int onRunTask(TaskParams taskParams) {
…
case LUKE_DECISION:
Bundle extras = taskParams.getExtras();
LukeDecision decision = (LukeDecision)
extras.getSerializable(...);
StarWarsUtils.makeDecision(decision, this);
return GcmNetworkManager.RESULT_SUCCESS;
…
}
}
Han Solo Recurring Location task?
Task task = new PeriodicTask.Builder()
.setService(BestTimeService.class)
.setPeriod(SIXTY_SEC)
.setFlex(10)
.setTag(BestTimeService.HAN_SOLO_LOCATION)
.setPersisted(true)
.build();
public class FinalBattleActivity
BestTimeService.javapublic int onRunTask(TaskParams taskParams) {
…
case HAN_SOLO_LOCATION:
.. request location...
return GcmNetworkManager.RESULT_SUCCESS;
…
}
Canceling TaskmGcmNetworkManager.cancelAllTasks(BestTimeService.class);
mGcmNetworkManager.cancelTask(
TAG,
BestTimeService.class
);
There is a problem hiding here
Not all devices shipped with Play Servicesint resultCode =
GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode == ConnectionResult.SUCCESS) {
mGcmNetworkManager.schedule(task);
} else {
// Deal with this networking task some other way
}
When Google Play updated it removes all scheduled periodic tasks
public class BestTimeService extends GcmTaskService {
@Override
public void onInitializeTasks() {
super.onInitializeTasks();
// Reschedule removed tasks here
}
}
Can we do better?
FCM/GCM with High Priority
{
"to" : "...",
"priority" : "high",
"notification" : {
...
},
"data" : {
...
}
}
AndroidManifest.xml
<service
android:name=".RebelsMessagingService">
<intent-filter>
<action
android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
public class RebelsMessagingService extends FirebaseMessagingService
@Override public void onMessageReceived(RemoteMessage remoteMessage) { Intent intent = new Intent(HanSoloReceiver.ACTION);
LocalBroadcastManager. getInstance(this). sendBroadcast(intent); }
Some dirty tricks. Be aware when using it…
public class RebelService extends Service implements Handler.Callback
public boolean handleMessage(Message msg) {
StarWarsUtils.doingNetworkCommunication());
handler.sendEmptyMessageDelayed(
WHAT_MAKE_NETWORK_REQUEST,
DELAY_MILLIS);
return true;
}
public class RebelService extends Service implements Handler.Callback
public void onCreate() {
Notification notification =
...
startForeground(101, notification);
}
Yonatan LevinGoogle Developer Expert
levin.yonatanparahall