Random Musings on the P Developer Preview 1
Each time Google releases a new developer preview, I rummage through the API differences report and the high-level overviews, to see if there are things that warrant more attention from developers, with an emphasis on mainstream features that any developer might reasonably use. I also tend to emphasize things that may not get quite as much attention, because they are buried in the JavaDocs.
As with Android O, Android P seems to be lacking a signature user-facing feature. It even lacks what I would consider to be a signature developer-facing feature. P, at least in the first developer preview, is an incremental advance. This might represent maturation of the platform, a focus on cleaning things up in preparation for future versions of Android, or changes in staff allocation emphasizing other Google projects. It might also be “sandbagging”, with signature features showing up in future developer previews.
That being said, there are a number of things that I found curious and interesting.
AppComponentFactory
Usually in these “random musings” posts, I have something that scares me. Occasionally, I have lots of things that scare me. Fortunately, this is a fairly mild release, and while these are early P days, I’m not scared yet.
However, AppComponentFactory
is going to be a double-edged sword.
You can define a subclass of AppComponentFactory
and register it in the
manifest in the <application>
element. Then, whenever the framework
needs to create an instance of an Activity
, Application
, ContentProvider
,
BroadcastReceiver
, or Service
, your AppComponentFactory
will be asked
to do that work. You are given a ClassLoader
, the String
of the component name
that appeared in the manifest, and an Intent
(for Activity
, Service
,
and BroadcastReceiver
). Your job is to return the desired object. It would
appear that you could chain to the superclass for those components that
you do not want to handle differently, allowing you to focus on the scenarios
where you want to do something different than what the framework normally does.
Let’s use an Activity
an an example. Normally, when the user taps the
home screen launcher icon, Android creates an instance of the designated
Activity
subclass using the zero-argument public constructor. However,
if the app has a registered an AppComponentFactory
and overrides the
instantiateActivity()
method, that will be called, and it needs to return
the Activity
.
For example, instantiateActivity()
could:
-
Use some other sort of constructor, perhaps involving dependency injection
-
Have a generic placeholder
<activity>
element, and decide in Java code what the actualActivity
will be, by using other information in theIntent
and perhaps other state information in the app -
Create some sort of
ActivityWrapper
that wraps the realActivity
subclass, where the wrapper can do logging and such, forwarding all calls onto the wrappedActivity
And so on. Hence, this capability could be useful for a variety of scenarios.
However, it certainly simplifies the implementation of certain types of malware and crapware. Now I don’t even need to mess with the existing code. All I need to do is:
-
Create and register an
AppComponentFactory
that overridesinstantiateActivity()
(or whatever) to put my own wrapper around the app’s real component -
Ship the altered app by whatever my preferred distribution channel is
-
Get results from my wrappers (e.g., report everything the user types in, slap ad banners over the UI, replace the app’s own ad banners with my own)
So, app repackagers will be all over this. Right now, that’s probably a semi-manual process; this change makes it easier to automate. So, with the good, we also get the bad.
This facility first came to my attention from Mishaal Rahman of XDA-Developers.
It existed in some Android 8.0 builds, and possibly Android 8.1, as hidden
APIs on Application
, rather than through this separate AppComponentFactory
class.
Changes You Should Pay Attention To
Some deprecations that I pointed out earlier – such as accessing hidden APIs and device admin policy changes, do appear to be coming to fruition. In particular, libraries that you use might be using some hidden APIs, even if you are not. Keep a close eye on your dependencies and upgrade them as appropriate.
Also, a rumored change has come to pass: the framework Fragment
- and Loader
-related
classes are deprecated. This is a nudge to get you to use the ones from the Support
Library, for consistent implementations across device versions. Note that there
are not direct analogues for everything in the Support Library (e.g., no WebViewFragment
).
Once you set your targetSdkVersion
to P
(or, eventually, 28?), you will be
forced to use https
by default. Attempts to use http
URLs will result in
an exception. If you still need access to unencrypted Web content, you will need to
opt into that behavior.
Also, once you set your targetSdkVersion
to P
, you need to request
a new FOREGROUND_SERVICE
permission.
This is a normal
permission, so you just need the <uses-permission>
element
in the manifest. It is not really clear why they introduced this, but it is something
that you will need. OTOH, this seems to be the only change related to the
years-old “War on Background Processing”, so perhaps we have turned the corner
on that subject.
If you have ancient instrumentation tests, they may break, until you either update them to use JUnit4 or you opt into the legacy testing library.
Android 7.0-8.1 stopped requiring FLAG_ACTIVITY_NEW_TASK
. I pointed out this
flaw ~18 months ago, and again
a year ago. Much to my shock
and amazement, this has been addressed with P, and so
you need that flag again
when starting an activity from a non-activity Context
, such as from a Service
or BroadcastReceiver
. This returns us to the behavior from Android 6.0 and
older OS versions, and so you probably already have this flag, unless your
minSdkVersion
is 24 or higher.
While there are many other behavior changes, those seem to be the ones that are likely to affect the most developers.
Stuff That Needs More Explanation
Android P has slices of… something. There is a whole family of classes
related to this, including
SliceManager
and
SliceProvider
.
The JavaDocs for SliceProvider
has:
A SliceProvider allows an app to provide content to be displayed in system spaces. This content is templated and can contain actions, and the behavior of how it is surfaced is specific to the system surface.
However:
-
It is unclear what “system spaces” and “system surfaces” refer to (lockscreen? notification shade? something else?)
-
The JavaDocs for
Slice
and the release notes for 28.0.0-alpha1 Support Library suggest that the content is app-to-app, not app-to-system-surface
So, there may be something cool here, but the documentation is really scant on the subject.
Also, Activity
, Dialog
, and View
(at minimum) have a new requireViewById()
method. This works just like findViewById()
, but it throws an exception when
there is no View
matching the ID, instead of returning null
. It is unclear
where and why we would use this.
In addition, there is a StatsLog
class
that “provides an API for developers to send events to statsd”. It is unclear
what this means or why we would use it.
You can now attach a “semantic action” to a Notification.Action
, such as indicating
that this action will delete something, archive something, “thumbs up” something, etc.
It is unclear if this will be used for accessibility or if it has some other role.
Stuff That Seems Intriguing
We now have a Magnifier
that we can attach to a widget. Presumably, it embiggens things.
There is a static disableWebView()
method on WebView
, which tells Android
to not allow any WebView
use in this process. If you have a multi-process app,
and you know that some of your processes do not use WebView
, calling this will
prevent you from accidentally loading WebView
and incurring the memory overhead.
However, it does this by throwing an exception, so this may be the sort of thing
that you only use in debug
builds for tracking down unexpected WebView
usage.
StrictMode
now offers callbacks. For any violation, you can have a listener
called on an Executor
that you supply. Your listener is handed a Violation
object, which is a subclass of Throwable
. Hopefully (presumably?) this
Violation
is generated at the point of the bad code, so you can examine
the stack trace and decide what to do. This may make it possible to create
more advanced StrictMode
reporting tools, including tying them into your
existing crash logging system.
WebView
offers a TracingController
that can be used to “enable/disable tracing for parts
of code and to collect tracing data”. You get a JSON block back, designed for
use with chrome://tracing
in your favorite Web browser whose name starts
with “Chrom”.
Apps can now find out what autofill service the user has chosen, if any, via
getAutofillServiceComponentName()
on AutofillManager
.
This would be useful if your app wants to have a whitelist of trusted autofill
services that you are willing to use, blocking autofill if the user chose one
that you do not recognize.
Other Stuff Worth Noting
An AccessibiltyService
can now
take screenshots
on behalf of the user.
The API differences report will note things being removed that do not appear
to be removed. For example,
the differences report indicates that a bunch of methods were removed
from AbstractCursor
.
Those methods are still there in the full JavaDocs.
There is a new Settings
action (ACTION_STORAGE_VOLUME_ACCESS_SETTINGS
)
to take the user to a screen listing the removable media on the device and
which apps have access to that media. In particular, if the user denied
your app access to the removable media, and clicked “Don’t ask again” on the
dialog, sending the user to the Settings app is the only way your app will
ever get access to that storage volume.
Apps can now help the user enroll a fingerprint, via ACTION_FINGERPRINT_ENROLL
.
The change to yet another APK signing system means that there are new APIs
and deprecations if you check signatures at runtime. The former signatures
field on PackageInfo
is replaced with signingCertificateHistory
, which has
details about signing key revisions.
Also, the versionCode
is moving from an int
to a long
, as apparently
232 app versions weren’t quite enough.
In addition to regular KeyEvent
processing in a View
, there is now the notion
of a “fallback”, in the form of onKeyFallback()
and addKeyFallbackListener()
.
If the regular KeyEvent
processing does not consume the event, any fallbacks
are tried, before the KeyEvent
is handed to the window callback. It is not
completely clear where this would be useful, other than serving as a low-priority
handler of some KeyEvent
(i.e., if somebody else wants the event, have it, otherwise
I will use it).
You can call getMainExecutor()
on Context
to get an Executor
that will
execute its jobs on the main application thread. There are other ways of accomplishing
this, using Looper
and a custom Executor
implementation, but this is simpler.
JobInfo.Builder
has a few new options:
-
setEstimatedNetworkBytes()
provides a hint for how much bandwidth should be needed to execute this job. The idea is that jobs needing a lot of bandwidth may be postponed when the device has a lousy network connection, as excessive retries may chew up the battery. -
setImportantWhileForeground()
is strange. Based on the docs, it appears as though it will relax some of the restrictions imposed by Doze mode, if your app happens to be in the foreground at the time of the job. I guess this includes cases where the screen is off and your app was the last one to be in the foreground. Otherwise, if the screen is on and you are in the foreground, there should be no Doze restrictions in the first place. -
setIsPrefetch()
indicates that this job is pre-fetching some data for the user.JobScheduler
may adjust your job’s timing as a result, either to invoke it as part of launching your UI (e.g., if the user clicks on your launcher icon) or if there is a lot of spare bandwidth (even if that bandwidth is metered and you ordinarily try to avoid metered data). -
setRequiredNetwork()
allows you to spell out the details of what sort of network connection that you need, based on capabilities and transport types, as defined by aNetworkRequest
.
Where I Go From Here
Over the next several days, I will be putting Android P through its paces, in part by testing and re-raising a bunch of issues that probably haven’t been addressed yet.
An update to The Busy Coder’s Guide to Android Development, with coverage of Android P, should be out by the end of the month, if not sooner.