# WeCommunity **Repository Path**: kevin-ray-code/WeCommunity ## Basic Information - **Project Name**: WeCommunity - **Description**: 社区知识星球项目,使用Wan Android Api - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-14 - **Last Updated**: 2026-01-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 开发周期:1周 技术栈:Kotlin / Jetpack / Retrofit+OkHttp+Gson / Glide/ Zhipu AI / Activity/ BoardcastReceiver / ContentProvider / 协程 / postman # 一、项目概述与设计思路 本项目旨在构建一个基于 WanAndroid API 的 Android 知识社区应用。除了具备文章浏览、分类筛选、用户登录等基础功能外,创新性地引入了 “AI 锐评” 功能,利用智谱 AI 大模型对技术文章进行实时总结与犀利点评,提升用户的阅读效率与趣味性。 ## 1.1 核心功能架构 - 内容分发:首页文章流(支持分页、下拉刷新)、体系分类筛选、个人信息页。 - AI 赋能:全局悬浮球入口、文章内容智能提取。 - 用户体系:登录/注册、积分展示、收藏管理、二维码展示与存储 - 视觉交互:Material Design 风格、自定义拖拽视图、平滑的转场动画 ## 1.2 页面展示 ### 1.2.1 登陆与注册页 - 首次进入App后需要先登陆,如果没有账号的话需要先注册; - 登陆与注册均接通接口,对于密码的设置严格按照接口要求,在前端直接检测密码与账号的完整性以及是否合理,减少错误请求带来的网络开销; ### 1.2.2 首页 - 登陆之后进入首页,首页主要分为三部分; - 顶部导航栏:有首页字段以及一个搜索框(暂未开发) - 主体部分:主要包括banner以及文章列表,banner可以点击跳转,文章点击可以打开(见1.2.7 文章详情页)。上滑时banner与文章列表同时上滑,切换到其他界面后回到首页可以记录原本位置; - 底部导航栏:有三个按钮分别为 首页、分类以及我的,由三个Fragment组成,点击后跳转到对应的页面,图标填充颜色,字体加黑; ### 1.2.3 分类页 - 分类页同样由三部分构成: - 顶部导航栏包含分类字段以及搜索框,点击搜索框开启 按作者名称搜索功能,再次点击搜索图标收起搜索状态 - 主体部分由:一级筛选目录、二级筛选目录一级文章列表组成。选择对应的类别查看不同的分类,一级分类右侧有下拉展示框,点击之后显示所有分类,可以点击筛选对应分类; ### 1.2.4 个人信息页 - 个人信息页主要用于展示个人信息以及一些其他信息; - 每个按钮均可点击:点击“我的收藏”进入收藏列表(见1.2.5 收藏列表页面),点击“退出登陆”退出登陆状态回到登陆页面,点击“个人头像”进入个人资料页(见1.2.6 个人资料编辑页面); - 后续其他功能在开发中... ### 1.2.5 收藏列表页面 - 我的收藏页面的信息来自于之前所有可以点击“收藏”按钮的地方,点击“取消收藏”后,从我的收藏页面删除; - 从“我的收藏”进入文章详情页与首页文章详情页相同; ### 1.2.6 个人资料编辑页面 - 个人资料编辑页面展示个人所有信息,其中不存在点击按钮的信息不可以修改,存在点击按钮的信息可以进行编辑; - 点击二维码按钮打开“我的二维码”页面,支持保存到本地相册; ### 1.2.7 文章详情页 - 文章详情页展示详细的文章内容,可以从任何文章列表进入,主要为三部分+悬浮Ai按钮: - 顶部导航栏:中间显示文章标题,长标题会以跑马灯形式展示,不断滑动,左侧为“返回”按钮右侧为“更多”按钮; - 主体部分:主体部分为文章内容,由WebView直接打开文章链接; - 底部悬浮窗口:底部悬浮窗口一直悬浮,有三个按钮,支持“点赞”、“收藏”以及“分享”; - 悬浮Ai按钮:Ai悬浮按钮支持拖动,设置了自动吸附到边缘不影响阅读文章,点击按钮查看Ai锐评; ## 1.3 视频展示 暂时无法在飞书文档外展示此内容 ## 1.4 技术深度与架构设计 项目采用 MVC(Model-View-Controller) 架构思想,确保代码结构清晰,职责分离。 - 页面切换:采用单Activity多Fragment的结构,Activity保证底部导航栏一直存在,Fragment页面实现`toolbar` 满足各个页面顶部导航栏不同的需求; - 网络层:使用 Retrofit2 + OkHttp3 进行网络请求封装,配合 Gson 解析数据。 - 图片加载:使用 Glide 进行图片异步加载、缓存及圆角处理。 - 异步处理:全面采用 Kotlin Coroutines (协程) 替代传统的 Thread/Handler,简化异步逻辑,避免回调地狱。 二、关键技术点 (Jetpack & AI) - ViewBinding:替代 findViewById,确保视图调用的空安全,防止 NullPointerException。 - WebView与JS交互:通过 evaluateJavascript 注入脚本,从网页 DOM 树中提取正文内容,清洗后发送给 AI。 - 智谱 AI 集成:对接智谱大模型 API,通过自定义 Prompt(提示词)实现“锐评”风格的文本生成。 ```kotlin interface ApiService { // 登录 @POST("user/login") @FormUrlEncoded fun login( @Field("username") username: String, @Field("password") password: String ): Call> // 注册 @POST("user/register") @FormUrlEncoded fun register( @Field("username") username: String, @Field("password") password: String, @Field("repassword") repassword: String ): Call> // 获取首页文章列表 (页码作为路径参数) @GET("article/list/{page}/json") fun getArticles(@Path("page") page: Int): Call> // 收藏文章 (需要登录) @POST("lg/collect/{id}/json") fun collectArticle(@Path("id") id: Int): Call> // 取消收藏 (备用) @POST("lg/uncollect_originId/{id}/json") fun uncollectArticle(@Path("id") id: Int): Call> @GET("banner/json") fun getBanners(): Call>> // 获取我的收藏列表 // page 从 0 开始 @GET("lg/collect/list/{page}/json") fun getCollectionList(@Path("page") page: Int): Call> // 在收藏页面取消收藏 // 这里的 id 是收藏列表里的 id (originId 才是文章id) @POST("lg/uncollect/{id}/json") @FormUrlEncoded fun uncollectInMyList(@Path("id") id: Int, @Field("originId") originId: Int): Call> // 获取知识体系树 (一级和二级分类) @GET("tree/json") fun getSystemTree(): Call>> // 根据分类ID获取文章列表 (cid) @GET("article/list/{page}/json") fun getArticlesByCid( @Path("page") page: Int, @Query("cid") cid: Int ): Call> // 根据作者昵称搜索文章 @GET("article/list/{page}/json") fun getArticlesByAuthor( @Path("page") page: Int, @Query("author") author: String ): Call> } ``` # 三、核心功能实现与创意亮点 ## 3.1 创意功能:AI 悬浮锐评 这是本项目的最大亮点。为了实现类似“元宝锐评”的丝滑体验,我攻克了以下技术难点: 1. 自定义悬浮球 (FloatingActionButton):实现了随手指拖动的交互逻辑,并添加了屏幕边界吸附算法,防止按钮遮挡内容或移出屏幕。 2. 优雅的弹窗交互:使用 BottomSheetDialogFragment 承载 AI 回复,支持手势下滑关闭,体验符合 Android 现代交互规范。 3. UI 状态管理:手动控制 ProgressBar 的显示与隐藏,精确处理“加载中”、“成功显示”、“错误提示”三种状态。 [图片] ## 3.2 数据持久化与网络 - Cookie 持久化:利用 OkHttp 的拦截器(Interceptor)处理 Cookie,实现用户的自动登录与收藏状态同步。 - 登陆状态持久化:使用SP记录登陆状态,增加启动页,启动页判断登陆状态进而跳转到「登陆页」或者「首页」 - HTTP 明文适配:针对 Android 9.0+ 的网络安全策略,配置了 usesCleartextTraffic 以支持部分 HTTP 图片资源的加载。 # 四、遇到的挑战与解决方案 在开发过程中,我遇到了几个典型的技术难题,并通过询问带教、查阅文档和调试成功解决: ## 4.1 线程调度与 UI 卡顿 - 问题描述:在进行 AI 网络请求并尝试更新 UI 时,应用崩溃,报错 CalledFromWrongThreadException。 - 解决方案:深入理解协程调度器。在网络请求(IO 线程)结束后,使用 withContext(Dispatchers.Main) 显式切换回主线程更新 UI(如隐藏进度条、设置文本),保证了线程安全。 ## 4.2 复杂的图片显示异常 - 问题描述:在文章列表中,部分矩形封面图显示成了圆形,且部分图片加载失败。 - 问题分析:经排查,发现是因为网络图片加载失败(HTTP 限制或 URL 为空)时,Glide 显示了默认的占位图(ic_launcher),而该占位图本身是圆形的,导致视觉误判。 - 解决方案: 1. 在 AndroidManifest.xml 中开启 android:usesCleartextTraffic="true" 解决图片加载问题。 2. 优化 Glide 代码,配置矩形的 placeholder 和 error 占位图,确保视觉一致性。 ## 4.3 WebView 数据提取与清洗 - 问题描述:直接获取网页 HTML 包含大量标签和乱码,AI 无法理解。 - 解决方案:编写正则表达式逻辑,去除 HTML 标签、转义字符(如 \n, \"),仅保留纯文本发送给 AI,大幅提升了回复的准确度。 ## 4.4 首页切换数据丢失 - 问题描述:页面切换之后回到首页,每次都重新请求加载到第一页,没法记录状态 - 解决方案:改变Fragment切换方式,复用Fragment保存状态 ```kotlin private fun switchFragment(targetFragment: Fragment) { if(currentFragment == targetFragment) return val transaction = supportFragmentManager.beginTransaction() currentFragment?.let { transaction.hide(it) } if(!targetFragment.isAdded){ transaction.add(R.id.fragment_container, targetFragment) } else{ transaction.show(targetFragment) } transaction.commit() currentFragment = targetFragment } ``` ## 4.5 多个页面收藏状态不统一 - 问题描述:由于保存了Frgment状态导致页面取消收藏后,状态无法同步 - 解决方案: 1. 首页以及分类页面的详情页有不同的详情页类型,所以在不同的详情页收藏状态改变时需要同步到首页和分类页,因此给首页和分类页均注册BroadcastReceiver,这里由于两个页面的adapter不相同所以实现一个接口类进行封装然后在实例出adapter实例可以避免重复写代码。在详情页发送广播消息到同一个action,通知首页、分类页、收藏列表同步状态,具体流程图如下: 暂时无法在飞书文档外展示此内容 2. 收藏列表点进去文章详情后也需要同步状态给收藏列表,这里既可以跟前面一样注册BroadcastReceiver,也可以使用Activity Result API,我这里使用Activity Result API,主要是为了学习这种方式,调用流程如下: 暂时无法在飞书文档外展示此内容 ## 4.6 分类页面搜索功能输入键盘打开时压缩界面 - 问题描述: [图片] - 解决方案:清单文件中修改 .MainActivity 的属性,添加 android:windowSoftInputMode="adjustPan" # 五、代码质量与规范 - Git 规范:遵循 Conventional Commits 规范,使用 feat, fix, chore, refactor 等关键字管理提交日志,保持版本历史清晰。 - 代码注释:核心逻辑(如 AI 请求流程、WebView 交互)均包含清晰的注释。 - 资源管理:不硬编码字符串,API Key 等敏感信息通过 local.properties 配置,避免泄露。 ```kotlin object AiNetworkManager { private const val BASE_URL = "https://open.bigmodel.cn/" // 从配置文件中读取 private val API_KEY = BuildConfig.ZHIPU_API_KEY private val retrofit by lazy { val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val client = OkHttpClient.Builder() .addInterceptor(logging) .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build() Retrofit.Builder() .baseUrl(BASE_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() } val api: ZhipuApi by lazy { retrofit.create(ZhipuApi::class.java) } fun getApiKey(): String { if (API_KEY == "null" || API_KEY.isEmpty()) { throw IllegalStateException("请在 local.properties 中配置 ZHIPU_API_KEY") } return "Bearer $API_KEY" } } ``` # 六、测试与验证 为了确保应用稳定性,我进行了多维度的测试: - 功能测试:覆盖登录、列表滑动、AI 对话等核心路径。 - Monkey 测试:使用 adb shell monkey 对模拟器进行了 1000 次随机压力测试,确保无 Crash。 测试指令: adb devices adb -s emulator-5554 shell monkey -p com.example.wecommunity --throttle 300 -v 1000 测试结果: [图片] - 单元测试:引入 JUnit 4,对文本清洗工具类进行了单元测试,确保边界情况(如空文本)处理正确。 # 七、总结与展望 本项目在短时间内零基础的情况下,完成了一个具备基本功能的知识社区 App,实现了基础业务,还引入 AI 技术。本项目的实现过程中尽量使用了多种Android开发的基本组件,对于同一种问题的解决尽量采用了多种方式,尽量去了解不同的技术。此项目的实现目前还不是很完善,之后会多多学习技术,争取快速的入门Android开发。未来计划引入 Room 数据库实现离线阅读功能,并优化 AI 的流式输出体验。