移动开发技术
本文最后更新于 2024-07-01,文章内容可能已经过时。
移动开发技术
1.Android概述:
1.1 介绍:
2008年9月,google发布Android 1.0;它是第一个免费、开源的手机操作系统;基于Linux内核,使用Java语言开发;
1.2. 体系结构:
1.2.1 应用程序层:
Android平台的应用层上包括各类与用户直接交互的应用程序,或由java语言编写的运行于后台的服务程序。
1.2.2 应用程序框架层:
主要包含以下组件:
1.2.3 系统运行库层:
程序库:C库、媒体库、Surface Manager、WebKit、SGL、3Dlibraries、FreeType、SSL、SQLite等。
Android 运行库:每一个Android应用程序都在它自己的进程中运行,都拥有一 个独立的Dalvik虚拟机实例。
Dalvik虚拟机依赖于linux内核的一些功能,比如线程机制和 底层内存管理机制。
1.2.4 linux内核层:
Android的核心系统服务依赖于Linux内核,如安全性,内存管理,进程管理,网络协议栈和驱动模型。
Linux内核也同时作为硬件和软件栈之间的抽象层。
1.3 系统特点:
Android将界面设计与程序逻辑分离,界面布局使用XML文件,有利于界面的修改和维护;
Android提供轻量级的进程间通讯机制Intent,使跨进程组件通 信和发送系统广播成为可能;
在内存和进程管理方面,Android有自己的运行时和虚拟机;
Android提供Service作为无用户界面、长时间后台运行组件;
支持快速、高效的数据存储:SharedPreferences、文件存储、SQLite数据库
为了跨进程共享数据,Android提供ContentProvider接口 可以无需了解数据源、路径的情况下,对共享数据进行查询、添加、删除和更新等操作
2.环境搭建:
2.1 安装Android Studio:
2.2 配置Android SDK:
第一次运行时需配置
2.3 安装Intel HAXM:
为支持Intel VT虚拟技术(Virtualization Technology)的硬件加速。
2.4 配置Android Studio:
3.项目的创建和运行:
3.1 项目创建:
3.2 项目运行:
3.2.1 在模拟器AVD上运行程序:
AVD:Android Virtual Device,Android模拟器
AVD可以让开发人员不需使用物理设备即可预览、开发和测试Android应用程序。
3.2.2 在实际设备上运行:
3.2.3 项目结构:
3.2.3.1 AndroidManifest.xml:
每一个Android项目都有一个名为AndroidManifest.xml的配置文件,在所有项目中该文件的名称不变。
该文件是Android工程的一个全局配置文件,所有在项目 中使用的组件(如 Activity,Service,Content provider和Broadcast receivers等)都要在该文件中声明。
该文件还可以声明一些android权限等信息。
4.Activity:
在安卓开发中,Activity 是一个应用程序的基本组件之一,代表一个单一的屏幕,包含用户可以与之交互的用户界面。每个 Activity 通常用于一个特定的任务或功能,在一个应用程序中可能包含多个 Activity,这些 Activity 之间可以通过 Intent 进行导航和数据传递。
通常一个界面布局对应一个Activity ,但一个Activity不一定对应一个界面布局。
4.1 主要功能:
1.用户界面展示:每个 Activity 都有自己的布局文件(XML 文件),定义了这个屏幕的 UI 元素(如按钮、文本框等)。
2.用户交互处理:Activity 响应用户的输入,如触摸、点击和键盘事件,并根据这些输入执行相应的操作。
3.生命周期管理:Activity 有一系列的生命周期方法(如 onCreate, onStart, onResume, onPause, onStop, onDestroy),用于管理 Activity 从创建到销毁的整个过程。这些方法帮助开发者管理资源、保存状态以及在适当的时间执行任务。
4.2 生命周期:
Activity生命周期指Activity从启动到销毁的过程
Activity表现为四种状态:
活动状态Active:Activity在用户界面中处于最上层,完全能被用户看到,能够与用户进行交互。
暂停状态Pause:Activity在界面上被部分遮挡,不再处于用户界面的最上层,且不能够与用户进行交互。(如弹出选择框时)
停止状态Stop:Activity被其他Activity全部遮挡,界面完全不 能被用户看到。(如玩游戏时来电了
)非活动状态Dead:Activity没有启动或者被finish()。
随着Activity自身状态的变化,Android系统会调用不同的事件回调函数(7个):
onCreate():当 Activity 首次创建时调用。在这个方法中,通常完成基本的应用初始化逻辑,比如设置布局文件,初始化组件等。
onStart():当 Activity 变得可见时调用。在此方法中,应用程序可以执行一些初始化操作,比如开始动画等。
onResume():当 Activity 开始与用户交互时调用。此时 Activity 处于前台,是用户可以操作的状态。
onPause():当系统准备去启动或恢复另一个 Activity 时调用。在此方法中,应该保存数据,停止动画或其他 CPU 密集型操作,以便用户可以更快地返回 Activity。
onStop():当 Activity 不再可见时调用。在此方法中,可以执行一些较重的资源释放工作。
onDestroy():在 Activity 被销毁前调用,释放所有资源,清理内存。
onRestart():在 Activity 从停止状态重新启动前调用。在此方法中,可以重新初始化被 onStop 方法释放的资源。
5.调试:
Android程序使用断点调试有时可能不好(如多线程程序)。
故通常使用日志文件来记录调试信息。
Android提供一个静态Log类,利用Log类的相关方法,将 调试信息写入日志文件。
LogCat工具可以实时查看日志信息。
下面给出一个示例:
查看log信息:
6.Intent:
6.1 介绍:
启动其他的Activity
实现它们之间的单/双向通信。
Intent是一种组件之间消息传递机制,它是一个动作的完整描述:包含了动作产生组件、接收组件和传递的数据信息。
6.2 使用Intent启动Activity:
6.2.1 显式启动:
6.2.2 隐式启动:
无需指明具体启动哪一个Activity,而由Android系统根据Intent的动作和数据来决定启动哪一个Activity。
例如:希望启动一个浏览器,却不知道具体应该启动哪一 个Activity,此时则可以使用Intent的隐式启动,由Android 系统决定启动哪一个Activity来接收这个Intent。
隐式启动的可以是Android系统内置的Activity,也可是程 序本身的Activity,还可
是第三方应用程序的Activity。
6.3 数据传递:
6.3.1 单向数据传递:
通过在intent里面放ky对的方式去做。
Bundle类是一个存储和管理key-value值对的类,多应用于Activity之间相互传递值。
6.3.1.1 传递对象:
Bundle可以传递对象,但前提是这个对象需要序列化(一种用来处理对象流的机制, 以解决如网络传播、磁盘读写等对对象流读写操作时所引发的问题。)
Bundle的putSerializable()方法,可以存储已经序列化的对象数据(仍然是Key-Value形式);
接收数据时Bundle用getSerializable()方法,获得数据需要强制转化一下原来的对象类型。
6.3.2 双向数据传递:
工作原理:
代码框架:
6.4 使用:
// 显示注册信息的Toast消息
String info = "用户名: " + username + "\n" +
"密码: " + userPassword + "\n" +
"性别: " + userSex + "\n" +
"联系电话: " + userTel + "\n" +
"部门: " + userDept + "\n" +
"爱好: " + userFavs;
//系统提示符
Toast.makeText(this, info, Toast.LENGTH_LONG).show();
// 启动ResultActivity并传递数据
Intent intent = new Intent(MainActivity.this, ResultActivity.class);
intent.putExtra("userInfo", info);
startActivity(intent);
使用:
// 获取传递的数据
String userInfo = getIntent().getStringExtra("userInfo");
// 获取TextView组件
resultTextView = findViewById(R.id.resultTextView);
// 显示传递的数据
resultTextView.setText(userInfo);
7.用户界面:
7.1 介绍:
用户界面UI(User Interface)是系统和用户之间进行信息交换的媒介。
7.1.1 应解决的问题:
界面设计与程序逻辑完全分离:这不仅有利于并行开发,而且在后期修改界面时,不用再次修改程序的逻辑代码
根据不同型号的屏幕能自动调整界面元素的位置和尺寸:避免因为屏幕信息的变化而出现显示错误
增强用户体验:能够合理利用较小的屏幕显示空间,构造出符合人机交互规律的用户界面
7.2 MVC模式:
7.3 页面基本组件:
7.3.1 TextView:
用于显示信息。
在程序中控制:
7.3.2 ImageView:
用于显示图片。
7.3.3 Button:
按钮。
7.3.4 EditText:
文本框,用于输入和编辑字符串。
在程序中控制:
7.3.5 RadioButton:
单选按钮须归属一个组,每个组只能有一个选项被选中。
所以通常使用的是RadioGroup。
7.3.6 CheckBox:
复选框。
7.3.7 Spinner:
下拉框:
7.3.8 listview:
列表框。
ListView通常使用适配器来填充数据和设置显示样式。
7.4 adapter:
Adapter是连接后端数据和前端显示的适配器接口,是数据和UI(View)之间一个重要的纽带。
在常见的View(ListView,GridView)等地方都需要用到Adapter。
7.5 界面布局:
7.5.1 相对布局:RelativeLayout
相对布局是一种非常灵活的布局方式;
通过指定界面元素与其他元素的相对位置关系,来确定界面中所有元素的布局位置;
优点:能够最大程度保证在各种屏幕尺寸的手机上正确显示界面布局。
7.5.2 线性布局:LinearLayout:
线性布局是常用的一种布局方式;
通过设置android:orientation来确定是垂直还是水平布局:
android:orientation="vertical 垂直布局:每行仅包含一个界面元素
android:orientation="horizontal 水平布局 (默认):所有界面元素都在一行
7.5.3 框架布局:FrameLayout:
FrameLayout中所有界面对象都是从屏幕的左上角(0,0)开始布局,不能指定位置;
多个组件层叠排列,上一层的会覆盖下一层的控件;
第一个添加的组件放到最底层,最后添加的显示在最上面。
7.5.4 表格布局:
表格布局以行列的形式管理子元素,每一行是一个TableRow布局对象,当然也可以是普通的View对象;
TableRow表现为一个水平LinearLayout,里面的元素会 水平排列。
7.5.5 网格布局:GridLayout
Android4.0新支持的布局;
网格布局将用户界面划分为网格,界面元素可随意摆放在网格中,网格布局比表格布局更灵活;
在网格布局中界面元素可以占用多个网格。
7.5.6 绝对布局:AbsoluteLayout:
绝对布局能通过指定界面元素的坐标位置,来确定用户界面的整体布局。已经不推荐使用。
绝对布局不能够根据不同屏幕对界面元素进行位置调整。
7.6 界面菜单:
7.6.1 选项菜单:
Android手机上有个Menu键,当Menu按下的时候,在屏幕底部弹出一个菜单,这个菜单就叫OptionsMenu。每个Activity有且只有一个OptionsMenu,它为整个Activity服务。
7.6.2 上下文菜单:
上下文菜单类似Windows中的鼠标右键弹出菜单;
上下文菜单一般是通过长按(大约2秒)屏幕弹出。
7.7 编写方式:
项目整体分包如下:
7.7.1 activity_main.xml:
比如要编写主页面的逻辑,在activity_main.xml中编写。
文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<!-- 定义一个垂直线性布局,作为整个界面的容器 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<!-- 用户名标签 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户名"
android:textSize="20dp"
/>
<!-- 用户名输入框 -->
<EditText
android:id="@+id/name"
android:layout_width="400dp"
android:layout_height="50dp"
/>
<!-- 密码标签 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码"
android:textSize="20dp"
/>
<!-- 密码输入框 -->
<EditText
android:id="@+id/password"
android:layout_width="400dp"
android:layout_height="50dp"
android:inputType="textPassword"
/>
<!-- 性别标签 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="性别"
android:textSize="20dp"
/>
<!-- 性别单选按钮组 -->
<RadioGroup
android:id="@+id/sex"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- 男性单选按钮 -->
<RadioButton
android:layout_width="wrap_content"
android:layout_height="50dp"
android:id="@+id/man"
android:text="男"
android:textSize="20dp"/>
<!-- 女性单选按钮 -->
<RadioButton
android:layout_width="wrap_content"
android:layout_height="50dp"
android:id="@+id/woman"
android:text="女"
android:textSize="20dp"/>
</RadioGroup>
<!-- 联系电话标签 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="联系电话"
android:textSize="20dp"
/>
<!-- 联系电话输入框 -->
<EditText
android:id="@+id/tel"
android:layout_width="400dp"
android:layout_height="wrap_content"
android:inputType="text|phone"
/>
<!-- 部门标签 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="部门"
android:textSize="20dp"
/>
<!-- 部门下拉菜单 -->
<Spinner
android:id="@+id/dept"
android:layout_width="400dp"
android:layout_height="50dp"
android:entries="@array/dept"
/>
<!-- 爱好标签 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="爱好"
android:textSize="20dp"
/>
<!-- 爱好复选框组 -->
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="horizontal">
<!-- 书籍复选框 -->
<CheckBox
android:id="@+id/book"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="书籍"
android:textSize="20dp"/>
<!-- 运动复选框 -->
<CheckBox
android:id="@+id/sport"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="运动"
android:textSize="20dp"/>
<!-- 音乐复选框 -->
<CheckBox
android:id="@+id/music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="音乐"
android:textSize="20dp"/>
<!-- 电影复选框 -->
<CheckBox
android:id="@+id/movie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="电影"
android:textSize="20dp"/>
</LinearLayout>
<!-- 确定按钮 -->
<Button
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="确定"
android:onClick="myclick"
/>
//从文件读取
<Spinner
android:id="@+id/dept"
android:layout_width="400dp"
android:layout_height="50dp"
android:entries="@array/dept"
/>
</LinearLayout>
LinearLayout的orientation 属性设置为 vertical,这意味着所有子视图将垂直排列。如果需要特殊指定,在大的内部添加新的LinearLayout标签。
7.7.2 activity.main:
package com.example.myapp;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在这里绑定xml文件
setContentView(R.layout.activity_main);
}
// 用于登录的方法
public void login(View view) {
// 显示登录的Toast消息
Toast.makeText(this, "登录操作", Toast.LENGTH_SHORT).show();
}
// 用于处理忘记密码的方法
public void forgetPass(View view) {
// 显示忘记密码的Toast消息
Toast.makeText(this, "忘记密码操作", Toast.LENGTH_SHORT).show();
}
// 用于打开注册界面的方法
public void register(View view) {
// 显示注册的Toast消息
Toast.makeText(this, "打开注册界面操作", Toast.LENGTH_SHORT).show();
}
// 确定按钮点击事件处理方法
public void myclick(View view) {
// 显示确定操作的Toast消息
Toast.makeText(this, "确定操作", Toast.LENGTH_SHORT).show();
}
}
在xml中,通过onclick标签指定按钮具体绑定的方法:
<!-- 注册按钮 -->
<Button
android:id="@+id/register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="注册"
android:onClick="register"
/>
7.7.3 获取对象的id:
//初始化
private EditText name, password, tel;
private RadioGroup sex;
private Spinner dept;
private ArrayList<CheckBox> favs = new ArrayList<>();
private Button ok;
//读取相关方法:
// 获取性别方法
private String getSex() {
int selectedId = sex.getCheckedRadioButtonId();
RadioButton selectedRadioButton = findViewById(selectedId);
return selectedRadioButton.getText().toString();
}
8.广播机制:
在Android中,广播(Broadcast)是一种广泛运用在应用程序之间传输信息的机制。
广播消息实质就是将一个Intent对象用sendBroadcast方法发送出去。
Android的每个应用程序都可以对自己感兴趣的广播进行注册,只自己所关心的广播内容。
8.1 BroadcastReceiver:
BroadcastReceiver是对发送出来的Broadcast进行过滤并响应的一类组件,是Android系统的四大组件之一。一般要在AndroidManifest.xml中注册。
8.2 广播类型:
8.2.1 系统广播:
系统广播就是由Android系统发出的广播。
如系统启动完成了、系统关闭了、拨打电话了、收到短信 了、手机没电了、新安装了一个应用程序、在耳机口上插 入耳机、设备内存不足、屏幕关闭等等。
8.2.1.1 示例:
在gui中创建广播接收器BroadcastReceiver,生成的代码框架:
编写完成具体的代码之后,在AndroidManifest.xml中进行注册:
8.2.2 自定义广播:
8.2.2.1 发送:
在广播发送端(如某个APP的Activity),把信息装入一个Intent对 象(含一个自定义的消息标识串,值任意,能保证唯一性即可), 然后sendBroadcast(intent)把 Intent 广播出去。
8.2.2.2 接收:
接收端创建Broadcast Receiver。并编写onReceive()方法。
消息广播标识串,自定义值,具有唯一性即可。
9.Service:
Service是Android系统四大组件之一,它是一种长生命周期的,没有可视化界面,运行于后台的一种服务程序。
9.1 类型:
9.1.1 Started启动的:常用于应用程序内部:
Started形式是指当一个应用组件(如Activity)通过startService()方法 开启的服务。一旦开启,该服务就可以无限期地在后台运行,哪怕 开启它的组件被销毁。
在应用程序中定义service组件。服务通过调用startService(intent)启动,stopService(intent)结束。在服务内部可以调用stopSelf() 来自己停止。
通常,开启的服务执行一个单独的操作且并不向调用者返回一个结果。无论调用了多少次startService(),只需调用一次stopService()来停止。
9.1.2 Bound绑定的:用于应用程序之间:
Bound形式是指一个应用组件通过调用 bindService() 方法与服务绑定。调用 unbindService()关闭连接。
多个组件可以同时绑定到一个服务,但当全部绑定解除后,服务 就被销毁。
9.1.3 生命周期:
9.1.4 编写流程:
1.使用GUI创建一个service。
2.在生成的代码框架中编写代码
10.数据存储和访问:
10.1 简单数据存储:
应用程序一般允许用户自己定义配置信息,如界面背景颜色、字体大小和字体颜色等。
使用SharedPreferences保存用户的自定义配置信息,并在程序启动时自动加载这些自定义的配置信息。
10.2 SharedPreferences:
SharedPreferences是一种轻量级的数据保存方式。
通过SharedPreferences可以将NVP(Name/Value Pair,名称/值对)保存在Android的文件系统中,而且SharedPreferences完全屏蔽的对文件系统的操作过程。开发人员仅是通过调用SharedPreferences对NVP进行保存和读取。
SharedPreferences不仅能够保存数据,还能够实现不同应用程序间的数据共享。
10.2.1 访问模式:
私有(MODE_PRIVATE):仅有创建程序有权限对其进行读取或写入。
全局读(MODE_WORLD_READABLE):不仅创建程序可以对其进行读取或写入,其他应用程序也读取操作的权限,但没有写入操作的权限。
全局写(MODE_WORLD_WRITEABLE):创建程序和其他程序都可以对其进行写入操作,但没有读取的权限。
10.2.2 使用:
10.2.2.1 定义变量:
//私有模式
public static int MODE = MODE_PRIVATE;
//混合模式
public static int MODE = Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE;
//定义名称
public static final String PREFERENCE_NAME = "SaveSetting";
10.2.2.2 创建SharedPreferences对象:
SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCE_NAME, MODE);
10.2.2.3 使用:
在获取到SharedPreferences对象后,则可以通过SharedPreferences.Editor对象对SharedPreferences进行修改,最后调用commit()函数保存修改内容。
SharedPreferences广泛支持各种基本数据类型,包括整型、布尔型、浮点型和长型等等。
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("Name", "Tom");
editor.putInt("Age", 20);
editor.putFloat("Height", );
editor.commit();
如果需要从已经保存的SharedPreferences中读取数据,同样是调用getSharedPreferences()函数,并在函数的第1个参数中指明需要访问的SharedPreferences名称,最后通过get<Type>()函数获取保存SharedPreferences中的NVP。
SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCE_NAME, MODE);
String name = sharedPreferences.getString("Name","Default Name");
int age = sharedPreferences.getInt("Age", 20);
float height = sharedPreferences.getFloat("Height",);
get<Type>()函数的第1个参数是NVP的名称。第2个参数是在无法获取到数值的时候使用的缺省值。
10.3 文件存储:
Android使用的是基于Linux的文件系统。
程序开发人员可以建立和访问程序自身的私有文件。也可以访问保存在资源目录中的原始文件和XML文件。还可以在SD卡等外部存储设备中保存文件。
10.3.1 内部存储:
Android系统允许应用程序创建仅能够自身访问的私有文件,文件保存在设备的内部存储器上,在Linux系统下的/data/data/<package name>/files目录中。
Android系统不仅支持标准Java的IO类和方法,还提供了能够简化读写流式文件过程的函数。
10.3.1.1 openFileOutput()函数:
openFileOutput()函数为写入数据做准备而打开的应用程序私文件,如果指定的文件不存在,则创建一个新的文件。
文件模式:
10.3.1.2 openFileInput()函数:
openFileInput()函数为读取数据做准备而打开应用程序私文件。
10.3.2 外部存储:
编程访问SD卡首先需要检测系统的/sdcard目录是否可用。如果不可用,则说明设备中的SD卡已经被移除,
在Android模拟器则表明SD卡映像没有被正确加载。如果可用,则直接通过使用标准的Java.io.File类进行访问。
将数据保存在SD卡通过“生产随机数列”按钮生产10个随机小数。通过“写入SD卡”按钮将生产的数据保存在SD卡的目录下。SDcardFileDemo示例说明了如何将数据保存在SD卡。
11.ContentProvider:
Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的。
ContentProvider提供了应用程序之间共享数据的方法。 一个应用程序可以通过ContentProvider将自己的数据暴露出去。
应用程序通过ContentProvider访问数据而不需要关心数据具体的存储及访问过程,这样既提高了数据的访问效率,同时也保 护了数据。
应用程序使用 ContentResolver 对象,利用URI,才能访问ContentProvider提供的数据集。
11.1 数据:
ContentProvider可以形象地看作是"数据库"。ContentProvider数据集类似于数据库的"表"。ContentProvider数据集的每条记录都包含一个long型的字 段_ID,用来唯一标识每条记录。
11.2 URL:
URI:统一资源标识符(Uniform Resource Identifier),用来标识资源的逻辑位置。(定义远程或本地的可用资源)
每一个Content Provider 都对外提供一个能够唯一标识自己 数据集的公开URI;如果一个Content Provider管理多个数据集,则应给每个数据集分配一个独立的URI
11.2.1 格式:
举个例子:
11.3 使用:
//创建
ContentResolver resolver = getContentResolver();
//crud:
resolver.query()、insert()、update()、delete()
11.3.1 提供端: