Android 天气APP(三十六)运行到本地AS更新项目版本依赖去掉ButterKnife
Posted 初学者-Study
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 天气APP(三十六)运行到本地AS更新项目版本依赖去掉ButterKnife相关的知识,希望对你有一定的参考价值。
运行到本地AS、更新项目版本依赖、去掉ButterKnife
前言
最近发现这个项目好似迎来了第二春,GitHub上的Start和Fork增加的很快,我的猜测是学生在通过这个项目来学习和完成自己的作业。随着android版本的更新,Android Studio的更新,项目中一些内容不能在新版本中很好的使用,甚至出现编译不了的情况,这对我来说没啥,但是对于拿到项目满心欢喜的学生来说,运行不了,无疑是致命的,而我写在项目Readme下面我将演示一下怎么在本地运行这个项目的代码。
正文
在之前写代码的时候还是有一些细节没有做到位,例如代码版本的管理就没有做,导致你看过前几篇文章,跟着写遇到问题,想要看源码,发现源码没有区分章节,而是整体一起的,这无疑给学习的同学造成了麻烦,那么我这篇文章在写之前将之前的代码添加到一个分支中,你学习的话先下载分支的代码,然后跟着写,写完之后就是当前最新的版本代码,这里的版本是2.8。项目地址:GoogWeather
你切换到Release_2.8再去下载就是之前的所有代码,下载代码你肯定会吧。
一、新版Android Studio编译运行
下载代码之后,我们就需要在Android Studio中运行,这里我们可以使用Google官网中最新的AS版本,这里我使用的版本如下图所示:
这里的版本是海豚,也属于比较新的版本了。你现在下载的可能比这个还要新一点,不过也没关系,操作方式是一样的,因为我开始写这个项目的时候是20年4月份,那时候用的是Android Studio3.5.2,和现在的新版本差别还是蛮大的,下面我们通过AS打开这个项目。
打开之后AS会检查你这个项目采用的是什么版本的gradle,如果检查到当前电脑环境没有此项目中的gradle,就会下载,这里要注意网络环境问题,如果一个网络环境不行就切换其他的网络环境,例如换个WIFI,使用手机热点等方式。
下载好之后AS就会根据这个gradle来编译你的项目,注意一点,高版本AS可以编译低版本的项目,而如果低版本AS项目编译gradle版本高于AS本身gradle版本的项目,则需要降低项目版本,或者提高AS版本,这两种方式就不展开讲了。还有一点就是编译的时候和项目的JDK也有关系,例如我之前的项目采用的JDK是1.8,AS中自带了两个1.8和11两个版本,新项目都是采用11的版本。JDK没有问题之后就会下载你的项目所依赖的远程仓库代码,下载之后再进行编译。这个过程可能会比较长,和项目所使用的库多少及当前网速快慢有关。最后一点,项目使用的Android SDK是28,因此会在编译过程中,提示你下载28的SDK,下载之后再编译就好了,目前我编译了20分钟,终于编译完成了。
① 升级项目gradle版本
在编译完成之后,你可能会看到这样一个提示窗口。
在Android Studio的右下角出现,什么情况下会出现呢?当你的项目gradle版本与当前AS的gradle版本不匹配时就会出现,我们点击这个upgraded,出现弹窗。
点击Begin Upgrade ,开始升级项目的gradle版本。
这里点击Run Selected Steps,运行选中步骤。同样会下载gradle版本,这里下载的是6.7.1,下载完会进行编译。这个过程大概也要十几分钟,完成后,再看。
然后通过下拉框,将4.2.2改成7.3.0,再点击Run Selected Steps,这里也需要一点时间。
这里只剩下最后一步了,最后这个是建议步骤,可做可不做。
② 切换JDK版本
做完之后,我们运行一下,然后会遇到一个问题。
我们的项目之前采用1.8,而现在更新了项目的gradle版本,这个版本最低要求是11,所以我们需要更改项目的JDK版本。怎么修改呢?点击File → Settings… → Gradle。
这里会看到Gradle JDK是1.8,点击右边的下拉按钮,我们切换到11
然后点击 Apply 按钮,应用一下刚才的修改,再点击 OK 关闭这个窗口,然后我们再运行一下,这里要用真机运行。因为虚拟机缺少定位传感器,同时SDK不一定兼容虚拟机。
③ BuildConfig报错
运行后发现报错了。
修改的方式很简单,如下图所示,我们补全一下报名就可以了。
下面没有报错了,我们再运行一下:
OK,没有问题,现在能够正常运行了,检查一下布局预览是否能正常显示。布局预览没有问题,那么我们的项目也在新版本的AS上面跑起来了。在你们哪里不一定能定位成功,因为还需要修改一个地方。
二、百度的SDK使用
鉴权失败,这个会导致无法进行定位,下面说一下这个问题是怎么产生和解决的。
这里要注意一个问题,首先你在百度地图开放平台上是否创建了应用,没有创建的话,你就需要去创建。创建应用的方式,在天气App的第一篇文章中已经说明了,先不要着急去看,听我说完,创建应用需要几个条件,发布版SHA1、开发版SHA1、应用包名。
① 开发版SHA1的作用是什么?
开发版SHA1是让你能够在debug运行模式下使用SDK中的功能,注意这个debug运行要在真机上,不要妄想使用虚拟机或者模拟器,问题一大堆,你要是愿意死磕,也可以,鱼死不死不好说,但网一定破。
② 什么时候需要更换开发版SHA1?
开发版SHA1随着你本地的环境改变而改变,就拿我们当前这种情况来说,本地没有项目,下载下来到AS中进行编译,这时就需要更换开发版SHA1的值,如果你之前没有创建百度地图平台应用的话,则就是在创建应用的时候将开发版SHA1填进去。另一种情况,比如之前的项目在文件夹A下,现在你挪到文件夹B下,那么你同样需要再次获取项目的开发版SHA1的值,更新平台上的开发版SHA1值。
③ 怎么获取开发版SHA1?
由于Android Studio版本发生了改变,获取开发版SHA1的获取方式也一样改变了,在你准备获取SHA1版本之后请先配置好Java JDK的环境变量,再来操作。假设你已经配置好了环境变量,下面进行开发版SHA1的获取,Win + R ,输入cmd,进入命令窗口。
cd .android
先切换到.android目录下,然后输入keytool -list -v -keystore debug.keystore。
keytool -list -v -keystore debug.keystore
回车之后会让你输入密钥,默认的密钥就是android,你输入的时候是不可见的,光标也不会有反应,你只管输入就行,输入完回车就能看到SHA1了,如下图所示。
这里的SHA1就是开发版SHA1,然后将SHA1值更新一下再保存。
现在你再运行一般来说就不会出现定位不成功的情况,也不会出现SDK鉴权失败的情况了。
④ 发布版SHA1的作用是什么?
开发版SHA1的作用是让你在电脑本地通过Android Studio调试运行的时候,能够使用正常使用SDK中功能。而一个应用你不可能只有自己在使用吧,你可能会发给别人,这个时候你就需要将项目打包成APK,将apk发布给别人安装,至于怎么打包,我也有文章说明,搜一下就可以了。而获取发布版SHA1,你需要先成功打一个包,然后再进行发布版SHA1的获取,具体的操作获取方式参考第一篇文章,那个大体没有改动,只有最后不用选择v1、v2。
三、依赖库更新
首先我们需要修改app的build.gradle和mvplibrary的build.gradle。
app的build.gradle代码如下:
apply plugin: 'com.android.application'
android
compileSdkVersion 31
defaultConfig
applicationId "com.llw.goodweather"
minSdkVersion 21
targetSdkVersion 31
versionCode 1
versionName "2.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildTypes
release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
compileOptions //指定使用的JDK11
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
// 自定义打包
android.applicationVariants.all variant ->
variant.outputs.all
outputFileName = "GoodWeather$variant.versionName.apk"
sourceSets
main
jniLibs.srcDir 'libs'
//disable automatic ndk-build
namespace 'com.llw.goodweather'
dependencies
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//butterknife 这个部分比较特殊,所以不管是模块还是项目里都要引入依赖,否则你的控件会报空对象
implementation 'com.jakewharton:butterknife:10.2.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
implementation project(':mvplibrary')//引入模块 然后将项目里的依赖移动到模块的build.gradle里
//Bugly SDK
implementation 'com.tencent.bugly:crashreport:3.4.4'
// 友盟基础组件库(所有友盟业务SDK都依赖基础组件库)
implementation "com.umeng.umsdk:common:9.5.2" //(必选)
implementation "com.umeng.umsdk:asms:1.4.1" // asms包依赖(必选)
implementation "com.umeng.umsdk:apm:1.7.0" // U-APM包依赖(必选)
api版本由28更改为31,一些依赖库的版本更新了,有第三方的,也有android自己的库,jdk版本也更新了。
下面是mvplibrary的build.gradle。
apply plugin: 'com.android.library'
android
compileSdkVersion 31
defaultConfig
minSdkVersion 21
targetSdkVersion 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
buildTypes
release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
compileOptions
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
namespace 'com.llw.mvplibrary'
dependencies
implementation fileTree(dir: 'libs', include: ['*.jar'])
api 'androidx.appcompat:appcompat:1.4.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//在模块中添加的依赖若想在项目中使用,则implementation改成api
//butterknife 绑定视图依赖BindView,告别findById,不过你还得安装一个butterknife插件才行
api 'com.jakewharton:butterknife:10.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
//Google Material控件,以及迁移到AndroidX下一些控件的依赖
api 'com.google.android.material:material:1.6.0'//更强
api 'androidx.lifecycle:lifecycle-extensions:2.2.0'
api 'androidx.annotation:annotation:1.3.0'
api 'androidx.legacy:legacy-support-v4:1.0.0'
//RecyclerView最好的适配器,让你的适配器一目了然,告别代码冗余
api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'
//图片加载框架
api 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
//权限请求框架
api 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar'
//状态栏
api 'com.readystatesoftware.systembartint:systembartint:1.0.3'
//支持okhttp
api 'com.squareup.okhttp3:okhttp:3.14.9'
//retrofit2
api 'com.squareup.retrofit2:retrofit:2.9.0'
//这里用api 是为了让其他模块也可以使用gson
api 'com.squareup.retrofit2:converter-gson:2.9.0'
//日志拦截器
api 'com.squareup.okhttp3:logging-interceptor:3.10.0'
api 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
//rxjava
api 'io.reactivex.rxjava2:rxandroid:2.1.1'
api 'io.reactivex.rxjava2:rxjava:2.2.12'
api 'androidx.preference:preference:1.2.0'
//阿里巴巴 FastJson
api 'com.alibaba:fastjson:1.2.57'
//下拉刷新框架
api 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-14'
//没有使用特殊Header,可以不加这行
api 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.0-alpha-14'
//自由嵌套的RadioGroup
api 'com.github.fodroid:XRadioGroup:v1.5'
//EventBus
api 'org.greenrobot:eventbus:3.1.1'
//蠕虫蠕动动画TabLayout
api 'com.ogaclejapan.smarttablayout:library:2.0.0@aar'
//Optional: see how to use the utility.
api 'com.ogaclejapan.smarttablayout:utils-v4:2.0.0@aar'
//Android SQLite操作框架
api 'org.litepal.guolindev:core:3.1.1'
//列表item侧滑删除
api 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.3.0'
//下拉框
api 'com.github.arcadefire:nice-spinner:1.4.3'
① AndroidManifest.xml更新
这里同样更改了一些库的版本,会造成什么问题我们目前尚未可知,有问题再解决就是了,下面Sync Now,然后运行。
这里出现了问题,因为31表示Android 12,在Android 12中android:export需要显式指定元素<activitycom.llw.goodweather.ui.SplashActivity>。针对Android 12及更高版本的应用,当相应的组件定义了意图过滤器时,需要为“Android:exported”指定显式值。意思很明显我们的启动页面,需要增加exported属性,并且值设置为true。在AndroidManifest.xml中找到SplashActivity。
修改后运行一下。
② BaseRecyclerViewAdapterHelper更新
这里使用的是3.0.4版本,相较于之前的改版,改动不到,但由于一个类的路径更改,所以代码中涉及到的地方同样需要更换。
如图所示,我们发现这个com.chad.library.adapter.base包下,已经没有这个BaseViewHolder
了,那么我们重新导包试试看,将之前的那个报错的导报语句删除掉,然后鼠标悬停在BaseViewHolder上面。
使用快捷键Alt + Enter进行导包,导包后会增加一行import代码:
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
我们再对比之前报错的那个导包语句看看
import com.chad.library.adapter.base.BaseViewHolder;
可以看到,区别只是新版本中多了一个viewholder包的层级,那么我们就只需要将新的这个导报语句替换掉报错的导包语句即可,使用全局替换,快捷键Ctrl + Shift + R 。
上面一行是原来的报错导包,下面这一行是我们需要替换的导包语句,有21处需要替换,点击Replace All,替换所有,会弹窗提示你,问你是否确定要这么做,点击Replace。替换完成之后,我们再运行试试看。
发现了一个新的错误,是添加点击事件的方法没有了,因此我们还需要修改这个部分的代码。首先将
helper.addOnClickListener(R.id.item_city);
这行代码注释掉或者删掉,然后我们进入使用适配器监听点击的地方,在MainActivity中,你找到如下图所示的代码:
这个OnItemChildClickListener
接口也没有了,那么我们需要怎么添加子控件的点击事件呢?修改方式如下图所示:
这里可以看到我们通过
areaAdapter.addChildClickViewIds(R.id.item_city);
添加需要进行点击的控件Id,注意这里可以是多个控件Id,例如:
areaAdapter.addChildClickViewIds(R.id.item_city, R.id.item_area);
你会发现new OnItemChildClickListener()是灰色的,这表示可以省略掉,精简代码,使用Lambda表达式,点击灰色部分,使用快捷键,Alt + Enter,回车出现弹窗。
这里点击Relpace with lambda选项,替换为lambda,替换后如下图所示。
这里你可以替换也可以不替换,随你自己。关于适配器添加点击事件的方法你已经学会了,那么你可以更改其他的适配器中的点击方式了,这个就没有快捷的方式了,你需要一个个去改,我就不重复说明了。所有适配器改完之后再运行看看,哦豁,又报错了。
这个报错的原因是mContext值没有了,这是一个上下文,之前的依赖库版本中是有的,现在没有了,就需要替换了,将mContext,getContext()
即可,自行替换就好了。然后再运行你会发现,不报错了,可以运行了,这个库的问题改完了。但是还有一个隐藏问题在WallPaperAdapter类中。
这个getAdapterPosition()方法没有了,这个我们也需要替换,将
helper.getAdapterPosition()
改成
getItemPosition(item)
即可。
好了,这个依赖库我们就改完了,开不开心?来人,奏乐,起舞,老子改了大半天BUG了,还不能享受享受吗?嗯?
四、替换ButterKnife
为什么要替换掉呢?因为在新版Android Studio中你已经不能使用ButterKnife的插件了,ButterKnife的作者也告诉我们不再维护这个库了,推荐我们使用ViewBinding。
① 开启ViewBinding
首先要使用ViewBinding,我们需要先开启它。在app的build.gradle的android闭包中增加如下所示代码:
buildFeatures
viewBinding true //开启ViewBinding
然后Sync Now,下面我们以AboutUsActivity为例来修改一下,使用ViewBinding。
② 创建UiVBCallback
还记得之前我们使用的框架吗?是MVP,现在我们去掉了ButterKnife,所以响应的底层也需要改动,之前我们有一个UiCallBack,所以为了区分,我在base包下新建了一个vb包,表示这是使用ViewBinding所需要的一些类和接口,那么我们在vb包下新建一个UiVBCallback接口,代码如下:
public interface UiVBCallback
default void onRegister()
//初始化savedInstanceState
default void initBeforeView(Bundle savedInstanceState)
//初始化
void initData();
这里三个方法,很简单,前面两个不是必须要实现的,最后一个是必须要实现的,因为我们会涉及到ViewBinding的简单封装,除了第一个onRegister()方法你不清楚,其他的方法你之前应该都见过了,这个onRegister()的作用实际上是为了让你更好的使用Activity Result API,因为我们startActivityResult()过时了。具体的你可以上网看一下,这里就不展开说明了,用到了再说。
③ 创建BaseVBActivity
为了能让所有的Activity都使用ViewBinding,我们创建一个类似于BaseActivity的抽象基类,在com.llw.mvplibrary.base.vb包下新建BaseVBActivity类,代码如下:
public abstract class BaseVBActivity<T extends ViewBinding> extends AppCompatActivity implements UiVBCallback
public T binding;
protected Activity context;
private static final int FAST_CLICK_DELAY_TIME = 500;
private static long lastClickTime;
private Dialog mDialog;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
onRegister();
super.onCreate(savedInstanceState);
initBeforeView(savedInstanceState);
this.context = this;
//添加继承这个BaseVBActivity的Activity
BaseApplication.getActivityManager().addActivity(this);
Type type = this.getClass().getGenericSuperclass();
if (type instanceof ParameterizedType)
try
Class<T> clazz = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
//反射
Method method = clazz.getMethod("inflate", LayoutInflater.class);
binding = (T) method.invoke(null, getLayoutInflater());
catch (Exception e)
e.printStackTrace();
setContentView(binding.getRoot());
initData();
/**
* 弹窗出现
*/
protected void showLoadingDialog()
if (mDialog == null)
mDialog = new Dialog(context, R.style.loading_dialog);
mDialog.setContentView(R.layout.dialog_loading);
mDialog.setCancelable(false);
mDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
mDialog.show();
/**
* 弹窗消失
*/
protected void dismissLoadingDialog()
if (mDialog != null)
mDialog.dismiss();
mDialog = null;
/**
* 返回
*/
protected void Back(Toolbar toolbar)
toolbar.setNavigationOnClickListener(v ->
context.finish();
if (!isFastClick())
context.finish();
);
/**
* 两次点击间隔不能少于500ms
*
* @return flag
*/
protected static boolean isFastClick()
boolean flag = true;
long currentClickTime = System.currentTimeMillis();
if ((currentClickTime - lastClickTime) >= FAST_CLICK_DELAY_TIME)
flag = false;
lastClickTime = currentClickTime;
return flag;
这里的核心思想就是反射。
④ 使用BaseVBActivity
下面我们修改一下AboutUsActivity中的代码,如下所示:
public class AboutUsActivity extends BaseVBActivity<ActivityAboutUsBinding> implements View.OnClickListener
private String updateUrl = null;
private String updateLog = null;
private boolean is_update = false;
/**
* 博客地址
*/
private final String CSDN_BLOG_URL = "https://blog.csdn.net/qq_38436214/category_9880722.html";
/**
* 源码地址
*/
private final String GITHUB_URL = "https://github.com/lilongweidev/GoodWeather";
@Override
public void initData()
Back(binding.toolbar);
//蓝色状态栏
StatusBarUtil.setStatusBarColor(context, R.color.about_bg_color);
//设置文字下划线
binding.tvCopyEmail.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG);
//抗锯齿
binding.tvCopyEmail.getPaint().setAntiAlias(true);
binding.tvVersion.setText(APKVersionInfoUtils.getVerName(context));
AppVersion appVersion = LitePal.find(AppVersion.class, 1);
updateLog = appVersion.getChangelog();
//提示更新
if (!appVersion.getVersionShort().equals(APKVersionInfoUtils.getVerName(context)))
is_update = true;
//显示红点
binding.vRed.setVisibility(View.VISIBLE);
updateUrl = appVersion.getInstall_url();
updateLog = appVersion.getChangelog();
else
//隐藏红点
binding.vRed.setVisibility(View.GONE);
is_update = false;
//添加点击事件
binding.layAppVersion.setOnClickListener(this);
binding.tvBlog.setOnClickListener(this);
binding.tvCode.setOnClickListener(this);
binding.tvCopyEmail.setOnClickListener(this);
binding.tvAuthor.setOnClickListener(this);
@SuppressLint("NonConstantResourceId")
@Override
public void onClick(View v)
switch (v.getId())
case R.id.lay_app_version://版本更新
if (is_updateAndroid 天气APP(三十六)运行到本地AS更新项目版本依赖去掉ButterKnife
Android 天气APP(三十五)修复BUG升级网络请求框架