移动开发示例工程UI组件【免费下载链接】AndroidProjectAndroid 技术中台但愿人长久搬砖不再有项目地址https://gitcode.com/gh_mirrors/an/AndroidProject点击查看免费下载导读AndroidProject 是作者Android 轮子哥维护多年的开源安卓技术中台工程定位介于模板与架构之间既提供可直接复制改造的页面、对话框、动效等模板代码又沉淀了一套经过大量项目实践验证的代码组织方式与通用封装Activity/Fragment/Dialog 基类、网络请求、屏幕适配、构建多环境等。本文以仓库 README.md 为主线结合 HelpDoc.md 与各模块源码系统梳理它的模块结构、基类设计、核心封装与关键技术取舍帮助你理解技术中台究竟解决了什么痛点以及如何用它快速搭建新项目或重构旧项目。一、项目定位为什么它叫技术中台而不是模板工程或架构AndroidProject 在 README 中明确说明它已经从安卓架构正式更名为安卓技术中台并给出了一个精炼的解释——技术中台可以理解为模板 架构一般写模板代码的人做不了架构设计而做架构设计的人又不想写模板代码技术中台同时吸收了二者的优点既能保证前期快速开发模板又能保证后续维护时快速迭代架构。也就是说一个 App 大概率会出现的功能页面、对话框、列表、弹窗、网络请求、权限申请、状态栏沉浸等在工程里已经写好拿来即可用同时底层又保持了一套统一、规范、可复用的代码设计不会因为前期开发快而留下成堆的技术债务。README 用一句话概括了它的价值主张你不需要再麻木 Copy 原有旧项目的代码只需改动少量代码就能得到想要的效果你会发现开发新项目其实是一件很快乐的事。值得注意的是README 也强调 AndroidProject 的定位是技术架构不是什么都做的 Demo 工程因此在功能上刻意保持克制只收录大多数项目通用的部分详见本文技术选型与取舍章节。二、项目亮点与自检标准README 的六项承诺README 用六个维度描述了作者对工程的自我要求这些既是项目亮点也是任何开发者拿这套工程时可以参考的验收标准App 优化已进行全面的内存优化、布局优化、代码优化、瘦身优化并对结果进行了长期测试。从源码可见AppApplication.java 中在主进程内完成 SDK 初始化、在onLowMemory/onTrimMemory中联动清理 Glide 图片缓存属于典型的内存治理手段。代码规范参照 Android SDK、Support 源码及阿里巴巴代码规范文档对代码命名并对难点代码注释、重点代码说明。代码统一对常见代码封装到基类或工具类中不追求过度封装根据实际场景和代码维护性考虑尽量保证同一个功能的代码在项目中不重复。敏捷开发通用功能已写好可用于新项目开发或旧项目重构可将开发周期缩短近一半的时间此结论来自 README 项目方表述具体效果以个人实践评估为准。无任何瑕疵对小屏手机、全面屏手机、带虚拟按键手机进行适配优化。兼容性优良依赖大量开源社区用户在真机上的测试反馈持续打磨。其中 README 还特别点出了一个优秀的代码设计范例——对startActivityForResult的改良后文有完整源码剖析并提示这只是冰山一角更多优秀设计需要阅读源码去发掘。三、仓库模块结构多模块 Gradle 工程的划分思路从 settings.gradle 可以看到工程由以下模块组成include :app include :library:core include :library:base include :library:smallestWidth include :library:customWidget include :library:umengSdk各模块职责划分清晰模块路径职责appapp/业务演示壳工程包含各 Activity/Fragment/Dialog/Popup/Adapter 的示例、AOP 注解与切面、网络 API 定义等library:corelibrary/core与技术无关的通用能力ActivityAction、ClickAction、HandlerAction、KeyboardAction 等接口化 Action以及 ActivityManager、CacheDataManager、ThreadPoolManager 等管理器library:baselibrary/base技术基类层BaseActivity、BaseFragment、BaseDialog、BaseAdapter、BasePopupWindow 等library:smallestWidthlibrary/smallestWidth最小宽度smallestWidth屏幕适配的资源与换算工具library:customWidgetlibrary/customWidget自研自定义控件ClearEditText、PasswordEditText、CountdownView、SwitchButton 等library:umengSdklibrary/umengSdk友盟登录、分享、统计 SDK 的统一封装这种core纯能力→ base技术基类→ app业务的分层方式正是代码统一思想的落地业务代码只关心业务通用能力沉淀到下层模块天然避免了重复代码。另外工程还通过localMaven/本地 Maven 仓库内置了腾讯 open-sdk、友盟 umsdk 等不便从公共仓库拉取的依赖保证工程开箱可构建。四、基类设计BaseActivity / BaseFragment / BaseDialog 的技术基座4.1 BaseActivity约束式生命周期 回调优化BaseActivity.java 继承自AppCompatActivity同时实现ActivityAction、ClickAction、HandlerAction、BundleAction、KeyboardAction、FixOrientationAction六个接口化 Action并为子类定义了统一模板Override protected void onCreate(Nullable Bundle savedInstanceState) { if (!isAllowOrientation(this)) { fixScreenOrientation(this); } super.onCreate(savedInstanceState); initActivity(); } protected void initActivity() { initLayout(); initView(); initData(); }即每个页面统一走initLayout() → initView() → initData()三段式流程只留getLayoutId()、initView()、initData()三个抽象方法给子类实现。此外它内置了大量通用细节initLayout()中加载布局后调用initSoftKeyboard()为根布局设置点击监听、点击外部自动隐藏软键盘并屏蔽内存泄漏隐患在finish()、startActivityForResult等时机统一隐藏软键盘onNewIntent()中刷新setIntent避免singleTop复用时拿到旧 IntentdispatchKeyEvent()会把按键事件先派发给处于RESUMED状态的子 Fragment实现 Fragment 级按键拦截。4.2 BaseFragment生命周期绑定 防 getActivity 为空BaseFragment.java 有几个值得借鉴的设计通过getAttachActivity()替代getActivity()避免 Fragment 与 Activity 解绑后返回空指针实现Application.ActivityLifecycleCallbacks在onAttach注册、onDetach反注册从而让 Fragment 能感知宿主 Activity 的onActivityStart/Resume/Pause/Stop/Destroy解决Fragment 想要拿到 Activity 生命周期事件的常见痛点dispatchKeyEvent()支持在 Fragment 内直接处理返回键等按键事件initData()只在首次onResume时执行一次mLoading标志避免重复加载。4.3 BaseDialogLifecycle 化 监听器一对多 生命周期绑定BaseDialog.java 是仓库中信息量最大的类之一它把一个普通的AppCompatDialog升级成了LifecycleOwner自身持有LifecycleRegistry把onCreate/onStart/onStop/onDismiss桥接为 Lifecycle 事件并处理了复用 Dialog 对象时LifecycleRegistry的状态报错State must be at least CREATED ...等使得对话框也能参与 Lifecycle 感知监听器一对多原生 Dialog 的 OnShow/OnCancel/OnDismiss 监听只能有一个观察者BaseDialog 通过addOnShowListener/addOnCancelListener/addOnDismissListener改为观察者集合并在遍历时拷贝集合防止ConcurrentModificationException防内存泄漏通过ListenersWrapper、ShowListenerWrapper等基于SoftReference的包装类持有外部监听对象避免横竖屏切换、Dialog 存活期过长导致的泄漏生命周期自动释放内部DialogLifecycle使用Application.registerActivityLifecycleCallbacksAndroid 10 用Activity.registerActivityLifecycleCallbacks监听宿主 Activity当 Activity 销毁时自动dismiss尚未关闭的 Dialog这正是 HelpDoc 中用 registerActivityLifecycleCallbacks 替代 DialogFragment 管控 Dialog 生命周期方案的实现本体。同时Builder提供了链式 APIsetContentView、setThemeStyle、setAnimStyle、setWidth/setHeight、setGravity、setCancelable、setCanceledOnTouchOutside、setBackgroundDimEnabled/Amount、addOnShowListener等并在create()中根据重心自动匹配默认动画TOP/BOTTOM/LEFT/RIGHTshow()前还会判断Activity.isFinishing()/isDestroyed()避免在后台或销毁态弹出对话框。4.4 AppActivity业务级基类在技术基类之上AppActivity.java 又封装了一层业务基类注入ToastAction、TitleBarAction、ImmersionAction与网络监听OnHttpListenerObject标题栏统一通过acquireTitleBar()获取递归查找布局中的 TitleBarsetTitle()同步联动标题栏沉浸式状态栏统一配置ImmersionBar状态栏深色字体 导航栏白色 自动深浅色切换并针对Android 15 EdgeToEdge做了 WindowInsets 适配内置showLoadingDialog()/hideLoadingDialog()加载对话框管理通过计数器和 300ms 延迟避免频繁闪现且页面销毁时自动隐藏实现OnHttpListener后网络请求的开始/成功/失败/结束自动与加载对话框、Toast 提示联动。五、核心亮点源码剖析startActivityForResult 的改良设计README 重点点名了startActivityForResult的改良直接在方法上传入监听对象无需重写onActivityResult也无需手动传 requestCode。完整实现见 BaseActivity.javapublic void startActivityForResult(NonNull Class? extends Activity clazz, Nullable OnActivityCallback callback) { startActivityForResult(new Intent(this, clazz), null, callback); } public void startActivityForResult(NonNull Intent intent, Nullable OnActivityCallback callback) { startActivityForResult(intent, null, callback); } public void startActivityForResult(NonNull Intent intent, Nullable Bundle options, Nullable OnActivityCallback callback) { if (mActivityCallbacks null) { mActivityCallbacks new SparseArray(1); } // 请求码必须在 2 的 16 次方以内 int requestCode; do { requestCode new Random().nextInt((int) Math.pow(2, 16)); } while (mActivityCallbacks.indexOfKey(requestCode) ! -1); mActivityCallbacks.put(requestCode, callback); startActivityForResult(intent, requestCode, options); } Override protected void onActivityResult(int requestCode, int resultCode, Nullable Intent data) { OnActivityCallback callback; if (mActivityCallbacks ! null (callback mActivityCallbacks.get(requestCode)) ! null) { callback.onActivityResult(resultCode, data); mActivityCallbacks.remove(requestCode); return; } super.onActivityResult(requestCode, resultCode, data); }设计要点用SparseArrayOnActivityCallback维护requestCode → 回调映射requestCode 由框架随机生成2¹⁶ 以内并做去重开发者不再需要自己维护魔法数字回调用完后立即移除避免内存泄漏与回调错乱与 HelpDoc 中对比的谷歌ActivityResultContracts用法相比调用更简洁// 谷歌 ActivityResultContracts 的用法 registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallbackActivityResult() { Override public void onActivityResult(ActivityResult result) { Intent data result.getData(); int resultCode result.getResultCode(); } }).launch(new Intent(this, HomeActivity.class)); // AndroidProject 的用法 startActivityForResult(HomeActivity.class, new OnActivityCallback() { Override public void onActivityResult(int resultCode, Nullable Intent data) { // 直接在这里拿到结果 } });BaseFragment也通过宿主mActivity转发实现了同样的回调能力见 BaseFragment.java。六、网络请求封装EasyHttp 统一数据模型 全局拦截README 的作者其他开源项目列表中提到了自研网络框架 EasyHttp而它在工程内的接入方式非常有参考价值。6.1 统一接口数据结构 HttpDataHttpData.java 定义了业务接口的统一返回结构code / msg / data并封装了两个判断public boolean isRequestSuccess() { return code 200; } public boolean isTokenInvalidation() { return code 1001; }也就是说业务层约定code 200代表请求成功code 1001代表登录 Token 失效。6.2 请求处理策略 RequestHandlerRequestHandler.java 实现了IRequestHandler集中处理所有请求分支requestSuccess()按返回类型分发Response/Object/Headers/InputStream/Bitmap/StringJSON 统一用 GsonFactory 解析若结果是HttpData则填充响应头并依据isRequestSuccess()/isTokenInvalidation()抛出ResultException或TokenExceptionrequestFail()将各种底层异常翻译为业务可读异常——SocketTimeoutException→ 超时、UnknownHostException→ 区分断网/服务器异常、IOException→ 请求被取消等等其中TokenException的处理会自动跳转到登录页并销毁其余 Activity借助 ActivityManager.java 的finishAllActivities(LoginActivity.class)。6.3 服务器配置与全局参数RequestServer.java 通过AppConfig.getHostUrl() api/拼接 Host并统一采用表单提交InitManager.java 中则通过EasyConfig完成全局配置EasyConfig.with(okHttpClient) .setLogEnabled(AppConfig.isLogEnable()) .setServer(new RequestServer()) .setHandler(new RequestHandler(application)) .setCacheStrategy(new HttpCacheStrategy()) .setRetryCount(1) .setInterceptor(new IRequestInterceptor() { Override public void interceptArguments(NonNull HttpRequest? httpRequest, NonNull HttpParams params, NonNull HttpHeaders headers) { // 添加全局请求头 headers.put(token, 66666666666); headers.put(deviceOaid, UmengClient.getDeviceOaid()); headers.put(versionName, AppConfig.getVersionName()); headers.put(versionCode, String.valueOf(AppConfig.getVersionCode())); } }) .into();这种统一数据模型 统一异常翻译 全局拦截器的组合正是 README 中敏捷开发在数据层落地的方式所有页面写网络请求时只需要定义 API 接口并监听成功/失败回调重复的解析、鉴权、错误提示都被框架化处理。七、屏幕适配smallestWidth 最小宽度方案落地HelpDoc 中详细记录了作者对屏幕适配的思考过程并在 v13.0 采用了通配符smallestWidth最小宽度适配方案仓库中对应 library/smallestWidth 模块资源侧在values-swXXXdp/下为各个宽度档位240dp 1024dp 等二十余档各维护一套dimens_dp.xml与dimens_sp.xml布局中引用dimen/dp_16、dimen/sp_10这类约定命名代码侧SmallestWidthAdaptation.java 提供dp2px/px2dp/sp2px/px2sp换算工具实现均以resources.getDimension(R.dimen.dp_1)为基准换算避免硬编码像素值。作者在 HelpDoc 中同时给出了背景知识谷歌原生按密度换算 dp/spldpi1dp0.75px、mdpi1dp1px、hdpi1dp1.5px、xhdpi1dp2px、xxhdpi1dp3px、xxxhdpi1dp4px这套方案适合手机场景而当需要适配平板、要求控件随屏幕尺寸等比缩放时通配符方案更可控。仓库还遵守了控件大小用 dp、字体大小用 sp的约定理由是 sp 会跟随系统字体设置变化照顾老年用户放大字体的需求。八、构建配置与多环境管理三套构建类型 多服务器切换8.1 多环境配置configs.gradleconfigs.gradle 是工程的多环境开关中枢定义了测试服test/ 预发布服pre/ 正式服product三种服务器类型并根据 Gradle 任务名自动选择if (taskName.endsWith(Debug)) { serverType SERVER_TYPE_TEST } else if (taskName.endsWith(Preview)) { serverType SERVER_TYPE_PREVIEW } else { serverType SERVER_TYPE_PRODUCT } // 从 Gradle 命令中读取参数配置例如./gradlew assembleRelease -P ServerTypetest if (project.hasProperty(ServerType)) { serverType project.properties[ServerType] }对应地输出LOG_ENABLE、HOST_URL、BUGLY_ID/BUGLY_KEY等配置测试/预发布环境默认开启日志、正式环境默认关闭。8.2 通用构建配置common.gradle 与 app/build.gradlecommon.gradle 提供各模块通用配置compileSdk 36、minSdk 23、targetSdk 36Java 21 兼容 JDK 核心库脱糖desugaring并把构建输出统一收敛到根目录build/下。app/build.gradle 则在应用模块补充构建类型debug / preview / releasedebug 带.debug包名后缀release 开启混淆proguard-app.pro、proguard-sdk.pro与资源压缩preview 介于两者之间resConfigs zh只保留中文资源、resConfigs xxhdpi只保留主流分辨率图片、ndk.abiFilters arm64-v8a只保留 64 位 soMMKV 2.0 不再支持 32 位这些都是瘦身优化的落地通过buildConfigField注入LOG_ENABLE / HOST_URL / BUGLY_ID / BUGLY_KEY供 AppConfig.java 读取androidAopConfig.include android.defaultConfig.applicationId只对自有包名做 AOP 织入easylauncher 插件为 debug/preview 构建的图标加角标便于区分版本。8.3 依赖管理gradle/libs.versions.toml 采用 Version Catalog 统一管理版本号可以看到工程的核心技术栈AGP 8.x Kotlin AndroidAOP、AppCompat/Material、XXPermissions权限、TitleBar标题栏、Toaster吐司、EasyHttp OkHttp网络、GsonFactory解析容错、Glide图片、ImmersionBar沉浸式、SmartRefreshLayout下拉刷新、MMKV存储、Bugly崩溃上报、LeakCanary Chucker调试期内存/流量排查等。九、技术选型与取舍HelpDoc 中沉淀的 20 条决策README 将 HelpDoc.md 作为常见问题解答入口该文档记录了作者在七年迭代中做出的重要取舍。以下是最具代表性的决策及其核心理由不用 MVP以及不迷信 MVC/MVVM 分类MVP 需要大量类与回调简单页面反而变复杂开发维护成本高。作者的观点是直接写 Activity但要做好代码封装与重复代码抽取把 Activity 控制成只含业务代码的类1000 行以内。不用 ButterKnife / ViewBinding / DataBinding共同痛点是自动生成类、无法在基类统一封装、每次都要写binding.xxxViewBinding 还会引发 xml id 与 Java 命名规范冲突DataBinding 把复杂判断逻辑塞进无法换行的 xml拖垮 CodeReview。结论是findViewById 一直挺好并推荐插件 FindViewByMe。不做组件化代码解耦是双刃剑——清单文件要维护两份、路由注解繁琐、模块间 SDK 初始化与数据交互难解耦中小型项目收益小于成本。移除界面侧滑经历 BGASwipeBackLayoutAndroid 9.0 上 100% 必现闪屏和 SmartSwipeAndroid 11 上 WindowManager 场景闪屏、作者长期不回复两次踩坑后在 v12.1 决定移除该功能。屏幕适配选型对比 AutoSizeBug 较多、谷歌原生 dp/sp手机场景最优与通配符方案平板场景更可控最终 v13.0 采用通配符方案见本文第七章。不用 DialogFragment 防泄漏列举了监听被覆盖、后台 show/dismiss 抛IllegalStateException、屏幕旋转后 Dialog 对象被置空等三大坑最终改用Application.registerActivityLifecycleCallbacks管控 Dialog 生命周期即 BaseDialog 的 DialogLifecycle 实现。不用腾讯 X5 WebViewX5 有自我更新机制掌控权在腾讯、生命周期跟随腾讯决策建议优先原生 WebView由于已做 WebView 统一封装后续替换成本低。不用单 Activity 多 Fragment全屏/横竖屏切换、Fragment 间通讯、系统回收后的回退栈重建、多 Fragment 一次性创建的性能问题都难以统一处理省一两句清单注册代码不构成价值。少用 ConstraintLayout无嵌套的代价是要为相邻 View 定义大量约束 ID、容易越界、删除/增改 View 会牵一发动全身建议以 LinearLayout/FrameLayout 组合、嵌套 2~3 层为主超过 5 层再考虑约束布局。最低兼容 Android 5.0minSdk 21不兼容 Android 4.4原因是其 dex 分包、矢量图支持差、API 高低版本兼容负担大。不用 EventBus非必需品、易滥用能用正常方式通讯就不要用事件总线。不用 Retrofit RxJava认为 Retrofit 功能太少通用请求头/参数、日志、动态 Host 都要自己实现、上传不支持进度、注解学习成本高最终自研 [EasyHttp] 并在仓库内通过 RequestHandler/RequestServer 完成业务化封装。不全盘 Jetpack 全家桶只取 LifecycleBaseDialog、EasyHttp 都在用LiveData/ViewModel 因不符合本工程的代码设计思想而未用。不二次封装图片加载框架曾封装过但移除了——抽取接口会扼杀 Glide 图形变换、监听、缓存等扩展 API 的灵活性付出和收益不成正比。不按业务划分包名业务职责往往不明确中小项目按类的作用activity/dialog/adapter/manager 等分包更清晰。不封装列表多 type原生RecyclerView.Adapter.getItemType()已支持无需重复造轮子。权限申请移除 AOP 注解因 AspectJ 插件与某些场景存在已知冲突而移除HelpDoc 中保留了可自行评估的Permissions注解与切面实现代码。关于升级由于它是架构而非可远程依赖的框架升级只能整体替换代码且必须经过严格自测——不推荐把最新代码直接搬到已上线的公司项目。此外HelpDoc 最后一节给出了作者看待新技术的方法论先问新技术解决什么痛点再判断它在前期开发或后续维护上能否创造价值不盲目迷信大厂出品谷歌的新技术不一定都是好的。这些决策共同刻画了 AndroidProject 的取舍哲学以中小型项目的主流场景为目标能解决问题的方案才采用无法稳定驾驭的特性坚决砍掉。十、如何上手与落地使用结合 README 与工程现状推荐的使用路径如下阅读先通读 README.md 与 HelpDoc.md理解项目的定位与取舍再按core → base → app的顺序阅读 library/core、library/base 与 app/src/main/java/com/hjq/demo 下的示例代码构建工程已内置gradlew与依赖配置可直接在根目录执行./gradlew assembleDebug或按需加-P ServerTypeproduct切换环境进行构建体验需注意仓库的签名配置AppSignature.jks、友盟/QQ/微信的 AppId 等属于示例值正式使用应替换为自己的密钥改造以app模块为蓝本删除不需要的页面与功能替换包名与图标把业务基类BaseActivity/AppActivity、BaseDialog 等与 common.gradle、configs.gradle 的构建约定迁移到自己的工程升级策略若希望跟进上游更新由于 AndroidProject 以代码形式交付而非远程依赖只能整体替换代码并做全量回归测试尤其要覆盖主流程功能。十一、总结AndroidProject 的核心理念可以用 README 的一句话收束如果架构是理论那么模板就是实践。它通过core / base / app三层模块划分、Activity/Fragment/Dialog 技术基类、EasyHttp 网络封装、smallestWidth 屏幕适配与三套构建环境把快速开发与可维护性这两个看似矛盾的目标统一在了同一个工程里而 HelpDoc.md 记录的每一次不用某技术的决策则展示了它作为技术中台的边界意识——只收编经过验证的通用能力对无法稳定驾驭的特性坚决说不。对于正在搭建新项目或准备重构旧项目的 Android 团队而言这份工程既是一份可直接复用的代码资产更是一份难得的架构决策参考。体验演示二维码见 picture/demo_code.png可扫码安装 README 中提供的示例 APK 快速查看常用界面、对话框与动效实现效果。赞分享移动开发示例工程UI组件【免费下载链接】AndroidProjectAndroid 技术中台但愿人长久搬砖不再有项目地址https://gitcode.com/gh_mirrors/an/AndroidProject点击查看免费下载相关推荐AndroidProject技术中台全面解析现代化Android开发架构AndroidProject技术中台全面解析现代化Android开发架构 AndroidProject是一个经过三年多精心打磨的Android技术中台项目集移动开发示例工程UI组件SEO专家智能体marketing-seo-specialist一份可直接落地的技术 SEO 全流程作战手册SEO专家智能体marketing seo specialist一份可直接落地的技术 SEO 全流程作战手册 在 agency agents zh 这个包人工智能AI 技能提示工程Viper Syscall 的 Visual Studio 工程模块直接系统调用与加密 Shellcode 的落地实战Viper Syscall 的 Visual Studio 工程模块直接系统调用与加密 Shellcode 的落地实战 Viper 红队平台内置的 Execu文档教程网络安全创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考