jetpack中的常用知识点笔记(2)

5,Room

Android采用SQLite作为数据库存储,开源社区常见的ORM(Object Relation Mapping)库有ORMLite,Green DAO等,Room和其他库一样,也是SQLite上提供了一层封装。
Room重要的三个概念

  • Entity:实体类,对应的是数据库的一张表结构,使用注释@Entity标记。(相当于java Bean)
  • Dao:包含访问一系列访问数据库的方法v,使用注释@Dao标记
  • Database:数据库持有者,作为与应用持久化相关数据的低层连接的主要接入点。使用注解@Database标记。另外需要满足以下条件:定义的类必须是一个继承了RoomDatabase的抽象类,在注解中需要定义与数据库相关联的实体类表。包含一个没有参数的抽象方法并且返回一个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();
        }
    }
}

怎样升级数据库:
使用Migration升级数据库
Room会先判断当前有没有直接从1到3的升级方案,如果有,就直接执行从1到3的升级方案,如果没有,那么Room会按照顺序先后执行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在每次数据升级过程中,都会导出一个Schema文件,这是一个json格式的文件,其中包含了数据库的基本信息,有了该文件能清楚的知道数据库的历次变更情况,极大地方便了开发者排查问题。


添加配置.png

schema.png

6,Navigation

Navigation的诞生是方便我们管理页面的App Bar。

  • 优势如下:
    1,可视化的页面导航图,类似于Apple Xcode中的StoryBoard,便于我们理清页面关系。
    2,通过destination和action完成页面导航。
    3,方便添加页面切换动画。
    4,页面间类型安全的参数传递。
    5,通过Navigation UI,对菜单,底部导航,抽屉菜单导航进行统一的处理。
    6,支持深层链接DeepLink。
  • 主要元素
    1,Navigation Graph,一种新的XML资源文件,包含应用程序所有的页面,以及页面间的关系。
    2,NavHostFragment,一个特殊的Fragment,可以将它看成其他Fragment的容器,Navigation Graph中的Fragment正是通过NavHostFragment进行展示的。
    3,NavController,用于在代码中完成Navigation Graph中具体的页面切换工作。
    三者之间的关系:当你想切换Fragment时,使用NavController对象,告诉它你想要去Navigation Graph中的哪个Fragment,NavController会将你想去的Fragment展示NavHostFragment中。
  • NavigationUI的作用
    Fragment的切换,除了Fragment页面本身的切换,通常还伴有App bar的变化。为了方便统一管理,Navigation组件引入了NavigationUI类。
    示例代码如下:
    首先创建一个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方式
    当App收到某个通知推送,我们希望用户在点击该通知时,能够直接跳转到展示该通知内容的页面,可以通过PendingIntent来完成。
    URL方式
    当用户通过手机浏览器浏览网站上某个页面时,可以在网页上放置一个类似于“在应用内打开”的按钮,如果用户已经安装有我们app,那么通过DeepLink就能打开相应的页面;如果用户没有安装,那么网站可以导航到应用程序的下载页面,引导用户安装应用程序。
    adb shell am start -a android.intent.action.VIEW -d"http:// "

7,WorkMarager

WorkMarager的作用:在后台执行任务,可能会消耗大量电量,WorkMarager为应用程序中那些不需要及时完成的任务,提供了一个统一的解决方案,以便在设备电量和用户体验之间达到一个较好的平衡。
特点:
不需要及时完成的任务。
保证任务一定会执行。
兼容范围广。最低兼容API14
使用方法:
1,添加依赖
2,使用Work类定义任务
3,使用WorkRequests配置任务
设置任务触发条件
将任务触发条件设置到WorkRequest
设置延迟执行任务
设置指数退避策略
为任务设置tag标签
4,将任务提交给系统
5,观察任务的状态
6,取消任务
7,参数传递
8,周期性任务
9,任务链
注意:WorkManager在原生系统执行是没问题的,在真机,如小米,华为等是不一定执行的,因为不同厂家对系统的修改都不一样,所以在真机上测试不一定有效,要做一定的适配。
添加依赖

 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");

        //任务执行完之后,返回数据
        Data outputData = new Data.Builder()
                .putString("output_data", "执行成功")
                .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) {
        //设置触发条件
        Constraints constraints = new Constraints.Builder()
                // .setRequiredNetworkType(NetworkType.CONNECTED)//网络连接时执行
                // .setRequiresBatteryNotLow(true) //不在电量不足执行
                // .setRequiresCharging(true) //在充电时执行
                // .setRequiresStorageNotLow(true) //不在存储容量不足时执行
                // .setRequiresDeviceIdle(true) //在待机状态下执行 调用需要API级别最低为23
                // NetworkType.NOT_REQUIRED:对网络没有要求
                // NetworkType.CONNECTED:网络连接的时候执行
                // NetworkType.UNMETERED:不计费的网络比如WIFI下执行
                // NetworkType.NOT_ROAMING:非漫游网络状态
                // NetworkType.METERED:计费网络比如3G,4G下执行。
                //注意:不代表恢复网络了,就立马执行
                .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
                .build();

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

        //配置任务
        //一次性执行的任务
        OneTimeWorkRequest workRequest1 = new OneTimeWorkRequest.Builder(MyWork.class)
                //设置触发条件
                .setConstraints(constraints)
                //设置延迟执行
                .setInitialDelay(5, TimeUnit.SECONDS)
                //指数退避策略
                .setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofSeconds(2))
                //设置tag标签
                .addTag("workRequest1")
                //参数传递
                .setInputData(inputData)
                .build();

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

        //任务提交给WorkManager
        WorkManager workManager = WorkManager.getInstance(this);
        workManager.enqueue(workRequest1);

        //观察任务状态
        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);
                }
            }
        });

        //取消任务
        /*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();

        //任务组合
        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();

        //任务链
        /*WorkManager.getInstance(this)
                .beginWith(workA)
                .then(workB)
                .enqueue();*/
    }
}

8,Paging

Paging是为了方便开发者完成分页加载而设计的一个组件,它为几种常见的分页机制提供了统一的解决方案。
Paging有3个核心类

  • PageListAdapter
    RecyclerView需要搭配适配器使用,如果希望使用Paging组件,适配器需要继承自PageListAdapter
  • PageList
    负责通知DataSource何时获取数据,以及如何获取数据。从DataSource获取的数据将存储在PageList中。
  • DataSource
    有三种PositionDataSource,PageKeyedDataSource,ItemKeyedDataSource
    执行具体的数据加载工作,数据可以来源网络,数据库,网络+数据库。数据的载入需要在子线程中进行。
    1,PositionDataSource
    适用于可通过任意位置加载数据,且目标数据源数固定的情况。
    2,PageKeyedDataSource
    适用于数据源已页的方式进行请求的情况。
    3,ItemKeyedDataSource
    适用于当目标数据的下一页需要依赖上一页数据中最后一个对象中的某个字段最为key的情况,此类分页形式常见于评论功能的实现。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容