网络处理
前言
在之前的文章中完成了对首页新闻数据的显示处理,也做了UI的简单优化,现在已经看上去比之前要上档次了一些,不过我们还得继续优化才行。
正文
文本就是对网络进行优化处理,我们得考虑到当进入这个App时没有网络的情况。
主页面中最重要的数据就是这个垃圾分类新闻数据了,而这个数据并不是实时更新的,因为首先没有必要每一次打开App时都请求,这有些浪费资源,毕竟我的每日免费次数只有100次,其次就是当我没有联网时进入主页面也不能说什么也看不到吧,这不合适。所以下面一个一个来解决。
首先是没有网络时得处理。做个处理之前,要先想一个问题是不是每一个页面都要这样处理,如果是的话就可以写一个工具类,这样方便调用。基于此,我就将这个工具类放到mvplibrary模块下的network包下的utils包中。
这个NetworkUtils工具类也是我网上找的一个,代码如下:
package com.llw.mvplibrary.network; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.provider.Settings; /** * 网络检测工具类 */ public class NetworkUtils { /** 网络不可用 */ public static final int NO_NET_WORK = 0; /** 是wifi连接 */ public static final int WIFI = 1; /** 不是wifi连接 */ public static final int NO_WIFI = 2; private NetworkUtils(){ /* cannot be instantiated */ throw new UnsupportedOperationException("cannot be instantiated"); } /** * 判断是否打开网络 * @param context * @return */ public static boolean isNetWorkAvailable(Context context){ boolean isAvailable = false ; ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = cm.getActiveNetworkInfo(); if(networkInfo!=null && networkInfo.isAvailable()){ isAvailable = true; } return isAvailable; } /** * 获取网络类型 * @param context * @return */ public static int getNetWorkType(Context context) { if (!isNetWorkAvailable(context)) { return NetworkUtils.NO_NET_WORK; } ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); // cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); if (cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting()) { return NetworkUtils.WIFI; } else { return NetworkUtils.NO_WIFI; } } /** * 判断当前网络是否为wifi * @param context * @return 如果为wifi返回true;否则返回false */ @SuppressWarnings("static-access") public static boolean isWiFiConnected(Context context){ ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = manager.getActiveNetworkInfo(); return networkInfo.getType() == manager.TYPE_WIFI ? true : false; } /** * 判断MOBILE网络是否可用 * @param context * @return * @throws Exception */ public static boolean isMobileDataEnable(Context context){ ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); boolean isMobileDataEnable = false; isMobileDataEnable = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnectedOrConnecting(); return isMobileDataEnable; } /** * 判断wifi 是否可用 * @param context * @return * @throws Exception */ public static boolean isWifiDataEnable(Context context){ ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); boolean isWifiDataEnable = false; isWifiDataEnable = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting(); return isWifiDataEnable; } /** * 跳转到网络设置页面 * @param activity */ public static void GoSetting(Activity activity){ Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); activity.startActivity(intent); } /** * 打开网络设置界面 */ public static void openSetting(Activity activity) { Intent intent = new Intent("/"); ComponentName cn = new ComponentName("com.android.settings", "com.android.settings.WirelessSettings"); intent.setComponent(cn); intent.setAction("android.intent.action.VIEW"); activity.startActivityForResult(intent, 0); } }
位置如下:
这里还需要一个在mvplibrary模块的AndroidManifest.xml中添加一个权限
<!--访问网络状态--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
添加位置如下
下面打开BaseActivity,在里面写入一个方法:
/** * 检查当前是否打开网络 */ protected boolean hasNetwork() { return (NetworkUtils.isNetWorkAvailable(context)); }
这么写的好处是,只要Activity继承了BaseActivity,那么就可以直接使用这个hasNetwork()方法去判断当前是否有网络。
下面就可以到MainActivity中去使用了。还记得之前是在哪里发起请求新闻数据的吗?
就在initView方法中,通过
mPresenter.getTrashNews(10);
发起网络请求,那么只要判断这里就可以了。
if (hasNetwork()) {//有网络 //请求垃圾分类新闻数据 mPresenter.getTrashNews(10); } else {//无网络 //加载默认数据 }
那么怎么去加载默认的数据呢?首先我们需要拿到默认的数据。
这个我已经拿到了,在Constant类中增加一个常量,代码如下:
/** * 默认新闻数据 无网络时加载 */ public static final String LOCAL_NEWS_DATA ="{\n" + " \"code\": 200,\n" + " \"msg\": \"success\",\n" + " \"newslist\": [\n" + " {\n" + " \"id\": \"a448c7cc172e1156b35fb266f96c49cf\",\n" + " \"ctime\": \"2021-04-17 08:44\",\n" + " \"title\": \"21项行政执法权将下放基层 垃圾不分类等行为将由街道乡镇直接处罚\",\n" + " \"description\": \"来源:北京日报 原标题:21项行政执法权将下放基层垃圾不分类等行为将由街道乡镇直接处罚 今后,餐饮单位向消费者主动提供一次性用品、单位和个人未按规定分类\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd20210417s/233/w676h357/20210417/e0e5-knvsnuf8545017.jpg\",\n" + " \"url\": \"http://news.sina.com.cn/c/2021-04-17/doc-ikmyaawc0158409.shtml\"\n" + " },\n" + " {\n" + " \"id\": \"854fd9faf85e0ebeb588aca038cf5893\",\n" + " \"ctime\": \"2021-04-17 06:42\",\n" + " \"title\": \"践行分类理念 上虞成立生活垃圾分类“百人宣讲团”\",\n" + " \"description\": \"4月16日下午,一支由100名宣讲师组成,来自各乡镇街道、部门单位、社区(行政村)、大学人员组成的上虞区生活垃圾分类讲师团正式成立。讲师团将通过宣传环保理念、普\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd20123/40/w480h360/20210416/e35f-knvsnuf8043448.jpg\",\n" + " \"url\": \"http://k.sina.com.cn/article_7505202169_1bf584bf902000uczm.html\"\n" + " },\n" + " {\n" + " \"id\": \"dde5b1a220c6957e8c60ca9f55ec8403\",\n" + " \"ctime\": \"2021-04-17 07:17\",\n" + " \"title\": \"垃圾不分类由街道乡镇直接处罚!北京21项行政执法权将下放基层\",\n" + " \"description\": \" 原标题:垃圾不分类由街道乡镇直接处罚!北京21项行政执法权将下放基层 今后,餐饮单位向消费者主动提供一次性用品、单位和个人未按规定分类投放生活垃圾、在\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd2021417s/602/w1280h922/20210417/0d27-knvsnuf8495071.jpg\",\n" + " \"url\": \"http://news.sina.com.cn/c/2021-04-17/doc-ikmxzfmk7288291.shtml\"\n" + " },\n" + " {\n" + " \"id\": \"3034adf78105d4a3ca53f1d5f14c53ef\",\n" + " \"ctime\": \"2021-04-17 07:36\",\n" + " \"title\": \"老旧小区垃圾分类咋推进?南京玄武区“公交式”流动收集提供新思路\",\n" + " \"description\": \"中国江苏网讯(记者聂龙妃)在南京市玄武区梅园新村街道的兰园19号楼旁的围墙上,有一张垃圾分类流动运输车时间表:每天早中晚3次收运,每个点位停靠5分钟,流运路线\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd20210417s/233/w676h357/20210417/e0e5-knvsnuf8545017.jpg\",\n" + " \"url\": \"http://k.sina.com.cn/article_5675440730_152485a5a0200134ph.html\"\n" + " },\n" + " {\n" + " \"id\": \"80f5a70563073544de441634f763af70\",\n" + " \"ctime\": \"2021-04-16 23:12\",\n" + " \"title\": \"海口美兰:“小手拉大手”开展垃圾分类宣传\",\n" + " \"description\": \"为引导辖区青少年学生践行垃圾分类,增强环保意识。4月15日,海口美兰区在三江镇开展新时代文明实践垃圾分类主题宣传活动,让垃圾分类以“小手拉大手”的形式走进千家万\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd20123/40/w480h360/20210416/e35f-knvsnuf8043448.jpg\",\n" + " \"url\": \"http://k.sina.com.cn/article_7517400647_1c0126e47059010dn9.html\"\n" + " },\n" + " {\n" + " \"id\": \"c50d760bc5fe5e09144d02914a87f009\",\n" + " \"ctime\": \"2021-04-16 20:41\",\n" + " \"title\": \"评选十佳社区、招募形象大使 济南将举办系列垃圾分类宣传推广活动\",\n" + " \"description\": \"大众网·海报新闻记者张稳通讯员蒋霞济南报道《济南市生活垃圾减量与分类管理条例》将于5月1日正式实施,记者从济南市城管局获悉,为贯彻济南市人大常委会贯彻\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd2021416s/320/w640h480/20210416/bd3f-knvsnuf7747977.png\",\n" + " \"url\": \"http://k.sina.com.cn/article_2620088113_9c2b5f310200137ds.html\"\n" + " },\n" + " {\n" + " \"id\": \"a5fb207c5c46a0ac10e5630201264b53\",\n" + " \"ctime\": \"2021-04-16 20:41\",\n" + " \"title\": \"济南垃圾分类新进展:每300户左右设置1处集中分类投放点\",\n" + " \"description\": \"大众网·海报新闻记者张稳济南报道4月16日上午,济南市召开2021年第一季度城市管理综合考评讲评暨城市品质提升百日攻坚行动推进会。会议部署了垃圾分类有关工\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd2021416s/450/w750h500/20210416/d2c4-knvsnuf7747865.jpg\",\n" + " \"url\": \"http://k.sina.com.cn/article_2620088113_9c2b5f310200137dr.html\"\n" + " },\n" + " {\n" + " \"id\": \"a8291cec82765336a1ca075f23c96fc7\",\n" + " \"ctime\": \"2021-04-16 20:47\",\n" + " \"title\": \"老旧小区空间小垃圾分类亭房难落地,南京玄武这个小区试点“公交化收运”\",\n" + " \"description\": \"扬子晚报网4月16日讯(通讯员诸海路实习生娄渊泽记者张可)南京玄武区梅园街道的兰园片区属于典型的老旧小区,里面有18栋居民楼、500户居民。由于空间有\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd20210416s/300/w720h380/20210416/7b74-knvsnuf7769851.jpg\",\n" + " \"url\": \"http://k.sina.com.cn/article_1653603955_628ffe73020012poo.html\"\n" + " },\n" + " {\n" + " \"id\": \"d8b40fa53a7f4dedeff9abbbd0e71754\",\n" + " \"ctime\": \"2021-04-16 21:00\",\n" + " \"title\": \"垃圾分类举手之劳 循环利用变废为宝(图)\",\n" + " \"description\": \" 大江网/大江新闻客户端讯通讯员刘美菊报道:为深入推进生活垃圾分类工作,倡导绿色生态、健康环保的生活方式,增强社区居民对垃圾分类知识的了解,积极参与到垃圾分\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd2021416s/74/w500h374/20210416/35bb-knvsnuf7798675.png\",\n" + " \"url\": \"http://k.sina.com.cn/article_1767961804_6960f4cc02000swdh.html\"\n" + " },\n" + " {\n" + " \"id\": \"b239d2bfc3265df7985dfd583b19fa3e\",\n" + " \"ctime\": \"2021-04-16 21:01\",\n" + " \"title\": \"南京玄武区开展垃圾流动收运试点 老旧小区垃圾分类问题化难为易\",\n" + " \"description\": \"老旧的院落,狭窄的街巷,这便是玄武区梅园新村街道兰园社区的样貌。作为拥有500多户居民的小区,兰园社区每天都会产生三、四十桶生活垃圾。近两年,垃圾分类作为南京市\",\n" + " \"source\": \"垃圾分类新闻\",\n" + " \"picUrl\": \"http://n.sinaimg.cn/sinakd2021416s/170/w554h416/20210416/7134-knvsnuf7837985.png\",\n" + " \"url\": \"http://k.sina.com.cn/article_5675440730_152485a5a0200134gq.html\"\n" + " }\n" + " ]\n" + "}\n";
这个数据是Json字符串,那么我们可以通过Gson将Json字符串转为实体,就好了。那么就这么来写。
if (hasNetwork()) {//有网络 //请求垃圾分类新闻数据 mPresenter.getTrashNews(10); } else {//无网络 //加载默认数据 TrashNewsResponse response = new Gson().fromJson(Constant.LOCAL_NEWS_DATA, TrashNewsResponse.class); mList.clear(); mList.addAll(response.getNewslist()); mAdapter.notifyDataSetChanged(); }
就是这么的简单。当然这里也不是这样就完了,你还需要在设置一下轮播图的显示,之前我没有设置这个轮播图的背景,那么它就是默认的主题背景色,因为它是放在CollapsingToolbarLayout下的。而现在当我没有网络自然轮播图也不能加载网络图片,此时就会显示你设置的背景图。这里我网上找到了一个还不错。
嗯,然后在activity_main.xml的banner控件中设置。
这样就好了。下面你可以运行一下了。
嗯,效果还是比较明显的。
下面还差一步,那就是对于每日一次的处理判断处理。
逻辑就是在有网络的情况下判断是否为当天第一次请求,是的话则请求数据,然后保存数据到数据库,不是的话就从数据库中取数据。
打开mvplibrary下的build.gradle,添加如下依赖:
//Android SQLite操作框架 api 'org.litepal.guolindev:core:3.1.1'
然后Sync Now。
下面在app模块下的assets新建一个litepal.xml文件,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?> <litepal> <!--数据库名称--> <dbname value="GoodTrash" /> <!--数据库版本--> <version value="1" /> <!--映射模型--> <list> <!--新闻表--> <mapping class="com.llw.goodtrash.model.News" /> </list> </litepal>
这里对应的是一个新闻表News,在model包下建一个News类,里面的代码如下:
package com.llw.goodtrash.model; import org.litepal.crud.LitePalSupport; /** * 新闻表 * @author llw */ public class News extends LitePalSupport { private String ctime; private String title; private String description; private String source; private String picUrl; private String url; public String getCtime() { return ctime; } public void setCtime(String ctime) { this.ctime = ctime; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public String getPicUrl() { return picUrl; } public void setPicUrl(String picUrl) { this.picUrl = picUrl; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
你会看到这个类继承了LitePalSupport,这是框架里面的技术支持,继承之后,就可以直接对这个类进行数据的
现在还不能使用这个数据库的,你还需要在程序运行的时候进行数据库的创建和初始化,这根代码就写在TrashApplication的onCreate方法中。如下图所示:
下面就可以操作这个数据表了,为了方便使用,这里在utils包下写一个NewsHelper工具类,里面的代码如下:
package com.llw.goodtrash.utils; import android.util.Log; import com.google.gson.Gson; import com.llw.goodtrash.model.News; import com.llw.goodtrash.model.TrashNewsResponse; import org.litepal.LitePal; import java.util.ArrayList; import java.util.List; /** * 新闻表帮助类 * * @author llw */ public class NewsHelper { private static final String TAG = "NewsHelper"; /** * 保存新闻数据 * * @param newsList * @return */ public static boolean saveNews(List<TrashNewsResponse.NewslistBean> newsList) { LitePal.deleteAll(News.class); boolean result = false; for (TrashNewsResponse.NewslistBean bean : newsList) { News news = new News(); news.setTitle(bean.getTitle()); news.setDescription(bean.getDescription()); news.setCtime(bean.getCtime()); news.setPicUrl(bean.getPicUrl()); news.setSource(bean.getSource()); news.setUrl(bean.getUrl()); news.save(); if (news.save()) { result = true; Log.d(TAG, "保存新闻数据成功"); } else { result = false; Log.d(TAG, "保存新闻数据失败"); } } Log.d(TAG, "保存的数据;" + new Gson().toJson(LitePal.findAll(News.class))); return result; } /** * 查询新闻表中所有新闻数据 * * @return List<TrashNewsResponse.NewslistBean> */ public static List<TrashNewsResponse.NewslistBean> queryAllNews() { List<TrashNewsResponse.NewslistBean> newsList = new ArrayList<>(); List<News> news = LitePal.findAll(News.class); Log.d(TAG, String.valueOf(news.size())); if (news != null && news.size() > 0) { for (int i = 0; i < news.size(); i++) { TrashNewsResponse.NewslistBean newslistBean = new TrashNewsResponse.NewslistBean(); newslistBean.setCtime(news.get(i).getCtime()); newslistBean.setDescription(news.get(i).getDescription()); newslistBean.setPicUrl(news.get(i).getPicUrl()); newslistBean.setSource(news.get(i).getSource()); newslistBean.setTitle(news.get(i).getTitle()); newslistBean.setUrl(news.get(i).getUrl()); newsList.add(newslistBean); } } return newsList; } }
也就是查询和保存的代码而已,很简单。下面去MainActivity中去使用它们,首先是当天第一次进入App时有网络的情况下,保存返回的数据。
就在getTrashNewsResponse方法中。如下图所示:
下面再去看后续进入App的数据操作。
首先要判断是不是第一次启动,这里也有一个工具类,再写这个类之前,先在Constant中定义。
/** * App首次启动 */ public static final String APP_FIRST_START = "appFirstStart"; /** * 今日启动APP的时间 */ public static final String START_UP_APP_TIME = "startAppTime";
然后在utils包下新建一个AppStartUpUtils类,里面的代码如下:
package com.llw.goodtrash.utils; import android.content.Context; import java.text.SimpleDateFormat; import java.util.Date; /** * APP启动判断工具类 * * @author llw */ public class AppStartUpUtils { /** * 判断是否是首次启动 * * @param context * @return */ public static boolean isFirstStartApp(Context context) { Boolean isFirst = SPUtils.getBoolean(Constant.APP_FIRST_START, true, context); // 第一次 if (isFirst) { SPUtils.putBoolean(Constant.APP_FIRST_START, false, context); return true; } else { return false; } } /** * 判断是否是今日首次启动APP * * @param context * @return */ public static boolean isTodayFirstStartApp(Context context) { String saveDate = SPUtils.getString(Constant.START_UP_APP_TIME, "2020-08-27", context); String todayDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); //第一次打开 if (!saveDate.equals(todayDate)) { SPUtils.putString(Constant.START_UP_APP_TIME, todayDate, context); return true; } else { return false; } } }
然后回到MainActivity,修改代码后如下图所示:
当然这个运行效果是一样的,只不过获取数据的渠道不同而已,这在实际开发中也是常用的方式。
然后这个主页面的网络处理就完成了,还有三个页面。这是三个页面就比较的简单了,下面先来看看TextInputActivity页面。
很简单吧。下面是ImageInputActivity。
然后是NewsDetailsActivity。
我是不是还漏掉了一个页面呢?你会说是不是还有语音输入页面没有做网络判断处理,这个页面不用做了,讯飞SDK中有对网络的检查,没有网络是无法进行语音输入的,自然也不可能会有这个网络请求的产生了。