通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

1 概述

作者:舍长III
原文链接: https://www.jianshu.com/p/cf6ce2139d94

为什么会有这篇文章呢,是因为我发现我以前在学习开发知识时,代码整理得太不好了。经常是会发生以前学习过的知识,现在却找不到代码在哪里的情况,于是又要重新开始。

因为Android开发涉及的内容很多,如果想要系统的去整理笔记的话,就必须要分文件夹,分项目进行管理。每个项目对应着不同方向的知识,而我们很多时候,每个练习项目都有着很多需要重复配置的东西。

比如常用的依赖啊,工具类,分包啊,还有基本的测试按钮,比如你想学习数据库操作,可能就会在xml布局中书写4个Button,再写上id,点击事件…基本上这些弄完,10分钟过去了。

而我接下来分享的小技巧,可能让你把原本半小时才能做完的事情,在5分钟内完成,因为我们会编写一个练习项目的模板,把重复的东西抽取出来。

文章不仅仅是为了介绍如何节约时间,更是因为在项目开发中经常要对各种Api进行封装处理,这也算是一个封装示例了。

项目采用Java语言,同时提供kotlin语言版本作为小彩蛋,需要说明的是,项目的开发环境是3.1.1,在3.0以下的软件运行起来需要调整,具体怎么调整我不告诉你,因为推荐升级软件。

项目源代码已经上传的Github仓库的template文件夹内了
https://github.com/13531982270/Blog4

现在,我们创建我们的template项目

2 创建项目结构

我们创建两个module,一个是默认的app,一个是appk,一个库common

  • app用来编写Java测试代码
  • appk用来编写kotlin测试代码
  • common库用于存放一些公共的依赖,工具类
    通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」
    当然,这里的app和appk并不要求百分百严格区分Java和kotlin代码,看实际情况,比如你比较多的时间是在使用Java,那么就在app里面写,偶尔也可以在里面写些kotlin,这都无所谓。

现在打开右上角的File-Prokect Structure,我们把common库的依赖添加到app和appk中,这样两个module就能使用Common里面的东西啦。

3 App运行库

完成分包,配置主题,创建Menu菜单,drawable-hdpi,color,assets文件夹操作

1)创建项目的包结构

我们在module下创建以下包
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

然后,我来具体解释一下每个包的含义(其实看名字也挺清晰的了)

  • adapter 适配器包,用来存放RecyclerView等的适配器
  • app
    创建自定义Application,因为后面可能配置很多第三方,而第三方的初始化建议是每个第三方单独的创建一个单例模式的类,提供一个init方法去初始化,不然全都写在Application里面,Application代码会太多,太凌乱,后面会提供一个AppConfig类作为示范,可以使用插件去生成单例模式的类,参考我的另一篇文章
    https://www.jianshu.com/p/6a3b0ae4aeb4
  • module
    业务包,用来存放我们的各种实际练习代码,这里视你的项目是用来做什么来定,假如你是实际开发,就是一个登录包,一个主页包,如图,假如该项目你是用来学习Android原生控件,那么下面就是imageview,textview这样子;
  • 还有一个refrence包,这是用来存放你的练习草稿的,怎么说呢,你在练习的时候如果有什么需要单独提取出来测试的,你都可以在里面创建一个Activity,那个draft.txt也很好理解,就是草稿,你可以把在网上复制的代码先放到里面,后面写时可以去参考;
  • net包用来存放网络请求相关的类;
  • util包用来存放工具类;
  • view包用来存放用到的自定义控件,可能你会有疑问了,实际开发中,view包不是都是放置在Common通用库里面的吗?是的,因为我们实际开发可能会使用组件化,多Module的开发,把view提取到Common通用库确确实实是为了让多个module来使用,但是这里我们只是练习模板项目,如果还要使用组件化那这个模板项目也就太复杂了,不符合我们当初节约时间的初衷,所以这里直接是,哪个Module的view就放在哪个module。

你可能还会有疑问,你App的包分得都那么详细了,那么还要Common通用库来做什么呢?

我是这么打算的,像下面举例子的L工具类,Glide工具类封装,都属于项目通用,必备的工具类,那就放在Common库,这样后面我们App模块的类不会太多。

而像一些只有在特定业务场景下才会使用达到的util工具类,你就直接放在App模块的util包下就可以了,毕竟工具类实在是太多了。我们不可能将每种都先考虑好放在Common库,而如果不在App下也设置一个util包,特定业务场景的工具类需要在Common库来回跳转就太麻烦了

比如下面这篇教程 https://www.jianshu.com/p/8b7186624ea5 中修改底部导航切换效果的工具类BottomNavigationViewHelper ,Common库还有的作用就是把经常用到的依赖,不至于使Module的代码太多。

2)配置主题和颜色

以前自己学习东西创建项目时,很喜欢把主题改成没有标题的,然后再到创建的Activity里面去创建几个Button,写点击事件去测试,这是多浪费时间的事情啊!

其实我们可以使用默认的主题来解决这个问题(为什么能解决后面会说),其实默认的主题并不丑,丑的是它的颜色,所以我们到res-values包下的colors文件,修改一下它的配色

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
</resources>

运行程序
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

或者更加青春一点的蓝色

<color name="colorAccent">#cc0094ff</color>
<color name="colorPrimary">#33aaff</color>
<color name="colorPrimaryDark">#2299ee</color>

通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」
你看,这样就好看多了吧

3)创建Menu文件,实现点击事件

上面已经解决了标题栏丑的问题,可能你还会觉得,默认的带标题的主题,未免有点占用我们的屏幕。是的,但是它的标题,却带给我们一个平时用的少,但是测试起来用的爽的东西,Menu
我们在res包下创建Menu文件夹,再创建menu.xml文件.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/one"
        android:title="one" />

    <item
        android:id="@+id/two"
        android:title="two" />

    <item
        android:id="@+id/three"
        android:title="three" />

    <item
        android:id="@+id/four"
        android:title="four" />

    <item
        android:id="@+id/five"
        android:title="five" />

    <item
        android:id="@+id/six"
        android:title="six" />
    <item
        android:id="@+id/seven"
        android:title="seven" />

    <item
        android:id="@+id/eight"
        android:title="eight" />
</menu>

8个按钮,足够你一个Activity测试了吧,毕竟比如练习数据库操作也就是增删改查,再多的代码也不适合放在同一个Activity里了,后面我们希望看到的,就是点击右边的…,就可以弹出我们的Menu测试按钮
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

之后我们在module-refrence包下创建JavaActiivty,将Activity作为第一启动项,编写以下代码来实现菜单的点击事件。

public class JavaActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_java);
        setTitle("tonjies的页面");//设置标题名称
    }


    //创建菜单
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return true;
    }

    //菜单选项
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int i = item.getItemId();
        switch (i) {
            //
            case R.id.one:
                Toast.makeText(this, "one", Toast.LENGTH_SHORT).show();
                break;
            //
            case R.id.two:
                Toast.makeText(this, "two", Toast.LENGTH_SHORT).show();
                break;
            //
            case R.id.three:
                Toast.makeText(this, "three", Toast.LENGTH_SHORT).show();
                break;
            //
            case R.id.four:
                Toast.makeText(this, "four", Toast.LENGTH_SHORT).show();
                break;
            //
            case R.id.five:
                Toast.makeText(this, "five", Toast.LENGTH_SHORT).show();
                break;
            //
            case R.id.six:
                Toast.makeText(this, "six", Toast.LENGTH_SHORT).show();
                break;
            //
            case R.id.seven:
                Toast.makeText(this, "seven", Toast.LENGTH_SHORT).show();
                break;
            //
            case R.id.eight:
                Toast.makeText(this, "eight", Toast.LENGTH_SHORT).show();
                break;

        }
        return true;
    }
}

运行效果如***意到我们onCreate方法里面的 setTitle(“tonjies的页面”);

如果我们不设置特定的标题,默认的标题名称就全都是项目名称,这样所有的页面标题都是一样,不利于区分。所以我们可以通过setTitle设置标题来区分是哪一个界面

通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

ok,我们已经完美的完成了,可能你会觉得,这样代码量也不少啊,是的,但是它在所有Activity里面的代码都是一样的啊!

我们需要在哪个Activity用,就直接复制粘贴就可以了,其实也不推荐复制粘贴的方法,我们有更高效的,使用软件自己的代码模板功能,接下来演示一下该方法

通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

代码模板具体的制作方法可以查看慕课网的教程的第三章,第二节
https://www.imooc.com/learn/924

不过实际上我们一般也用不了8个测试按钮这么多,我们快捷键一般设置4个就够了,当同时也把8个按钮的设置了,以防万一,比图4个按钮的快捷键是btnMenu,8个按钮的快捷键是btnMenu8这样。

另外如果你是使用Java代码开发,强烈推荐添加一个ButterKnife来节约我们findId控件的时间。

4)创建drawable-hdpi,color,asstes文件夹

可以将一些大图放置到比较高分辨率的文件夹下,因为放置在该文件夹里面的图片,多数情况下,如果屏幕分辨率较低,系统会帮助你进行一些压缩处理,避免当你不使用图片加载框架的时候,ImageView有可能因为图片过大而导致内存溢出,程序崩溃。

还有一个原因就是保持代码的整洁,我通常是.xml结尾的,例如选择器,使用shape制作的图片,vector图片就放置在drawable,而其它能直接看到的格式的,如,png,jpg,就会放置在drawable-hdpi。

通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」
我们可以事先防止几张图片在模板里,以备测试的不时之需。
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

有时候需要用网络图片来测试,所以也可以在Common的string.xml文件夹下添加几条网络图片链接

<resources>
    <string name="app_name">template</string>
    <string name="meizi_01">https://ww1.sinaimg.cn/large/0065oQSqly1fu7xueh1gbj30hs0uwtgb.jpg</string>
</resources>

而上面的颜色选择color包可能会在某些场景下用,比如使用BottomNavigationView的时候。

最后是我们的assets文件夹,用来存放如字体,本地调试html页面等,在源码里的Refrence包下会有调用字体文件的参考代码。

4 Common通用库

该库用来存放我们练习中几乎必备的工具类和相关的依赖,我们把这些东西提取在Common包里面,让app去添加它,这样app就不用显示过多的依赖,显得比较简洁,而且我们的appk同样可以使用它。

1)Log工具类

我们在common库中创建util文件夹,然后创建L日志打印工具类,简化我们后面的日志操作:

/**
 * Created by 舍长 on 2018/12/14
 * describe: 日志工具类
 */
public class L {
    public void d(String msg) {
        Log.d("helloWorld", msg);
    }
}

后面就可以直接L.d使用啦。

不过实际项目开发中要再加一个过滤操作,因为我们的App上线之后,如果Log不关闭,有泄漏数据的风险,涉及到安全问题。所以我们可以这样做。

public class L {

    private static final Boolean isOpen=true;

    //普通版
    public static void d(String msg) {
        //打开日志
        if(isOpen){
            Log.d("helloWorld", msg);
        }else {
            //关闭日志
        }
    }

    //进行String类型转换,所以我们可以随意传入任意类型的参数
    public static void d(Object msg) {
        String string = msg.toString();
        Log.d("helloWorld", string);
    }
}

设置一个变量,如何我们想要打印的话,就设置为true,不想打印的话,就将它设置为Fasle就可以啦,我们还重载了一个参数Object的d方法,该方法的作用是将传入的任意类型的字符串转换成String类型
这样做是为了方便我们可以传入任何类型的数据,这样我们就可以省去将数据转成String类型这一步了,

相信这难不倒你啦,接着我们来看第二个封装类,SharedPreferences封装类。

2)SharedPreferences封装类ACache

这里我们需要考虑一个问题,就是SharedPreferences和Log打印不一样,它是需要获取到当前的Context对象的,如下面所示在Activity中使用的getSharedPreferences,其实它隐藏了context的使用,实际代码是context.getSharedPreferences。

存数据

SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","tonjies");
editor.putInt("age",20);
editor.apply();

取数据

SharedPreferences preferences = getSharedPreferences("data", MODE_PRIVATE);
String name = preferences.getString("name", "");
int age = preferences.getInt("age", 18);
L.d("name:" + name);
L.d("age" + age);

你第一个想到的,可能是可以通过参数,将当前Activity的Context对象传进来进行封装。

可是这样每次都要写个Activity.this,一点都不酷,所以我们使用另外一种方法,封装统一的全局Context对象,这在组件化的开发中也很常见,实际操作并不复杂,我们来看一下.

我们的Common库中创建app包,在该包下创建BaseApp类,让该类继承于Application,作为我们的基础Application,然后我们提供一个getContext()方法来返回Context对象.

3)AGide

封装思想在开发中应该是很重要的,其中不仅仅是因为我们为了节约时间,更是为了稳妥起见。
举个例子,我们如果使用图片加载框架glide,我们并不会直接的去使用它,而是会把它的代码写在一个包装类里面,使用包装类的方法去加载图片,这样实际上还是使用glide,那么这样有什么意义呢?

/**
 * Created by 舍长 on 2019/1/2
 * describe:Glide封装类
 */
public class AGlide {
    //获取Url地址
    public static void getUrl(Context context, String url, ImageView imageView) {
        Glide.with(context).load(url).into(imageView);
    }
}

试想这么一个场景,某一天你因为某些原因,不得不放弃glide,使用另外一个图片加载框架,那简直就是灾难,因为你代码中使用的地方实在是太多了,你要一个个的去注释,然后改成新的Api,而如果你是使用包装类,你只要把包装类里面的glide代码改了就行了,对整个项目并没有太大的影响:

public class AGlide {
    //获取Url地址
    public static void getUrl(Context context, String url, ImageView imageView) {
//        Glide.with(context).load(url).into(imageView);
        //伪代码,这里假装使用Picasso的Api
    }
}

工具类部分举例子结束,除了这些常用的,我们还可以在工具类中封装Toast工具类啊,字体工具类啊,在源码中我会把它两补上。

3)添加常用依赖

这里需要注意的是,3.0以上后,AndroidStudio添加依赖推荐使用api和implementation和替代compile,不然build时就会报红(虽然还是可以运行)。

implementation和api的区别在于,implementation用于运行Module,api用于库添加依赖,什么意思呢,就是如果你在Common库里使用implementation来添加依赖,那么你的AppModule是检测不到你添加的依赖的。

换句话来说,implementation只能是当它自己的范围使用,不管你是库Module还是运行Module,所以如果我们想让在Common通用库里面加载的框架到AppModule运行Module里能使用的话,得使用api才行。

接下来我们在Common的budile.gradle下添加这几个依赖:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

     //Anko框架,用于kotlin开发扩展
    api "org.jetbrains.anko:anko-commons:0.10.7"

    //圆形处理框架
    api 'de.hdodenhof:circleimageview:2.2.0'

    //    材料设计
    api 'com.android.support:design:28.0.0'

    // Retrofit库
    api 'com.squareup.retrofit2:retrofit:2.4.0'
    api 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'    // 此处一定要注意使用RxJava2的版本
    api 'com.squareup.retrofit2:converter-gson:2.4.0'    // 支持Gson解析

    // Okhttp库
    api 'com.squareup.okhttp3:okhttp:3.11.0'
    api 'com.squareup.okhttp3:logging-interceptor:3.11.0'

    // 支持Gson解析
    api 'com.squareup.retrofit2:converter-gson:2.4.0'

    //    RxJava
    api 'io.reactivex.rxjava2:rxjava:2.1.7'
    api 'io.reactivex.rxjava2:rxandroid:2.0.1'

    //    图片加载框架
    api 'com.github.bumptech.glide:glide:4.7.1'

    // 卡片布局
    api 'com.android.support:cardview-v7:28.0.0'


    implementation 'com.android.support:appcompat-v7:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
    mavenCentral()
}

添加常用第三方

我们可以选择使用腾讯bugly进行程序的异常检测,集成步骤比较简单,官网文档非常详细。
因为也不需要添加程序特定包名,所以我们注册一个应用,后面就可以一直用同一个ID了。

为什么要使用它呢,如果是上线应用,就必须使用它来进行异常检测,不然别的用户使用出现适配问题了,鬼知道发生了什么。

但是如果是对于我们学习来说的话,有时候通过AndroidStudio你检测不出error在哪里,虽然场景比较少,比如使用RxJava进行封装的时候,如果是独立于Actiivty之外的封装,log检测不出来。

5 实际练习代码的分类

1)笔记库的搭建

有了上面的项目模板,后面我们需要的话,就可以直接复制,粘贴,改个名字就可以直接上手学习了,但是我们的Android开发的内容很多,显然不是一个项目能放得下的。

所以根据学习内容的不同,我大致将它们分为以下内容,进而我们可以为不同的练习内容设置不同的项目文件名
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

大致就是这样,我们复制以上数量的项目文件夹,更改完名字后,我们的笔记库到这里就结束了,也许整个过程是稍显复杂,但是我们只要制作一次。

后面我们需要找代码就方便很多,还有的好处就是如果你上次在某个方面学习到一半,有其他的事情,你下次可以很快的找到,继续学习。

上面可能你会有个疑问,开源库和主流框架的区别,其实本质上是没有区别,但是我去区分它们的标准是,它们是否极其重要,不可替代的,极其重要的,如RxJava,就把它归纳到框架里面。

而其它的,像提供某个UI控件的库,使用的少,我们可以找到替代的,甚至我们可以自己写一个的,就会被归纳到库library里面。

2)配合笔记软件

上面制作的笔记目录,不一定适合你,实际上你可以使用笔记软件,石墨文档制作属于你自己的练习项目名称目录。
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

而有一些笔记,也是使用笔记软件记录比较合适。简单来说就是,你的文字笔记目录和你的练习代码仓库的目录是一致的。

不过需要注意的是,文字笔记和我们的练习代码仓库一样,不同类别的笔记是重新创建新的笔记来写的

这是因为石墨文档如果你这是同一份文档写的内容太多就会卡,这里我把我的总目录文档发现出来作为参考:
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

点进去后就是每一类知识的具体笔记。

不过不同类别的文档都会被放置在Android笔记的文件夹中:
通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」

好了,本篇文章就到这里了,老实说,这是我写的30篇文章中感觉写得最累的一次,因为涉及的东西虽然不难,但是很多,所以很累。但是本篇文章基本都是干货,实际运用中,能节约你大量配置项目的时间。

文章并没有对RxJava和Retrofit的封装进行详细的讲解,因为这一块讲起来是得花很多的篇幅,不过如果真要说封装项目模块,它又几乎是必需的。

再叨一下,例如组件化方案 https://blog.****.net/guiying712/article/details/55213884, 路由框架 https://blog.****.net/zhaoyanjun6/article/details/76165252 的学习,虽然作为练习项目模块我不想用,但是实际开发用的还是蛮多的,带来的安利希望对大家有所帮助。

免费获取安卓开发架构的资料(包括Fultter、高级UI、性能优化、架构师课程、 NDK、Kotlin、混合式开发(ReactNative+Weex)和一线互联网公司关于android面试的题目汇总可以加:936332305 / 链接:点击链接加入【安卓开发架构】:https://jq.qq.com/?_wv=1027&k=515xp64

通用的Android练习项目模板配置「常用工具类,项目结构,模板使用」