Android JetPack快速上手

 学习地址

【Android Jetpack组件从入门到入坟,全家桶全面学习教程精讲,通俗易懂】

review

研究生期间接触过一部分android开发,近期有个小项目需要进行开发,临时恶补了一下Android相关知识点,突然发现Android新增了jetPack包,深入学习之下总结了以上的相关的点。

题外话:最近在新环境部署开发环境时一直部署不上去,我以为是端口的问题,结果是因为破解软件的路径有中文导致的,熬了很久,后面重新部署环境之后idea和webstrom的破解又没了,弄得我花了很久的时间,得不偿失了/(ㄒoㄒ)/~~。

中间经历了一堆问题:

  1. 创建全局viewmodel时,需要在Application构造工厂方法
  2. viewmodel初始化时,必须是无参构造函数
  3. Room中sql出的livedata默认不显示信息,只有observe变化才显示信息
  4. Room的CRUD都需要在异步后台线程中执行,建议respository中添加异步后台线程
  5. Room中初始的database内部的dao都是null,只有执行的时候才会赋值
  6. Room的db创建时使用instance创建,只有data/data/appName/database有值的时候才成功
  7. Room的db每次创建都要执行初始化,若两次初始化异常会报错
  8. navigation指向的Fragment不是指R.layout.Fragment,而是R.id.Fragment
  9. Layout若绑定了对应数据,则@{xxx}是单向绑定,修改值才会影响数据,@={xxx}才是双向绑定,可以observe到

这份代码属实写的非常久,每写一点都是一堆坑在等着我,我觉得我快累死了。


要点总结

1.视图处理

jetpack不是一个小的工具或者orm框架级别的开发组件,而是一整套完备的开发框架。想象一下一个前端框架(类似vue)该有的逻辑?

  1. 单页面应用:由于路由的切换只是不同页面的转换,因此一个html也就足够了,Android原先是将多个activity进行切换,现在单个activity成为主流。
  2. 多组件:单个页面的切换只是切换不同的组件进行显示,例如vue的compents,Android开发中Fragment进行了多组件的开发。
  3. 页面跳转:无论是前后端都需要一个路由管理器,注册路由后再主进程中进行页面切换调用。navigation作为路由进行组件开发。
  4. 事件触发:原有的Fragement代表不同的组件,它的内部创建内部类,开放内部类后,进行事件的编辑,主要的事件为用于view model的调用以及调用数据库。
  5. 页面数据绑定:页面数据绑定需要VM曾进行绑定。绑定的关键在于observe和set,observe用于监控数据,set用于修改数据。注意,这里的绑定可以监视远程数据和Room库表的数据。
  6. 全局变量:一个页面需要一个全局变量进行修改,vue中使用vuex,这里使用一个acitvity的vm进行数据绑定。

因此,其实Android的JetPack在视图部分其实取了mvvm思想,并且围绕着不同页面进行Fragment的编辑,其保有着单一职责的原则。

2.数据库处理

数据库使用的是Room框架,是一个小型的ORM框架,使用的是类似mybatis注解的方式,进行开发和设计数据。

3.全局监控

vue中一般使用eventBus或者添加listener系统监控,但是也并不是实时的。添加长连接也可以实现,但是断网了就不行了,后台添加拦截器也可以监控,但是这个断网也不行。Android中有全局的SystemObserve,可以监控app的状态,并且及时的修改状态。当断网或者切换流量时,会实时进行提醒。

4.后台服务

之前的Android后台服务一般都通过serivce进行执行。但是上传日志,上传活跃情况的操作,当突然关闭时,service停止了,但是后台依然希望获取,这时workerManager就可以及时进行上传.


整体架构


开发流程:

我们以一个登录模块为例,进行开发内容的详解

创建navgation
1.主视图配置

在activity的视图上进行添加navgation的视图进行覆盖,从此activity就成为了navigation的栈底,之后的页面都通过navController进行控制

mainActivity的视图如下:

<?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">
    <fragment
        android:id="@+id/main_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/main_nav" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.配置导航视图

在res中创建navigation目录,并在navigation目录中新建一个main_nav.xml,这个xml管理接下来的所有Fragment以及activity的跳转,假如有welcomeFragment和mainFragment此时的跳转方式如下:

<?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/main_nav"
    app:startDestination="@id/welcomeFragment2">

    <fragment
        android:id="@+id/welcomeFragment2"
        android:name="com.example.timemanagemaster.fragment.WelcomeFragment"
        android:label="WelcomeFragment" >
        <action
            android:id="@+id/action_welcomeFragment2_to_mainFragment3"
            app:destination="@id/mainFragment3" />
    </fragment>
    <fragment
        android:id="@+id/mainFragment3"
        android:name="com.example.timemanagemaster.fragment.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" />
</navigation>

在创建时推荐使用视图模式进行创建,点击+号即可进行创建

3.在fragment配置

设置一个BaseFragment,当fragment完成视图创建后,默认在此视图上获取navigationController

package com.example.timemanagemaster.base;

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;

import android.view.View;

import com.example.timemanagemaster.app.MyApplication;
import com.example.timemanagemaster.orm.repository.UserRepository;
import com.example.timemanagemaster.viewmodel.AppViewModel;

public abstract class BaseFragment extends Fragment {

    protected AppViewModel appViewModel;
    protected NavController navController;

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        initNav();
        afterView();
        observe();
    }
    private void initNav(){
        navController = Navigation.findNavController(this.requireView());
    }

    protected AppViewModel getAppViewModel(){
        ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity(), MyApplication.myApplication.getViewModelFactory());
        appViewModel = viewModelProvider.get(AppViewModel.class);
        return appViewModel;
    }



    protected void afterView(){};

    protected abstract void observe();

    protected void redirect(int id){
        navController.navigate(id);
    }
}

在接下来的fragment中,需要进行视图操作就在initView()中进行初始化,需要在视图完成初始化操作就在observe中进行操作。

4.创建目录导航

当需要进行目录导航时,使用navigation进行导航也是一个极为快速的方式,此时需要先创建目录。创建目录时需要先在res中创建menu类型的资源文件夹,其次创建nav_menu.xml文件

<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:title="@string/menu_home"
        android:icon="@drawable/welcome_icon"
        android:id="@+id/menu_home"
        />
    <item android:title="@string/menu_data"
        android:icon="@drawable/welcome_icon"
        android:id="@+id/menu_data"
        />
    <item android:title="@string/menu_plan"
        android:icon="@drawable/welcome_icon"
        android:id="@+id/menu_plan"
        />
    <item android:title="@string/menu_me"
        android:icon="@drawable/welcome_icon"
        android:id="@+id/menu_me"
        />
</menu>

完成目录创建后,在main_fragment中引入menu

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".fragment.MainFragment"
        >

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/nav_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@id/nav_menu"
            android:layout_below="@id/nav_text"
            />
        <View
            android:layout_width="match_parent"
            android:layout_height="0.3dp"
            android:layout_above="@+id/nav_menu"
            />
            <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/nav_menu"
                android:layout_alignParentBottom="true"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/white"
                app:menu="@menu/nav_menu"
                tools:layout_height="50dp"
                android:orientation="horizontal"
                />

    </RelativeLayout>
</layout>

引入需要BottomNavigationView的录入,完成录入后,可以在mainFragment中绑定并进行操作。

创建Fragment

创建Fragment时,需要使用binding对视图和fragment进行绑定,以loginFragment为例,他会自动生成FragmentLoginBinding类,需要初始化时进行操作

package com.example.timemanagemaster.fragment;


import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProvider;

import android.os.AsyncTask;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

import com.example.timemanagemaster.R;
import com.example.timemanagemaster.app.MyApplication;
import com.example.timemanagemaster.base.BaseFragment;
import com.example.timemanagemaster.databinding.FragmentLoginBinding;
import com.example.timemanagemaster.orm.AppDatabase;
import com.example.timemanagemaster.orm.model.User;
import com.example.timemanagemaster.orm.repository.UserRepository;
import com.example.timemanagemaster.viewmodel.AppViewModel;
import com.example.timemanagemaster.viewmodel.LoginViewModel;

import java.util.List;
import java.util.Objects;

import lombok.Data;

public class LoginFragment extends BaseFragment {

    private FragmentLoginBinding binding;
    private LoginViewModel mViewModel;
    public AppViewModel appViewModel;
    UserRepository repository;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        mViewModel = new ViewModelProvider(this).get(LoginViewModel.class);
        appViewModel = this.getAppViewModel();
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false);
        binding.setVm(mViewModel);
        binding.setClick(new ProxyClick());
        binding.setLifecycleOwner(this);
        repository = new UserRepository(MyApplication.myApplication);
        return binding.getRoot();
    }

    @Override
    protected void afterView() {
        super.afterView();
        appViewModel = new ViewModelProvider(requireActivity()).get(AppViewModel.class);
    }

    @Override
    protected void observe() {

    }
    // 辅助方法,用于显示 Toast
    private void showToast(String message) {
        Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
    }

    public class ProxyClick{
        public void submit() {
                // 使用异步任务执行数据库操作
                AsyncTask.execute(() -> {
                    String userName = mViewModel.getUserName().getValue();
                    String password =  mViewModel.getPassword().getValue();
                    // 在后台线程中获取用户数据,如果需要在 UI 中使用,需要在主线程中进行操作
                    repository.getUserByCredentials(userName, password).thenAccept(user->{
                        if (user != null) {
                            // 登录成功,如果需要在 UI 中操作,需要在主线程中执行
                            requireActivity().runOnUiThread(
                                    () -> {
                                        showToast("Login successful");
                                        redirect(R.id.mainFragment3);
                                    }
                            );
                            user.setIsLogin(1);
                            repository.updateUser(user);
                            appViewModel.setAppUser(user);
                        } else {
                            // 用户名或密码不正确,如果需要在 UI 中操作,需要在主线程中执行
                            getActivity().runOnUiThread(() -> showToast("Invalid username or password."));
                        }
                    });
                });
        }
    }


}

绑定后视图也要绑定data

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="adViewModel"
            type="com.example.timemanagemaster.viewmodel.AdViewModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        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">

        <ImageView
            android:background="@color/material_dynamic_tertiary50"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="TODO" />

        <View
            android:id="@+id/ad_baseline"
            android:layout_width="322dp"
            tools:layout_editor_absoluteY="0dp"
            android:layout_height="20dp"
            tools:ignore="MissingConstraints" />
        <TextView
            style="@style/ad.btn"
            android:textColor="@color/black"
            android:background="@color/ad_btn"
            android:id="@+id/tv_a"
            android:layout_width="60dp"
            android:layout_height="30dp"
            android:gravity="center"
            android:text="3s"
            app:layout_constraintLeft_toRightOf="@id/ad_baseline"
            app:layout_constraintTop_toBottomOf="@id/ad_baseline"
            tools:ignore="MissingConstraints" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
vm管理view

这里有一个大的坑点,千万要用DataBindingUtil,其他的无法自动生成对应的ViewModel,其次vm必须要有get/set函数支持。

1.activity和fragment的databinding方式不同

activity中的初始化为

package com.example.timemanagemaster.activity;

import android.os.Bundle;
import android.util.Log;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;

import com.example.timemanagemaster.R;
import com.example.timemanagemaster.app.MyApplication;
import com.example.timemanagemaster.databinding.ActivityMainBinding;
import com.example.timemanagemaster.orm.model.User;
import com.example.timemanagemaster.viewmodel.AppViewModel;

public class MainActivity extends AppCompatActivity {
    NavController navController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        ViewModelProvider viewModelProvider = new ViewModelProvider(
                this,
                MyApplication.myApplication.getViewModelFactory());
        AppViewModel appViewModel = viewModelProvider.get(AppViewModel.class);
        getActivityNavigator();

        appViewModel.getAppUser().observe(this, user -> {
            if(user!=null) {
                Log.d("TAG", "mainActvity: " + user.toString());
                if(user.getIsLogin()==0){
                    navController.navigate(R.id.loginFragment);
                }
            }
        });
    }

    private void getActivityNavigator(){
        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
        assert navHostFragment != null;
        navController = navHostFragment.getNavController();
    }


}
fragment中初始化为
package com.example.timemanagemaster.fragment;


import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProvider;

import android.os.AsyncTask;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

import com.example.timemanagemaster.R;
import com.example.timemanagemaster.app.MyApplication;
import com.example.timemanagemaster.base.BaseFragment;
import com.example.timemanagemaster.databinding.FragmentLoginBinding;
import com.example.timemanagemaster.orm.AppDatabase;
import com.example.timemanagemaster.orm.model.User;
import com.example.timemanagemaster.orm.repository.UserRepository;
import com.example.timemanagemaster.viewmodel.AppViewModel;
import com.example.timemanagemaster.viewmodel.LoginViewModel;

import java.util.List;
import java.util.Objects;

import lombok.Data;

public class LoginFragment extends BaseFragment {

    private FragmentLoginBinding binding;
    private LoginViewModel mViewModel;
    public AppViewModel appViewModel;
    UserRepository repository;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        mViewModel = new ViewModelProvider(this).get(LoginViewModel.class);
        appViewModel = this.getAppViewModel();
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false);
        binding.setVm(mViewModel);
        binding.setClick(new ProxyClick());
        binding.setLifecycleOwner(this);
        repository = new UserRepository(MyApplication.myApplication);
        return binding.getRoot();
    }

    @Override
    protected void afterView() {
        super.afterView();
        appViewModel = new ViewModelProvider(requireActivity()).get(AppViewModel.class);
    }

    @Override
    protected void observe() {

    }
    // 辅助方法,用于显示 Toast
    private void showToast(String message) {
        Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
    }

    public class ProxyClick{
        public void submit() {
                // 使用异步任务执行数据库操作
                AsyncTask.execute(() -> {
                    String userName = mViewModel.getUserName().getValue();
                    String password =  mViewModel.getPassword().getValue();
                    // 在后台线程中获取用户数据,如果需要在 UI 中使用,需要在主线程中进行操作
                    repository.getUserByCredentials(userName, password).thenAccept(user->{
                        if (user != null) {
                            // 登录成功,如果需要在 UI 中操作,需要在主线程中执行
                            requireActivity().runOnUiThread(
                                    () -> {
                                        showToast("Login successful");
                                        redirect(R.id.mainFragment3);
                                    }
                            );
                            user.setIsLogin(1);
                            repository.updateUser(user);
                            appViewModel.setAppUser(user);
                        } else {
                            // 用户名或密码不正确,如果需要在 UI 中操作,需要在主线程中执行
                            getActivity().runOnUiThread(() -> showToast("Invalid username or password."));
                        }
                    });
                });
        }
    }


}

fragment中需要在xml中进行data的绑定

    <data>
        <variable
            name="vm"
            type="com.example.timemanagemaster.viewmodel.LoginViewModel" />
        <variable
            name="click"
            type="com.example.timemanagemaster.fragment.LoginFragment.ProxyClick" />
    </data>
添加触发事件

触发事件之前都是添加findById后获取对应的按钮,然后在按钮中添加匿名内部类进行事件的触发,上图的xml中新增的variable click,可以用于双向绑定Fragment中的匿名内部类进行事件触发,实际开发内容如下:

    public class ProxyClick{
        public void submit(){
            Log.d("TAG", "submit: 123");
            mViewModel.setUserName("123");
        }
    }

最后在按钮处添加实际的触发,如下图

        <Button
            android:onClick="@{()->click.submit()}"
            />
添加orm/data

orm的data实际就是mvc里的model,那么直接lombok秒了,创建时要关注表名,外键,类型等内容的对应。以一个user为例

package com.example.timemanagemaster.orm.model;


import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.ColumnInfo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@Entity(tableName = "users")
@NoArgsConstructor
public class User {
    public User(String userName, String password, int wechatOpenID, int isLogin) {
        this.userName = userName;
        this.password = password;
        this.wechatOpenID = wechatOpenID;
        this.isLogin = isLogin;
    }

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int id;

    @ColumnInfo(name = "userName")
    private String userName;

    @ColumnInfo(name = "password")
    private String password;

    @ColumnInfo(name = "wechatOpenID")
    private int wechatOpenID;

    @ColumnInfo(name = "isLogin")
    private int isLogin;
}

添加orm/dao
package com.example.timemanagemaster.orm.dao;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Delete;
import androidx.room.Transaction;
import androidx.room.Update;

import com.example.timemanagemaster.orm.model.User;

import java.util.List;

@Dao
public interface UserDao {

    @Transaction
    @Query("SELECT * FROM users")
    List<User> getAllUsers();

    @Query("SELECT * FROM users WHERE id = :id")
    User getUserById(int id);

    @Query("SELECT * FROM users WHERE userName = :userName")
    User getUserByUsername(String userName);
    @Query("SELECT * FROM users WHERE userName = :userName AND password = :password")
    User getUserByCredentials(String userName, String password);

    @Query("SELECT * FROM users WHERE userName = :userName AND password = :password")
    LiveData<User> getLiveUserByCredentials(String userName, String password);
    @Insert
    void insertUser(User user);

    @Delete
    void deleteUser(User user);

    @Update
    void updateUser(User user);
}

添加orm/repository
package com.example.timemanagemaster.orm.repository;

import android.app.Application;
import android.os.AsyncTask;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import com.example.timemanagemaster.app.MyApplication;
import com.example.timemanagemaster.orm.AppDatabase;
import com.example.timemanagemaster.orm.dao.UserDao;
import com.example.timemanagemaster.orm.model.User;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import lombok.Getter;

public class UserRepository {
    private final UserDao userDao;
    private final Executor executor;

    public UserRepository(MyApplication application) {
        AppDatabase appDatabase = MyApplication.getAppDatabase();
        userDao = appDatabase.userDao();
        executor = Executors.newSingleThreadExecutor();
    }

    public User getUserById(int id) {
        return userDao.getUserById(id);
    }

    public User getUserByUsername(String userName) {
        return userDao.getUserByUsername(userName);
    }

    public CompletableFuture<User> getUserByCredentials(String userName, String password){
        return CompletableFuture.supplyAsync(()->
            userDao.getUserByCredentials(userName, password));
    }


    public CompletableFuture<LiveData<User>> getLiveUserByCredentials(String userName, String password){
        return CompletableFuture.supplyAsync(()->
                userDao.getLiveUserByCredentials(userName, password));
    }

    public void insertUser(User user) {
        executor.execute(()->{userDao.insertUser(user);});
    }

    public void deleteUser(User user) {
        executor.execute(()->{userDao.deleteUser(user);});
    }

    public void updateUser(User user){
        executor.execute(()->{userDao.updateUser(user);});
    }

}
添加database
package com.example.timemanagemaster.orm;

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

import com.example.timemanagemaster.app.MyApplication;
import com.example.timemanagemaster.orm.dao.DailyDao;
import com.example.timemanagemaster.orm.dao.MemberDao;
import com.example.timemanagemaster.orm.dao.PlanDao;
import com.example.timemanagemaster.orm.dao.TimeBlockDao;
import com.example.timemanagemaster.orm.dao.UserDao;
import com.example.timemanagemaster.orm.dao.WorkTypeDao;
import com.example.timemanagemaster.orm.model.Daily;
import com.example.timemanagemaster.orm.model.Member;
import com.example.timemanagemaster.orm.model.Plan;
import com.example.timemanagemaster.orm.model.TimeBlock;
import com.example.timemanagemaster.orm.model.User;
import com.example.timemanagemaster.orm.model.WorkType;

@Database(entities = {User.class,Daily.class, Member.class, Plan.class, TimeBlock.class,  WorkType.class},
        version = 1,
        exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
    public abstract DailyDao dailyDao();
    public abstract MemberDao memberDao();
    public abstract PlanDao planDao();
    public abstract TimeBlockDao timeBlockDao();
    public abstract WorkTypeDao workTypeDao();

    private static AppDatabase INSTANCE;//创建单例
    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            //此处对于数据库中的所有更新都需要写下面的代码
            database.execSQL("ALTER TABLE users "
                    + " ADD COLUMN last_update INTEGER");
        }
    };
    public static AppDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null)    {
                    INSTANCE = Room.databaseBuilder(
                                    context.getApplicationContext(),
                                    AppDatabase.class, "main.db")
                            .addCallback(sOnOpenCallback) //添加下面这一行
//                            .addMigrations(MIGRATION_1_2)
                            .fallbackToDestructiveMigration()
                            .build();
                }
            }
        }
        return INSTANCE;}

    private static RoomDatabase.Callback sOnOpenCallback =
            new RoomDatabase.Callback(){
                @Override
                public void onOpen (@NonNull SupportSQLiteDatabase db){
                    super.onOpen(db);
                }};
}

此时,需要额外在application中进行初始化

package com.example.timemanagemaster.app;

import android.app.Application;
import android.util.Log;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider;
import androidx.room.Room;

import com.example.timemanagemaster.orm.AppDatabase;
import com.example.timemanagemaster.orm.model.User;
import com.example.timemanagemaster.viewmodel.AppViewModel;

import java.util.List;
import java.util.Objects;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

public class MyApplication extends Application {

    @Getter
    private static AppDatabase appDatabase;
    public static MyApplication myApplication;
    @Getter
    private ViewModelProvider.Factory viewModelFactory;

    @Override
    public void onCreate() {
        super.onCreate();
        myApplication = this;
        appDatabase = AppDatabase.getDatabase(this);
        viewModelFactory = new ViewModelProvider.AndroidViewModelFactory(this);
    }
    
}
添加全局事件响应

全局事件使用AppViewModel进行监听,首先设置全局appViewModel

package com.example.timemanagemaster.viewmodel;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import com.example.timemanagemaster.orm.model.Member;
import com.example.timemanagemaster.orm.model.User;

import java.io.Closeable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


@Getter
@NoArgsConstructor
public class AppViewModel extends ViewModel {

    private final MutableLiveData<User> appUser = new MutableLiveData<>();
    private final MutableLiveData<Member> appMember = new MutableLiveData<>();

    public void setAppUser(User appUser) {
        this.appUser.postValue(appUser);
    }

    public void setAppMember(Member appMember) {
        this.appMember.postValue(appMember);
    }
}

在Acitvity中进行引用使用:

ViewModelProvider viewModelProvider = new ViewModelProvider(
        this,
        MyApplication.myApplication.getViewModelFactory());
AppViewModel appViewModel = viewModelProvider.get(AppViewModel.class);

在Fragment中进行引用:

protected AppViewModel getAppViewModel(){
    ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity(), MyApplication.myApplication.getViewModelFactory());
    appViewModel = viewModelProvider.get(AppViewModel.class);
    return appViewModel;
}

此时在activity中observe即可进行全局检测

首先获取activity中的navController

    private void getActivityNavigator(){
        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
        assert navHostFragment != null;
        navController = navHostFragment.getNavController();
    }

此时,appViewModel进行全局监听

        appViewModel.getAppUser().observe(this, user -> {
            if(user!=null) {
                Log.d("TAG", "mainActvity: " + user.toString());
                if(user.getIsLogin()==0){
                    navController.navigate(R.id.loginFragment);
                }
            }
        });
总结:

基本上大坑都被我趟过了,有什么问题可以评论区解析

  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值