简单对比ChatGPT,Cursor和Copilot

本文将从代码分析这一角度,来对比下ChatGPT,Cursor和Copilot 的使用体验.
分析代码,然后看看这些工具能不能帮助改善代码质量.

代码文件如下(随便找一个文件):

private const val TAG = "HomePageFragment"
@AndroidEntryPoint
class HomePageFragment: Fragment() {
    private var _binding: FragmentHomePageBinding? = null
    private val binding get() = _binding!!
    private val homePageViewModel: HomePageViewModel by activityViewModels()
    private val collectViewModel: CollectViewModel by viewModels()
    private val userViewModel: UserViewModel by activityViewModels()
    private val autoScrollViewModel: AutoScrollViewModel by activityViewModels()
    private val headerItemAdapter by lazy {
        HomePageHeaderItemAdapter(listOf()){link, title ->
            Log.d(TAG,"link = $link title = $title")
            val navController = findNavController()
            // 跳转到 WebFragment
            val bundle = bundleOf("link" to link, "title" to title)
            //跳转到带参数的 fragment
            navController.navigate(R.id.webFragment,bundle)
    private val headerAdapter by lazy {
        HomePageHeaderAdapter(headerItemAdapter)
    private val articleAdapter by lazy {
        HomePageBodyAdapter()
    private lateinit var concatAdapter: ConcatAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG,"inner onCreate")
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.d(TAG,"inner onCreateView")
        // Inflate the layout for this fragment
        _binding = FragmentHomePageBinding.inflate(inflater, container, false)
        headerAdapter.setOnViewAttachedListener {
            Log.d(TAG, "viewModel.autoScroll()")
            // 延时3s后 开启自动滑动
            viewLifecycleOwner.lifecycleScope.launch(exceptionHandler) {
                viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                    delay(3000L)
                    autoScrollViewModel.autoScroll()
        headerAdapter.setOnViewDetachedListener {
            Log.d(TAG, "viewModel.cancelAutoScroll()")
            autoScrollViewModel.cancelAutoScroll()
        headerAdapter.setTouchEventListener { motionEvent ->
            Log.d(TAG, "inner touch motionEvent = $motionEvent")
            autoScrollViewModel.touchViewPager2(motionEvent)
        articleAdapter.setOnItemClickListener { link, title ->
            Log.d(TAG,"link = $link title = $title")
            val navController = findNavController()
            // 跳转到 WebFragment
            val bundle = bundleOf("link" to link, "title" to title)
            //跳转到带参数的 fragment
            navController.navigate(R.id.webFragment,bundle)
        articleAdapter.setImageViewClickListener { id,collect->
            // 如果是已收藏状态 就取消收藏 如果是未收藏状态则 收藏
            Log.d(TAG,"inner setImageViewClickListener collect = $collect")
            if (collect){
                collectViewModel.unCollect(id)
            }else {
                collectViewModel.collect(id)
        concatAdapter = articleAdapter.withLoadStateFooter(FooterAdapter(articleAdapter::retry))
        concatAdapter.addAdapter(0,headerAdapter)
        // 为RecyclerView配置adapter
        binding.recyclerview.adapter = concatAdapter
        // 设置需要缓存的 ViewHolder数量 防止离屏后再显示时 频繁执行 onBindViewHolder
        binding.recyclerview.setItemViewCacheSize(10)
        binding.swipeLayout.setOnRefreshListener {
            Log.d(TAG,"inner setOnRefreshListener")
            // 刷新时 重新加载数据
            homePageViewModel.loadBanner()
            // 更新 PagingDataAdapter
            articleAdapter.refresh()
        binding.recyclerview.setTouchEventListener{
            Log.d(TAG,"inner setTouchEventListener")
            autoScrollViewModel.touchRecyclerview(it)
        return binding.root
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        subscribeUI()
    override fun onDestroyView() {
        Log.d(TAG,"inner onDestroyView")
        super.onDestroyView()
        binding.recyclerview.adapter = null
        _binding = null
    private fun subscribeUI() {
        // banner
        viewLifecycleOwner.lifecycleScope.launch(exceptionHandler) {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                homePageViewModel.banner.collect{ value ->
                    Log.d(TAG," value size = ${value.size}")
                    // 更新 list
                    headerItemAdapter.bannerList = value
                    Log.d(TAG," bannerList size = ${headerItemAdapter.bannerList.size}")
                    //数据改变刷新视图
                    headerAdapter.notifyItemChanged(0)
        viewLifecycleOwner.lifecycleScope.launch(exceptionHandler) {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
                autoScrollViewModel.autoScrollAction.collect {
                    when (it) {
                        is AutoScrollAction.AutoScroll -> {
                            Log.d(TAG, "inner AutoScroll")
                            // 通过设置动画,实现 自动滑动时 平滑滚动
                            headerAdapter.setCurrentItem()
                            Log.d(TAG, "after setCurrentItem position duration 300")
        viewLifecycleOwner.lifecycleScope.launch(exceptionHandler){
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                //监听 PagingData
                homePageViewModel.homePageArticleFlow
                    .catch {
                        Log.d(TAG,"Exception : ${it.message}")
                    .collectLatest {
                        Log.d(TAG,"inner collectLatest")
                        // paging 使用 submitData 填充 adapter
                        articleAdapter.submitData(it)
        //监听paging数据刷新状态
        viewLifecycleOwner.lifecycleScope.launch(exceptionHandler) {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                articleAdapter.loadStateFlow.collectLatest {
                    Log.d(TAG, "inner collectLatest loadState = $it")
                    binding.swipeLayout.isRefreshing = it.refresh is LoadState.Loading
        // 收藏
        viewLifecycleOwner.lifecycleScope.launch(exceptionHandler) {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                collectViewModel.collectAction.collect {
                    when (it) {
                        is CollectAction.Success -> {
                            Log.d(TAG,"CollectAction.Success")
                            articleAdapter.refresh()
                        is CollectAction.Error -> {
                            articleAdapter.refresh()
                        else -> {}
        // 取消收藏
        viewLifecycleOwner.lifecycleScope.launch(exceptionHandler) {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                collectViewModel.unCollectAction.collect {
                    when (it) {
                        is UnCollectAction.Success -> {
                            Log.d(TAG,"UnCollectAction.Success")
                            articleAdapter.refresh()
                        is UnCollectAction.Error -> {
                            articleAdapter.refresh()
        viewLifecycleOwner.lifecycleScope.launch(exceptionHandler) {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                userViewModel.homePageState.collect {
                    Log.d(TAG, "inner homePageState collect it = $it")
                    when(it){
                        is StateUiAction.StateChanged -> {
                            Log.d(TAG,"inner StateChanged")
                            //自动刷新
                            articleAdapter.refresh()
                            Log.d(TAG, "after articleAdapter.refresh")
                            userViewModel.resetHomePageState()
                        else -> {}
    companion object {
        @JvmStatic
        fun newInstance() = HomePageFragment()

1.ChatGPT

使用ChatGPT,对话如下:

#### You:
下面我会给你发送一段代码,你帮我看看有哪里需要改进的,如果你明白了,就回复明白.
#### ChatGPT:
好的,请发送您的代码。我会尽力帮助您分析和提出改进意见。
#### ChatGPT:
这段代码是一个Kotlin文件,包含一个HomePageFragment类的实现。这个类是一个Fragment,用于显示从API获取的文章,这些文章以RecyclerView的形式展示。HomePageFragment有三个ViewModels,使用Hilt进行注入: HomePageViewModel、CollectViewModel和AutoScrollViewModel。
HomePageFragment有三个适配器:
HomePageHeaderAdapter:RecyclerView的头部适配器,它显示一个可以自动滚动的横幅。
HomePageHeaderItemAdapter:HomePageHeaderAdapter中项目的适配器,它在标题中显示一个单独的项目,并处理单击事件。
HomePageBodyAdapter:RecyclerView中文章的适配器。
HomePageFragment还有一个ConcatAdapter,将HomePageHeaderAdapter和HomePageBodyAdapter组合起来。ConcatAdapter还包括一个FooterAdapter,当用户滚动到列表底部时,它会显示一个加载动画。
HomePageFragment有几个处理事件的函数:
setOnRefreshListener:处理当用户下拉SwipeRefreshLayout时的事件。
setTouchEventListener:处理RecyclerView上的触摸事件。该函数用于处理用户触摸RecyclerView时的事件。
setOnItemClickListener:处理RecyclerView中项目的单击事件。该函数用于处理用户在列表中单击文章时的事件。
setImageViewClickListener:处理RecyclerView中项目的图片单击事件。该函数用于处理用户单击文章的收藏按钮时的事件。
HomePageFragment还有一个subscribeUI函数,观察ViewModels的变化,并相应地更新UI。
#### You:
有哪里需要改进的吗
#### ChatGPT:
我无法评估代码的运行情况和性能。但是,如果您想进一步改进代码,您可以尝试以下几点:
1. 优化 RecyclerView 的性能:当列表中的项很多时,RecyclerView 的性能可能会受到影响。您可以尝试使用 DiffUtil 或 PagedList 来减少 RecyclerView 更新的开销。
2. 管理 ViewModel 的生命周期:如果 ViewModel 的生命周期不正确地管理,可能会导致内存泄漏或崩溃。您可以尝试使用 ViewModelScope 或手动管理 ViewModel 的生命周期来确保 ViewModel 能够正确地释放资源。
3. 减少布局嵌套:如果布局嵌套太深,可能会影响应用程序的性能。您可以尝试简化布局,使用 ConstraintLayout 或使用视图绑定来减少布局嵌套。
4. 使用 Retrofit 的缓存功能:如果您正在使用 Retrofit 进行网络请求,可以尝试使用其缓存功能来减少网络请求的次数。
5. 代码重构:如果您的代码需要重构,可以使用 Android Studio 中的重构工具来使代码更加模块化和易于维护。

2.Cursor

进入Cursor,将文件打开,将代码全选后,使用Chat功能,对话如下:

帮我分析下这个文件的代码 这个文件定义了一个 HomePageFragment 类,继承自 Fragment。它还使用了几个 view model,包括 HomePageViewModel、CollectViewModel、UserViewModel 和 AutoScrollViewModel。 HomePageFragment 类为 RecyclerView 设置了几个适配器,包括 HomePageHeaderItemAdapter、HomePageHeaderAdapter 和 HomePageBodyAdapter。这些适配器用于在屏幕顶部显示文章列表和横幅。 HomePageFragment 类还设置了一个 SwipeRefreshLayout,允许用户刷新文章列表。当用户刷新列表时,HomePageViewModel 用于加载新的文章集并更新 PagingDataAdapter。 总的来说,这个文件为显示文章列表和横幅的主页设置了 UI。它使用了几个 view model 来管理数据,并提供了一种让用户刷新文章列表的方式。 有哪里需要改进的吗 这个文件看起来已经很不错了,但是如果你想改进它,可以考虑以下几点: 为了提高代码的可读性,你可以将一些逻辑提取到单独的函数中。例如,HomePageFragment 中的 onCreateView 函数可以将一些设置逻辑提取到单独的函数中。 你可以考虑使用 ViewModel 的 SavedStateHandle 来保存一些状态,例如用户是否已经收藏了某篇文章。这样,当用户旋转设备或者重新打开应用时,这些状态可以被恢复。 如果你的应用需要支持多语言,你可以将一些字符串提取到资源文件中,这样可以方便地进行翻译和本地化。 最后,你可以考虑添加一些动画效果来提高用户体验。例如,当用户下拉刷新时,可以添加一个下拉刷新的动画,当用户点击文章时,可以添加一个过渡动画来平滑地切换到下一个界面。 希望这些建议能够对你有所帮助!

3.Copilot

Copilot 似乎无法直接分析整个文件?也可能是我没找到方法.
但是它根据代码的某一行,或一段函数去生成注释倒是很方便,只要给个注释//,然后再使用Tab补全就可以.

Cursor和Copilot 都是基于GPT的微调模型,code方面专业性更强.ChatGPT给出的结果可能更泛化,可能更适合打开思路.各有优缺点.
New bing可能现在被削得太厉害了,所以code review效果不太好.

但是new bing,用来写文,现在真是一把好手.联网的优势无法忽视.
给它一个主题,例如:Copilot,Cursor,ChatGPT使用和对比分析.
它能生成段落,博客,电子邮件,创意等数据.这个跟Notion AI的功能挺像的.
下面是生成的段落.

Copilot、Cursor和ChatGPT都是基于人工智能的编码辅助工具,它们可以根据你的输入或注释生成代码片段。但是,它们之间也有一些区别和优缺点。下面是一个简单的对比分析:
- Copilot是Github和OpenAI合作开发的一款VS Code插件,它使用了大量的开源代码库作为训练数据,可以生成多种编程语言的代码,而且能够根据代码上下文提供更合适的建议。但是,Copilot目前还处于技术预览阶段,需要申请才能使用,而且有时候会生成重复或错误的代码。
- Cursor是OpenAI最近推出的一款在线IDE,它也可以生成多种编程语言的代码,而且可以帮助你提供想法并编写代码。但是,Cursor目前还不支持中文输入,而且需要联网才能使用,而且有时候会生成不符合规范或逻辑的代码。
- ChatGPT是一个基于GPT-3模型的在线聊天机器人,它可以根据你的对话生成代码片段,而且可以用自然语言来交流。但是,ChatGPT目前只支持Python语言,而且需要输入完整的注释才能生成代码,而且有时候会生成无关或无效的代码。