1. 程式人生 > >Android JetPack架構篇,一個實戰專案帶你學懂JetPack

Android JetPack架構篇,一個實戰專案帶你學懂JetPack

640?wx_fmt=png


今日科技快訊


第五屆世界網際網路大會昨日開幕,來自76個國家的1500餘位嘉賓出席大會。騰訊公司董事會主席兼執行長馬化騰在大會開幕式演講中表示,全球產業都在進行數字化,在此期間機遇挑戰並存,產業網際網路機會巨大。


作者簡介


本篇來自 walker不抽菸 的投稿,給大家介紹一下 Android JetPack架構相關知識。一起來看看!希望大家喜歡。

walker不抽菸 的部落格地址:

https://blog.csdn.net/itismelzp


前言


本文翻譯自:Android Room with a View - Java,地址如下:


https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0

架構元件的目的是提供對應用程式體系結構的指導,併為諸如生命週期管理和資料持久化等常見任務提供開發庫。

架構元件幫你構造一個魯棒、易測試、可維護和少模板程式碼的應用。

架構元件是什麼?

為了介紹相關術語,這裡有簡短的介紹一下各架構元件以及它們之前如何協作。注意這個程式碼庫包含一部分架構元件,它們是:LiveData、ViewModel和Room。每個元件會在使用的時候做解釋。下圖是基本的架構形式。

640?wx_fmt=png

  • Entity: 當註解的類,用於描述資料庫表。

  • SQLite database: SQLite資料庫。

  • DAO: 資料訪問物件。SQL查詢到方法的對映。

  • Room database: 在SQLite資料庫之上的資料庫層。

  • Repository: 資料倉庫,用於管理資料來源。

  • ViewModel: 提供資料給UI。是Repository與UI的連線中心。

你要構建什麼

這個Demo用於在Room中儲存words(單詞)列表,並顯示在RecyclerView中。這是個簡單的示例,但也足以為它來作為開發應用的模板。

  • Demo的功能:

  • 獲取與儲存"單詞";

  • 單詞顯示在MainActivity的RecyclerView中;

  • 通過懸浮按鈕調起另一個activity,用於輸入單詞。

640?wx_fmt=jpeg

RoomWordSample架構預覽

下圖展示了應用的各個部分。除了SQLite database,其他部分都用在自己建立的類中封裝。

640?wx_fmt=png

你會學到什麼

學會如何使用架構元件庫和生命週期庫設計和構建應用程式。

這裡有許多步驟去使用架構元件和推薦的框架。最重要的是學會模型建立的作用、理解各部分組合與資料流向。通過這個Demo,你不單只是簡單的複製和貼上本文的程式碼,還要理解其內部原理。

你需要掌握什麼

  • Android Studio 3.0或更高版本的使用。

  • 一臺Android裝置或模擬器。

你必須熟悉Java程式設計,面向物件設計,Android開發基礎。尤其:

  • RecyclerView 及其介面卡adapters

  • SQLite資料庫及SQLite查詢語言

  • 執行緒與AsyncTask

  • 瞭解一些資料與UI分離的構架概念,如MVP、MVC

本Demo著重於Android架構元件,非主要程式碼可行自行復制與貼上。


Android Studio的優勢


建立應用

開啟Android Studio建立應用:

  1. 新建應用RoomWordSample,目標sdk為26+

  2. 不選include Kotlin support和include C++ support

  3. 下一步,選Phone & Tablet,minimum SDK選API 26

  4. 下一步,選擇Basic Activity

  5. 下一步,完成。

640?wx_fmt=png

更新gradle檔案

新增元件庫到gradle。在Module:app的build.gradle中dependencies末尾加入:

// Room components
implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion"
annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion"
androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion"

// Lifecycle components
implementation "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion"
annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion"

中Project:RoomWordSample的build.gradle中加入版本資訊:

ext {
   roomVersion = '1.1.1'
   archLifecycleVersion = '1.1.1'
}

建立實體

本demo的資料是“單詞”,因此首先建立一個Word類,併為其建立建構函式與必要的get方法。這樣Room才可以例項化物件。

640?wx_fmt=png

下面是Word類:

public class Word {

   private String mWord;

   public Word(@NonNull String word) {this.mWord = word;}

   public String getWord(){return this.mWord;}
}

為了使Word類對Room庫有意義,我們需要為它加註解。註解用了將實體與資料庫相關聯,Room根據相應的註解資訊去生成對應的程式碼。

  • @Entity(tableName = "word_table") 每一個@Entity類代表資料庫中的一張表。tableName為生成表的表名。

  • @PrimaryKey 每個實體需要一個主鍵。

  • @NonNull 表示引數、欄位或返回值不能為null。

  • @ColumnInfo(name = "word") 指定與成員變數對應的列名。

  • 為一個欄位需要是public的或提供get方法。

添加註解的Word類:

@Entity(tableName = "word_table")
public class Word {

   @PrimaryKey
   @NonNull
   @ColumnInfo(name = "word")
   private String mWord;

   public Word(String word) {this.mWord = word;}

   public String getWord(){return this.mWord;}
}

建立DAO(資料訪問物件)

  • 什麼是DAO

DAO即資料訪問物件,你可以指定SQL查詢語句,並將它與方法關聯起來。編譯器會對常規SQL註解進行編譯檢查,如@Insert。

DAO物件必須是個介面或抽象類。

預設情況下,所有的查詢必須在單獨執行緒中執行。

Room將通過DAO物件去建立相應的介面。

  • DAO的寫法

DAO是程式碼的基礎,它用於提供word的增、刪、改、查。

  • 建立一個名為WordDao的介面。

  • 為WordDao新增@Dao註解

  • 宣告一個插入方法void insert(Word word);

  • 為上述方法新增@Insert註解,並且不需要為其提供SQL語句!(同樣的用法還有@Delete and @Update)

  • 宣告方法void deleteAll();

  • 這裡沒有方便的註解可以用於刪除多個實體,因此需要用@Query註解

  • 還需要為@Query註解提供SQL語句@Query("DELETE FROM word_table")

  • 建立方法List<Word> getAllWords();

  • 為其添加註解與[email protected]("SELECT * from word_table ORDER BY word ASC")

下面是其完整的程式碼:

@Dao
public interface WordDao {

   @Insert
   void insert(Word word);

   @Query("DELETE FROM word_table")
   void deleteAll();

   @Query("SELECT * from word_table ORDER BY word ASC")
   List<Word> getAllWords();
}

LiveData類

當資料被改變後,通常你需要作一些操作,例如將更新的資料展示在UI上。這就意味著你必須去觀察這些資料,以便於當資料改變時你能做出反應。根據資料不同的儲存方式,這可能會很棘手。觀察貫串多個元件中資料的變化,你必須要編寫一個顯式、嚴格依賴的呼叫鏈。這使得測試和除錯變得非常困難。

LiveData是 lifecycle library 中,用於資料觀察的類,可用於解決上述難題。在你的方法中使用LiveData為返回值。這樣Room將會為你生成所有必須的程式碼,當資料庫更新時,自動去更新LiveData。

使用LiveData的目的是為了管理資料的更新。但是LiveData類並沒有提供公有的更新資料的方法。我們應當使用MutableLiveData,它有兩個公有方法(setValue(T)、postValue(T))用於儲存資料。通常,MutableLiveData是在ViewModel中使用,然後ViewModel只向觀察者暴露不可變的LiveData物件。

在WordDao中,改變getAllWords()方法的返回值:

@Query("SELECT * from word_table ORDER BY word ASC")
LiveData<List<Word>> getAllWords();

後面我們會在MainActivity的onCreate()方法中建立一個Observer物件,並覆蓋其onChanged()方法。當LiveData改變時,觀察者會被通知然後onChanged()會被回撥。這時你可以更新介面卡中的快取資料,然後在介面卡中更新UI。

新增Room資料庫

  • 什麼是Room資料庫?

Room是在SQLite之上的資料庫層。Room用於處理我們曾經用SQLiteOpenHelper來處理任務。

  • Room通過DAO向資料庫傳送查詢

  • 預設情況下,為了避免降低UI執行緒的效能,Room不允許在主執行緒中執行資料庫操作

  • Room提供了編譯時的SQL語句檢查

  • 建立的Room類必須是抽象的,並且繼承RoomDatabase

  • 通常,在整體應用中只需要一個Room資料庫例項,即單例。

  • 實現Room資料庫

  • 建立一個public abstract類WordRoomDatabase,並繼承RoomDatabase。即public abstract class WordRoomDatabase extends RoomDatabase {}

  • 標註其為一個Room資料庫,@Database(entities = {Word.class}, version = 1),宣告其在資料庫中的實體,並指定版本號。實體可以宣告多個,宣告的實體將在資料庫中建立對應的表。

  • 定義使用資料庫的DAO。給每一個@Dao提供get方法。public abstract WordDao wordDao();

完整程式碼如下:

@Database(entities = {Word.class}, version = 1)
public abstract class WordRoomDatabase extends RoomDatabase {
   public abstract WordDao wordDao();

}
  • 使WordRoomDatabase作為單例。

private static volatile WordRoomDatabase INSTANCE;

static WordRoomDatabase getDatabase(final Context context) {
    if (INSTANCE == null) {
        synchronized (WordRoomDatabase.class) {
           if (INSTANCE == null) {
                    // Create database here
           }
        }
    }
    return INSTANCE;
}
  • 例項化RoomDatabase物件:使用Room的databaseBuilder,從WordRoomDatabase類的應用上下文context中建立RoomDatabase物件,並將資料庫全名為"word_database"。

// Create database here
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
       WordRoomDatabase.class, "word_database")
       .build();

下面是完整程式碼:

@Database(entities = {Word.class}, version = 1)
public abstract class WordRoomDatabase extends RoomDatabase {

   public abstract WordDao wordDao();

   private static volatile WordRoomDatabase INSTANCE;

   static WordRoomDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (WordRoomDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            WordRoomDatabase.class, "word_database")
                            .build();
                }
            }
        }
        return INSTANCE;
    }
}

建立Repository(資料倉庫)

  • 什麼是Repository?

Repository是一個可訪問多資料來源的類。它並非構架元件庫中的一部分,但它是程式碼分離和體系結構的最佳實踐建議。Repository用於處理資料操作,它為應用提供資料訪問介面。

640?wx_fmt=png

  • 為什麼要使用Repository?

Repository管理查詢執行緒,並允許您使用多個後端。在最常見的示例中,Repository實現了決定是從網路獲取資料還是從本地快取中獲取結果的邏輯。

  • Repository的實現

  • 建立一個公共類WordRepository

  • 新增兩個成員變數

private WordDao mWordDao;
private LiveData<List<Word>> mAllWords;
  • 新增一個建構函式,該建構函式獲取資料庫的控制代碼並初始化成員變數。

WordRepository(Application application) {
    WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
    mWordDao = db.wordDao();
    mAllWords = mWordDao.getAllWords();
}
  • 為getAllWords()新增一個包裝器。Room在單獨的執行緒上執行所有查詢。觀察到LiveData資料更改時,將通知觀察者。

LiveData<List<Word>> getAllWords() {
   return mAllWords;
}
  • 為insert()方法新增一個包裝器。使用AsyncTask來執行,確保其是在非UI執行緒中執行。

public void insert (Word word) {
    new InsertAsyncTask(mWordDao).execute(word);
}
  • InsertAsyncTask的實現

private static class insertAsyncTask extends AsyncTask<WordVoidVoid{

    private WordDao mAsyncTaskDao;

    insertAsyncTask(WordDao dao) {
        mAsyncTaskDao = dao;
    }

    @Override
    protected Void doInBackground(final Word... params) {
        mAsyncTaskDao.insert(params[0]);
        return null;
    }
}

下面是完整程式碼:

public class WordRepository {

   private WordDao mWordDao;
   private LiveData<List<Word>> mAllWords;

   WordRepository(Application application) {
       WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
       mWordDao = db.wordDao();
       mAllWords = mWordDao.getAllWords();
   }

   LiveData<List<Word>> getAllWords() {
       return mAllWords;
   }


   public void insert (Word word) {
       new insertAsyncTask(mWordDao).execute(word);
   }

   private static class insertAsyncTask extends AsyncTask<WordVoidVoid{

       private WordDao mAsyncTaskDao;

       insertAsyncTask(WordDao dao) {
           mAsyncTaskDao = dao;
       }

       @Override
       protected Void doInBackground(final Word... params) {
           mAsyncTaskDao.insert(params[0]);
           return null;
       }
   }
}

建立ViewModel

  • 什麼是ViewModel?

ViewModel的作用是向UI提供資料,並儲存配置更改。ViewModel充當Repository 和UI之間的通訊中心。還可以使用ViewModel在fragments之間共享資料。ViewModel是 lifecycle library 庫的一部分。

640?wx_fmt=png

  • 為什麼使用ViewModel?

當配置被更改時,ViewModel以一種有生命週期感知的方式儲存應用的UI資料。將應用程式的UI資料與Activity和Fragment類分離,可以讓你更好地遵循單一責任原則:你的activities和fragments負責將資料繪製到螢幕上,而ViewModel則負責儲存和處理UI所需的所有資料。

在ViewModel中,對於UI將使用或顯示的可變資料,請使用LiveData。使用LiveData有幾個好處:

  • 您可以在資料上放置一個觀察者(而不是輪詢更改),並且只在資料實際更改時更新UI。

  • Repository和UI由ViewModel完全分離。沒有來自ViewModel的資料庫呼叫,使得程式碼更易於測試。

  • ViewModel的實現

  • 建立WordViewModel類,使其繼承AndroidViewModel。

public class WordViewModel extends AndroidViewModel {}
  • 新增一個私有成員變數來儲存對儲存庫的引用。

private WordRepository mRepository;
  • 新增一個私有LiveData成員變數來快取單詞列表。

private LiveData<List<Word>> mAllWords;
  • 新增一個建構函式,該建構函式獲取對儲存庫的引用,並從儲存庫獲取單詞列表。

public WordViewModel (Application application) {
       super(application);
       mRepository = new WordRepository(application);
       mAllWords = mRepository.getAllWords();
}
  • 為所有單詞新增一個get方法。這完全隱藏了對UI的實現。

   LiveData<List<Word>> getAllWords() { return mAllWords; }
  • 建立一個呼叫Repository的insert()方法的包裝器insert()方法。這樣,insert()的實現對於UI就完全透明瞭。

public void insert(Word word) { mRepository.insert(word); }

下面是WordViewModel的實現:

public class WordViewModel extends AndroidViewModel {

   private WordRepository mRepository;

   private LiveData<List<Word>> mAllWords;

   public WordViewModel (Application application) {
       super(application);
       mRepository = new WordRepository(application);
       mAllWords = mRepository.getAllWords();
   }

   LiveData<List<Word>> getAllWords() { return mAllWords; }

   public void insert(Word word) { mRepository.insert(word); }
}

警告:不要將context傳遞到ViewModel例項中。不要在ViewModel中儲存活動、片段或檢視例項或它們的context。

新增XML佈局

在value/styes.xml中新增列表項的樣式:

<!-- The default font for RecyclerView items is too small.
The margin is a simple delimiter between the words. -->

<style name="word_title">
   <item name="android:layout_width">match_parent</item>
   <item name="android:layout_height">26dp</item>
   <item name="android:textSize">24sp</item>
   <item name="android:textStyle">bold</item>
   <

相關推薦

Android JetPack架構一個實戰專案JetPack

今日科技快訊 第五屆世界網際網路大會昨日開幕,來自76個國家的1500餘位嘉賓出席大會。騰訊公司董事會主席兼執行長馬化騰在大會開幕式演講中表示,全球產業都在進行數字化,在此期間機遇挑戰並存,產業網際網路機會巨大。 作者簡介 本篇來自 w

一個專案瞭解rest framework

這邊宣告一下,以下是參考官方文件來的。 新建專案 新建名為dimples的django專案 在其中建立一個名為astart的APP: 新建目錄 # 新建目錄 mkdir dimples cd dimples 新建虛擬環境 virtualenv env e

GitHub 熱點速覽 Vol.34:亞馬遜、微軟開源專案硬核技術

![](https://img2020.cnblogs.com/blog/759200/202008/759200-20200824230332585-1697508771.png) 作者:HelloGitHub-**小魚乾** > 摘要:站在巨人的肩膀上才能看得更遠,本週上榜的 computerv

Android Studio compile project新增一個本地專案依賴包時提示程式包不存在

問題情景 在一個專案裡,建了一個library專案和application專案,在application專案的build.gradle將library專案compile project進來,編譯的時候都正常,但是執行的時候,提示程式包不存在。 解決辦法

Android開發】找樂一個笑話App的制作過程記錄

override pbo rdm data root 恰恰 功能 sql htm 緣起 想做一個笑話App的原因是由於在知乎上看過一個帖子。做Android能夠有哪些數據能夠練手,裏面推薦了幾個數據開放平臺。在這些平臺中無一不是有公共的笑話接口,當時心

Android封裝jar包把當前專案設定成module封裝手機振動jar包給unity呼叫

Android封裝jar包或者把當前專案設定成module操作步驟都一樣,此處以手機振動為例,封裝jar包給Unity平臺使用,具體如下: Android部分: 1.建立unity要呼叫的手機振動的類:PhoneVibrate package com.lin.phonevibrate;

系統架構演化改造ssm專案

階段一:單應用架構 完成 問題一:記憶體溢位java.lang.OutOfMemoryError 解決:對tomcat容器,可以在啟動時對jvm設定記憶體限度。對tomcat,可以在catalina.bat中新增: set CATALINA_OPTS=-Xms128M -Xmx256M set JAVA

Python 爬蟲速成教程還有35個實戰專案送給

前兩天,有個小夥伴問了黑馬哥這樣一個問題:Python可以爬到視訊網站上vip才能看到的視訊嗎?聽到這個問題,你是什麼反應?我當時的內心:開玩笑,還有Python爬不到的東西嗎? 今天黑馬哥就給大家總結了一些Python爬取各種東西的案例,讓你看看Python到底有多強大,而且黑馬哥還給大家準備

C# 串列埠操作系列(1) -- 入門一個標準的簡陋的串列埠例子。

我假設讀者已經瞭解了c#的語法,本文是針對剛打算解除串列埠程式設計的朋友閱讀的,作為串列埠程式設計的入門範例,也是我這個系列的基礎。 我們的開發環境假定為vs2005(雖然我在用vs2010,但避免有些網友用2005,不支援lambda,避免不相容,就用2005來做例子)

Android 取得 ListView中每一個Item專案的值

首先我們需要建立 ListView ,這裡假定我們已經建立好了並且使用SimpleAdapter設定好了adapter資料,看一下我們的adapter ArrayList<HashMap<String, String>> list =

SpringMVC配置雙資料來源一個java專案同時連線兩個資料庫

資料來源在配置檔案中的配置 [java] view plain copy  print? <pre name=“code”class=“java”><?xml version=“1.0” encoding=“UTF-8”?>   <beans xml

C# 串列埠操作系列(1) -- 入門一個標準的簡陋的串列埠例子

我假設讀者已經瞭解了c#的語法,本文是針對剛打算解除串列埠程式設計的朋友閱讀的,作為串列埠程式設計的入門範例,也是我這個系列的基礎。 我們的開發環境假定為vs2005(雖然我在用vs2010,但避免有些網友用2005,不支援lambda,避免不相容,就用2005來做例子

Android Studio安裝和第一個HelloAS專案

本篇將介紹Android Studio的安裝和執行的一個應用程式 本機系統環境:win7 64位 1.下載android-studio-bundle-145.3360264-windows,下載地址http://www.android-studio.org/

月薪30k+專案分紅哥大教授探索“區塊鏈+AI”抓緊時間投簡歷吧!手慢無...

每週日,「區塊鏈大本營」人才快報與你不見不散!在這裡,可以第一時間瞭解區塊鏈的行業動態、技術風向與人才需求。 如果你是求職者,請將個人簡歷以Word文件形式(統一格式"求職+姓名+目標公司+應聘職位+手機號")發至郵箱; 如果你是招聘者,請將職位資

文章AWS re:Invent 2018大會揭祕Amazon Aurora

本文由雲+社群發表 | 本文作者: 劉峰,騰訊雲NewSQL資料庫產品負責人。曾職於聯想研究院,Teradata北京研發中心,從事資料庫相關工作8年。2017年加入騰訊資料庫產品中心,擔任NewSQL資料庫產品負責人。 雲資料庫與傳統資料庫的戰爭已打響,一個字概括就是“搶”。 如火如茶的 AWS re

文章AWS re:Invent 2018大會揭秘Amazon Aurora

2018年 保健 god 發揮 監控服務 insight 遊戲機 pci 雲計算 本文由雲+社區發表 | 本文作者: 劉峰,騰訊雲NewSQL數據庫產品負責人。曾職於聯想研究院,Teradata北京研發中心,從事數據庫相關工作8年。2017年加入騰訊數據庫產品中心,擔任Ne

實戰專案掌握JavaSE知識

目標模擬實現一個基於文字介面的《團隊人員排程軟體》 熟悉Java面向物件的高階特性,進一步掌握程式設計技巧和除錯技巧 主要涉及以下知識點: 類的繼承和多型物件的關聯static和final修飾符特殊類的使用異常處理 需求說明:模擬實現基於文字介面的《團隊人員排程軟體》。 該

一文Cascade R-CNN一個使的檢測更加準確的網路

論文名稱:Cascade R-CNN: Delving into High Quality Object Detection 作者:Zhaowei Cai & Nuno Vasconcelos 論文連結:https://arxiv.org/abs/1712.0072

Android 去標題欄Activity 滿屏那些所不知道的 Theme Style

開篇前給大家介紹一個小技巧,就是PC插入U/硬碟載入太慢的解決辦法之一,xp/win7均適用: ①視窗+R鍵 ②鍵入 services.msc 後回車 ③找到 Shell Hardware Detec

架構師五分鐘Volatile的作用及原理

  文章簡介 分析volatile的作用以及底層實現原理,這也是大公司喜歡問的問題 內容導航 vola