Preserve open tabs

Authored by Niedzielski.


Preserve open tabs

  • Always preserve tabs opened by the user.
  • When changing languages, open a new main page tab in the foreground.
  • Add Gson library and update ProGuard configuration. The new library increases the size of the release APK by ~50KiB.

Initial research in Phab T103954 suggested that the AOSP Browser
implementation was favorable. Upon further research, converting Bundles
to Parcels and writing them to disk is highly discouraged in the Parcel
API documentation:

Parcel is not a general-purpose serialization mechanism. This class

(and the corresponding Parcelable API for placing arbitrary objects
into a Parcel) is designed as a high-performance IPC transport. As
such, it is not appropriate to place any Parcel data in to persistent
storage: changes in the underlying implementation of any of the data
in the Parcel can render older data unreadable.

This made the Browser approach ultimately unfavorable. Alternatives
considered included Java serialization, databases, a custom file format,
and SharedPreferences.

It was unfortunate that Bundles could not be used as a large portion of
app state that is desirable to preserve has mixed responsibilities with
Bundles and Parcelable marshalling, which is encouraged in many official
examples. This influenced the consideration of alternatives. Additional
recording mechanisms leveraged in the app include databases and

Java serialization is a classic choice. However, Effective Java
dedicates a chapter to the dangers and encourages very judicial usage of
the technique:

Allowing a class's instances to be serialized can be as simple as

adding the words "implements Serializable" to its declaration. Because
this is so easy to do, there is a common misconception that
serialization requires little effort on the part of the programmer.
The truth is far more complex. While the immediate cost to make a
class serializable can be negligible, the long-term costs are often

A major cost of implementing Serializable is that it decreases the
flexibility to change a class's implementation once it has been
released. When a class implements Serializable, its byte-stream
encoding (or serialized form) becomes part of its exported API. Once
you distribute a class widely, you are generally required to support
the serialized form forever, just as you are required to support all
other parts of the exported API. If you do not make the effort to
design a custom serialized form, but merely accept the default, the
serialized form will forever be tied to the class's original internal
representation. In other words, if you accept the default serialized
form, the class's private and package-private instance fields become
part of its exported API, and the practice of minimizing access to
fields loses its effectiveness as a tool for information hiding.

In the interest of avoiding such an initial investment, as well as long
term costs, Java serialization was not chosen.

Although a scalable choice, most databases on Android have uncomfortable
lifecycles, even considering wrappers such as SQLiteOpenHelper, that
frequently introduce bugs, especially when used in conjunction with
Activity and Fragment lifecycles.

A custom file format was briefly considered, but it was immediately more
favorable to prefer established formats such as JSON.

SharedPreferences in themselves, support (all?) Java primitives, and
could be used. They're favorable because they supply an asynchronous
write mechanism which may be used from the main thread, i.e., no
worrying lifecycles, and are commonly used in general. However,
marshallers and unmarshallers would still need to be built.

Gson supports reflection based to and fro JSON serialization without the
need to write marshallers and unmarshallers. This is two edged sword in
that there are still the versioning concerns of Serializable, but Gson
supplies annotations to avoid these issues.

Gson used in conjunction with SharedPreferences supplies a full

Bug: T106709
Change-Id: I5eacc8c07844253974cba6eb479bbcc8c5a2fa4f