Skip to main content

Android Package Naming Conventions

Free2016-02-28#Android#package by layer#package by feature#安卓项目分层#android package naming convention

To be exact, it is the Android class organization convention, not referring to package names like com.example.app, but rather package definitions like util, dao, service, etc. (using packages to organize classes, which basically means how to layer them).

Foreword

Recently, while making an Android mini-game, I had quite a few Activities. Layering them by view, com, core, dao, service, util, etc., always felt uncomfortable. After searching around, I realized that dividing package names this way is no longer the mainstream practice.

1. PBL (Package By Layer)

Organizing classes into packages by layer is the traditional approach, dividing them into view, com, core, dao, and other packages, putting classes with the same functions together.

Writing all the classes of an entire project under the default package is not impossible, but having too many unrelated classes squeezed together is not only bad for maintainability but also visually uncomfortable for yourself.

src
└─net
    └─ayqy
        └─app_gowithme
                SearchPlan.java
                ShowDetail.java
                ShowMap.java
                ShowRoute.java
// Or slightly better
src
└─net
    └─ayqy
        └─app_notepad
                DAO.java
                Dialog.java
                MainActivity.java
                myEditText.java
                ShowList.java

P.S. On Windows, tree /f can generate the above output.

The package naming method (or class organization method) I am used to is to divide by class function (the so-called PBL), for example:

src
│  summary.txt
│
└─net
    └─ayqy
        └─app_rsshelper
            │  MainActivity.java
            │  SettingActivity.java
            │
            ├─com
            ├─core
            │      Consts.java
            │      Urls.java
            │
            ├─dao
            ├─service
            │      HtmlFetcher.java
            │      JsInvoker.java
            │      MainServ.java
            │      RssFetcher.java
            │
            ├─util
            │      HtmlHelper.java
            │      InputStream2String.java
            │      RssHelper.java
            │
            └─vo
                    RssItem.java

The functions of each layer are as follows:

  • default package

View layer, defines Activities to implement only UI display and navigation.

  • com (component)

Custom components, including custom Views and View combinations.

  • core

Core classes, defining configuration data and constants.

  • dao (data access object)

DB operation classes, including dbHelper.

  • service

Each Activity corresponds to a Service, responsible for implementing the business logic of that Activity. In addition, other classes closely related to the Activity that the Service depends on (which require the Activity's context, such as JsInvoker) are also defined here.

  • util

Utility classes, including modelHelper (e.g., RssHelper).

  • vo (value object)

model, or bean, containing only data structure definitions and getters/setters (can also have equals, compareTo, etc., if needed, but try to keep the model pure).

I was first exposed to JSP, so I always thought this layering method was good, and I simply copied it over to Android (even to WinForm and WPF). Fortunately, the projects were small, and I didn't feel anything wrong with it because there was no alternative available.

The disadvantages of PBL are as follows:

  • Low cohesion and low modularity within the package

Usually, classes under the same package are unrelated to each other, and may never be related.

  • High coupling between packages

Usually, in one class, you need to import a bunch of things from different packages.

  • Troublesome to code

Implementing a feature requires editing multiple files in different directories. You often open a bunch of tabs in the IDE and are reluctant to close them because they are hard to find once closed.

Similarly, deleting is also troublesome. You have to search through several packages, and if you are not careful, you make a mistake.

2. PBF (Package By Feature)

Organize classes into packages by function (module). According to app functions, they are divided into login, feedback, settings, orders, shipping, etc., for example:

// Medical app
src
└─com
    └─domain
        └─app
            ├─doctor
            │      DoctorAction.java - an action or controller object
            │      Doctor.java - a Model Object
            │      DoctorDAO.java - Data Access Object
            │      database items (SQL statements)
            │      user interface items (perhaps a JSP, in the case of a web app)
            │      ... not just java code, anything related to this feature should be placed here
            │
            ├─drug
            ├─patient
            ├─report
            ├─security
            ├─webmaster
            └─util

Simply put, each feature corresponds to a package, and the package name is the feature name. Except for common classes defined in packages like util, all classes implementing that feature are under that package.

The benefits are obvious, as follows:

  • High cohesion within the package, low coupling between packages

If you want to add a new feature somewhere, you only change things under a certain package.

Layering by class function (PBL) reduces code coupling but brings package coupling. To add a new feature, you need to change model, dbHelper, view, service, etc., meaning modifying code in several packages. The more places you change, the easier it is to introduce new problems, right?

Packaging by feature (PBF), all things related to featureA are in the featureA package. Within the feature, there is high cohesion and high modularity, and low coupling between different features. Related things are put together, which makes them easier to find.

  • Packages have package-private scope

If you are responsible for developing this feature, everything in this directory is yours.

The PBL approach places all utility methods under the util package. When Xiao Zhang develops a new feature and finds he needs an xxUtil, but it's not generic, where should it go? Under the layering principle, we have no choice but to put it in the util package. It seems inappropriate, but putting it in other packages is even more so. As features increase, more util classes are defined. Later, when Xiao Li develops a feature and needs an xxUtil, also not generic, he goes to the util package and finds it's already there but cannot be reused. He has to abandon the name xx and change it to xxxUtil... because PBL packages have no private scope; every package is public (cross-package method calls are common, and every package is accessible to others).

If it's PBF, Xiao Zhang's xxUtil naturally goes under featureA, and Xiao Li's xxUtil under featureB. If a util seems generic, they can check the util package to see if the utility method should be added to xxUtil. Class naming conflicts disappear.

PBF packages have package-private scope. featureA should not access anything under featureB (if it must, it indicates a problem with the interface definition).

  • Easy to delete features

Statistics show that no one uses a new feature, so that feature needs to be removed in this version.

If it's PBL, you have to dig out and delete all the involved code and classes from the feature entry point to the entire business process. One slip and you're doomed.

If it's PBF, it's easy. First, delete the corresponding package, then delete the feature entry point (the entry point will definitely report an error after the package is deleted), and you're done.

  • High level of abstraction

The general method to solve a problem is from abstract to concrete. PBF package names are abstractions of functional modules, and the classes inside the packages are implementation details, which conforms to the abstract-to-concrete flow. PBL reverses this.

PBF starts by determining the AppName, divides packages according to functional modules, and then considers the specific implementation details of each module. PBL, on the other hand, requires considering from the beginning whether a dao layer is needed, whether a com layer is needed, etc.

  • Separate logic code only through classes

PBL separates both classes and packages, while PBF separates logic code only through classes.

There is no need to separate through packages, because an awkward situation can also arise in PBL:

    ├─service
            │      MainServ.java

According to PBL, everything under the service package is a Controller, so the Serv suffix shouldn't be needed. But in reality, for coding convenience, the service package is often directly imported. The Serv suffix is to avoid naming conflicts between the imported class and classes in the current package. Of course, you can do without the suffix, but you have to write the full package path, like new net.ayqy.service.Main(), which is troublesome.

PBF is very convenient; no import is needed, just new MainServ().

  • Package size becomes meaningful

In PBL, it's reasonable for package sizes to grow infinitely because more and more features are added.

In PBF, if a package is too large (too many classes in the package), it indicates that this part needs refactoring (dividing into sub-packages).

3. PBF Specific Practice (Google I/O 2015)

The most representative is Google I/O 2015, structured as follows:

java
└─com
    └─google
        └─samples
            └─apps
                └─iosched
                    │  AppApplication.java  Defines Application class
                    │  Config.java          Defines configuration data (constants)
                    │
                    ├─about
                    │      AboutActivity.java
                    │
                    ├─appwidget
                    │      ScheduleWidgetProvider.java
                    │      ScheduleWidgetRemoteViewsService.java
                    │
                    ├─debug
                    │  │  DebugAction.java
                    │  │  DebugActivity.java
                    │  │  DebugFragment.java
                    │  │
                    │  └─actions
                    │          DisplayUserDataDebugAction.java
                    │          ForceAppDataSyncNowAction.java
                    │          ForceSyncNowAction.java
                    │          ...
                    │
                    ├─explore
                    │  │  ExploreIOActivity.java
                    │  │  ExploreIOFragment.java
                    │  │  ExploreModel.java
                    │  │  ...
                    │  │
                    │  └─data
                    │          ItemGroup.java
                    │          LiveStreamData.java
                    │          MessageData.java
                    │          ...
                    │
                    ├─feedback
                    │      FeedbackApiHelper.java
                    │      FeedbackConstants.java
                    │      FeedbackHelper.java
                    │      ...
                    │
                    ├─framework
                    │      FragmentListener.java
                    │      LoaderIdlingResource.java
                    │      Model.java
                    │      ...Defines interfaces and implementation
                    │
                    ├─gcm
                    │  │  GCMCommand.java
                    │  │  GCMIntentService.java
                    │  │  GCMRedirectedBroadcastReceiver.java
                    │  │  ...
                    │  │
                    │  └─command
                    │          AnnouncementCommand.java
                    │          NotificationCommand.java
                    │          SyncCommand.java
                    │          ...
                    │
                    ├─io
                    │  │  BlocksHandler.java
                    │  │  HandlerException.java
                    │  │  HashtagsHandler.java
                    │  │  ...Handles model
                    │  │
                    │  ├─map
                    │  │  └─model
                    │  │          MapData.java
                    │  │          Marker.java
                    │  │          Tile.java
                    │  │
                    │  └─model
                    │          Block.java
                    │          DataManifest.java
                    │          Hashtag.java
                    │          ...
                    │
                    ├─map
                    │  │  InlineInfoFragment.java
                    │  │  MapActivity.java
                    │  │  MapFragment.java
                    │  │  ...
                    │  │
                    │  └─util
                    │          CachedTileProvider.java
                    │          MarkerLoadingTask.java
                    │          MarkerModel.java
                    │          ...
                    │
                    ├─model
                    │      ScheduleHelper.java
                    │      ScheduleItem.java
                    │      ScheduleItemHelper.java
                    │      ...Defines model and implements model-related operations
                    │
                    ├─myschedule
                    │      MyScheduleActivity.java
                    │      MyScheduleAdapter.java
                    │      MyScheduleFragment.java
                    │      ...
                    │
                    ├─provider
                    │      ScheduleContract.java
                    │      ScheduleContractHelper.java
                    │      ScheduleDatabase.java
                    │      ...Implements ContentProvider
                    │      (Also defines other classes the provider depends on, like db operations here)
                    │
                    ├─receiver
                    │      SessionAlarmReceiver.java
                    │
                    ├─service
                    │      DataBootstrapService.java
                    │      SessionAlarmService.java
                    │      SessionCalendarService.java
                    │
                    ├─session
                    │      SessionDetailActivity.java
                    │      SessionDetailConstants.java
                    │      SessionDetailFragment.java
                    │      ...
                    │
                    ├─settings
                    │      ConfMessageCardUtils.java
                    │      SettingsActivity.java
                    │      SettingsUtils.java
                    │
                    ├─social
                    │      SocialActivity.java
                    │      SocialFragment.java
                    │      SocialModel.java
                    │
                    ├─sync
                    │  │  ConferenceDataHandler.java
                    │  │  RemoteConferenceDataFetcher.java
                    │  │  SyncAdapter.java
                    │  │  ...
                    │  │
                    │  └─userdata
                    │      │  AbstractUserDataSyncHelper.java
                    │      │  OnSuccessListener.java
                    │      │  UserAction.java
                    │      │  ...
                    │      │
                    │      ├─gms
                    │      │      DriveHelper.java
                    │      │      GMSUserDataSyncHelper.java
                    │      │
                    │      └─util
                    │              UserActionHelper.java
                    │              UserDataHelper.java
                    │
                    ├─ui
                    │  │  BaseActivity.java
                    │  │  CheckableLinearLayout.java
                    │  │  SearchActivity.java
                    │  │  ...BaseActivity and custom UI components
                    │  │
                    │  └─widget
                    │          AspectRatioView.java
                    │          BakedBezierInterpolator.java
                    │          BezelImageView.java
                    │          ...Custom small UI widgets
                    │
                    ├─util
                    │      AboutUtils.java
                    │      AccountUtils.java
                    │      AnalyticsHelper.java
                    │      ...Utility classes, provide static methods
                    │
                    ├─videolibrary
                    │      VideoLibraryActivity.java
                    │      VideoLibraryFilteredActivity.java
                    │      VideoLibraryFilteredFragment.java
                    │      ...
                    │
                    └─welcome
                            AccountFragment.java
                            AttendingFragment.java
                            ConductFragment.java
                            ...

In addition to packages named by feature, packages similar to layers are also defined above, as follows:

  • framework

  • io

  • model

  • provider

  • receiver

  • service

  • ui

  • util

Compare it with the general PBL conventions, as follows:

General PBL ConventionsGoogle I/O 2015
activities (Activity classes used in pages)feature/
base (BaseActivity class that can be shared by each Activity class in pages)ui
adapter (Adapter classes used in pages)Generic ones in ui, specific ones in feature/
tools (Public utility method classes)util
bean/unity (Element classes)model
db (Database operation classes)Database operations are in provider, data processing (like json parsing) in io
view/ui (Custom View classes)ui
service (Service services)service
broadcast (Broadcast services)feature/

3. Summary

Referring to the code structure of Google I/O 2015, PBF can specifically be done like this:

src
└─com
    └─domain
        └─app
            │  Config.java Configuration data, constants
            │
            ├─framework
            │      Defines interfaces and related base classes
            │
            ├─io
            │      Data definition (model), data operations (like json parsing, but excluding db operations)
            │
            ├─model
            │      Defines model (data structures and getters/setters, compareTo, equals, etc., no complex operations)
            │      and modelHelper (provides APIs for easy operation on the model)
            │
            ├─provider
            │      Implements ContentProvider and its dependent db operations
            │
            ├─receiver
            │      Implements Receiver
            │
            ├─service
            │      Implements Service (like IntentService), used to asynchronously do stuff in an independent thread
            │
            ├─ui
            │      Implements BaseActivity, as well as custom views and widgets, related Adapters are also placed here
            │
            ├─util
            │      Implements utility classes, provides static methods
            │
            ├─feature1
            │      Item.java                Defines model
            │      ItemHelper.java          Implements modelHelper
            │      feature1Activity.java    Defines UI
            │      feature1DAO.java         Private db operations
            │      feature1Utils.java       Private utility functions
            │      ...other private classes
            │
            ├─...other features

Of course, such a code organization scheme is not only applicable to Android. As long as it works well for you, it's good.

References

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment