jetpack 學(xué)習(xí)筆記(ViewModel+ViewBinding+livedata+retrofit)

用jetpack框架編寫了一個(gè)簡(jiǎn)易的網(wǎng)絡(luò)請(qǐng)求流程,效果如下:


3.jpg

7.jpg

2.jpg
4.jpg

由于接口對(duì)接的是我們開(kāi)發(fā)環(huán)境地址,所以我把地址相關(guān)的屏蔽了,見(jiàn)諒稳其,這個(gè)接口對(duì)接的是整個(gè)app第一個(gè)接口獲取token,有了這個(gè)token我們就可以作為用戶的唯一ID去請(qǐng)求應(yīng)用的其他接口,實(shí)現(xiàn)方式如下:
1炸卑、編寫xml布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    .......
</androidx.constraintlayout.widget.ConstraintLayout>

2既鞠、引入視圖綁定viewbinding(用于替換butterkife),我不擅長(zhǎng)xml里面去寫雙向綁定邏輯,所以這里沒(méi)有用databinding盖文;注意:視圖綁定在 Android Studio 3.6 Canary 11 及更高版本中可用嘱蛋。首先在你需要啟動(dòng)綁定的module中聲明:

android {
        ...
        viewBinding {
            enabled = true
        }
    }

然后在activity中進(jìn)行初始化:

public class MainActivity extends BaseActivity {

    private ActivityMainBinding mainBinding;
    private LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mainBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mainBinding.getRoot());
        mainBinding.tvTitle.setText("jetpack 登錄測(cè)試");

        //初始化viewModel
        loginViewModel = new ViewModelProvider(this,
                ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())).get(LoginViewModel.class);
        btLoginClick();
    }

    public void btLoginClick() {
        mainBinding.btLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              
            }
        });
    }

    @Override
    protected void onDestroy() {
        mainBinding = null;
        super.onDestroy();
    }

上述代碼并沒(méi)有調(diào)用任何layout文件,其實(shí)我們只要聲明了viewbinding,我們的activity_main.xml文件會(huì)自動(dòng)對(duì)應(yīng)上生成類ActivityMainBinding五续;轉(zhuǎn)換名稱:xml名稱的駝峰+Binding洒敏;當(dāng)我們按住ctrl并用鼠標(biāo)點(diǎn)擊ActivityMainBinding時(shí)會(huì)自動(dòng)跳轉(zhuǎn)到activity_main.xml;并且我們?cè)趚ml聲明的控件id(android:id="@+id/tv_title")疙驾,可以通過(guò)mainBinding.tvTitle的形式進(jìn)行調(diào)用凶伙;這樣也避免了控件可能出現(xiàn)未初始化的問(wèn)題。
仔細(xì)觀察我們上面還添加了LoginViewModel 它碎,我們當(dāng)前需要通過(guò)它得到我們需要的數(shù)據(jù)函荣,現(xiàn)在補(bǔ)全點(diǎn)擊事件:

mainBinding.btLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Editable etPhone=mainBinding.etPhone.getText();
                Editable etPsw=mainBinding.etPsw.getText();
                if(etPhone==null||etPsw==null){
                    return;
                }
                String phone = etPhone.toString();
                String psw = etPsw.toString();
                LogUtil.e("yy----phone:"+phone+"\tpsw:"+psw);
                showLoadingDialog("登錄中显押,請(qǐng)稍候");
                loginViewModel.getTokenEntityLiveData(phone, psw).observe(
                        MainActivity.this, new Observer<Resource<TokenEntity>>() {
                            @Override
                            public void onChanged(Resource<TokenEntity> resource) {
                                hideLoadingDialog();
                                resource.handle(new Resource.OnHandleCallback<TokenEntity>() {
                                    @Override
                                    public void onLoading() {
                                        LogUtil.e("yy---onLoading");
                                    }

                                    @Override
                                    public void onSuccess(TokenEntity data) {
                                        LogUtil.e("yy---token:" + data.toString());
                                    }

                                    @Override
                                    public void onFailure(String msg) {
                                        LogUtil.e("yy---onFailure:" + msg);
                                    }

                                    @Override
                                    public void onError(Throwable error) {
                                        LogUtil.e("yy---onError:" + error.getMessage());
                                    }

                                    @Override
                                    public void onCompleted() {
                                        LogUtil.d("yy---onCompleted");
                                    }
                                });
                            }
                        });
            }
        });

上面代碼我們通過(guò).observe(this, 觀察者)綁定了觀察者,在livedata 數(shù)據(jù)改變的時(shí)候會(huì)通知onChanged(){}回調(diào)方法進(jìn)行刷新傻挂,resource.handle這個(gè)東西是自己對(duì)返回包裝過(guò)后的乘碑,這個(gè)建議自己寫,原本返回的東西是一個(gè)json 字符串踊谋,外面也包裝了一個(gè)公共的狀態(tài)殼子蝉仇,如下:

{
    "data": {
        ....
    },
    "code": 0,
    "msg": "成功",
    "validate": ""
}

接著外面看下viewmodel層旋讹,里面持有

public class LoginViewModel extends AndroidViewModel {
    private TokenRepository repository;
    private MutableLiveData<Resource<TokenEntity>> liveData;

    public LoginViewModel(@NonNull Application application) {
        super(application);

        repository = TokenRepository.getInstance();
    }

    public MutableLiveData<Resource<TokenEntity>> getTokenEntityLiveData(String phone, String psw) {
        liveData = repository.getToken(phone, psw);
        return liveData;
    }

}

數(shù)據(jù)相關(guān)的對(duì)象官方建議外面都放在這里殖蚕,比如liveData<pojo A>、liveData<pojo B>沉迹;它是這樣說(shuō)的:
??架構(gòu)組件為界面控制器提供了 ViewModel 輔助程序類睦疫,該類負(fù)責(zé)為界面準(zhǔn)備數(shù)據(jù)。在配置更改期間會(huì)自動(dòng)保留 ViewModel 對(duì)象鞭呕,以便它們存儲(chǔ)的數(shù)據(jù)立即可供下一個(gè) Activity 或 Fragment 實(shí)例使用蛤育。例如,如果您需要在應(yīng)用中顯示用戶列表葫松,請(qǐng)確保將獲取和保留該用戶列表的責(zé)任分配給 ViewModel瓦糕,而不是 Activity 或 Fragment,
從界面控制器邏輯中分離出視圖數(shù)據(jù)所有權(quán)的做法更易行且更高效。
ViewModel 里面外面還持有了一個(gè)數(shù)據(jù)幫助類對(duì)象TokenRepository腋么,這個(gè)對(duì)象主要用于連接viewmodel和網(wǎng)路數(shù)據(jù)或者數(shù)據(jù)庫(kù)數(shù)據(jù)或者其它數(shù)據(jù)來(lái)源咕娄,TokenRepository內(nèi)部處理如下:

/**
     * 獲取token  通過(guò)賬號(hào)和密碼
     *
     * @param username
     * @param password
     * @return
     */
    public MutableLiveData<Resource<TokenEntity>> getToken(String username, String password) {
        final MutableLiveData<Resource<TokenEntity>> liveData = new MutableLiveData<>();
        try {
            RequetTokenBody tokenBody = new RequetTokenBody();
            tokenBody.username = username;
            tokenBody.password = MD5Tool.md5Digest(password).toUpperCase();
            tokenBody.grant_type = "password";
            tokenBody.client_id = ConfigKey.CLIENT_ID;
            tokenBody.client_secret = ConfigKey.CLIENT_KEY;
            tokenBody.scope = "com.xx.xxxx.tv";
            BaseReqApi.getInstance().getService()
                    .getToken(tokenBody)
                    .enqueue(new Callback<BaseResult<TokenEntity>>() {
                        @Override
                        public void onResponse(Call<BaseResult<TokenEntity>> call, Response<BaseResult<TokenEntity>> response) {
                            liveData.setValue(Resource.response(response.body()));
                        }

                        @Override
                        public void onFailure(Call<BaseResult<TokenEntity>> call, Throwable t) {
                            liveData.setValue(Resource.error(t));
                        }
                    });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return liveData;
    }

可以看到,我們網(wǎng)絡(luò)真正的請(qǐng)求和返回是在這里處理的,
發(fā)送請(qǐng)求:是以json的格式進(jìn)行請(qǐng)求的珊擂,我們配置了一個(gè)請(qǐng)求參數(shù)對(duì)象圣勒,然后調(diào)用retrofit請(qǐng)求參數(shù)配置注解BaseService:

public interface BaseService {
    /**
     * 登錄
     *
     * @param
     * @return
     */
    @POST("oauth/token")
    Call<BaseResult<TokenEntity>> getToken(@Body RequetTokenBody body);

}

請(qǐng)求格式相關(guān)配置是在攔截器進(jìn)行處理的,在發(fā)送請(qǐng)求前進(jìn)行攔截:

/**
     * 獲取接口實(shí)例
     */
    public BaseService getService() {
        OkHttpClient httpClient=new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS)
                .addInterceptor(new HeaderInterceptor())
//                .cookieJar(cookieJar)
                .build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://xxx/zhloauth2-api-project/")
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        return retrofit.create(BaseService.class);
    }
public class HeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        String agent = UserAgentUtils.get(App.getContext());
        Request request = chain.request()
                .newBuilder()
                .removeHeader("User-Agent")
                .addHeader("Accept", "application/json")
                .addHeader("Connection", "Keep-Alive")
                .addHeader("Charset", "UTF-8")
                .addHeader("User-Agent", agent)
                .build();
        return chain.proceed(request);
    }
}

到這里摧扇,整個(gè)請(qǐng)求流程就基本結(jié)束了圣贸。demo還是比較簡(jiǎn)單,實(shí)際項(xiàng)目中肯定還需要對(duì)代碼進(jìn)行封裝扛稽,對(duì)多種場(chǎng)景也需要分別處理吁峻。繼續(xù)學(xué)習(xí)ing.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市在张,隨后出現(xiàn)的幾起案子锡搜,更是在濱河造成了極大的恐慌,老刑警劉巖瞧掺,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耕餐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡辟狈,警方通過(guò)查閱死者的電腦和手機(jī)肠缔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門夏跷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人明未,你說(shuō)我怎么就攤上這事槽华。” “怎么了趟妥?”我有些...
    開(kāi)封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵猫态,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我披摄,道長(zhǎng)亲雪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任疚膊,我火速辦了婚禮义辕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寓盗。我一直安慰自己灌砖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布傀蚌。 她就那樣靜靜地躺著基显,像睡著了一般。 火紅的嫁衣襯著肌膚如雪善炫。 梳的紋絲不亂的頭發(fā)上撩幽,一...
    開(kāi)封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音销部,去河邊找鬼摸航。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舅桩,可吹牛的內(nèi)容都是我干的酱虎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼擂涛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼读串!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起撒妈,我...
    開(kāi)封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤恢暖,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后狰右,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體杰捂,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年棋蚌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嫁佳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挨队。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蒿往,靈堂內(nèi)的尸體忽然破棺而出盛垦,到底是詐尸還是另有隱情,我是刑警寧澤瓤漏,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布腾夯,位于F島的核電站,受9級(jí)特大地震影響蔬充,放射性物質(zhì)發(fā)生泄漏蝶俱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一娃惯、第九天 我趴在偏房一處隱蔽的房頂上張望跷乐。 院中可真熱鬧肥败,春花似錦趾浅、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至纽谒,卻和暖如春证膨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鼓黔。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工央勒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人澳化。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓崔步,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親缎谷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子井濒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容