从校园到职场:我的Android开发新手期
入职初体验
2016年9月初,我正式成为了一名Android开发工程师。从校园走向社会,从实验室的小项目到真正的企业级应用,这个转变比我想象的要大得多。
第一天来到公司,HR小姐姐很热情地接待了我,给我办了入职手续。我的工位在一个靠窗的位置,阳光洒在桌面上,可我却感觉坐立不安。看着周围同事们熟练地敲击键盘,讨论着我听不懂的技术术语,心里既兴奋又忐忑。
第一次接触企业级代码
我的mentor是一位看起来很温和的工程师,叫张哥。他给我介绍了团队的情况:
“欢迎加入我们!我们主要做电商App的Android端开发。你现在先熟悉一下项目,有什么问题随时问我。”
然后他就给了我一个Git账号,让我clone项目代码。我当时连Git的基本操作都不太熟练,手忙脚乱地在终端里输入命令:
git clone https://github.com/company/android-app.git
打开项目的一瞬间,我惊呆了!这哪里是我在学校里写的那些小程序能比的:
app模块就有20000+行代码- 各种
activity、fragment、utils包 - 还有我从来没听说过的
model、presenter、view包
张哥看我一脸懵,笑着说:
“别担心,刚开始都这样。我们用的是MVP架构,你先看看代码结构,有什么不懂的就问。”
MVP架构的困惑
说实话,第一次听到MVP(Model-View-Presenter)这个词时,我完全不明白它和我之前学的MVC有什么区别。在学校里,我们都是直接在Activity里写逻辑,现在突然要分成三个部分,感觉好复杂。
张哥给我看了一个简单的例子:
// Contract接口
public interface LoginContract {
interface View {
void showLoading();
void hideLoading();
void navigateToHome();
void showError(String message);
}
interface Presenter {
void login(String username, String password);
}
}
// Presenter实现
public class LoginPresenter implements LoginContract.Presenter {
private LoginContract.View view;
private LoginModel model;
public LoginPresenter(LoginContract.View view) {
this.view = view;
this.model = new LoginModel();
}
@Override
public void login(String username, String password) {
view.showLoading();
model.login(username, password, new LoginCallback() {
@Override
public void onSuccess() {
view.hideLoading();
view.navigateToHome();
}
@Override
public void onFailure(String error) {
view.hideLoading();
view.showError(error);
}
});
}
}
虽然看懂了,但总觉得这样写代码好复杂啊!为什么不能直接在Activity里写逻辑呢?
技术栈的冲击
这一个多月中,我接触到了太多以前没用过的技术:
Retrofit网络请求
之前在学校都是用HttpURLConnection或者HttpClient,看到Retrofit的注解方式,我觉得太神奇了:
public interface ApiService {
@FormUrlEncoded
@POST("user/login")
Call<LoginResponse> login(@Field("username") String username,
@Field("password") String password);
@GET("user/info")
Call<UserInfo> getUserInfo(@Query("userId") String userId);
}
Picasso图片加载
我一开始用的是最原始的方法:
// 错误的写法
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(url);
runOnUiThread(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
});
}
}).start();
结果不仅代码丑陋,还会导致内存泄漏!张哥推荐我使用Picasso:
Picasso.with(context)
.load(userAvatarUrl)
.placeholder(R.drawable.default_avatar)
.error(R.drawable.error_avatar)
.into(imageView);
哇,这也太简单了吧!一行代码就搞定了,而且还处理了占位图和错误图。
RxJava的初体验
还有RxJava,看起来很酷,但是概念好多,我还需要时间理解:
apiService.register(username, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<RegisterResponse>() {
@Override
public void onSubscribe(Disposable d) {
// 订阅
}
@Override
public void onNext(RegisterResponse response) {
// 成功回调
}
@Override
public void onError(Throwable e) {
// 错误处理
}
@Override
public void onComplete() {
// 完成
}
});
第一个功能开发
今天张哥让我开发一个简单的功能:用户注册页面。我兴奋极了,终于可以自己写代码了!
我按照MVP的模式,新建了三个文件:
// RegisterActivity.java
public class RegisterActivity extends AppCompatActivity implements RegisterContract.View {
// ...
}
// RegisterPresenter.java
public class RegisterPresenter implements RegisterContract.Presenter {
// ...
}
// RegisterModel.java
public class RegisterModel {
// ...
}
可是写到一半,我遇到了问题:如何在注册成功后跳转到登录页面?我在presenter里写了:
@Override
public void register(String username, String password) {
model.register(username, password, new RegisterCallback() {
@Override
public void onSuccess() {
// 这里怎么跳转?
// 我想用startActivity,但是presenter里没有Context
}
});
}
我只好去问张哥,他告诉我:
“在view接口里定义一个跳转方法,然后在Activity里实现它。”
// 在Contract里加方法
interface View {
void navigateToLogin();
}
// 在presenter里调用
view.navigateToLogin();
// 在Activity里实现
@Override
public void navigateToLogin() {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
}
原来如此!这样的设计确实让代码更清晰了。
Code Review的教训
今天是我第一次参加代码审查,紧张得手心直冒汗。
张哥看了我的注册功能代码,指出了很多问题:
- 内存泄漏:
// 我写的有问题的代码
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
// 这样会导致Activity无法被回收
}
}), 1000);
- 网络请求没在子线程:
// 错误:在主线程进行网络请求
Call<RegisterResponse> call = apiService.register(username, password);
Response<RegisterResponse> response = call.execute(); // 阻塞主线程!
- 没有处理异常:
// 我的代码没有try-catch,网络异常会导致崩溃
张哥耐心地教我:
“Android开发要特别注意内存管理、线程安全和异常处理。这些都是坑,踩过几次就记住了。”
从惶恐到适应
转眼间已经工作一个多月了,回顾这段时间的点点滴滴:
技术收获:
- 学会了MVP架构的基本使用
- 掌握了Retrofit + OkHttp的网络请求
- 学会了Picasso图片加载
- 初步了解了RxJava
- 学会了使用GreenDAO进行数据库操作
心态变化:
- 从最初的惶恐不安,到现在逐渐适应
- 从"什么都不会"的焦虑,到"每天都在进步"的踏实
- 学会了主动提问,不再害怕暴露自己的不足
技术栈清单(2016年11月版):
Java 7/8
Android SDK 23 (6.0)
MVP Architecture
Retrofit 2.0
OkHttp 3.0
Gson 2.0
Picasso 2.5
GreenDAO 3.0
RxJava 1.0
Butter Knife 8.0
虽然现在我还是个菜鸟,但我相信只要保持学习的热情,不断积累经验,总有一天我也能成为像张哥那样优秀的工程师。
现在的我还有很多不足:
- 对Android底层原理了解不够
- 性能优化经验为零
- 自定义View几乎不会写
- 单元测试完全没接触过
但是,我有时间,有热情,有优秀的团队指导,这就足够了!