序我最近完成了移动编程的课程,从其他Android开发项目的经验中,我收获了很多。因此,本文总结了Android开发中的一些常见问题、技巧和指南。1.发展环境Android Studio是Google基
我最近完成了移动编程的课程,从其他Android开发项目的经验中,我收获了很多。因此,本文总结了Android开发中的一些常见问题、技巧和指南。
1.发展环境Android Studio是Google基于IntelliJ IDEA Community Edition开发的Android开发免费集成开发环境。其便捷的开发调试和可视化的UI编辑,可以让Android开发事半功倍。本项目使用的集成开发环境为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版本:
Android Studio新建项目时,选择一个活动模板作为默认的MainActivity,首先选择空活动作为开始。接下来,最低SDK指的是这个项目支持的最低Android SDK。点击“帮助我选择”,将出现以下界面,帮助您选择合适的最低Android SDK版本:
图1 Android API版本选择帮助界面
累计分布表示在支持Android版本或更高版本的情况下,可以支持Android的所有Android设备的百分比。可以看出,最小SDK越小,可以支持的设备就越多,但没必要盲目追求支持更多的设备,需要根据一定的市场调研和经验来决定。本项目选择支持默认的Android 6.0及以上版本。
Android常见的开发模式有MVC、MVP、MVVM等。(详见文章“/s2/]学习笔记| Android开发的几种常见模式”,其中MVC非常好用,结构清晰易懂。为了简化开发,本项目采用MVC模式进行开发。3.2 Android项目文件和目录结构介绍安卓应用配置文件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: 存放字符串,按照自定义的字符串名进行获取,方便多语言程序的本土化。themesthemes.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}
正如生物一样,Activity和Fragment作为安卓交互程序也有“生死”,也就是生命周期。
Activity生命周期
:
如图为一个Activity从被启动到被停止的生命周期:
就像生物一样,活动和片段,作为安卓交互程序,都有“生与死”,也就是生命周期。
活动生命周期
:
图为一个活动从开始到停止的生命周期:
图2活动生命周期[2]
onCreate(): Ativity被创建的时候,常用于初始化基本布局,使用setContentView()加载布局文件,进行一些其它基本不耗时间的操作,实在需要就用异步线程,避免页面长时间空白。onStart(): Activity被展示的时候,也就是说被创建了不一定要显示出来,但要显示出来了才onStart()。onPause(): Activity将要被挂起的时候,页面失去焦点无法交互,此时Activity仍可见,比如将转入后台运行。onResume():Activity已经从后台唤起并显示出来,将要但还未获得焦点无法操作的时候。onStop(): Activity以及失去焦点且要转入后台的时候,此时Activity已不可见。onRestart(): Activity被挂起后又被唤醒的时候,此时Activity还未显示出来。onDestroy(): Activity被彻底销毁的时候。
片段生命周期[/S2/]:
片段生命周期类似于活动生命周期,但值得一提的是:
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处理程序消息处理中提到。
线程通常使用线程帮助器类Runnable来执行Runnable的方法run()中的网络请求任务,这需要强制重写。例如:
Runnable networkRunnable = new Runnable() { @Override public void run() { //可以在此处执行网络请求和数据解析操作,拉取联系人消息之类 }};Thread thread = new Thread(networkRunnable);thread.start();//启动线程
而且,耗时的操作通常是在线程中执行的,比如数据库读写和一些其他的文件操作。可见多线程是Android开发中的一项重要技术。
3.3.3处理器和消息处理正如上一节所提到的,Handler是一个关于不同线程如何通信的易于使用的解决方案。如果把每个线程比作一个做自己工作的工人,Handler就像一个中间人,负责处理每个工人发来的消息,有权操作UI线程中的组件,比如更新TextView的文本(非UI线程不能操作)。“工作者”如何向处理者发送消息?例如,在成功处理网络请求后,很容易实现:
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,但可以是任何Objectmsg.sendToTarget();
处理程序收到消息后的处理一般如下:
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; } }; };
活动之间的跳转使用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是基于ListView上的viewholder的回收概念的升级版,功能强大。当然不应该用在不需要回收的场景,比如实现多行可选标签。如果标签滑出屏幕并被回收,所选状态将一起丢失,除非使用其他对象来记录所选状态。
回收商视图的适用场景是有大量列表数据需要展示的场景,比如微信的微信官方账号页面的推文卡片、微信的朋友圈、QQ的好友列表、小红书的瀑布帖等。
与ListView类似,您需要使用Adapter adapter使数据适应视图进行显示。区别在于RecyclerView的BaseAdapter已经封装了viewholder模式。目前RecyclerView的适配器有很多优秀的第三方库。比如Github上CymChad的开源适配助手
basecyclervieadapterhelper封装了基本的适配操作,短短几行代码就可以实现基本的适配操作,还封装了很多功能丰富的类和接口,比如可以实现下拉刷新和上拉加载的接口,MultiItemadapter<>适配器概念适配器
如上所述,列表视图需要一个中间件:适配器来使数据适应布局,这是从结构化数据到结构化视图的中间过程。纵观整个项目发展,可以发现有很多地方都在使用这个概念。除了列表视图的适配器,加载片段的ViewPager的PagerAdapter也使用了适配器的概念,使得ViewPager更容易管理多个片段,减少耦合。类似地,这种思想也可以应用于具有多个网络请求的场景。利用工厂模式和适配器思想,网络请求的返回结果可以适配到实体类对象或UI视图,这对于降低耦合度和提高多态性非常有帮助。