跳到主要內容
黯羽輕揚每天積累一點點

Android 包命名規範

免費2026-03-05#Android#package by layer#package by feature#安卓项目分层#android package naming convention

寫在前面

最近在做一個 Android 小遊戲,Activity 略多,按 view, com, core, dao, service, util 等等來分層總感覺不舒服,出去翻了一會兒才發現這樣劃分包名已經不是主流做法了

一. PBL(Package By Layer)

按層分包組織 class,也就是傳統做法,分為 view, com, core, dao 等等包,把職能相同的 class 放在一起

在 default package 下寫完整個專案的所有 class 也不是不可以,但太多不相關的 class 擠在一起,且不說可維護性什麼的,單是自己看著也難受

src
└─net
    └─ayqy
        └─app_gowithme
                SearchPlan.java
                ShowDetail.java
                ShowMap.java
                ShowRoute.java
// 或者稍微好一点点的
src
└─net
    └─ayqy
        └─app_notepad
                DAO.java
                Dialog.java
                MainActivity.java
                myEditText.java
                ShowList.java

P.S. windows 下,tree /f 可以生成上面的東西

筆者習慣的包命名方法(或者說是類別組織方法)是按照 class 職能(所謂的 PBL)來分,比如:

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

各層職能如下:

  • default package

View 層,定義 Activity 只實作 UI 展示,以及跳轉

  • com(component)

自定義元件,包括自定義 View 和 View 組合

  • core

核心類別,定義配置資料以及常量

  • dao(data access object)

db 操作類別,包括 dbHelper

  • service

每個 Activity 對應一個 Service,負責實作該 Activity 的業務邏輯,此外 Service 依賴的其他與 Activity 密切相關的類別(需要 Activity 的 context,比如 JsInvoker)也在此定義

  • util

工具類別,包括 modelHelper(比如 RssHelper)

  • vo(value object)

model,或者說是 bean,只包含資料結構定義以及 getter/setter(需要的話還可以有 equals、compareTo 等等,反正盡量保持 model 純粹)

筆者先接觸了 JSP,所以一直認為這套分層方法挺好,在 Android 裡就照搬過來了(甚至搬到了 WinForm 和 WPF),所幸專案規模都很小,沒覺得有什麼不妥,因為沒有第二個方案可選

PBL 的缺點如下:

  • package 內低內聚低模組化

通常同一個包下的各個 class 彼此不相關,也可能永遠不會相關

  • package 之間高耦合

通常在一個 class 裡需要 import 來自不同 package 的一堆東西

  • 碼起來麻煩

實作一塊功能需要編輯不同目錄下的多個檔案,經常用 IDE 開一堆分頁還都捨不得關閉,因為關了不好找

同樣,刪起來也麻煩,在幾個包下找來找去,一不小心就出錯

二. PBF(Package By Feature)

按功能(模組)分包組織 class,按 app 功能分為 login、feedback、settings、orders、shipping 等等,例如:

// 医药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)
            │      ...不只能放java代码,只要是该功能相关的都应该放在这里
            │
            ├─drug
            ├─patient
            ├─report
            ├─security
            ├─webmaster
            └─util

簡單地說,每塊功能對應一個 package,包名就是功能名,除了通用的類別定義在 util 之類的 package 下之外,實作該功能的所有類別都在該 package 下

好處很明顯,如下:

  • package 內高內聚,package 之間低耦合

哪塊要添新功能,只改某一個 package 下的東西

按 class 職能分層(PBL)降低了程式碼耦合,但帶來了 package 耦合,要添新功能,需要改 model、dbHelper、view、service 等等,需要改動好幾個 package 下的程式碼,改動的地方越多,越容易產生新問題,不是嗎?

按功能分包(PBF),featureA 相關的所有東西都在 featureA 包,feature 內高內聚高度模組化,不同 feature 之間低耦合,相關的東西都放在一起,還好找

  • package 有私有作用域(package-private scope)

你負責開發這塊功能,這個目錄下所有東西都是你的

PBL 的方式是把所有工具方法都放在 util 包下,小張開發新功能時發現需要一個 xxUtil,但它又不是通用的,那應該放在哪裡?沒辦法,按照分層原則,我們還得放在 util 包下,好像不太合適,但放在其他包更不合適,功能越來越多,util 類別也越定義越多。後來小李負責開發一塊功能時發現需要一個 xxUtil,同樣不通用,去 util 包一看,怎麼已經有了,而且還沒法複用,只好放棄 xx 這個名字,改為 xxxUtil……因為PBL 的 package 沒有私有作用域,每一個包都是 public(跨包方法調用是很平常的事情,每一個包對其他包來說都是可存取的)

如果是 PBF,小張的 xxUtil 自然放在 featureA 下,小李的 xxUtil 在 featureB 下,如果覺得 util 好像是通用的,就去 util 包看看要不要把工具方法添進 xxUtil,class 命名衝突沒有了

PBF 的 package 有私有作用域,featureA 不應該存取 featureB 下的任何東西(如果非存取不可,那就說明介面定義有問題)

  • 很容易刪除功能

統計發現新功能沒人用,這個版本那塊功能得去掉

如果是 PBL,得從功能入口到整個業務流程把受到牽連的所有能刪的程式碼和 class 都��出來刪掉,一不小心就完蛋

如果是 PBF,好說,先刪掉對應包,再刪掉功能入口(刪掉包後入口肯定報錯了),完事

  • 高度抽象

解決問題的一般方法是從抽象到具體,PBF 包名是對功能模組的抽象,包內的 class 是實作細節,符合從抽象到具體,而 PBL 弄反了

PBF 從確定 AppName 開始,根據功能模組劃分 package,再考慮每塊的具體實作細節,而 PBL 從一開始就要考慮要不要 dao 層,要不要 com 層等等

  • 只透過 class 來分離邏輯程式碼

PBL 既分離 class 又分離 package,而 PBF 只透過 class 來分離邏輯程式碼

沒有必要透過 package 分離,因為 PBL 中也可能出現尷尬的情況:

    ├─service
            │      MainServ.java

按照 PBL,service 包下的所有東西都是 Controller,應該不需要 Serv 後綴,但實際上通常為了碼起來方便,直接 import service 包,Serv 後綴是為了避免引入的 class 和當前包下的 class 命名衝突,當然,不用後綴也可以,得寫清楚包路徑,比如 new net.ayqy.service.Main(),麻煩

而 PBF 就很方便,無需 import,直接 new MainServ() 即可

  • package 的大小有意義了

PBL 中包的大小無限增長是合理的,因為功能越添越多

而 PBF 中包太大(包裡 class 太多)表示這塊需要重構(劃分子包)

三. PBF 具體實踐(Google I/O 2015)

最有代表性的是 Google I/O 2015,結構如下:

java
└─com
    └─google
        └─samples
            └─apps
                └─iosched
                    │  AppApplication.java  定义Application类
                    │  Config.java          定义配置数据(常量)
                    │
                    ├─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
                    │      ...定义interface并实现
                    │
                    ├─gcm
                    │  │  GCMCommand.java
                    │  │  GCMIntentService.java
                    │  │  GCMRedirectedBroadcastReceiver.java
                    │  │  ...
                    │  │
                    │  └─command
                    │          AnnouncementCommand.java
                    │          NotificationCommand.java
                    │          SyncCommand.java
                    │          ...
                    │
                    ├─io
                    │  │  BlocksHandler.java
                    │  │  HandlerException.java
                    │  │  HashtagsHandler.java
                    │  │  ...处理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
                    │      ...定义model以及实现model相关操作
                    │
                    ├─myschedule
                    │      MyScheduleActivity.java
                    │      MyScheduleAdapter.java
                    │      MyScheduleFragment.java
                    │      ...
                    │
                    ├─provider
                    │      ScheduleContract.java
                    │      ScheduleContractHelper.java
                    │      ScheduleDatabase.java
                    │      ...实现ContentProvider
                    │      (也在此处定义provider依赖的其它类,比如db操作)
                    │
                    ├─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以及自定义UI组件
                    │  │
                    │  └─widget
                    │          AspectRatioView.java
                    │          BakedBezierInterpolator.java
                    │          BezelImageView.java
                    │          ...自定义小UI控件
                    │
                    ├─util
                    │      AboutUtils.java
                    │      AccountUtils.java
                    │      AnalyticsHelper.java
                    │      ...工具类,提供静态方法
                    │
                    ├─videolibrary
                    │      VideoLibraryActivity.java
                    │      VideoLibraryFilteredActivity.java
                    │      VideoLibraryFilteredFragment.java
                    │      ...
                    │
                    └─welcome
                            AccountFragment.java
                            AttendingFragment.java
                            ConductFragment.java
                            ...

上面除了按 feature 命名的 package 外,也定義了類似於 layer 的 package,如下:

  • framework

  • io

  • model

  • provider

  • receiver

  • service

  • ui

  • util

對比一下 PBL 的一般規範,如下:

PBL一般規範Google I/O 2015
activities(頁面用到的 Activity 類別)feature/
base(頁面中每個 Activity 類別共享的可寫成一個 BaseActivity 類別)ui
adapter(頁面用到的 Adapter 類別)通用的在 ui 裡,不通用的在 feature/
tools(公共工具方法類別)util
bean/unity(元素類別)model
db(資料庫操作類別)資料庫操作在 provider 裡,資料處理(比如 json 解析)在 io 裡
view/ui(自定義的 View 類別)ui
service(Service 服務)service
broadcast(Broadcast 服務)feature/

三. 總結

參考 Google I/O 2015 的程式碼結構,PBF 具體可以這樣做:

src
└─com
    └─domain
        └─app
            │  Config.java 配置数据、常量
            │
            ├─framework
            │      定义interface以及相关基类
            │
            ├─io
            │      数据定义(model)、数据操作(比如json解析,但不包括db操作)
            │
            ├─model
            │      定义model(数据结构以及getter/setter、compareTo、equals等等,不含复杂操作)
            │      以及modelHelper(提供便于操作model的api)
            │
            ├─provider
            │      实现ContentProvider,及其依赖的db操作
            │
            ├─receiver
            │      实现Receiver
            │
            ├─service
            │      实现Service(比如IntentService),用于在独立线程中异步do stuff
            │
            ├─ui
            │      实现BaseActivity,以及自定义view and widget,相关的Adapter也放这里
            │
            ├─util
            │      实现工具类,提供静态方法
            │
            ├─feature1
            │      Item.java                定义model
            │      ItemHelper.java          实现modelHelper
            │      feature1Activity.java    定义UI
            │      feature1DAO.java         私有db操作
            │      feature1Utils.java       私有工具函数
            │      ...其它私有class
            │
            ├─...其它feature

當然,這樣的程式碼組織方案不僅僅適用於 Android,用著覺得好就好

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論