学习地址
【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.ConstraintLayoutxmlns: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"><fragmentandroid: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"><fragmentandroid:id="@+id/welcomeFragment2"android:name="com.example.timemanagemaster.fragment.WelcomeFragment"android:label="WelcomeFragment" ><actionandroid:id="@+id/action_welcomeFragment2_to_mainFragment3"app:destination="@id/mainFragment3" /></fragment><fragmentandroid: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;@Overridepublic 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"?>
<menuxmlns: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"?>
<layoutxmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"><RelativeLayoutxmlns: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.AppCompatTextViewandroid:id="@+id/nav_text"android:layout_width="match_parent"android:layout_height="wrap_content"/><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_above="@id/nav_menu"android:layout_below="@id/nav_text"/><Viewandroid:layout_width="match_parent"android:layout_height="0.3dp"android:layout_above="@+id/nav_menu"/><com.google.android.material.bottomnavigation.BottomNavigationViewandroid: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;@Overridepublic 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();}@Overrideprotected void afterView() {super.afterView();appViewModel = new ViewModelProvider(requireActivity()).get(AppViewModel.class);}@Overrideprotected void observe() {}// 辅助方法,用于显示 Toastprivate 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"?>
<layoutxmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="adViewModel"type="com.example.timemanagemaster.viewmodel.AdViewModel" /></data><androidx.constraintlayout.widget.ConstraintLayoutxmlns: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"><ImageViewandroid:background="@color/material_dynamic_tertiary50"android:layout_width="match_parent"android:layout_height="match_parent"android:contentDescription="TODO" /><Viewandroid:id="@+id/ad_baseline"android:layout_width="322dp"tools:layout_editor_absoluteY="0dp"android:layout_height="20dp"tools:ignore="MissingConstraints" /><TextViewstyle="@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;@Overrideprotected 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;@Overridepublic 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();}@Overrideprotected void afterView() {super.afterView();appViewModel = new ViewModelProvider(requireActivity()).get(AppViewModel.class);}@Overrideprotected void observe() {}// 辅助方法,用于显示 Toastprivate 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><variablename="vm"type="com.example.timemanagemaster.viewmodel.LoginViewModel" /><variablename="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");}}
最后在按钮处添加实际的触发,如下图
<Buttonandroid: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);@Insertvoid insertUser(User user);@Deletevoid deleteUser(User user);@Updatevoid 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) {@Overridepublic 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(){@Overridepublic 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 {@Getterprivate static AppDatabase appDatabase;public static MyApplication myApplication;@Getterprivate ViewModelProvider.Factory viewModelFactory;@Overridepublic 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);}}});
总结:
基本上大坑都被我趟过了,有什么问题可以评论区解析