android入门学习
第一章
android系统架构
四层:Linux内核层,系统运行库层,应用框架层,应用层
-
Linux内核层
提供底层硬件驱动 -
系统运行库层
通过一些c/c++库位android系统提供特性支持,例如SQLite数据库,OpenGL|ES库,Webkit浏览器内核
还有android运行时库,主要提供核心库,包括Dalvik虚拟机(后改为ART运行环境),使每个应用能运行在独立的进程中,并拥有一个Dalvik虚拟机实例 -
应用层框架
提供构建应用程序时可能用到的各种API,开发者可以通过使用这些API构建应用 -
应用层
所有安装在手机上的应用程序都是这一层,包括自带和第三方开发
android已发布版本
4.0不再区分平板和手机
5.0使用ART替代Dalvik虚拟机
6.0后需要运行时权限
7.0加入多窗口模式功能
目前面向4.0后可以覆盖大部分手机,6.0可以覆盖一半以上
android应用开发特色
-
四大组件
活动Activity,服务Service,广播接收器Broadcast Receiver,内容提供器Content Provider
活动是门面,看的见的都放在活动中
服务在后台,退出应用还可以运行
广播接收器接收来自各处的消息,也可以向外广播消息
内容提供器为应用程序之间共享数据提供可能 -
系统控件
可以使用系统的也可以自己定制 -
SQLite数据库
自带轻量级速度快的嵌入式关系型数据库,支持标准SQL语法,封装了API -
多媒体
-
地理位置定位
搭建开发环境
先学java,了解基本后再学android开发,一边学习一边补充
需要的工具:JDK,android SDK,android studio
创建第一个项目
Application Name应用名称,安装后会显示在手机上的名称
Company Domain公司域名,
Package Name项目包名,android studio会根据应用名和公司域名自动生成合适包名,一般是倒序
项目目录解析:
- .gradle和.idea
自动生成的一些文件,无需手动编辑 - app
项目的代码和资源都在该目录下 - build
编译时自动生成的文件,也无需过多关心 - gradle
包含gradle wrapper的配置文件 - build.gradle
项目全局的gradle构建脚本,通常不需要修改 - gradle.properties
全局的gradle配置文件,这里配置的属性会影响项目所有的gradle编译脚本 - XXX.iml
iml是IntelliJ IDEA项目都会生成的文件,用于标示这是一个IntelliJ IDEA项目,不需要修改 - local.properties
指定本机android SDK路径,自动生成,如果SDK路径改变再修改即可 - setting.gradle
用于指定项目中所有引入的模块
app目录解析:
- build
编译自动生成 - libs
使用的第三方jar包,在该目录下的jar包会被自动添加到构建路径下去 - androidTest
测试用例,自动化测试 - java
所有编写的java代码 - res
项目中使用的图片,布局,字符串等资源都放在这 - AndroidManifest.xml
整个项目的配置文件,在程序中的四大组件都要在该文件中注册,在该文件添加权限声明 - test
编写Unit Test测试用例 - proguard-rules.pro
用于指定项目代码的混淆规则,避免破解
没有在AndroidManifest.xml中注册的活动是不能使用的
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.category.LAUNCHER"/>
这两行表示该活动是APP的主活动
活动继承自AppCompatActivity,是一种向下兼容的Activity,是Activity的子类,onCreate()方法是活动创建时必定执行的方法
逻辑与视图分离,在布局文件中写界面,在活动中再引入,setContentView()给当前活动引入布局文件
详解资源
res目录中是资源文件
在values/strings.xml中有应用程序名的字符串,有两种方式引用:
在代码中R.string.app_name
,在xml中@string/app_name
build.gradle文件
Gradle是一个项目构建工具,基于Groovy的领域特定语言DSL来声明项目设置,摒弃传统基于XML(Ant和Maven)的各种繁琐配置
项目有两个build.gradle文件,最外层的:
jcenter是一个代码托管仓库,可以在项目中引用jcenter上任何开源项目
dependencies闭包中石油classpath声明一个gradle插件,后面是版本号
app目录下的build.gradle文件:
<apply plugin:'com.android.application'
application表示这是一个应用程序模块,如果是library表示一个库模块
android闭包中:compileSdkVersion指定项目编译版本,buildToolsVersion指定项目构建工具版本
defaultConfig闭包中:applicationId指定项目包名,minSdkVersion指定最低兼容的系统版本,targetSdkVersion指你在该版本目标做过充分测试,系统会为你启动最新的功能和特性,versionCode版本号,versionName版本名
buildTypes闭包中:一般会有两个子闭包,一个debug测试版,一个release正式版。minifyEnable指定是否进行代码混淆,proguardFiles用于指定混淆的规则文件
dependencies闭包:指定当前项目的所有依赖关系,通常种依赖:本地依赖,库依赖,远程依赖。第一个fileTree是本地依赖,将libs中的jar包添加到构建路径中
日志工具
日志工具类Log(android.util.Log),有五个方法:
verbose:琐碎的,级别最低的
debug:调试信息
info:重要数据
warn:警告信息
error:错误信息
例如Log.d(tag, msg),tag用于对信息进行过滤,msg是打印的内容
TAG一般写在开头,以当前类为tag string,如private static final String TAG = "HelloWorldActivity";
第二章
活动
活动Activity可以包含用户界面的组件,主要用于和用户进行交互,一个应用可以包含多个活动
销毁活动点击back按钮即可,或者在代码里调用finish()
活动注册
<activity android:name=".FirstActivity" 活动名称
android.label="This is first activity"> 活动标题栏
<intent-filter>
...
</intent-filter>
在活动中使用Menu
不占用空间的menu
在res目录下新建menu文件,再新建菜单文件(menu resource file)
<menu xmln:android="xxxx>
<item
android:id="@+id/add_item"
android:title="Add"/>
<item
android:id="@+id/remove_item"
android:title="Remove"/>
</menu>
id标识符,title标题,item菜单项
在活动中重写onCreateOptionsMenu()方法
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
//super.onCreateOptionsMenu(menu);
}
getMenuInflater()获取MenuInfalter对象,调用inflat()创建,第一参数是菜单资源文件,第二参数指定菜单项添加到哪一个menu对象中,使用该方法传入的menu,最后返回true表示允许菜单显示
菜单响应事件,重写onOptionsItenSelected()
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.add_item:
Toast.makeText(this, "Add", Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this, "Remove", Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}
显式Intent
Intent是各组件之间交互的一种重要方式,指明当前组件要执行的动作,还可以传递数据,一般可用于启动活动,启动服务,发送广播
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
Intent构造函数第一参数为Context启动活动的上下文,第二参数为启动的目标活动
回到上一个活动直接back按键即可
隐式Intent
指定一系列抽象的action和category。由系统分析合适的活动启动
<action android:name="org.nuaa.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
隐式响应必须action和category都符合
每个Intent只能指定一个action,但是能指定多个category,如果隐式没有一个活动响应,会崩溃。
使用隐式,可以启动其他程序的活动,例如:
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("https://www.baidu.com"));
startActivity(intent);
在活动的<intent-filter>
标签中可以添加<data>
标签,配置以下内容:
scheme协议,host主机,port端口,path主机与端口之后的内容,mimeType可以处理的数据类型
只有<data>
与Intent携带的Data完全一致,当前活动才能响应Intent
传递数据
Intent提供了一系列putExtra()方法的重载,可以将数据暂存在Intent中传递,打开一个活动后,再取出
intent.putExtra("dataName", data);
取出的时候,先用getIntent()获取活动,在使用getXXXExtra()获取数据
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
返回数据给上一个活动
startActivityForResult()也可以用于启动,但是在活动销毁时候能给返回一个结果给上一个活动
第一参数是Intent,第二参数是请求码,用于判断数据来源
在期待返回数据的活动中添加返回数据的逻辑,重写onBackPress()
Intent intent = new Intent();
intent.putExtra("return_data", "Hello First Return");
setResult(RESULT_OK, intent);
finish();
构建intent,但是仅仅是传递数据,setResult方法第一参数是返回处理结果,RESULT_OK或者是RESULT_CANCELED,第二参数是intent
在上一个活动中重写onActivityResult()方法
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String returndata = data.getStringExtra("return_data");
Log.d("FirstActivity", returndata);
}
break;
default:
}
}
第一参数requestCode是启动活动传入的请求码,第二参数resultCode是返回数据传入的处理结果,第三参数data来自intent
生命周期
返回栈
活动在一个栈,四个状态:
运行:栈顶
暂停:不在栈顶,但仍然可见
停止:不在栈顶,完全不可见
销毁:从返回栈移除
生存周期
onCreate():创建时调用,初始化操作
onStart():由不可见变为可见
onResume():处于返回栈的栈顶,正在运行
onPasue():系统准备启动或者恢复另一个活动,一般可以将消耗资源释放,保存关键数据
onStop():完全不可见时调用
onDestroy():被销毁之前调用
onRestart():在停止变为运行之前调用
完整生存期:onCreate()和onDestroy()之间
可见生存期:onStart()和onStop()之间
前台生存期:onResume()和onPasue()之间
活动被回收
onSaveInstanceState()在活动被回收之前被调用,解决被回收时保存数据
Bundle类型参数
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "something";
outState.putString("dataKey", tempData);
}
数据恢复
if (savedInstanceState != null) {
String tmpData = savedInstanceState.getString("data_key");
}
活动的启动模式
四种:standard,singleTop,singleTask,singleInstance
可以通过Manifest的<activity>
标签指定android:launchMode选择启动模式
- standard
默认的启动模式,每启动一个活动,在返回栈入栈,每次启动创建一个新的活动实例 - singleTop
在启动活动时,发现如果返回栈栈顶时该活动,则认为可以直接使用,无需再创建,不过如果不在栈顶则还是会创建新活动 - singleTask
每次启动时会检查返回栈,在栈中会直接使用,将该活动之上的活动全部出栈,直到该活动到栈顶,没有则创建 - singleInstance
不同于前三种,会启用一个新的返回栈管理该活动,解决共享活动实例问题。单独一个栈管理该活动,不管是哪一个应用调用该活动
启动顺序:A -> B -> C B在单独的栈
返回顺序:C -> A -> B AC在一个栈,返回后AC栈为空,跳到来B单独的栈
活动技巧
- 知晓当前活动
创建一个BaseActivity类,普通类,继承AppCompatActivity,重写onCreate方法,打印实例类名Log.d("BaseActivity", getClass().getSimpleName());
所有活动都继承该类即可 - 退出所有活动
使用一个专门的集合类对所有活动进行管理
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
activities.clear();
}
}
在创建活动的时候,onCreate中调用addActivity,onDestroy中调用removeActivity,需要一键退出的时候调用finishAll
还可以再加上杀掉当前进程的代码,只能用于杀掉当前程序进程
android.os.Process.killProcess(android.os.Process.myPid());
- 启动活动的最佳写法
编写一个actionStart方法,该方法的参数就是需要传递的数据
public static void actionStart(Context context, String data1, String data2) {
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("param1", data1);
intent.putExtra("param2", data2);
startActivity(intent);
}