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.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);}}});
总结:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/14551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VBA语言専攻每周通知20240524

通知20240524 各位学员∶本周MF系列VBA技术资料增加611-615讲&#xff0c;T3学员看到通知后请免费领取,领取时间5月24日晚上18:00-5月26日晚上18:00。本次增加内容&#xff1a; MF611:用InputBox录入日期 MF612:信息提示10秒后关自动关闭 MF613:只是信息提示10秒 MF614:显…

如何解决Nginx反向代理不生效?

目录 背景 过程 日志 检查配置文件 重启服务 检查容器内的配置文件 容器和宿主机 其他 背景 用了两年的nginx新加的反向代理不生效 Docker挂载的配置文件启动的Nginx&#xff0c;配置一切正常&#xff0c;但是反向代理不生效&#xff0c;???先自查一波 过程 日志 …

RDDM论文阅读笔记

CVPR2024的残差去噪模型。把diffusion 模型的加噪过程分解为残差diffusion和noise diffusion&#xff0c;其中残差diffusion模拟从target image到degraded image的过程&#xff0c;而noise diffusion则是原来的diffusion过程&#xff0c;即从图片到高斯噪声的加噪过程。前者可以…

如何让社区版IDEA变得好用

如何让社区版IDEA变得好用 背景 收费版的idea功能非常强大&#xff0c;但是费用高。社区版的免费&#xff0c;但是功能被阉割了。如何才能让社区版Idea变得好用&#xff0c;就需要各种插件支持了。经过全局配置编码&#xff0c;maven&#xff0c;jdk版本&#xff0c;在加上各…

架构二。。

1、CAP 只能3选2 1&#xff09;一致性&#xff08;Consistency&#xff09; 客户每次读都是返回最新的写操作结果 2&#xff09;可用性&#xff08;Availability&#xff09; 非故障节点在合理的时间内返回合理的响应 3&#xff09;分区容忍性&#xff08;Partition Tolerance…

Ribbon负载均衡(自己总结的)

文章目录 Ribbon负载均衡负载均衡解决的问题不要把Ribbon负载均衡和Eureka-Server服务器集群搞混了Ribbon负载均衡代码怎么写ribbon负载均衡依赖是怎么引入的&#xff1f; Ribbon负载均衡 负载均衡解决的问题 首先Ribbon负载均衡配合Eureka注册中心一块使用。 在SpringCloud…

计算机网络之应用层知识点总结

6.1 网络应用模型 &#xff08;1&#xff09;应用层概述 &#xff08;2&#xff09;网络应用模型的介绍 客户/服务器&#xff08;C/S&#xff09;模型 P2P模型 6.2 域名解析系统DNS &#xff08;1&#xff09;DNS系统介绍 &#xff08;2&#xff09;域名 &#xff08;3&#…

学习平台|基于Springboot+vue的学习平台系统的设计与实现(源码+数据库+文档)

学习平台系统 目录 基于Springboot&#xff0b;vue的学习平台系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3学生功能模块 4教师功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八…

不小心丢失mfc140u.dll文件怎么办?mfc140u.dll丢失的解决办法

当您发现mfc140u.dll文件不见了或者受损&#xff0c;别担心&#xff0c;我们可以一起解决这个问题&#xff01;首先&#xff0c;您可能会注意到一个小提示&#xff0c;当您尝试打开某些程序时&#xff0c;屏幕上会跳出一个消息说“找不到mfc140u.dll”或者“mfc140u.dll文件缺失…

解决docker中container运行闪退终止的问题

在运行bindmount-test时&#xff0c;点击完运行按钮后闪退结束运行。 第一步查看log日志&#xff1a; 2024-05-18 23:46:18 Error: Cannot find module /app/nodemon 2024-05-18 23:46:18 at Function.Module._resolveFilename (internal/modules/cjs/loader.js:668:15) …

2024-5-24 石群电路-15

2024-5-24&#xff0c;星期五&#xff0c;22:15&#xff0c;天气&#xff1a;晴&#xff0c;心情&#xff1a;晴。今天最后一天上班&#xff0c;终于要放返校假啦&#xff0c;开心&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;不过放假也不能耽误…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(八)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 15 节&#xff09; P15《14.ArkUI组件-状态管理state装饰器》 回到最初的 Hello World 案例&#xff0c;首先验证 如果删掉 State…

物理服务器介绍

物理服务器介绍 概述分类按服务器应用分类按服务器结构分类塔式服务器机架式服务器刀片式服务器机架式服务器与刀片式服务器的对比按处理器个数分类按处理器架构分类 主板概述工作原理物理结构技术参数 CPU概述工作原理指令集相关技术技术参数主流产品 内存概述类型相关技术技术…

C语言-atoi()库函数的模拟实现

文章目录 前言一、atoi()库函数的介绍及使用1.1 atoi()库函数介绍1.2 atoi()库函数使用 二、atoi()库函数的模拟实现2.1 函数设计2.2 函数实现思路2.3 具体实现2.4 测试 总结 前言 本篇文章介绍c语言中库函数atoi()的使用&#xff0c;以及模拟实现库函数。 一、atoi()库函数的…

Threes 特效 炫酷传送门HTML5动画特效

基于Three.js的HTML5 3D动画&#xff0c;这个动画模拟了游戏中的一个炫酷的3D场景&#xff0c;支持360度视角查看&#xff0c;也支持鼠标滚轮进行缩放。画面中主要展现了一个游戏中传送门的效果&#xff0c;同时还有路两边的围栏、灯笼、石头&#xff0c;以及星光闪闪的萤火虫&…

IO端口编址

统一编址 特点 独立编址 特点 内存地址分配 区别 应用 IO端口地址译码 硬件上的实现 示例1&#xff1a; 示例2&#xff1a; IO指令 软件上的实现 示例

2024最佳画图软件合集,操作简单无需下载!

随着数字时代的到来&#xff0c;绘画软件已经成为创造性表达和艺术创作不可或缺的工具。无论你是设计师、艺术家&#xff0c;还是只是对创作充满热情&#xff0c;2024年的绘画软件集都为你提供了各种各样的选择&#xff0c;这样你就可以在数字画布上释放你的想法。本文将重点推…

XV4001系列陀螺仪传感器广泛用于车载导航系统

随着汽车电子化趋势的加速&#xff0c;越来越多的汽车配备一系列先进的车载导航系统&#xff0c;这些导航系统功能的实现都依赖于精确的传感器数据(位置、车速、转向角度、车轮转速等)。传感器作为这些系统的核心组件&#xff0c;其准确性和可靠性直接影响到整个导航系统的性能…

大模型时代,掌握Event Stream技术提升Web响应速度

大模型时代,每天搜索都可能会用到一种或多种大模型,在大文本输出的时候,页面是一字一字,一段一段的慢慢输出出来的,这背后是如何实现的呢?我们以KIMI为例 先抓个请求 我们发现界面展示是一句话,但是接口返回的时候是一个字一个字的。 普通请求 多了Event Stream的处理 …

DDoS攻击的最新动态及市场趋势分析

随着数字化转型的加速和网络连接设备的增加&#xff0c;分布式拒绝服务(Distributed Denial of Service, DDoS)攻击已经成为全球网络安全领域的一大威胁。根据最新的市场研究报告&#xff0c;预计到2028年&#xff0c;DDoS防护软件市场的复合年增长率将达到14%以上&#xff0c;…