写在最前面
为了防止无意义的争论,请耐心阅读以下部分,大约需要 10 分钟,理念不和请及时关闭页面
适合的阅读人群
- 职业目标是成为 移动端负责人 /技术总监 /CTO 的 Android 和 iOS 程序员
- 后端程序员,希望成为全栈工程师,进而成为第一类人
- 产品经理,可以高效的部分代替 Skentch/AxureRP 等设计工具,快速搭建产品原型,或了解思想,最终目的是更顺畅的推进项目
- 创业者 / 公司高管
不适合阅读人群
- 热衷自己重复造轮子,但又没能力造好,也不接受别人观点的程序员
- 现有体系的既得利益者,提高团队 / 社会效率会损害现有利益的(例如没什么能力的中高层管理人员)
阅读方式
- 本文希望能深入浅出,兼顾 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 框架和其他框架有什么不同点
主要有两点
- 大多数框架只关心技术层面,在这个意义上,就算用了 kotlin 省了几行代码,用了 MVVM 方便各层分别测试,提出各种架构,但其实最耗费时间的一块,和产品经理的沟通上,毫无帮助。Genos 希望在业务逻辑层有一定抽象,让产品经理 /客户端 /服务器端在同一个语境下沟通,而不需要转译
- 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://v2ex.ih06.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);
}
- 扩展阅读: Retrofit
- for iOS: Retrofit 就是 Android 界的 Alamofire
_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://v2ex.ih06.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 专家了