学习笔记 | Android Studio安卓开发入门经验总结 干货

warning: 这篇文章距离上次修改已过1105天,其中的内容可能已经有所变动。

前言

最近完成了移动编程课程的学习,加上其它安卓开发项目的经历,感觉收获颇为丰富。故在此总结整理安卓开发中比较常见的一些问题,技巧和指南。

0.目录


1.开发环境

Android Studio是谷歌基于IntelliJ IDEA社区版开发的,面向安卓开发的免费集成开发环境。其方便快捷的开发调试和可视化UI编辑可以令安卓开发事半功倍。本项目所使用集成开发环境为Android Studio 4.1.1,编程语言为Java,使用JDK 1.8,使用Gradle 4.1.1进行项目工程构建和依赖管理。

2. 项目新建

Android Studio新建项目时选择一个Activity模板作为默认MainActivity,先选择Empty Activity作为开始。接着,Minimum SDK指的是本项目所支持的最小安卓SDK,点击“Help me choose”会出现如下界面帮助选择合适的安卓最小SDK版本:

图1 安卓API版本选择帮助界面图1 安卓API版本选择帮助界面



其中CUMULATIVE DISTRIBUTION表示如果支持该安卓版本以上设备,则预估能支持安卓设备占所有安卓设备的百分比。可见Minimum SDK越小所能支持设备越多,但没有必要一味追求支持更多设备,这需要根据一定的市场调查与经验来决定。本项目选择支持默认的安卓6.0及以上。

3.项目结构

3.1 开发模式

安卓开发常见的开发模式有MVCMVPMVVM等(详见本站文章 “学习笔记 | Android开发常用的几种模式”),其中MVC非常容易上手,结构清晰易懂,为了简化开发,本项目使用MVC模式进行开发。

3.2 安卓项目文件及目录结构简介

  • 安卓应用配置文件AndroidManifest.xml: 用于配置包名、应用权限、应用图标及名称、主题等基本信息,此外包括了应用的Activity相关配置,没有在此进行注册的Activity是不能被启动的。
  • 程序代码java: 在java目录下的对应包名中存放包括Activity在内的各java程序文件。
  • 资源文件res: 存放用于UI相关的各类资源,主要有:

    • drawable:存放可被绘制的图形,包括矢量图和位图,以及由xml编写的各类图层、状态选择器等比较实用的前端UI部件。
    • layout: 以xml文件形式编写的用户交互界面,可以在Android Studio中进行实时渲染预览、可视化编辑等。
    • values:

      • arrays.xml: 存放数组,在程序中按照自定义的数组名进行读取。
      • colors.xml: 存放Hex色值,在程序中按照自定义的颜色名字进行读取。
      • dimens.xml: 存放尺寸信息。
      • strings.xml: 存放字符串,按照自定义的字符串名进行获取,方便多语言程序的本土化。
      • themes

        • themes.xml: 程序主题,包括主色次色和各类样式。
        • themes.xml(night): 程序夜间主题。
    • mipmap: 存放贴图文件,如果期望贴图有放大缩小动画之类的可以获得更好的图像表现。
    • xml: 存放一些其它xml格式的文件,例如网络安全配置文件network_security_config。
  • Gradle构建配置文件build.gradle

    • 项目级构建配置build.gradle(Project: $project_name):用于配置适用于项目的Gradle构建设置,例如使用的Gradle版本,构建脚本的仓库,依赖包仓库。例如:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        //这里放置项目构建仓库
        google()
        jcenter()
    }
    dependencies {
        //这里放置项目构建所需的依赖,而不是模块(总之平时用的依赖一般都不是放这里)的依赖
        classpath "com.android.tools.build:gradle:4.1.1"
        // NOTE: Do not place your application dependencies here; they belong in the individual module build.gradle files
    }
}

allprojects {
    repositories {
    //依赖包仓库,也就是依赖包从哪下载,一般使用国内镜像下载快很多
        maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}//阿里云的仓库,便于下载依赖包
        maven{ url "https://jitpack.io"}
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

  • 模块级构建配置build.gradle(Module: $project_name.app)
…
dependencies {
//这里才是放置要用到的第三方依赖的地方
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
    implementation 'androidx.fragment:fragment:1.2.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation 'com.squareup.okhttp3:okhttp:3.10.0'//http通信的依赖库
    implementation 'com.github.bumptech.glide:glide:3.7.0'//加载图像的依赖库
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
    implementation 'com.makeramen:roundedimageview:2.2.1'
    implementation 'com.google.code.gson:gson:2.8.7'
}


3.3 程序设计要点

3.3.1 Activity和Fragment的生命周期

正如生物一样,Activity和Fragment作为安卓交互程序也有“生死”,也就是生命周期。
Activity生命周期
如图为一个Activity从被启动到被停止的生命周期:

图2 Activity生命周期[2] 图2 Activity生命周期[2]

  • onCreate(): Ativity被创建的时候,常用于初始化基本布局,使用setContentView()加载布局文件,进行一些其它基本不耗时间的操作,实在需要就用异步线程,避免页面长时间空白。
  • onStart(): Activity被展示的时候,也就是说被创建了不一定要显示出来,但要显示出来了才onStart()。
  • onPause(): Activity将要被挂起的时候,页面失去焦点无法交互,此时Activity仍可见,比如将转入后台运行。
  • onResume():Activity已经从后台唤起并显示出来,将要但还未获得焦点无法操作的时候。
  • onStop(): Activity以及失去焦点且要转入后台的时候,此时Activity已不可见。
  • onRestart(): Activity被挂起后又被唤醒的时候,此时Activity还未显示出来。
  • onDestroy(): Activity被彻底销毁的时候。

Fragment生命周期
Fragment生命周期与Activity周期较为类似,但其中比较值得提及的是:

  • onAttach():Fragment与Activity建立关联的时候,也就是此时Fragment已经知道了拥有自己的“上司”Activity是谁。
  • onActivityCreated():此时建立关联的Activity已经结束了onCreate()并返回。
  • onCreateView():此时初始化Fragment布局,也是将基本的布局加载好,不建议耗时间的操作,实在需要就用异步线程。
  • onDestroyView():Fragment的视图已经被销毁,但与Activity的关联未销毁,仍然可以重新创建视图。
  • onDetach():与Activity的关联将要被解除,Activity在onDestory()时会自动调用与之有关联的Fragment的onDetach()方法。

3.3.2 网络请求与异步线程

Android 4.0以后网络请求不能在主线程中执行已经是老生常谈了,这是为例放置线程阻塞应用无响应。解决方案一般就是启动异步线程进行处理,UI线程和网络请求就分离开了,各干各的。但这之间如何通信呢,或者说网络请求结束,不管成功失败,如何告诉UI线程?这将在3.3.3 Handler消息处理中提到。
线程通常还要用到线程辅助类Runnable,在Runnable的要求强制重写的方法run()中执行网络请求任务。例如:

Runnable networkRunnable = new Runnable() {
    @Override
    public void run() {
        //可以在此处执行网络请求和数据解析操作,拉取联系人消息之类
     }
};
Thread thread = new Thread(networkRunnable);
thread.start();//启动线程

不仅如此,耗时操作通常都是在线程中执行的,例如数据库读写和一些其它文件操作之类,可见,多线程是安卓开发中一个重要的技术。

3.3.3 Handler和消息处理

上节中提到,不同线程间如何通信,Handler就是一个易用的方案。如果把各个线程比作各干各活的工人,Handler就像是个中间人,负责把各个工人传来的消息进行处理,并有权操作UI线程中的组件,比如更新TextView的文字(非UI线程是不能操作的)。“工人”如何给Handler发一条消息?实现起来很简单,比如网络请求处理成功以后:

private final int NETWORK_PROCESS_OK = 1;//定义一个数字代号代表网络处理成功
private final int NETWORK_PROCESS_FAIL = 0; //代表网络处理失败
Message msg = handler.obtainMessage();//需要保证此时Handler的实例handler已经实例化不为空。
msg.what= NETWORK_PROCESS_OK;
msg.sendToTarget();
//失败以后也可以传回原因:
Message msg = handler.obtainMessage();
msg.what= NETWORK_PROCESS_FAIL;
msg.obj=reason;//reason一般是String,但可以是任何Object
msg.sendToTarget();

而Handler接收到消息后的处理一般如下:

handler = new Handler(){
     public void handleMessage(Message msg) {  
            switch (msg.what) {  
            case NETWORK_PROCESS_OK:  
                textview.setText("处理成功!");  
                break; 
           case  NETWORK_PROCESS_FAIL:  
                textview.setText("处理失败!原因:" + msg.obj.toString());  
                break;
            }  
        };  
};


3.3.4 Activity间的跳转

Activity间使用Intent类进行跳转和数据传输,具体如下:

Intent intent = new Intent(this,TargetActivity.class);//this是一个Activity对象
intent.putExtra(“param1”,paramString1);//通过intent传送额外数据,可以在目标Activity中,使用getIntent()获取传入的intent对象,利用该intent对象的getStringExtra()接受传入的String类型参数,当然也有其它类型的,此处不列举。
startActivity(intent);

值得注意的是,Intent所能传输的数据容量是有上限的,过多的数据不建议使用Intent进行传输。

3.3.5 回收型列表视图RecyclerView的使用

RecyclerView是基于viewholder的回收理念在ListView上的一个升级版,功能强大,当然在不需要进行回收的场景就当然不要用了,例如实现多行可选择的标签,如果标签滑出屏幕外被回收了的话,选中状态也会一起丢失,除非用额外的对象进行选中状态记录。
RecyclerView的适用场景是有大量用于展示的列表数据的场景,比如微信的公众号页面的推文卡片、微信朋友圈、QQ的好友列表、小红书的瀑布流帖子等。
ListView类似,要将数据适配到视图上进行展示需要使用适配器Adapter,不同的是RecyclerView的BaseAdapter已经将viewholder模式封装好了,而目前RecyclerView的适配器有很多优秀的第三方库,例如Github上开源的CymChad的适配器助手BaseRecyclerViewAdapterHelper,封装了基本的适配器操作,秩序简短的几行代码就能实现基本的适配操作,也封装了很多功能丰富的类与接口,例如可以实现下拉刷新,上拉加载的接口,可以实现多布局共存的MultiItemAdapter<T>等等[4],因此在合适的场景下使用是很好的,避免重复造轮子,但为了学习和了解更底层的原理,本项目中还是用最原始的ListView和RecyclerView的自带适配器。

3.3.6 适配器理念Adapter

如上所说,列表视图需要一个中间件:适配器,来将数据适配到布局上,这是一个从结构化的数据到结构化的视图的中间过程,纵观整个项目开发,可以发现有很多地方在使用这样的理念,除了列表视图的适配器以外,装载Fragment的ViewPagerPagerAdapter也是同样使用了适配器的概念,使得ViewPager可以比较方便的管理多个Fragment,降低耦合。同样的,这样的理念也可以运用到有着多种网络请求的场景中,使用工厂模式和适配器理念,将网络请求返回结果适配到实体类对象或UI视图里,这对于降低耦合度和提高多态性是很有帮助的。

4. 参考文档

[1] Android中 mipmap与drawable区别
https://blog.csdn.net/userzhanghao123/article/details/70243872
[2] Activity与Fragment生命周期探讨
https://www.jianshu.com/p/1b3f829810a1
[3] fragment生命周期(总结)
https://blog.csdn.net/weixin_33774615/article/details/88126868
[4] BaseRecyclerViewAdapterHelper
https://github.com/CymChad/BaseRecyclerViewAdapterHelper

添加新评论