jetpack中的常用知識點筆記(2)

5,Room

Android采用SQLite作為數(shù)據(jù)庫存儲,開源社區(qū)常見的ORM(Object Relation Mapping)庫有ORMLite,Green DAO等,Room和其他庫一樣,也是SQLite上提供了一層封裝。
Room重要的三個概念

  • Entity:實體類,對應(yīng)的是數(shù)據(jù)庫的一張表結(jié)構(gòu),使用注釋@Entity標(biāo)記。(相當(dāng)于java Bean)
  • Dao:包含訪問一系列訪問數(shù)據(jù)庫的方法v,使用注釋@Dao標(biāo)記
  • Database:數(shù)據(jù)庫持有者,作為與應(yīng)用持久化相關(guān)數(shù)據(jù)的低層連接的主要接入點。使用注解@Database標(biāo)記。另外需要滿足以下條件:定義的類必須是一個繼承了RoomDatabase的抽象類,在注解中需要定義與數(shù)據(jù)庫相關(guān)聯(lián)的實體類表。包含一個沒有參數(shù)的抽象方法并且返回一個Dao對象。
    首先build中
 implementation 'androidx.room:room-runtime:2.2.5'
 annotationProcessor 'androidx.room:room-compiler:2.2.5'
@Entity(tableName = "student")
public class Student {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    public int id;

    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
    public String name;

    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    public int age;

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Ignore
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Ignore
    public Student(int id) {
        this.id = id;
    }
}
@Dao
public interface StudentDao {

    @Insert
    void insertStudent(Student... students);

    @Delete
    void deleteStudent(Student... students);

    @Update
    void updateStudent(Student... students);

    @Query("SELECT * FROM student")
    List<Student> getAllStudent();

    @Query("SELECT * FROM student WHERE id = :id")
    List<Student> getStudentById(int id);
}
@Database(entities = {Student.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {

    private static final String DATABASE_NAME = "my_db.db";
    private static MyDatabase mInstance;

    //private MyDatabase(){}

    public static synchronized MyDatabase getInstance(Context context){
        if(mInstance == null){
            mInstance = Room.databaseBuilder(context.getApplicationContext(),
                    MyDatabase.class,
                    DATABASE_NAME)
                    //.allowMainThreadQueries()
                    .build();
        }
        return mInstance;
    }

    public abstract StudentDao getStudentDao();

}

在Activity的代碼

public class MainActivity extends AppCompatActivity {

    private StudentRecyclerViewAdapter adapter;
    private StudentDao studentDao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RecyclerView recycleView = findViewById(R.id.recycleView);
        recycleView.setLayoutManager(new LinearLayoutManager(this));
        List<Student> students = new ArrayList<>();
        adapter = new StudentRecyclerViewAdapter(students);
        recycleView.setAdapter(adapter);

        MyDatabase database = MyDatabase.getInstance(this);
        studentDao = database.getStudentDao();
    }

    public void mInsert(View view) {
        Student s1 = new Student("Jack", 20);
        Student s2 = new Student("Rose", 18);
        new InsertStudentTask(studentDao).execute(s1,s2);
    }

    class InsertStudentTask extends AsyncTask<Student, Void, Void> {

        private StudentDao studentDao;

        public InsertStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(Student... students) {
            studentDao.insertStudent(students);
            return null;
        }
    }

    public void mDelete(View view) {
        Student s1 = new Student(2);
        new DeleteStudentTask(studentDao).execute(s1);
    }

    class DeleteStudentTask extends AsyncTask<Student, Void, Void> {

        private StudentDao studentDao;

        public DeleteStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(Student... students) {
            studentDao.deleteStudent(students);
            return null;
        }
    }

    public void mUpdate(View view) {
        Student s1 = new Student(3,"Jason", 21);
        new UpdateStudentTask(studentDao).execute(s1);
    }

    class UpdateStudentTask extends AsyncTask<Student, Void, Void> {

        private StudentDao studentDao;

        public UpdateStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(Student... students) {
            studentDao.updateStudent(students);
            return null;
        }
    }

    public void mQuery(View view) {
        new GetAllStudentTask(studentDao).execute();
    }

    class GetAllStudentTask extends AsyncTask<Void,Void,Void>{

        private StudentDao studentDao;

        public GetAllStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            List<Student> students = studentDao.getAllStudent();
            adapter.setStudents(students);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            adapter.notifyDataSetChanged();
        }
    }
}

怎樣升級數(shù)據(jù)庫:
使用Migration升級數(shù)據(jù)庫
Room會先判斷當(dāng)前有沒有直接從1到3的升級方案,如果有,就直接執(zhí)行從1到3的升級方案,如果沒有,那么Room會按照順序先后執(zhí)行Migration(1,2),Migration(2,3)以完成升級
修改MyDatabase的代碼如下

public static synchronized MyDatabase getInstance(Context context){
        if(mInstance == null){
            mInstance = Room.databaseBuilder(context.getApplicationContext(),
                    MyDatabase.class,
                    DATABASE_NAME)
                    //.allowMainThreadQueries()
                    .addMigrations(MIGRATION_1_2,MIGRATION_2_3,MIGRATION_3_4)
                    //.fallbackToDestructiveMigration()
                    .createFromAsset("prestudent.db")
                    .build();
        }
        return mInstance;
    }

    static final Migration MIGRATION_1_2 = new Migration(1,2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE student ADD COLUMN sex INTEGER NOT NULL DEFAULT 1");
        }
    };

    static final Migration MIGRATION_2_3 = new Migration(2,3) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE student ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1");
        }
    };

    static final Migration MIGRATION_3_4 = new Migration(3,4) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("CREATE TABLE temp_student (" +
                    "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"+
                    "name TEXT,"+
                    "age INTEGER NOT NULL,"+
                    "sex TEXT DEFAULT 'M',"+
                    "bar_data INTEGER NOT NULL DEFAULT 1)");
            database.execSQL("INSERT INTO temp_student (name,age,sex,bar_data)" +
                    "SELECT name,age,sex,bar_data FROM student");
            database.execSQL("DROP TABLE student");
            database.execSQL("ALTER TABLE temp_student RENAME TO student");
        }
    };

Schema文件:Room在每次數(shù)據(jù)升級過程中,都會導(dǎo)出一個Schema文件,這是一個json格式的文件,其中包含了數(shù)據(jù)庫的基本信息,有了該文件能清楚的知道數(shù)據(jù)庫的歷次變更情況,極大地方便了開發(fā)者排查問題。


添加配置.png

schema.png

6,Navigation

Navigation的誕生是方便我們管理頁面的App Bar。

  • 優(yōu)勢如下:
    1,可視化的頁面導(dǎo)航圖,類似于Apple Xcode中的StoryBoard,便于我們理清頁面關(guān)系。
    2,通過destination和action完成頁面導(dǎo)航。
    3,方便添加頁面切換動畫。
    4,頁面間類型安全的參數(shù)傳遞。
    5,通過Navigation UI,對菜單,底部導(dǎo)航,抽屜菜單導(dǎo)航進(jìn)行統(tǒng)一的處理。
    6,支持深層鏈接DeepLink。
  • 主要元素
    1,Navigation Graph,一種新的XML資源文件,包含應(yīng)用程序所有的頁面,以及頁面間的關(guān)系。
    2,NavHostFragment,一個特殊的Fragment,可以將它看成其他Fragment的容器,Navigation Graph中的Fragment正是通過NavHostFragment進(jìn)行展示的。
    3,NavController,用于在代碼中完成Navigation Graph中具體的頁面切換工作。
    三者之間的關(guān)系:當(dāng)你想切換Fragment時,使用NavController對象,告訴它你想要去Navigation Graph中的哪個Fragment,NavController會將你想去的Fragment展示NavHostFragment中。
  • NavigationUI的作用
    Fragment的切換,除了Fragment頁面本身的切換,通常還伴有App bar的變化。為了方便統(tǒng)一管理,Navigation組件引入了NavigationUI類。
    示例代碼如下:
    首先創(chuàng)建一個navigation資源文件,位置如圖所示


    navigation

    其中代碼如下

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/my_nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.dongnaoedu.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_homeFragment_to_detailFragment"
            app:destination="@id/detailFragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim" />
        <argument
            android:name="user_name"
            app:argType="string"
            android:defaultValue="unknown"/>
        <argument
            android:name="age"
            app:argType="integer"
            android:defaultValue="0"/>
    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:name="com.dongnaoedu.navigation.DetailFragment"
        android:label="fragment_detail"
        tools:layout="@layout/fragment_detail" >
        <action
            android:id="@+id/action_detailFragment_to_homeFragment"
            app:destination="@id/homeFragment" />
    </fragment>
</navigation>

activity_main.xml的布局如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/my_nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity中的代碼如下

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        NavController navController = Navigation.findNavController(this, R.id.fragment);
        NavigationUI.setupActionBarWithNavController(this,navController);
    }

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.fragment);
        return navController.navigateUp();
    }
}

如果兩個Fragment之間要通信的話代碼如下

public class HomeFragment extends Fragment {
    public HomeFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_home, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Button button = getView().findViewById(R.id.button);
        button.setOnClickListener((v)->{
            /*Bundle args = new Bundle();
            args.putString("user_name","jack");*/
            Bundle args = new HomeFragmentArgs.Builder()
                    .setUserName("rose")
                    .setAge(18)
                    .build().toBundle();
            NavController navController = Navigation.findNavController(v);
            navController.navigate(R.id.action_homeFragment_to_detailFragment,args);
        });
    }
}
public class DetailFragment extends Fragment {
    public DetailFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_detail, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Button button = getView().findViewById(R.id.button2);
        /*Bundle args = getArguments();
        String userName = args.getString("user_name");*/
        //Log.d("ning","userName:"+userName);

        HomeFragmentArgs args = HomeFragmentArgs.fromBundle(getArguments());
        String userName = args.getUserName();
        int age = args.getAge();
        Log.d("ning",userName+","+age);

        button.setOnClickListener((v)->{
            NavController navController = Navigation.findNavController(v);
            navController.navigate(R.id.action_detailFragment_to_homeFragment);
        });
    }
}
  • 深層連接DeepLink
    PendingIntent方式
    當(dāng)App收到某個通知推送,我們希望用戶在點擊該通知時,能夠直接跳轉(zhuǎn)到展示該通知內(nèi)容的頁面,可以通過PendingIntent來完成。
    URL方式
    當(dāng)用戶通過手機瀏覽器瀏覽網(wǎng)站上某個頁面時,可以在網(wǎng)頁上放置一個類似于“在應(yīng)用內(nèi)打開”的按鈕,如果用戶已經(jīng)安裝有我們app,那么通過DeepLink就能打開相應(yīng)的頁面;如果用戶沒有安裝,那么網(wǎng)站可以導(dǎo)航到應(yīng)用程序的下載頁面,引導(dǎo)用戶安裝應(yīng)用程序。
    adb shell am start -a android.intent.action.VIEW -d"http:// "

7,WorkMarager

WorkMarager的作用:在后臺執(zhí)行任務(wù),可能會消耗大量電量,WorkMarager為應(yīng)用程序中那些不需要及時完成的任務(wù),提供了一個統(tǒng)一的解決方案,以便在設(shè)備電量和用戶體驗之間達(dá)到一個較好的平衡。
特點:
不需要及時完成的任務(wù)。
保證任務(wù)一定會執(zhí)行。
兼容范圍廣。最低兼容API14
使用方法:
1,添加依賴
2,使用Work類定義任務(wù)
3,使用WorkRequests配置任務(wù)
設(shè)置任務(wù)觸發(fā)條件
將任務(wù)觸發(fā)條件設(shè)置到WorkRequest
設(shè)置延遲執(zhí)行任務(wù)
設(shè)置指數(shù)退避策略
為任務(wù)設(shè)置tag標(biāo)簽
4,將任務(wù)提交給系統(tǒng)
5,觀察任務(wù)的狀態(tài)
6,取消任務(wù)
7,參數(shù)傳遞
8,周期性任務(wù)
9,任務(wù)鏈
注意:WorkManager在原生系統(tǒng)執(zhí)行是沒問題的,在真機,如小米,華為等是不一定執(zhí)行的,因為不同廠家對系統(tǒng)的修改都不一樣,所以在真機上測試不一定有效,要做一定的適配。
添加依賴

 implementation 'androidx.work:work-runtime:2.4.0-alpha03'

自定義MyWork

public class MyWork extends Worker {

    public MyWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        String inputData = getInputData().getString("input_data");
        Log.d("ning","inputData:"+inputData);

        //SystemClock.sleep(2000);
        Log.d("ning","MyWork doWork");

        //任務(wù)執(zhí)行完之后,返回數(shù)據(jù)
        Data outputData = new Data.Builder()
                .putString("output_data", "執(zhí)行成功")
                .build();
        return Result.success(outputData);
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void mAddWork(View view) {
        //設(shè)置觸發(fā)條件
        Constraints constraints = new Constraints.Builder()
                // .setRequiredNetworkType(NetworkType.CONNECTED)//網(wǎng)絡(luò)連接時執(zhí)行
                // .setRequiresBatteryNotLow(true) //不在電量不足執(zhí)行
                // .setRequiresCharging(true) //在充電時執(zhí)行
                // .setRequiresStorageNotLow(true) //不在存儲容量不足時執(zhí)行
                // .setRequiresDeviceIdle(true) //在待機狀態(tài)下執(zhí)行 調(diào)用需要API級別最低為23
                // NetworkType.NOT_REQUIRED:對網(wǎng)絡(luò)沒有要求
                // NetworkType.CONNECTED:網(wǎng)絡(luò)連接的時候執(zhí)行
                // NetworkType.UNMETERED:不計費的網(wǎng)絡(luò)比如WIFI下執(zhí)行
                // NetworkType.NOT_ROAMING:非漫游網(wǎng)絡(luò)狀態(tài)
                // NetworkType.METERED:計費網(wǎng)絡(luò)比如3G,4G下執(zhí)行。
                //注意:不代表恢復(fù)網(wǎng)絡(luò)了,就立馬執(zhí)行
                .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
                .build();

        Data inputData = new Data.Builder()
                .putString("input_data","jack")
                .build();

        //配置任務(wù)
        //一次性執(zhí)行的任務(wù)
        OneTimeWorkRequest workRequest1 = new OneTimeWorkRequest.Builder(MyWork.class)
                //設(shè)置觸發(fā)條件
                .setConstraints(constraints)
                //設(shè)置延遲執(zhí)行
                .setInitialDelay(5, TimeUnit.SECONDS)
                //指數(shù)退避策略
                .setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofSeconds(2))
                //設(shè)置tag標(biāo)簽
                .addTag("workRequest1")
                //參數(shù)傳遞
                .setInputData(inputData)
                .build();

        //周期性任務(wù)
        //不能少于15分鐘
        PeriodicWorkRequest workRequest2 = new PeriodicWorkRequest.Builder(MyWork.class,Duration.ofMinutes(15))
                .build();

        //任務(wù)提交給WorkManager
        WorkManager workManager = WorkManager.getInstance(this);
        workManager.enqueue(workRequest1);

        //觀察任務(wù)狀態(tài)
        workManager.getWorkInfoByIdLiveData(workRequest1.getId()).observe(this, new Observer<WorkInfo>() {
            @Override
            public void onChanged(WorkInfo workInfo) {
                Log.d("ning",workInfo.toString());
                if(workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED){
                    String outputData = workInfo.getOutputData().getString("output_data");
                    Log.d("ning","outputData:"+outputData);
                }
            }
        });

        //取消任務(wù)
        /*new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                workManager.cancelWorkById(workRequest1.getId());
            }
        }, 2000);*/

    }
}
public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void mAddWork(View view) {
        OneTimeWorkRequest workA = new OneTimeWorkRequest.Builder(AWorker.class)
                .build();
        OneTimeWorkRequest workB = new OneTimeWorkRequest.Builder(BWorker.class)
                .build();

        OneTimeWorkRequest workC = new OneTimeWorkRequest.Builder(CWorker.class)
                .build();
        OneTimeWorkRequest workD = new OneTimeWorkRequest.Builder(DWorker.class)
                .build();
        OneTimeWorkRequest workE = new OneTimeWorkRequest.Builder(EWorker.class)
                .build();

        //任務(wù)組合
        WorkContinuation workContinuation1 = WorkManager.getInstance(this)
                .beginWith(workA)
                .then(workB);

        WorkContinuation workContinuation2 = WorkManager.getInstance(this)
                .beginWith(workC)
                .then(workD);

        List<WorkContinuation> taskList = new ArrayList<>();
        taskList.add(workContinuation1);
        taskList.add(workContinuation2);

        WorkContinuation.combine(taskList)
                .then(workE)
                .enqueue();

        //任務(wù)鏈
        /*WorkManager.getInstance(this)
                .beginWith(workA)
                .then(workB)
                .enqueue();*/
    }
}

8,Paging

Paging是為了方便開發(fā)者完成分頁加載而設(shè)計的一個組件,它為幾種常見的分頁機制提供了統(tǒng)一的解決方案。
Paging有3個核心類

  • PageListAdapter
    RecyclerView需要搭配適配器使用,如果希望使用Paging組件,適配器需要繼承自PageListAdapter
  • PageList
    負(fù)責(zé)通知DataSource何時獲取數(shù)據(jù),以及如何獲取數(shù)據(jù)。從DataSource獲取的數(shù)據(jù)將存儲在PageList中。
  • DataSource
    有三種PositionDataSource,PageKeyedDataSource,ItemKeyedDataSource
    執(zhí)行具體的數(shù)據(jù)加載工作,數(shù)據(jù)可以來源網(wǎng)絡(luò),數(shù)據(jù)庫,網(wǎng)絡(luò)+數(shù)據(jù)庫。數(shù)據(jù)的載入需要在子線程中進(jìn)行。
    1,PositionDataSource
    適用于可通過任意位置加載數(shù)據(jù),且目標(biāo)數(shù)據(jù)源數(shù)固定的情況。
    2,PageKeyedDataSource
    適用于數(shù)據(jù)源已頁的方式進(jìn)行請求的情況。
    3,ItemKeyedDataSource
    適用于當(dāng)目標(biāo)數(shù)據(jù)的下一頁需要依賴上一頁數(shù)據(jù)中最后一個對象中的某個字段最為key的情況,此類分頁形式常見于評論功能的實現(xiàn)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容