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,一经查实,立即删除!

相关文章

学习javascript的数组

1.什么是数组? 数组&#xff1a;&#xff08;Array&#xff09;是一种数据类型&#xff0c;属于引用数据类型。 作用&#xff1a;在单个变量名下存储多个数据 2.声明语法 let 数组名 [数据1&#xff0c;数据2......]; 注意事项&#xff1a; 数组是按照顺序保存&#xff0…

企业级大数据平台建设方案

企业级大数据平台建设方案 方案简介硬件软件分布式存储&#xff1a;Foreman作为集群管理工具Sparkcloudera 方案简介 该方案是多年前在Roadstar.ai任职时的建设方案&#xff0c;现将方案部分细节开源&#xff0c;结合本博客的其他文章&#xff0c;能够建立可靠的企业大数据平台…

来盘点我的校园生活(4)

今天我们班出大事了&#xff01; 今天英语老师没来&#xff0c;我们以为其他英语老师来了&#xff0c;但下午一去办公室&#xff0c;我小脑直接萎缩&#xff0c;我都看得怀疑人生了&#xff0c;一个英语老师没有&#xff0c;作业咋办呢&#xff1f; 后来通过18班班主任英语老…

ollama导入自己微调后的模型胡言乱语

1.ollama导入自己微调后的模型胡言乱语 原因&#xff1a;Modelfile的模板格式不对&#xff0c;对的如下所示 ##格式0FROM tinyllama-my-model.gguf### Set the system message SYSTEM """ You are a super helpful helper. """PARAMETER stop …

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:显…

代码随想录算法跟练 | Day3 | 链表Part1

个人博客主页&#xff1a;http://myblog.nxx.nx.cn 代码GitHub地址&#xff1a;https://github.com/nx-xn2002/Data_Structure.git Day3 203.移除链表元素 题目链接&#xff1a; https://leetcode.cn/problems/remove-linked-list-elements/ 题目描述&#xff1a; 给你一个…

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

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

【FAQ】HarmonyOS SDK 闭源开放能力 —Ads Kit

1.问题描述&#xff1a; 开屏广告效果最好的实现方式&#xff1f; 解决方法&#xff1a; 1、动画效果和开发者的实现方式有关&#xff0c;和开屏广告页面本身没什么关系的&#xff1b; 2、示例代码中使用Router跳转的方式展示广告&#xff0c;主要是用于演示广告接口怎么集…

RDDM论文阅读笔记

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

rocketmq 学习一

官方文档&#xff1a;RocketMQ 官方网站 | RocketMQ 一 介绍 RocketMQ 诞生于 2012 年&#xff0c;诞生即开源。2012&#xff5e;2015 年&#xff0c;RocketMQ 一直在通过内部电商业务打磨自身服务能力,并在 2015 年于阿里云上线公测。2016 年&#xff0c;阿里云 RocketMQ 完…

如何让社区版IDEA变得好用

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

《扑克牌游戏》

描述 有一个扑克牌游戏&#xff0c;游戏规则是不断地摸牌&#xff0c;尽可能地使手上的牌的点数接近于10&#xff0c;最完美的情况是总点数为10&#xff0c;不可以超过10&#xff0c;否则就爆了。输入10张牌的点数&#xff0c;(每张点数不超过10)&#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…

lua函数执行和虚拟机指令

Stack based vs Register based VM 可直接参考 Stack based vs Register based VM lua函数调用 先看一下lua函数的结构&#xff1a; /* ** Function Prototypes */ typedef struct Proto {CommonHeader;TValue *k; /* constants used by the function */Instruction *code;…

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

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&#…

面试字节大模型算法实习岗,感觉有点崩溃。。。

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接…

学习平台|基于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) …