Android MVVM框架搭建(五)Navigation + Fragment + BottomNavigationView

  • 移动指定View的宽度

  • @param view

*/

public static void moveViewWidth(View view, TranslateCallback callback) {

view.post(() -> {

//通过post拿到的tvTranslate.getWidth()不会为0。

TranslateAnimation translateAnimation = new TranslateAnimation(0, view.getWidth(), 0, 0);

translateAnimation.setDuration(1000);

translateAnimation.setFillAfter(true);

view.startAnimation(translateAnimation);

//动画监听

translateAnimation.setAnimationListener(new Animation.AnimationListener() {

@Override

public void onAnimationStart(Animation animation) {

}

@Override

public void onAnimationEnd(Animation animation) {

//检查Android版本

callback.animationEnd();

}

@Override

public void onAnimationRepeat(Animation animation) {

}

});

});

}

/**

  • 移动指定View的宽度

  • @param view 需要位移的View

  • @param callback 位移动画回调

  • @param translateAnimation 位移动画 (自行配置)

*/

public static void moveViewWidth(View view, TranslateCallback callback, TranslateAnimation translateAnimation) {

view.post(() -> {

//通过post拿到的tvTranslate.getWidth()不会为0。

view.startAnimation(translateAnimation);

//动画监听

translateAnimation.setAnimationListener(new Animation.AnimationListener() {

@Override

public void onAnimationStart(Animation animation) {

}

@Override

public void onAnimationEnd(Animation animation) {

//检查Android版本

callback.animationEnd();

}

@Override

public void onAnimationRepeat(Animation animation) {

}

});

});

}

public interface TranslateCallback {

//动画结束

void animationEnd();

}

}

因为在启动页需要知道程序有没有登录,因此在Constant中增加一个常量,如下所示:

/**

  • 是否登录过

*/

public static final String IS_LOGIN = “isLogin”;

下面我们修改一下SplashActivity的代码,使用这个常量来判断需要跳转到那个页面,代码如下:

public class SplashActivity extends BaseActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

ActivitySplashBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_splash);

setStatusBar(true);

EasyAnimation.moveViewWidth(binding.tvTranslate, () -> {

binding.tvMvvm.setVisibility(View.VISIBLE);

jumpActivity(MVUtils.getBoolean(Constant.IS_LOGIN) ? MainActivity.class : LoginActivity.class);

});

}

}

这里我继承了BaseActivity,然后设置了状态栏深色模式,因为我们的页面是白色的,如果状态栏也是白色就看不出来了,后面就是在动画结束的时候跳转页面,很简单的代码。这个页面的代码就写完了,下面我们修改LoginActivity中的代码,首先是修改继承的Activity为BaseActivity。里面的代码如下:

public class LoginActivity extends BaseActivity {

private ActivityLoginBinding dataBinding;

private LoginViewModel loginViewModel;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

//数据绑定视图

dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);

loginViewModel = new LoginViewModel();

//Model → View

User user = new User(“admin”, “123456”);

loginViewModel.getUser().setValue(user);

//获取观察对象

MutableLiveData user1 = loginViewModel.getUser();

user1.observe(this, user2 -> dataBinding.setViewModel(loginViewModel));

dataBinding.btnLogin.setOnClickListener(v -> {

if (loginViewModel.user.getValue().getAccount().isEmpty()) {

showMsg(“请输入账号”);

return;

}

if (loginViewModel.user.getValue().getPwd().isEmpty()) {

showMsg(“请输入密码”);

return;

}

//记录已经登录过

MVUtils.put(Constant.IS_LOGIN,true);

showMsg(“登录成功”);

jumpActivity(MainActivity.class);

});

}

}

这里就没啥好说的,就是使用了BaseActivity中的方法。同时我修改了一下布局中的代码,我将这两个TextView隐藏了

同时我们修改一下图片显示之前的占位图或者说是默认背景图。两个图片如下:

在这里插入图片描述

在这里插入图片描述

然后一个加载图片出错时显示的图片:

在这里插入图片描述

首先是MainActivity中,显示必应图片的位置,修改一下activity_main.xml

在这里插入图片描述

然后打开CustomImageView,增加如下代码:

private static final RequestOptions OPTIONS = new RequestOptions()

.placeholder(R.drawable.wallpaper_bg)//图片加载出来前,显示的图片

.fallback(R.drawable.wallpaper_bg) //url为空的时候,显示的图片

.error(R.mipmap.ic_loading_failed);//图片加载失败后,显示的图片

将这个值配置进去,如下图所示:

在这里插入图片描述

下面我们运行一下看是什么效果。

在这里插入图片描述

效果还可以的,下面进入主页面的代码编写。

五、主页面


当到了每日壁纸页面时,我们需要再提供一个入口可以进入下一个页面,现在的每日壁纸页面不能算是真正意义上的主页面,因此我们写一个入口,可以在MainActivity中增加一个浮动按钮,页面上下滑动时控制按钮的显示和消失。下面在activity_main.xml中增加如下布局代码:

<com.google.android.material.floatingactionbutton.FloatingActionButton

android:id=“@+id/fab_home”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_gravity=“end|bottom”

android:layout_margin=“20dp”

android:background=“@color/purple_500”

android:onClick=“toHome”

android:src=“@mipmap/ic_home”

app:backgroundTint=“@color/purple_500”

app:fabSize=“auto”

tools:ignore=“UsingOnClickInXml”

android:contentDescription=“主页” />

添加的位置如下,这里的图标到我的源码里拿就好,白色的放出来也看不见。

在这里插入图片描述

下面回到MainActivity中,继承BaseActivity,在initView方法中增加如下代码:

//页面上下滑动监听

dataBinding.scrollView.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {

if (scrollY > oldScrollY) {

//上滑

dataBinding.fabHome.hide();

} else {

//下滑

dataBinding.fabHome.show();

}

});

然后也增加一个方法,当点击时跳转到HomeActivity,我们将在这个HomeActivity中显示Fragment,现在还没有,下面会创建的。

public void toHome(View view) {

jumpActivity(HomeActivity.class);

}

代码添加位置如下图所示:

在这里插入图片描述

下面在activity包下创建一个HomeActivity,对应的布局是activity_home.xml,在改动之前我们先做好准备的工作。

六、Navigation使用


在res包下新建一个navigation包,包下新建一个nav_graph.xml,里面的代码如下:

<?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/nav_graph”

app:startDestination=“@id/news_fragment”>

<fragment

android:id=“@+id/news_fragment”

android:name=“com.llw.mvvm.ui.fragment.NewsFragment”

android:label=“news_fragment”

tools:layout=“@layout/news_fragment” />

<fragment

android:id=“@+id/video_fragment”

android:name=“com.llw.mvvm.ui.fragment.VideoFragment”

android:label=“video_fragment”

tools:layout=“@layout/video_fragment” />

这里就是将Fragment配置到Navigation中,app:startDestination表示显示的第一个Fragment。那么这一步就完成了,下面是另一个操作,就是通过点击底部导航栏菜单去进行Fragment的切换。我们在res下新建一个menu包,包下新建一个navigation_menu.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<item

android:id=“@+id/news_fragment”

android:icon=“@mipmap/ic_hot_news”

android:title=“新闻” />

<item

android:id=“@+id/video_fragment”

android:icon=“@mipmap/ic_hot_video”

android:title=“视频” />

这里有两个图标,同样是白色的。

在这里插入图片描述

下面我们回到activity_home.xml中,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<layout 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”>

<RelativeLayout

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“.ui.activity.HomeActivity”>

<com.google.android.material.appbar.MaterialToolbar

android:id=“@+id/toolbar”

android:layout_width=“match_parent”

android:layout_height=“?attr/actionBarSize”

android:background=“@color/purple_500”>

<TextView

android:id=“@+id/tv_title”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_gravity=“center”

android:text=“头条新闻”

android:textColor=“@color/white”

android:textSize=“18sp”

android:textStyle=“bold” />

</com.google.android.material.appbar.MaterialToolbar>

<fragment

android:id=“@+id/nav_host_fragment”

android:name=“androidx.navigation.fragment.NavHostFragment”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_above=“@+id/bottom_navigation”

android:layout_below=“@+id/toolbar”

app:navGraph=“@navigation/nav_graph” />

<com.google.android.material.bottomnavigation.BottomNavigationView

android:id=“@+id/bottom_navigation”

android:layout_width=“match_parent”

android:layout_height=“?attr/actionBarSize”

android:layout_alignParentBottom=“true”

android:background=“#FFF”

app:menu=“@menu/navigation_menu” />

这里分为三个部分,一个是标题栏、一个是装载Fragment的容器,另一个是控制Fragment切换的。

下面我们进入到HomeActivity页面,修改代码如下:

public class HomeActivity extends BaseActivity {

private ActivityHomeBinding binding;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

binding = DataBindingUtil.setContentView(this, R.layout.activity_home);

initView();

}

/**

  • 初始化

*/

private void initView() {

//获取navController

NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);

binding.bottomNavigation.setOnNavigationItemSelectedListener(item -> {

switch (item.getItemId()) {

case R.id.news_fragment:

binding.tvTitle.setText(“头条新闻”);

navController.navigate(R.id.news_fragment);

break;

case R.id.video_fragment:

binding.tvTitle.setText(“热门视频”);

navController.navigate(R.id.video_fragment);

break;

default:

}

return true;

});

}

}

下面在fragment包下创建一个BaseFragment,里面的代码如下:

public class BaseFragment extends Fragment {

protected AppCompatActivity context;

@Override

public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

return super.onCreateView(inflater, container, savedInstanceState);

}

@Override

public void onViewCreated(@NonNull @NotNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

}

@Override

public void onAttach(@NonNull @NotNull Context context) {

super.onAttach(context);

if(context instanceof AppCompatActivity){

this.context = (AppCompatActivity) context;

}

}

@Override

public void onDetach() {

super.onDetach();

context = null;

}

protected void showMsg(String msg) {

Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();

}

}

然后修改NewsFragment中的代码:

public class NewsFragment extends BaseFragment {

private NewsFragmentBinding binding;

public static NewsFragment newInstance() {

return new NewsFragment();

}

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,

@Nullable Bundle savedInstanceState) {

binding = DataBindingUtil.inflate(inflater, R.layout.news_fragment, container, false);

return binding.getRoot();

}

@Override

public void onActivityCreated(@Nullable Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

NewsViewModel mViewModel = new ViewModelProvider(this).get(NewsViewModel.class);

}

}

再修改VideoFragment的代码:

public class VideoFragment extends BaseFragment {

private VideoFragmentBinding binding;

public static VideoFragment newInstance() {

return new VideoFragment();

}

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,

@Nullable Bundle savedInstanceState) {

binding = DataBindingUtil.inflate(inflater, R.layout.video_fragment, container, false);

return binding.getRoot();

}

@Override

public void onActivityCreated(@Nullable Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

VideoViewModel mViewModel = new ViewModelProvider(this).get(VideoViewModel.class);

}

}

这里我们在点击底部导航栏时切换Fragment并且更改一下标题栏的文字,下面运行一下。

在这里插入图片描述

详细的使用说明可以看看这篇文章:Android Navigation + Fragment 制作APP主页面导航(步骤 + 源码),看完后你了解的也许会更多。

七、聚合数据请求


这里我们使用聚合的API数据,聚合API,点击进入完成注册登录,然后可以申请数据API。

在这里插入图片描述

在这里插入图片描述

申请免费的API,每天有一百次请求,因此我们可以把数据请求一次之后保存到本地数据库中。

① NetworkApi

这两个API的接口是不同的地址,修改一下NetworkApi中的setUrlType方法。

private static void setUrlType(int type) {

switch (type) {

case 0:

//必应

BASE_URL = “https://cn.bing.com”;

break;

case 1:

//热门壁纸

BASE_URL = “http://service.picasso.adesk.com”;

break;

case 2:

//聚合API 1

BASE_URL = “http://v.juhe.cn”;

break;

case 3:

//聚合API 2

BASE_URL = “http://apis.juhe.cn”;

break;

default:

break;

}

}

这里两个接口分别是用于请求新闻数据和视频数据的。

② ApiService

在ApiService中增加两个接口,代码如下所示:

/**

  • 聚合新闻数据

*/

@GET(“/toutiao/index?type=&page=&page_size=&is_filter=&key=99d3951ed32af2930afd9b38293a08a2”)

Observable news();

/**

  • 聚合热门视频数据

*/

@GET(“/fapig/douyin/billboard?type=hot_video&size=20&key=a9c49939cae34fc7dae570b1a4824be4”)

Observable video();

针对这个情况我们同样需要对数据库进行一次升级,这一次我们增加两个表。

③ 数据库升级

首先在bean包下新建两个实体,News和Video。里面的内容都是我根据接口返回的数据制作的,News里的代码如下:

@Entity(tableName = “news”)

public class News {

@PrimaryKey(autoGenerate = true)

private int uid;

private String uniquekey;

private String title;

private String date;

private String category;

private String author_name;

private String url;

private String thumbnail_pic_s;

private String is_content;

public int getUid() {

return uid;

}

public void setUid(int uid) {

this.uid = uid;

}

public String getUniquekey() {

return uniquekey;

}

public void setUniquekey(String uniquekey) {

this.uniquekey = uniquekey;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public String getDate() {

return date;

}

public void setDate(String date) {

this.date = date;

}

public String getCategory() {

return category;

}

public void setCategory(String category) {

this.category = category;

}

public String getAuthor_name() {

return author_name;

}

public void setAuthor_name(String author_name) {

this.author_name = author_name;

}

public String getUrl() {

return url;

}

public void setUrl(String url) {

this.url = url;

}

public String getThumbnail_pic_s() {

return thumbnail_pic_s;

}

public void setThumbnail_pic_s(String thumbnail_pic_s) {

this.thumbnail_pic_s = thumbnail_pic_s;

}

public String getIs_content() {

return is_content;

}

public void setIs_content(String is_content) {

this.is_content = is_content;

}

public News() {}

@Ignore

public News(String uniquekey, String title, String date, String category, String author_name, String url, String thumbnail_pic_s, String is_content) {

this.uniquekey = uniquekey;

this.title = title;

this.date = date;

this.category = category;

this.author_name = author_name;

this.url = url;

this.thumbnail_pic_s = thumbnail_pic_s;

this.is_content = is_content;

}

}

Video的代码如下:

@Entity(tableName = “video”)

public class Video {

@PrimaryKey(autoGenerate = true)

private int uid;

private String title;

private String share_url;

private String author;

private String item_cover;

private String hot_words;

public int getUid() {

return uid;

}

public void setUid(int uid) {

this.uid = uid;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public String getShare_url() {

return share_url;

}

public void setShare_url(String share_url) {

this.share_url = share_url;

}

public String getAuthor() {

return author;

}

public void setAuthor(String author) {

this.author = author;

}

public String getItem_cover() {

return item_cover;

}

public void setItem_cover(String item_cover) {

this.item_cover = item_cover;

}

public String getHot_words() {

return hot_words;

}

public void setHot_words(String hot_words) {

this.hot_words = hot_words;

}

@Ignore

public Video(String title, String share_url, String author, String item_cover, String hot_words) {

this.title = title;

this.share_url = share_url;

this.author = author;

this.item_cover = item_cover;

this.hot_words = hot_words;

}

public Video() {}

}

然后是Dao类,在dao包下新建一个NewsDao和VideoDao的接口,NewsDao代码如下:

@Dao

public interface NewsDao {

@Query(“SELECT * FROM news”)

Flowable<List> getAll();

@Insert(onConflict = OnConflictStrategy.REPLACE)

Completable insertAll(List news);

@Query(“DELETE FROM news”)

Completable deleteAll();

}

VideoDao代码如下:

@Dao

public interface VideoDao {

@Query(“SELECT * FROM video”)

Flowable<List> getAll();

@Insert(onConflict = OnConflictStrategy.REPLACE)

Completable insertAll(List videos);

@Query(“DELETE FROM video”)

Completable deleteAll();

}

最后我们进入AppDatabase中,对数据库进行升级迁移,在AppDatabase中新增如下代码:

/**

  • 版本升级迁移到3 新增新闻表和视频表

*/

static final Migration MIGRATION_2_3 = new Migration(2, 3) {

@Override

public void migrate(SupportSQLiteDatabase database) {

//创建新闻表

database.execSQL("CREATE TABLE news " +

"(uid INTEGER NOT NULL, " +

"uniquekey TEXT, " +

"title TEXT, " +

“date TEXT,” +

“category TEXT,” +

“author_name TEXT,” +

“url TEXT,” +

“thumbnail_pic_s TEXT,” +

“is_content TEXT,” +

“PRIMARY KEY(uid))”);

//创建视频表

database.execSQL("CREATE TABLE video " +

"(uid INTEGER NOT NULL, " +

“title TEXT,” +

“share_url TEXT,” +

“author TEXT,” +

“item_cover TEXT,” +

“hot_words TEXT,” +

“PRIMARY KEY(uid))”);

}

};

然后再增加两个抽象方法,就是之前的两个数据操作类,我们这样写了之后通过Room的编译时技术会对这两个抽象类中的接口进行一个实现,不需要我们去管它。

public abstract NewsDao newsDao();

public abstract VideoDao videoDao();

下面就是对数据库进行升级了,如下图所示:

在这里插入图片描述

注意我标注的地方,少一个都会出现升级不成功,或者你直接都编译不成功或者程序运行闪退的情况。

现在我们的数据库有了,接下来要做的就是数据的或者和保存了。

④ 数据存储库

下面就是Repository了,我们在repository包下新建NewsRspository和VideoRepository两个类,然后为了方便管理数据的请求方式,我们同样需要在Constant中增加几个常量来保存当天是否有请求网络接口数据,在Constant中增加如下代码:

/**

  • 今日是否请求了聚合新闻数据

*/

public static final String IS_TODAY_REQUEST_NEWS = “isTodayRequestNews”;

/**

  • 今日请求聚合新闻数据的时间戳

*/

public static final String REQUEST_TIMESTAMP_NEWS = “newsRequestTimestamp”;

/**

  • 今日是否请求了聚合视频数据

*/

public static final String IS_TODAY_REQUEST_VIDEO = “isTodayRequestVideo”;

/**

  • 今日请求聚合视频数据的时间戳

*/

public static final String REQUEST_TIMESTAMP_VIDEO = “videoRequestTimestamp”;

然后我们再来编辑NewsRepository的代码:

@SuppressLint(“CheckResult”)

public class NewsRepository {

private static final String TAG = NewsRepository.class.getSimpleName();

final MutableLiveData news = new MutableLiveData<>();

public final MutableLiveData failed = new MutableLiveData<>();

/**

  • 获取新闻数据

  • @return news

*/

public MutableLiveData getNews() {

//今日此接口是否已经请求

if (MVUtils.getBoolean(Constant.IS_TODAY_REQUEST_NEWS)) {

if (DateUtil.getTimestamp() <= MVUtils.getLong(Constant.REQUEST_TIMESTAMP_NEWS)) {

getNewsForLocalDB();

} else {

getNewsForNetwork();

}

} else {

getNewsForNetwork();

}

return news;

}

/**

  • 从本地数据库获取新闻

*/

private void getNewsForLocalDB() {

Log.d(TAG, “getNewsForLocalDB: 从本地数据库获取 新闻数据”);

NewsResponse newsResponse = new NewsResponse();

NewsResponse.ResultBean resultBean = new NewsResponse.ResultBean();

List<NewsResponse.ResultBean.DataBean> dataBeanList = new ArrayList<>();

Flowable<List> listFlowable = BaseApplication.getDb().newsDao().getAll();

CustomDisposable.addDisposable(listFlowable, newss -> {

for (News news1 : newss) {

NewsResponse.ResultBean.DataBean dataBean = new NewsResponse.ResultBean.DataBean();

dataBean.setUniquekey(news1.getUniquekey());

dataBean.setTitle(news1.getTitle());

dataBean.setDate(news1.getDate());

dataBean.setAuthor_name(news1.getAuthor_name());

dataBean.setCategory(news1.getCategory());

dataBean.setThumbnail_pic_s(news1.getThumbnail_pic_s());

dataBean.setIs_content(news1.getIs_content());

dataBeanList.add(dataBean);

}

resultBean.setData(dataBeanList);

newsResponse.setResult(resultBean);

news.postValue(newsResponse);

});

}

/**

  • 从网络获取壁纸数据

*/

private void getNewsForNetwork() {

Log.d(TAG, “getNewsForNetwork: 从网络获取 热门壁纸”);

NetworkApi.createService(ApiService.class, 2).

news().compose(NetworkApi.applySchedulers(new BaseObserver() {

@Override

public void onSuccess(NewsResponse newsResponse) {

if (newsResponse.getError_code() == 0) {

//保存本地数据

saveNews(newsResponse);

news.setValue(newsResponse);

} else {

failed.postValue(newsResponse.getReason());

}

}

@Override

public void onFailure(Throwable e) {

failed.postValue("News Error: " + e.toString());

}

}));

}

/**

  • 保存热门壁纸数据

*/

private void saveNews(NewsResponse newsResponse) {

MVUtils.put(Constant.IS_TODAY_REQUEST_NEWS, true);

MVUtils.put(Constant.REQUEST_TIMESTAMP_NEWS, DateUtil.getMillisNextEarlyMorning());

Completable deleteAll = BaseApplication.getDb().newsDao().deleteAll();

CustomDisposable.addDisposable(deleteAll, () -> {

Log.d(TAG, “saveNews: 删除数据成功”);

List newsList = new ArrayList<>();

for (NewsResponse.ResultBean.DataBean dataBean : newsResponse.getResult().getData()) {

newsList.add(new News(dataBean.getUniquekey(),dataBean.getTitle(),dataBean.getDate(),dataBean.getCategory(),

dataBean.getAuthor_name(),dataBean.getUrl(),dataBean.getThumbnail_pic_s(),dataBean.getIs_content()));

}

//保存到数据库

Completable insertAll = BaseApplication.getDb().newsDao().insertAll(newsList);

Log.d(TAG, “saveNews: 插入数据:” + newsList.size() + “条”);

//RxJava处理Room数据存储

CustomDisposable.addDisposable(insertAll, () -> Log.d(TAG, “saveNews: 新闻数据保存成功”));

});

}

}

这里的代码和之前WallPaperRepository中的代码神似,逻辑上基本一致,只不过是不同的接口和不同的数据表,同事我在当前的这个Repository中增加了一个异常信息的LiveData,因为请求接口你可能会需要很多情况,最理想的时能获取到数据,但是也有其他情况,例如接口地址错误访问不到、请求返回的数据为空,请求次数达到上限等一些异常。因此我们有必要做一个异常信息的处理,然后传递到ViewModel中,最终在Activity中对这个异常进行观察,及时通知到页面上。不然我光打印日志,用户是看不到的。

VideoRepository的代码如下:

@SuppressLint(“CheckResult”)

public class VideoRepository {

public static final String TAG = VideoRepository.class.getSimpleName();

final MutableLiveData video = new MutableLiveData<>();

public final MutableLiveData failed = new MutableLiveData<>();

/**

  • 获取视频数据

  • @return video

*/

public MutableLiveData getVideo() {

//今日此接口是否已经请求

if (MVUtils.getBoolean(Constant.IS_TODAY_REQUEST_VIDEO)) {

if (DateUtil.getTimestamp() <= MVUtils.getLong(Constant.REQUEST_TIMESTAMP_VIDEO)) {

getVideoForLocalDB();

} else {

getVideoForNetwork();

}

} else {

getVideoForNetwork();

}

return video;

}

/**

  • 从本地数据库获取新闻

*/

private void getVideoForLocalDB() {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2021年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

【Android高级架构视频学习资源】

Android部分精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

final String TAG = VideoRepository.class.getSimpleName();

final MutableLiveData video = new MutableLiveData<>();

public final MutableLiveData failed = new MutableLiveData<>();

/**

  • 获取视频数据

  • @return video

*/

public MutableLiveData getVideo() {

//今日此接口是否已经请求

if (MVUtils.getBoolean(Constant.IS_TODAY_REQUEST_VIDEO)) {

if (DateUtil.getTimestamp() <= MVUtils.getLong(Constant.REQUEST_TIMESTAMP_VIDEO)) {

getVideoForLocalDB();

} else {

getVideoForNetwork();

}

} else {

getVideoForNetwork();

}

return video;

}

/**

  • 从本地数据库获取新闻

*/

private void getVideoForLocalDB() {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-lgu8HTCh-1711754396055)]

[外链图片转存中…(img-VcfrEnRY-1711754396056)]

[外链图片转存中…(img-5CnbTm6F-1711754396056)]

[外链图片转存中…(img-qjgpJ6UP-1711754396056)]

[外链图片转存中…(img-Rml1qSmR-1711754396057)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2021年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
[外链图片转存中…(img-e977EaVR-1711754396057)]

【Android高级架构视频学习资源】

Android部分精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android MVVM框架搭建需要使用Java语言。MVVM框架是一种基于模型-视图-视图模型的设计模式,它可以帮助开发者更好地组织和管理Android应用程序的代码。在搭建MVVM框架时,需要使用一些常用的开源框架,如Data Binding、LiveData、ViewModel等。同时,还需要了解一些基本的Android开发知识和Java语言的基础知识。搭建MVVM框架需要一定的技术水平和经验,但是它可以提高应用程序的可维护性和可扩展性,让开发者更加高效地开发Android应用程序。 ### 回答2: 在Android开发过程中,MVVM框架已经成为了一种非常流行的设计模式,因为它能够很好地解决传统的MVC模式中存在的问题。这篇文章将着重讲解如何使用Java来构建MVVM框架MVVM框架主要由三个部分组成:View、ViewModel和Model。其中,View指的是用户界面;ViewModel则是View和Model之间的中间层,它包含View所需的数据和业务逻辑;而Model则是数据层,负责从数据源中获取和管理数据。 在Java中构建MVVM框架,可以使用以下几个关键技术: 1. Data Binding:Data Binding是一种新兴的技术,可以轻松地将数据绑定到用户界面上。在Android中,Data Binding库已经成为了MVVM框架构建中不可少的一部分。 2. LiveData:LiveData是Jetpack组件库中的一部分,它是用于构建响应式应用程序的一种强大的工具,也是MVVM模式的关键组成部分。LiveData可以监听数据源中的更改,并在数据发生变化时通知ViewModel。 3. ViewModel:ViewModel是MVVM中的重要组成部分,主要用于保存状态、管理数据和处理业务逻辑。它允许View对Model进行观察,从而实现MVVM框架的完整性和数据驱动。 4. RxJava:RxJava是一种Reactive编程框架,可以用于处理异步事件流,并在应用程序中实现非阻塞的、响应式编程。RxJava可以与MVVM框架结合使用,使架构更加灵活和响应。 搭建MVVM框架的过程中,首先需要创建View、ViewModel和Model三个关键组件。ViewModel将负责将Model的数据更新到View上,并处理业务逻辑。此外,使用Data Binding和LiveData建立数据流,实现双向数据绑定,保持ViewModel和View的同步。 最终,使用RxJava将ViewModel和View解耦,实现响应性和灵活性。在整个MVVM框架中,使用Java构建时需要注意的重要细节包括内存泄漏问题、持久性存储问题等等,尽可能地在代码编写时去考虑这些问题,以确保框架的可靠性和稳定性。 总而言之,使用Java搭建MVVM框架需要一定的技术积累和编码能力,但这种框架Android开发中已经被广泛应用,相信有机会成为您的一个不错的选择。 ### 回答3: MVVM是Model-View-ViewModel的缩写,是一种软件架构模式。在安卓应用开发中,我们可以利用MVVM框架搭建Java开发项目,实现数据和用户交互分离,让代码更加清晰易懂,可读性更高。 MVVM架构中,Model代表实体模型,View代表图形界面,ViewModel作为桥梁连接Model和View,负责将Model转化成View的形式,以及监听View的变化反向更新Model。 在搭建Android MVVM框架时,我们需要先定义布局文件,将View的控件和ViewModel关联。然后定义ViewModel类,在类中添加模型数据和业务逻辑。最后,在Activity或Fragment中实现ViewModel的数据绑定和监听,将数据和页面连接起来。 在Java开发中,我们可以利用一些主流的MVVM框架,例如Google官方提供的Android Architecture Components框架,它包含LiveData、ViewModel、Room和DataBinding四个组件,能够快速搭建MVVM架构,实现组件之间的数据通信和交互。 除此之外,还有Databinding框架,它可以在布局文件中直接绑定数据,省去了一些繁琐的代码,但是需要一定的学习成本。 总而言之,MVVM框架搭建Java项目是非常有利于提高代码可维护性和可读性,通过桥梁连接View与Model,让开发更加高效。不同的情况需要使用不同的框架,开发者可以根据自己的需求进行选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值