2016年10月:我的Android开发之路正式开启

记录2016年9月入职Android开发岗位的心路历程和学习过程

-- 次阅读

从校园到职场:我的Android开发新手期

入职初体验

2016年9月初,我正式成为了一名Android开发工程师。从校园走向社会,从实验室的小项目到真正的企业级应用,这个转变比我想象的要大得多。

第一天来到公司,HR小姐姐很热情地接待了我,给我办了入职手续。我的工位在一个靠窗的位置,阳光洒在桌面上,可我却感觉坐立不安。看着周围同事们熟练地敲击键盘,讨论着我听不懂的技术术语,心里既兴奋又忐忑。

第一次接触企业级代码

我的mentor是一位看起来很温和的工程师,叫张哥。他给我介绍了团队的情况:

“欢迎加入我们!我们主要做电商App的Android端开发。你现在先熟悉一下项目,有什么问题随时问我。”

然后他就给了我一个Git账号,让我clone项目代码。我当时连Git的基本操作都不太熟练,手忙脚乱地在终端里输入命令:

git clone https://github.com/company/android-app.git

打开项目的一瞬间,我惊呆了!这哪里是我在学校里写的那些小程序能比的:

  • app模块就有20000+行代码
  • 各种activityfragmentutils
  • 还有我从来没听说过的modelpresenterview

张哥看我一脸懵,笑着说:

“别担心,刚开始都这样。我们用的是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的教训

今天是我第一次参加代码审查,紧张得手心直冒汗。

张哥看了我的注册功能代码,指出了很多问题:

  1. 内存泄漏
// 我写的有问题的代码
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        // 这样会导致Activity无法被回收
    }
}), 1000);
  1. 网络请求没在子线程
// 错误:在主线程进行网络请求
Call<RegisterResponse> call = apiService.register(username, password);
Response<RegisterResponse> response = call.execute(); // 阻塞主线程!
  1. 没有处理异常
// 我的代码没有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几乎不会写
  • 单元测试完全没接触过

但是,我有时间,有热情,有优秀的团队指导,这就足够了!

-- 次访问
Powered by Hugo & Stack Theme
使用 Hugo 构建
主题 StackJimmy 设计