学习地址
【Android Jetpack组件从入门到入坟,全家桶全面学习教程精讲,通俗易懂】
review
研究生期间接触过一部分android开发,近期有个小项目需要进行开发,临时恶补了一下Android相关知识点,突然发现Android新增了jetPack包,深入学习之下总结了以上的相关的点。
题外话:最近在新环境部署开发环境时一直部署不上去,我以为是端口的问题,结果是因为破解软件的路径有中文导致的,熬了很久,后面重新部署环境之后idea和webstrom的破解又没了,弄得我花了很久的时间,得不偿失了/(ㄒoㄒ)/~~。
中间经历了一堆问题:
- 创建全局viewmodel时,需要在Application构造工厂方法
- viewmodel初始化时,必须是无参构造函数
- Room中sql出的livedata默认不显示信息,只有observe变化才显示信息
- Room的CRUD都需要在异步后台线程中执行,建议respository中添加异步后台线程
- Room中初始的database内部的dao都是null,只有执行的时候才会赋值
- Room的db创建时使用instance创建,只有data/data/appName/database有值的时候才成功
- Room的db每次创建都要执行初始化,若两次初始化异常会报错
- navigation指向的Fragment不是指R.layout.Fragment,而是R.id.Fragment
- Layout若绑定了对应数据,则@{xxx}是单向绑定,修改值才会影响数据,@={xxx}才是双向绑定,可以observe到
这份代码属实写的非常久,每写一点都是一堆坑在等着我,我觉得我快累死了。
要点总结
1.视图处理
jetpack不是一个小的工具或者orm框架级别的开发组件,而是一整套完备的开发框架。想象一下一个前端框架(类似vue)该有的逻辑?
- 单页面应用:由于路由的切换只是不同页面的转换,因此一个html也就足够了,Android原先是将多个activity进行切换,现在单个activity成为主流。
- 多组件:单个页面的切换只是切换不同的组件进行显示,例如vue的compents,Android开发中Fragment进行了多组件的开发。
- 页面跳转:无论是前后端都需要一个路由管理器,注册路由后再主进程中进行页面切换调用。navigation作为路由进行组件开发。
- 事件触发:原有的Fragement代表不同的组件,它的内部创建内部类,开放内部类后,进行事件的编辑,主要的事件为用于view model的调用以及调用数据库。
- 页面数据绑定:页面数据绑定需要VM曾进行绑定。绑定的关键在于observe和set,observe用于监控数据,set用于修改数据。注意,这里的绑定可以监视远程数据和Room库表的数据。
- 全局变量:一个页面需要一个全局变量进行修改,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);
}
}
});
总结:
基本上大坑都被我趟过了,有什么问题可以评论区解析