V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ny
V2EX  ›  程序员

零基础 10 分钟学会开发商业产品级 Android 客户端(以 V2EX 为例)

  •  
  •   ny · 2018-02-08 17:58:16 +08:00 · 1135 次点击
    这是一个创建于 2261 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写在最前面

    为了防止无意义的争论,请耐心阅读以下部分,大约需要 10 分钟,理念不和请及时关闭页面

    适合的阅读人群

    1. 职业目标是成为 移动端负责人 /技术总监 /CTO 的 Android 和 iOS 程序员
    2. 后端程序员,希望成为全栈工程师,进而成为第一类人
    3. 产品经理,可以高效的部分代替 Skentch/AxureRP 等设计工具,快速搭建产品原型,或了解思想,最终目的是更顺畅的推进项目
    4. 创业者 / 公司高管

    不适合阅读人群

    1. 热衷自己重复造轮子,但又没能力造好,也不接受别人观点的程序员
    2. 现有体系的既得利益者,提高团队 / 社会效率会损害现有利益的(例如没什么能力的中高层管理人员)

    阅读方式

    • 本文希望能深入浅出,兼顾 Android 零基础和资深程序员,所以会写的略为繁琐,相关的知识点会以进阶标注,并附上链接,对于

    为什么是 Android

    其实也有 iOS 版和服务器版,但是有一定工作经验的人应该都有体会,同一个项目组,一年经验的 iOS 程序员往往比三年经验的 Android 做出的产品更稳定更好看,因为要写好 Android 太难了,Android 更需要这样一份指南。 Android 从最初发展到现在,单单官方最佳实践指导,就经历了

    • 从 Activity -> Fragment
    • 从 Handler -> AsyncTask -> Loader -> LiveData
    • 从 MVC -> MVP -> 各 MVP 变种 -> MVVM
    • 从 Java -> Rxjava/Dagger -> Kotlin ->
    • 可能有的人还转到了 React 路线

    这个混乱程度,也就是比前端届强一点吧

    可以看看这个官方项目,现在开发一个 Android, 复杂到什么程度 https://github.com/googlesamples/android-architecture-components (然而最终成品恐怕还是不如 iOS 随便做一下来的好)

    然后你掌握了以上这些所有技术栈,都跟上了最新版

    • 你能轻松工作了么?不能,你还需要花大量时间和产品经理沟通

    • 你的工资涨了么?大多数人也涨不了多少,而且未来还要跟着继续折腾。

    • 我们是目的为了给用户提供更好的产品,不要把有限的生命耗费在无意义的事物上

    • 社会有分工,厨师为了做一盘肉,并不需要自己去养猪,何况大多数厨师也养不好猪

    以下教程基于自研的 Genos 框架 https://github.com/nyssance/genos

    Genos 框架和其他框架有什么不同点

    主要有两点

    1. 大多数框架只关心技术层面,在这个意义上,就算用了 kotlin 省了几行代码,用了 MVVM 方便各层分别测试,提出各种架构,但其实最耗费时间的一块,和产品经理的沟通上,毫无帮助。Genos 希望在业务逻辑层有一定抽象,让产品经理 /客户端 /服务器端在同一个语境下沟通,而不需要转译
    2. Genos 兼顾了易用性和扩展性,对于只想使用的,可以很方便的使用,希望自定义的也可以方便的扩展。关键是没有依赖,你学习的,写的就是 Android,而不是一种中间语言和规则,未来你不使用 Genos,你所学的知识也没有浪费。

    其他

    • 人生苦短,祝大家能有更多时间,如果你觉得省下的时间没用,至少能多帮产品经理改几个颜色改几个像素再改回去,多做几个动画,是不是?
    • 目前采用最新的 ViewModel 加 Repository 架构, 用了 LiveData 但没用 DataSource, 暂时没有用 Room 和 Paging ( Paging 写的很难用)。其实绝大多数人永远无需关注这些底层问题,到底是 MVP 还是 MVVM,用没用 RecyclerView 等等,Genos 会处理好一切,保持最时髦的架构,满足你追新的欲望。除非你是优秀的全职 Android 程序员,你只需要用就行了。

    教程开始

    完整代码见 https://github.com/nyssance/genos-samples 的 V2EXSample

    第零步 (1 分钟到一天到放弃不等): 下载 Java8 和 Android Studio 3.0.1, 学一点 Java

    第一步 (1 分钟,视病情轻重,强迫症患者可能会到 5-30 分钟不等,主要用于纠结 package 名和项目名): 创建一个新项目

    Start a new project. [官方指南][https://developer.android.com/studio/projects/create-project.html]

    Screen | Configure ------ | --------- Target Android Devices | Phone and Tablet : API 17 Add an Activity to Mobile | Empty Activity

    配置_Gradle Scripts: build.gradle (Module: app)_.

    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        // 替换为 Genos
        // implementation 'com.android.support:appcompat-v7:26.1.0'
        // implementation 'com.android.support.constraint:constraint-layout:1.0.2'
        implementation 'com.nyssance.genos:genos:1.0.0-rc2'
        ...
    

    第二步 (2 分钟): 创建模型类

    • 访问 V2EX 最热主题地址 https://www.v2ex.com/api/topics/hot.json
    • 复制取第一条数据,贴到 http://www.jsonschema2pojo.org/, Source type 选 JSON, Annotation style 选 Gson, 选中 Use primitive types, 本教程不要选 Include getters and setters (实际工作中看个人偏好)
    • 生成 Topic, Node, Member 三个模型类,下载放入你的项目源码中(真实工作中,所有模型类应由服务端生成提供给客户端程序员,如果贵司不是这个方式,且认为这是客户端自己的工作,嗯,考虑换家公司吧,工程和管理水平太弱)
    • for iOS: Gson 就是 Android 界的 ObjectMapper

    第三步 (5 分钟): 写必要的类

    以下代码示例均不含 import, 如果你是 Mac 电脑,把光标移动到每个标红的地方,然后Alt + 回车,会自动完成需要的操作

    AppService.java (实际工作中这个类也应该手写,而应该由服务器端根据格式生成,因为 API 都是服务器端提供的)

    public interface APIService {
        @GET("topics/{pk}.json")
        Call<List<Topic>> topicList(@Path("pk") String pk);
    
        @GET("nodes/show.json")
        Call<Node> nodeList(@Query("name") String name);
    
        @GET("members/show.json")
        Call<Member> memberList(@Query("id") int id);
    }
    

    _AppManager.java

    public class AppManager extends BaseAppManager {
        private static final AppManager INSTANCE = new AppManager();
        public static APIService API;
    
        private AppManager() {
            super();
        }
    
        public static AppManager getInstance() {
            return INSTANCE;
        }
    
        @Override
        public void settings() {
            BASE_URL = "https://www.v2ex.com/api/";
            // Create retrofit
            API = onCreateRetrofit().create(APIService.class);
        }
    
        @Override
        public void route(Fragment fragment, String uri) {
    
        }
    }
    

    TopicList.java public class TopicList extends TableList<Topic, SubtitleHolder> {

    @Override
    protected void onPrepare() {
        mCall = API.topicList("hot"); // 最热
        mTileId = R.layout.list_item_subtitle; // 可以定义你自己的 layout, 定义每一行的样式,对应的 Holder 也需要换成你自己的
    }
    
    @Override
    protected void onDisplayItem(Topic item, SubtitleHolder holder, int viewType) {
        holder.title.setText(item.title);
        holder.subtitle.setText("id: " + item.id);
        holder.setImage(holder.icon, "https:" + item.node.avatarNormal);
    }
    
    @Override
    protected void onOpenItem(Topic item) {
        Snackbar.make(mListView, "Replace with your own action", Snackbar.LENGTH_SHORT).show();
        // 在这里进行点击后的 startActivity 等操作
    }
    
    @Override
    protected void loadMore(int size, int position) {
        // 我没发现 V2EX API 怎么翻页,所以暂时覆写该方法,屏蔽了该功能,能?page=1 这种格式的可以自动翻页, 形式如 mCall = API.topicList("hot", mPage)
    }
    

    }

    • TableList 就是一行只有一个元素的 Fragment, CollectionList/GridList 用于一行多个, iOS 看这个名字应该很好理解 扩展阅读: RecyclerView
    • 命名规则 模型名+List/模型名+Detail, 绝大多数情况, 只需要扩展 List/Detail 这两种接口,因为 API 返回的数据 不是 List 就是 Detail (以后会提供标准的 Create 和 Update 页面)。另外根据业务需求,也可能根据 DDD 划分一些自己的页面,这些可能服务器端更容易理解
    • V2EX 返回数据是直接的一个列表,如果你的接口返回是包在 data 中的,可以继承genos.ui.fragment.ListFragment写一个自己的 TableList, 然后写一个自己的 ListModel, 覆写 TransformListFromData 方法即可

    第四步(2 分钟): 修改必要的类

    MainActivity.java

    public class MainActivity extends TabBarActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mFragments.append(R.id.navigation_hot, new TopicList());
            mFragments.append(R.id.navigation_latest, PlaceholderFragment.newInstance(2));
            mFragments.append(R.id.navigation_me, PlaceholderFragment.newInstance(3));
            onNavigationItemSelected(R.id.navigation_hot);
        }
    }
    
    • TabBarActivity 是底部导航,需要侧栏导航,继承genos.ui.activity.DrawerActivity即可
    • TabBarActivity 的默认菜单名是 res/menu 里的 navigation_tab_bar,DrawerActivity 是 navigation_drawer, 创建同名文件,可替换为自己的菜单,你也可以用其他名字,手动在onCreateOptionsMenu中 inflate
    • XXXListActivity 会自动搜寻名为 list_xxx 的菜单和字符串设为菜单和标题,你也可以手动装配
    • Fragment 因为名称会被混淆,所有设置均需要手动

    第五步 (1 分钟) 运行

    你现在是 Android 专家了

    太长了,不想写了

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4405 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 04:05 · PVG 12:05 · LAX 21:05 · JFK 00:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.