前语

  最近发现这个项目好似迎来了第二春,GitHub上的Start和Fork增加的很快,我的猜测是学生在经过这个项目来学习和完结自己的作业。跟着Android版别的更新,Android Studio的更新,项目中一些内容不能在新版别中很好的运用,乃至呈现编译不了的状况,这对我来说没啥,但是关于拿到项目满心欢喜的学生来说,运转不了,无疑是丧命的,而我写在项目Readme下面我将演示一下怎样在本地运转这个项目的代码。

正文

  在之前写代码的时分还是有一些细节没有做到位,例如代码版别的办理就没有做,导致你看过前几篇文章,跟着写遇到问题,想要看源码,发现源码没有差异章节,而是全体一同的,这无疑给学习的同学造成了费事,那么我这篇文章在写之前将之前的代码增加到一个分支中,你学习的话先下载分支的代码,然后跟着写,写完之后便是当时最新的版别代码,这儿的版别是2.8。项目地址:GoogWeather

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

你切换到Release_2.8再去下载便是之前的一切代码,下载代码你必定会吧。

一、新版Android Studio编译运转

  下载代码之后,咱们就需求在Android Studio中运转,这儿咱们能够运用Google官网中最新的AS版别,这儿我运用的版别如下图所示:

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  这儿的版别是海豚,也归于比较新的版别了。你现在下载的或许比这个还要新一点,不过也没联系,操作办法是相同的,由于我开端写这个项目的时分是20年4月份,那时分用的是Android Studio3.5.2,和现在的新版别差别还是蛮大的,下面咱们经过AS翻开这个项目。

  翻开之后AS会查看你这个项目采用的是什么版别的gradle,假如查看到当时电脑环境没有此项目中的gradle,就会下载,这儿要留意网络环境问题,假如一个网络环境不行就切换其他的网络环境,例如换个WIFI,运用手机热点等办法。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  下载好之后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 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

在Android Studio的右下角呈现,什么状况下会呈现呢?当你的项目gradle版别与当时AS的gradle版别不匹配时就会呈现,咱们点击这个upgraded,呈现弹窗。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

点击Begin Upgrade ,开端升级项目的gradle版别。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

这儿点击Run Selected Steps,运转选中进程。相同会下载gradle版别,这儿下载的是6.7.1,下载完会进行编译。这个进程大约也要十几分钟,完结后,再看。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife
然后经过下拉框,将4.2.2改成7.3.0,再点击Run Selected Steps,这儿也需求一点时间。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

这儿只剩下最终一步了,最终这个是主张进程,可做可不做。

② 切换JDK版别

做完之后,咱们运转一下,然后会遇到一个问题。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

咱们的项目之前采用1.8,而现在更新了项目的gradle版别,这个版别最低要求是11,所以咱们需求更改项目的JDK版别。怎样修正呢?点击File → Settings… → Gradle。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

这儿会看到Gradle JDK是1.8,点击右边的下拉按钮,咱们切换到11

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

然后点击 Apply 按钮,运用一下方才的修正,再点击 OK 封闭这个窗口,然后咱们再运转一下,这儿要用真机运转。由于虚拟机缺少定位传感器,一同SDK纷歧定兼容虚拟机。

③ BuildConfig报错

运转后发现报错了。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

修正的办法很简略,如下图所示,咱们补全一下报名就能够了。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

下面没有报错了,咱们再运转一下:

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

OK,没有问题,现在能够正常运转了,查看一下布局预览是否能正常显现。布局预览没有问题,那么咱们的项目也在新版别的AS上面跑起来了。在你们哪里纷歧定能定位成功,由于还需求修正一个当地。

二、百度的SDK运用

  鉴权失利,这个会导致无法进行定位,下面说一下这个问题是怎样产生和处理的。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  这儿要留意一个问题,首要你在百度地图开放渠道上是否创立了运用,没有创立的话,你就需求去创立。创立运用的办法,在天气App的榜首篇文章中现已说明晰,先不要着急去看,听我说完,创立运用需求几个条件,发布版SHA1、开发版SHA1、运用包名。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

① 开发版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了,如下图所示。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife
这儿的SHA1便是开发版SHA1,然后将SHA1值更新一下再保存。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife
现在你再运转一般来说就不会呈现定位不成功的状况,也不会呈现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,然后运转。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  这儿呈现了问题,由于31表明Android 12,在Android 12中android:export需求显式指定元素<activitycom.llw.goodweather.ui.SplashActivity>。针对Android 12及更高版别的运用,当相应的组件定义了目的过滤器时,需求为“Android:exported”指定显式值。意思很明显咱们的发动页面,需求增加exported属性,并且值设置为true。在AndroidManifest.xml中找到SplashActivity。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

修正后运转一下。

② BaseRecyclerViewAdapterHelper更新

  这儿运用的是3.0.4版别,相较于之前的改版,改动不到,但由于一个类的途径更改,所以代码中涉及到的当地相同需求替换。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  如图所示,咱们发现这个com.chad.library.adapter.base包下,现已没有这个BaseViewHolder了,那么咱们重新导包试试看,将之前的那个报错的导报句子删去掉,然后鼠标悬停在BaseViewHolder上面。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

运用方便键Alt + Enter进行导包,导包后会增加一行import代码:

import com.chad.library.adapter.base.viewholder.BaseViewHolder;

咱们再比照之前报错的那个导包句子看看

import com.chad.library.adapter.base.BaseViewHolder;

  能够看到,差异只是新版别中多了一个viewholder包的层级,那么咱们就只需求将新的这个导报句子替换掉报错的导包句子即可,运用全局替换,方便键Ctrl + Shift + R 。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  上面一行是本来的报错导包,下面这一行是咱们需求替换的导包句子,有21处需求替换,点击Replace All,替换一切,会弹窗提示你,问你是否确定要这么做,点击Replace。替换完结之后,咱们再运转试试看。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  发现了一个新的过错,是增加点击工作的办法没有了,因而咱们还需求修正这个部分的代码。首要将

helper.addOnClickListener(R.id.item_city);

这行代码注释掉或许删掉,然后咱们进入运用适配器监听点击的当地,在MainActivity中,你找到如下图所示的代码:

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  这个OnItemChildClickListener接口也没有了,那么咱们需求怎样增加子控件的点击工作呢?修正办法如下图所示:

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

这儿能够看到咱们经过

areaAdapter.addChildClickViewIds(R.id.item_city);

增加需求进行点击的控件Id,留意这儿能够是多个控件Id,例如:

areaAdapter.addChildClickViewIds(R.id.item_city, R.id.item_area);

  你会发现new OnItemChildClickListener()是灰色的,这表明能够省掉掉,精简代码,运用Lambda表达式,点击灰色部分,运用方便键,Alt + Enter,回车呈现弹窗。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

这儿点击Relpace with lambda选项,替换为lambda,替换后如下图所示。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  这儿你能够替换也能够不替换,随你自己。关于适配器增加点击工作的办法你现已学会了,那么你能够更改其他的适配器中的点击办法了,这个就没有方便的办法了,你需求一个个去改,我就不重复说明晰。一切适配器改完之后再运转看看,哦豁,又报错了。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

  这个报错的原因是mContext值没有了,这是一个上下文,之前的依靠库版别中是有的,现在没有了,就需求替换了,将mContextgetContext()即可,自行替换就好了。然后再运转你会发现,不报错了,能够运转了,这个库的问题改完了。但是还有一个躲藏问题在WallPaperAdapter类中。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

这个getAdapterPosition()办法没有了,这个咱们也需求替换,将

helper.getAdapterPosition()

改成

getItemPosition(item)

即可。

好了,这个依靠库咱们就改完了,开不高兴?来人,奏乐,起舞,老子改了大半天BUG了,还不能享用享用吗?嗯?

四、替换ButterKnife

  为什么要替换掉呢?由于在新版Android Studio中你现已不能运用ButterKnife的插件了,ButterKnife的作者也告知咱们不再维护这个库了,推荐咱们运用ViewBinding。

① 敞开ViewBinding

  首要要运用ViewBinding,咱们需求先敞开它。在app的build.gradle的android{}闭包中增加如下所示代码:

	buildFeatures {
        viewBinding true    //敞开ViewBinding
    }

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

然后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_update) {
                    showUpdateAppDialog(updateUrl, updateLog);
                } else {
                    ToastUtils.showShortToast(context, "当时已是最新版别");
                }
                break;
            case R.id.tv_blog://博客地址
                jumpUrl(CSDN_BLOG_URL);
                break;
            case R.id.tv_code://源码地址
                jumpUrl(GITHUB_URL);
                break;
            case R.id.tv_copy_email://仿制邮箱
                ClipboardManager myClipboard = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
                ClipData myClip = ClipData.newPlainText("text", "lonelyholiday@qq.com");
                myClipboard.setPrimaryClip(myClip);
                ToastUtils.showShortToast(context, "邮箱已仿制");
                break;
            case R.id.tv_author://作者
                ToastUtils.showShortToast(context, "你为啥关键我呢?");
                break;
            default:
                ToastUtils.showShortToast(context, "点你咋的!");
                break;
        }
    }
    /**
     * 跳转URL
     *
     * @param url 地址
     */
    private void jumpUrl(String url) {
        if (url != null) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
        } else {
            ToastUtils.showShortToast(context, "未找到相关地址");
        }
    }
    AlertDialog updateAppDialog = null;
    /**
     * 更新弹窗
     *
     * @param downloadUrl 下载地址
     * @param updateLog   更新内容
     */
    private void showUpdateAppDialog(String downloadUrl, String updateLog) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context)
                .addDefaultAnimation()//默许弹窗动画
                .setCancelable(true)
                .setText(R.id.tv_update_info, updateLog)
                .setContentView(R.layout.dialog_update_app_tip)//载入布局文件
                .setWidthAndHeight(SizeUtils.dp2px(context, 270), ViewGroup.LayoutParams.WRAP_CONTENT)//设置弹窗宽高
                .setOnClickListener(R.id.tv_cancel, v -> {//取消
                    updateAppDialog.dismiss();
                }).setOnClickListener(R.id.tv_fast_update, v -> {//立即更新
                    ToastUtils.showShortToast(context, "正在后台下载,下载后会自动装置");
                    downloadApk(downloadUrl);
                    updateAppDialog.dismiss();
                });
        updateAppDialog = builder.create();
        updateAppDialog.show();
    }
    /**
     * 清除APK
     */
    public static File clearApk(String apkName) {
        File apkFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), apkName);
        if (apkFile.exists()) {
            apkFile.delete();
        }
        return apkFile;
    }
    /**
     * 下载APK
     */
    private void downloadApk(String downloadUrl) {
        clearApk("GoodWeather.apk");
        //下载办理器 获取体系下载服务
        DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
        //设置运转运用的网络类型,移动网络或许Wifi都能够
        request.setAllowedNetworkTypes(request.NETWORK_MOBILE | request.NETWORK_WIFI);
        //设置是否允许漫游
        request.setAllowedOverRoaming(true);
        //设置文件类型
        MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
        String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(downloadUrl));
        request.setMimeType(mimeString);
        //设置下载时或许下载完结时,告诉栏是否显现
        request.setNotificationVisibility(request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setTitle("下载新版别");
        request.setVisibleInDownloadsUi(true);//下载UI
        //sdcard目录下的download文件夹
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "GoodWeather.apk");
        //将下载请求放入队列
        downloadManager.enqueue(request);
    }
}

  在这儿面我将关于ButterKnife的注解都删去掉了,findViewBinding经过ViewBinding的编译时技术在编译进程中就给咱们生成了findViewById的代码了,这个代码就在ActivityAboutUsBinding类中,这个类是编译时生成了,只需你是layout下的xml,在敞开了ViewBinding之后都会生成对应的类文件,文件命名规矩便是布局名称 驼峰 + Binding,例如布局名称为activity_main.xml,生成的文件便是ActivityMainBinding,掌握这个规则就好了,文件中心就这么多,控件实例化了,但是咱们还是需求自己写控件点击的代码,还是比较简略的吧。这个封装的中心思路便是经过编译时生成类,类继承自ViewBinding,经过泛型能够传入任何继承自ViewBinding的类,传入的类经过反射拿到详细的类,再经过类拿到里边的办法,经过办法拿到实例化之后的binding,最终setContentView,将binding.getRoot()设置进去即可,留意一点反射会比不反射在编译的时分耗时一些,子类能够直接拿到父类的binding,经过binding得到xml中的控件id,你会看到很多的binding.xxx,相信你应该能够了解了,不了解也没有联系,你先用,用的多了就了解了,一同我去掉了getLayoutId()。

⑤ 创立MvpVBActivity

  现在基本的Activity能够运用ViewBinding了,下面是用到MVP的Activity,这儿咱们依然保留之前的办法,所以咱们需求创立一个能够运用ViewBinding的MVPActivity,在mvp包下新建MvpVBActivity,代码如下:

public abstract class MvpVBActivity<T extends ViewBinding, P extends BasePresenter> extends BaseVBActivity<T> {
    protected P mPresent;
    @Override
    public void initBeforeView(Bundle savedInstanceState) {
        mPresent = createPresent();
        mPresent.attach((BaseView) this);
    }
    protected abstract P createPresent();
    @Override
    public void onDestroy() {
        super.onDestroy();
        mPresent.detach((BaseView) this);
    }
}

全体上来说,改动不是很大,不过这种办法你能够多加了解一下。

好了,下面咱们来运用一下MvpVBActivity。

⑥ 运用MvpVBActivity

  相同咱们需求一个例子来运用这个MvpVBActivity,那么就来改造一下WallPaperActivity吧。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife
  这儿咱们在泛型里边增加了一个ActivityWallPaperBinding,然后修正一下initData办法。

	@Override
    public void initData() {
        //加载弹窗
        showLoadingDialog();
        //高亮状态栏
        StatusBarUtil.StatusBarLightMode(this);
        //左上角的回来
        Back(binding.toolbar);
        initWallPaperList();
        binding.fabSetting.setOnClickListener(v -> {
            binding.fabSetting.hide();
            int type = SPUtils.getInt(Constant.WALLPAPER_TYPE, 4, context);
            showSettingDialog(type);
        });
    }

  由于只需一个点击工作,我就直接写在这儿面了,本来的点击注解记住删去掉哦,运用便是这么的简略,你学会了吗?后续的其他Activity自己去修正,不会也没有联系,源码就在哪里,你拿去比照看一下就能够了,记住一个工作,假如你的电脑环境,版别,代码、运转环境和作者相同,SDK装备没有问题,那么你就必定没有问题,那么假如有问题,怎样办,找到问题的发生原因,或许一开端你还不够熟练,但是处理问题比制作问题更能让一个人成长,实在处理不了再去求助,才会有醍醐灌顶的效果,遇到问题先自己想办法处理,这儿说一个标准吧,遇到问题之后,百度上查找,前面七页成果你都看一遍,百分之八十的问题就能处理,耐心很重要,很重要,千万不要浮躁,牢记、牢记。

⑦ 去掉ButterKnife依靠

  现在咱们现已不需求ButterKnife了,能够去掉了,有五处当地,榜首个当地,在工程的build.gradle中去掉butterknife的插件。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

第二个当地,在app模块的build.gradle中去掉butterknife的依靠库和注解处理器库。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

第三个当地,在mvplibrary模块的build.gradle中butterknife的依靠库和注解处理器库。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife
现在关于gradle中的改动就完结了,Sync Now,然后运转一下,看看App中是否哪里有报错,查看是否还有别的类运用了依靠库中的注解和办法。不出意外的话。

第五处,你的BaseActivity会报错,由于现在现已有了BaseVBActivity了,所以BaseActivity能够删掉,或许你把BaseActivity里边的关于butterknife的信息删掉也能够,留意BaseFragment中的相关信息也要删掉。

第四处,删掉KnifeKit,连同kit包一同删掉。

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

好了,你能够再次运转有残留包名的就删掉,直到你能够正常运转到手机上即可。

五、保存图片的适配

  之前的版别是根据Android10做的,而现在目标版别是Android12,因而保存图片的途径需求改动一下,也很简略。依据版别修正一下途径的值就能够了,翻开ImageActivity,找到saveImageToGallery()办法,修正代码如下图所示:

Android 天气APP(三十六)运行到本地AS、更新项目版本依赖、去掉ButterKnife

然后你就能够在Android10以上的版别下载图片成功了。

六、源码

时隔一年多再次更新,老来俏啊~

源码地址:GoodWeather 欢迎 Star 和 Fork

本文正在参加「金石计划 . 分割6万现金大奖」