2023-11-01 19:30
最近做了一个Android+server的小项目,可以像短视频app一样,再Android端浏览播放server上提供的视频,并且上传视频到server。
本身没什么亮点,功能简陋,界面粗糙,是个学习项目,没什么实际使用价值。
亮点在于,从开始的架构,到最终的代码,绝大部分是由ChatGPT完成的。
做这个的初衷是,作为一名安卓程序员,我想试试ChatGPT能在多大程度上取代我。
分阶段向ChatGPT描述需求,由它提供代码。代码调试也尽量交给它。
这个过程中,逐渐发现了几个技巧:
你是一个熟悉安卓和python的全栈工程师,擅长编写优雅、健壮的代码。你将创建一个可以播放在线视频的Android app和与之配合的后台服务器(使用flask)。你使用Intellij作为IDE。
首先我们需要编写Android app,使用Kotlin。
我将一步步列出需要进行的工作。这条消息只需回复“好的”。在之后的消息中我们将完成具体工作。
好的。
首先我们需要设计所需的api,包括:
1,获取视频列表,每次返回最多20条视频的id以及缩略图。注意app将使用瀑布流UI,当划到列表底部时,我们会再次通过此api获取后面20条视频的信息,因此该api需要使用参数来指定起始的视频编号。
2,播放某一个id对应的视频。
设计上面两个api及响应格式。
另外,作为一个在线播放视频的app,暂不考虑用户注册和登录功能,我们还需要什么api?
首先,我们来设计上述的API。
获取视频列表:
Endpoint: /videos
Method: GET
Parameters:
start: 起始的视频编号,默认为0
limit: 返回的视频数量,默认为20
……
这样做的好处是避免对话过长导致prompt超出字数限制,从而丢失上下文。当一个问题解决后,你可以通过修改问题的功能,在提纲prompt下面另起一个分支:
你是一个熟悉安卓和python的全栈工程师,擅长编写优雅、健壮的代码。你将创建一个可以播放在线视频的Android app和与之配合的后台服务器(使用flask)。你使用Intellij作为IDE。
首先我们需要编写Android app,使用Kotlin。
我将一步步列出需要进行的工作。这条消息只需回复“好的”。在之后的消息中我们将完成具体工作。
好的。
假设服务器已经布置完成,提供以下api:
1,获取视频列表:
……
app需要以下功能:
1,一个向右滑出的设置栏,其包含一个输入框用于手动输入服务器ip地址,并将其储存在本地。
2,在app启动时检查是否存有ip地址,如果找到,自动连接服务器获取视频列表(应包含最少0个、最多20个视频的信息)。以每行2个的格式在主屏幕上显示其thumbnail。
3,主屏幕使用瀑布流UI,当划到列表底部时,再次通过适当的参数获取后面20条视频的信息例如,假设主屏幕已经显示了20个视频,则下一次获取视频列表的url应该是:“/videos? start=20”。
4,当点击某一个视频的thumbnail时,通过api “ /<video_id>/play”播放该视频。
根据以上功能,列出Android app需要包含哪些组件。
根据您描述的功能,以下是Android app所需的组件:
1. Activities & Fragments:
MainActivity: 主屏幕,显示视频列表。
VideoPlayerActivity: 用于播放选中的视频。
SettingsFragment: 向右滑出的设置栏,用于输入服务器IP地址。
2. Adapters:
VideoListAdapter: 用于在MainActivity中显示视频列表的适配器。它将使用瀑布流布局(例如,使用RecyclerView与StaggeredGridLayoutManager)。
3. Models:
Video: 一个简单的数据类,包含视频的ID和缩略图URL。
……
例如,上面的对话中,ChatGPT给Android app规划了基本架构,列出了若干个类,如MainActivity等。于是我修改了我的问题:
假设服务器已经布置完成,提供以下api:
1,获取视频列表:
……
app包含以下组件:
1,Activities & Fragments:
- MainActivity: 主屏幕,显示视频列表。
- VideoPlayerActivity: 用于播放选中的视频。
- SettingsFragment: 向右滑出的设置栏,用于输入服务器IP地址。
2,Adapters:
- VideoListAdapter: 用于在MainActivity中显示视频列表的适配器。它将使用瀑布流布局(例如,使用RecyclerView与StaggeredGridLayoutManager)。
……
下面我们依次编写这些类。此条消息回复“好的”。之后的消息将会指定要编写的类。
好的。
编写model类 Video
data class Video(
val id: String,
val thumbnail: String
)
……
通过不断修改之前的提问,确保只有对当前问题有用的信息包含在prompt中。
但是要注意,随着项目的逐步完成,需要在prompt中告诉ChatGPT当前的进度,以避免它做重复的工作。例如:
app包含以下组件:
……
现在App已能正常工作。无需对功能进行调整。
请问如何将MainActivity设置为只允许竖屏浏览?
现有SettingsFragment类代码:
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
class SettingsFragment : PreferenceFragmentCompat() {
……
而其中用到的R.xml.preferences代码如下:
<?xml version="1.0" encoding="utf-8"?\>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"\>
……
根据上面已存在的代码,编写 AppPreferences 类
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
class AppPreferences(context: Context) {
private val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
companion object {
private const val SERVER_IP_KEY = "server_ip"
private const val DEFAULT_SERVER_IP = "192.168.1.1"
}
……
整个项目花了大约四五天。比我没有使用AI时的工作速度要快很多。
ChatGPT提供了Android端大约90%的代码,和server端99%的代码。对于这部分代码,我做的仅仅是复制粘贴。当然,我仍然需要完全理解这些代码,但是借助询问ChatGPT,做到完全理解不算很难。
比起具有复杂的项目结构、涉及众多Kotlin类和布局文件的Android项目,ChatGPT编写只有一两个python文件的server更加得心应手。短小紧凑的python代码更方便加入prompt,使代码生成过程更为顺滑。
代码调试阶段,ChatGPT可以解决Android端大约70%的bug;剩余30%还是依赖于程序员自身的能力。ChatGPT解决bug的能力,有时不仅依赖于prompt有没有包含足够的信息,而且受到提问侧重点的影响。
本项目中,ChatGPT对一个View加载的bug始终没有找到原因。事后证明此bug涉及ViewThumbnailView和item_video这两个文件。我翻查了与ChatGPT的交流过程,并引导它重新尝试解决bug:
app包含以下组件:
……
其中MainActivity的代码为:
……
VideoThumbnailView的代码为:
……
VideoListAdapter的代码为:
……
当启动app时,尽管已经收到了包含20个Video的视频列表,但屏幕上没有视频被加载。debug发现:VideoListAdapter.onCreateViewHolder()方法被执行了1次,VideoListAdapter.onBindViewHolder()方法没有被执行。
从你提供的代码中,我发现了几个可能的问题。首先,我们来解决VideoListAdapter.onCreateViewHolder()被调用,但VideoListAdapter.onBindViewHolder()没有被调用的问题。
1. 检查 RecyclerView 的高度:
如果RecyclerView的高度为0或者它被其他视图遮挡,那么onBindViewHolder可能不会被调用。确保RecyclerView在布局中有足够的空间,并且没有被其他视图遮挡。
2. 检查 RecyclerView 的 Adapter 数据:
……
3. 检查 LayoutManager:
……
4. 检查 Item 的高度:
……
关于Item的高度:以下是item_view.xml的代码:
……
你的item_view.xml布局文件看起来是正常的。ImageView的高度被明确地设置为200dp,所以它不应该是问题的原因。
但是,我注意到你在VideoListAdapter.onCreateViewHolder()中直接实例化了VideoThumbnailView,而没有使用LayoutInflater来从item_view.xml中加载布局。这可能是问题的原因。
当你直接实例化VideoThumbnailView时,它可能没有正确地加载其内部的布局,导致onBindViewHolder没有被调用。
……
app包含以下组件:
……
其中VideoThumbnailView的代码为:
……
其加载的layout文件:item_video.xml的代码为:
……
VideoListAdapter的代码为:
……
当启动app时,尽管已经收到了包含20个Video的视频列表,但屏幕上没有视频被加载。debug发现:VideoListAdapter.onCreateViewHolder()方法被执行了1次,VideoListAdapter.onBindViewHolder()方法没有被执行。
如果 onCreateViewHolder() 被调用了,但 onBindViewHolder() 没有被调用,这通常意味着 RecyclerView 认为它没有任何数据来绑定到视图上。这可能是由几个原因造成的:
1. 数据集为空:确保传递给 VideoListAdapter 的 videos 列表不是空的。
2. RecyclerView的布局管理器:确保你的 RecyclerView 已经设置了一个布局管理器。如果没有布局管理器,RecyclerView 将不会知道如何排列其子视图。
3. 适配器未正确连接:确保你已经将 VideoListAdapter 实例设置为 RecyclerView 的适配器。
4. 数据集更改通知:如果数据在适配器创建后发生了更改,确保你调用了 notifyDataSetChanged() 或相关的通知方法来通知适配器数据已经更改。
5. 视图尺寸问题:如果 item_video.xml 的布局有问题,例如高度设置为 wrap_content 但子视图没有固定高度或者是不可见的,那么 onBindViewHolder() 可能不会被调用,因为 RecyclerView 可能认为没有足够的空间来渲染任何内容。
检查以下几点:
· 确保 videos 列表在设置适配器之前已经被初始化并且包含数据。
……
· 检查 item_video.xml 的布局,确保它在 RecyclerView 中能够正确显示。
如果以上都确认无误,那么可能需要更详细的调试来查看为什么 onBindViewHolder() 没有被调用。可以考虑打印日志或使用断点来检查 RecyclerView 的状态和 videos 列表的内容。
(其中第5点虽然与正确答案稍有接近,但仍然没有实际指出问题根源。)
app包含以下组件:
……
其中VideoThumbnailView的代码为:
……
其加载的layout文件:item_video.xml的代码为:
……
VideoListAdapter的代码为:
……
检查VideoThumbnailView的实例化和inflate过程是否有问题。
在VideoThumbnailView的Kotlin代码中,你已经正确地通过LayoutInflater加载了布局,并且初始化了thumbnailImageView。这是一个常见的做法来创建自定义视图。
然而,有一点需要注意的是,你在XML布局文件中使用了自定义视图的完整类名作为根元素。这在某些情况下可能会导致问题,因为你已经在自定义视图的构造函数中加载了布局。这意味着布局可能会被加载两次,一次是在XML文件中指定的时候,另一次是在自定义视图的init块中。
为了避免这个问题,你应该在XML文件中使用常规的布局组件(如FrameLayout, LinearLayout, RelativeLayout等)作为根元素,而不是自定义视图的类名。然后,你可以在自定义视图的init块中加载这个布局。
……
对比第二次与第三次提问,ChatGPT进行推断所依据的代码信息是相同的,起作用的是我明确地要求它检查有问题的地方,相当于是把正确答案告诉它了。这在实际debug中是不太可能做到的。从ChatGPT在这个案例的表现来看,它对于涉及代码较广、隐藏较深的bug仍然比较棘手。
除了debug,其他ChatGPT不擅长、需要人为干预的工作集中在几个部分:
app包含以下组件:
……
其中VideoThumbnailView的代码为:
……
请编写VideoListAdapter的onCreateViewHolder()方法。
为了完成onCreateViewHolder()方法,我们需要做以下几步:
1. 使用LayoutInflater从XML布局文件中加载VideoThumbnailView。
2. 使用加载的VideoThumbnailView创建一个新的VideoViewHolder实例。
3. 返回这个新创建的VideoViewHolder实例。
以下是VideoListAdapter中onCreateViewHolder()方法的实现:
……
注意VideoThumbnailView在init时也会inflate自身。这与你在onCreateViewHolder中的代码是否会冲突?
对,你提到了一个很好的点。由于VideoThumbnailView在其构造函数中已经执行了inflate操作,我们不应该再次在onCreateViewHolder中进行inflate。否则,会导致布局层次不正确,因为我们实际上会得到一个嵌套的VideoThumbnailView。
为了解决这个问题,我们应该直接创建VideoThumbnailView的实例,而不是再次从XML布局文件中加载它。
以下是修正后的onCreateViewHolder()方法:
……
从这个意义上说,尽管ChatGPT提供了90%的代码,但并不能认为它完成了90%的工作。程序员作为AI代码的审核者,实际上在提高效率、减少无用功上面发挥了作用。尽管在AI与人类的结对编程中,绝大多数时候是人类在问、AI在答,但是二者的角色定位应该是人类为导师,AI为学徒。AI回答人类的提问体现了它作为工具的一面。它具备比人类渊博的知识,但它不能监管指导人类的编程工作,相反,它的代码需要人类去审核验收。
最后,ChatGPT能够取代我吗?
现在当然还不能。
未来,也许是很近的未来,世界也许不再需要一个Android程序员。
但是,肯定会需要一个指导、审核ChatGPT进行Android编程的人类。