android的系统架构
应用程序层
Android 装配了一个核心应用程序集合,包括 E-mail 客户端、SMS 短消息程序、日历、地图、浏览器、联系人管理程序和其他程序,所有应用程序都是用 Java 编程语言编写的。
用户开发的 Android 应用程序和 Android 的核心应用程序是同一层次的,它们都是基于 Android 的系统 API 构建的。
应用程序框架层
应用程序的体系结构旨在简化组件的重用,任何应用程序都能发布它的功能且任何其他应用程序都可以使用这些功能(需要服从框架执行的安全限制),这一机制允许用户替换组件。
开发者完全可以访问核心应用程序所使用的 API 框架。通过提供开放的开发平台,Android 使开发者能够编制极其丰富和新颖的应用程序。开发者可以自由地利用设备硬件优势访问位置信息、运行后台服务、设置闹钟、向状态栏添加通知等。
系统/服务 | 英文名称 | 说明 |
---|---|---|
视图 | View | 这里的视图指的是丰富的、可扩展的视图集合,可用于构建一个应用程序,包括列表 (Lists)、网格 (Grids)、文本框 (TextBoxes)、按钮 (Buttons),甚至是内嵌的 Web 浏览器。 |
内容管理器 | Content Provider | 内容管理器使得应用程序可以访问另一个应用程序的数据(如联系人数据库)或者共享自己的数据。 |
资源管理器 | Resource Manager | 资源管理器提供访问非代码资源,如本地字符串、图形和分层文件 (layout files)。 |
通知管理器 | Notification Manager | 通知管理器使得所有的应用程序都能够在状态栏显示通知信息。 |
活动管理器 | Activity Manager | 在大多数情况下,每个 Android 应用程序都运行在自己的 Linux 进程中。当应用程序的某些代码需要运行时,这个进程就被创建并一直运行下去,直到系统认为该进程不再有用为止,然后系统将回收该进程占用的内存以便分配给其他的应用程序。活动管理器管理应用程序生命周期,并且提供通用的导航回退功能。 |
系统库
Android 本地框架是由 C/C++ 实现的,包含 C/C++ 库,以供 Android 系统的各个组件使用。这些功能通过 Android 的应用程序框架为开发者提供服务。
名称 | 说明 |
---|---|
系统C语言库 | 标准C语言系统库 (libc) 的 BSD 衍生,调整为基于嵌入式 Linux 设备。 |
媒体库 | 基于 PacketVideo 的 OpenCORE,这些库支持播放和录制许多流行的音频和视频格式,以及静态图像文件,包括 MPEG4、H.264、MP3、AAC、AMR、JPG、PNG。 |
界面管理 | 管理访问显示子系统,并且为多个应用程序提供 2D 和 3D 图层的无缝融合。 |
LibWebCore | 新式的 Web 浏览器引擎,支持 Android 浏览器和内嵌的 Web 视图。 |
SGL | 一个内置的 2D 图形引擎。 |
3D 库 | 基于 OpenGL ES 1.0 APIs 实现,该库可以使用硬件 3D 加速或包含高度优化的 3D 软件光栅。 |
FreeType | 位图和矢量字体显示渲染。 |
SQLite | SQLite 是一个所有应用程序都可以使用的强大且轻量级的关系数据库引擎。 |
Android 运行环境
Android 包含一个核心库的集合,该核心库提供了 Java 编程语言核心库的大多数功能。几乎每一个 Android 应用程序都在自己的进程中运行,都拥有一个独立的 Dalvik 虚拟机实例。
Dalvik 是 Google 公司自己设计的用于 Android 平台的 Java 虚拟机。Dalvik 虚拟机是 Google 等厂商合作开发的 Android 移动设备平台的核心组成部分之一,它可以支持已转换为 .dex (Dalvik Executable) 格式的 Java 应用程序的运行。
.dex 格式是专为 Dalvik 设计的一种压缩格式,适合内存和处理器速度有限的系统。
Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个 Dalvik 应用作为一个独立的 Linux 进程执行。Dalvik 虚拟机依赖 Linux 内核提供基本功能,如线程和底层内存管理。
Linux内核
Android 基于 Linux 提供核心系统服务,例如安全、内存管理、进程管理、网络堆栈、驱动模型。除了标准的 Linux 内核外,Android 还增加了内核的驱动程序,如Binder (IPC) 驱动、显示驱动、输入设备驱动、音频系统驱动、摄像头驱动、WiFi驱动、蓝牙驱动、电源管理。
Linux 内核也作为硬件和软件之间的抽象层,它隐藏具体硬件细节而为上层提供统一的服务。
分层的好处就是使用下层提供的服务为上层提供统一的服务,屏蔽本层及以下层的差异,当本层及以下层发生了变化时,不会影响到上层,可以说是高内聚、低耦合。
开发环境搭建
- JDK(Java Document Kit):Java开发包
- SDK(Software Document Kit):软件开发包
SDK是什么?
SDK (Software Development Kit) 软件开发工具包是软件开发工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。Android SDK 就是 Android 专属的软件开发工具包。
1) add-ons
该目录中存放 Android 的扩展库,比如 Google Maps,但若未选择安装 Google API,则该目录为空。
2) docs
该目录是 developer.Android.com 的开发文档,包含 SDK 平台、工具、ADT 等的介绍,开发指南,API 文档,相关资源等。
3) extras
该目录用于存放 Android 附加支持文件,主要包含 Android 的 support 支持包、Google 的几个工具和驱动、Intel 的 IntelHaxm。
4) platforms
该目录用于存放 Android SDK Platforms 平台相关文件,包括字体、res 资源、模板等。
5) platform-tools
名称 | 作用 |
---|---|
api 目录 | api-versions.xml 文件,用于指明所需类的属性、方法、接口等 |
lib 目录 | 目录中只有 dx.jar 文件,为平台工具启动 dx.bat 时加载并使用 jar 包里的类 |
aapt.exe | 把开发的应用打包成 APK 安装文件,如果用 Eclipse 开发,就不用通过命令窗口输入命令+参数实现打包 |
adb.exe (Android Debug Bridge 调试桥) | 通过它连接 Android 手机(或模拟器)与 PC 端,可以在 PC 端上控制手机的操作。如果用 Eclipse 开发,一般情况 下 ADB 会自动启动,之后我们可以通过 DDMS 来调试 Android 程序。 |
aidl.exe (Android Interface Definition Language) | Android 内部进程通信接口的描述语言,用于生成可以在 Android 设备进行进程间通信 (Inter-Process Communication,IPC) 的代码 |
dexdump.exe | 可以反编译 .dex 文件,例如 .dex 文件里包含 3 个类,反编译后也会出现 3 个 .class 文件,通过这些文件可以大概了解原始的 Java 代码。 |
dx.bat | 将 .class 字节码文件转成 Android 字节码 .dex 文件 |
fastboot.exe | 可以进行重启系统、重写内核、查看连接设备、写分区、清空分区等操作 |
Android llvm-rs-cc.exe | Renderscript 采用 LLVM 低阶虚拟机,llvm-rs-cc.exe 的主要作用是对 Renderscript 的处理 |
NOTICE.txt 和 source.properties | NOTICE.txt 只是给出一些提示的信息;source.properties 是资源属性信息文件,主要显示该资源生成时间、系统类型、资源 URL 地址等。 |
6) samples
samples 是 Android SDK 自带的默认示例工程,里面的 apidemos 强烈推荐初学者学习。
7) system-images
8) temp
9) tools
作为 SDK 根目录下的 tools 文件夹,这里包含重要的工具,比如 ddms 用于启动 Android 调试工具,如 logcat、屏幕截图和文件管理器;而 draw9patch 则是绘制 Android 平台的可缩放 PNG 图片的工具;sqlite3 可以在 PC 上操作 SQLite 数据库;而 monkeyrunner 则是一个不错的压力测试应用,模拟用户随机按钮;mksdcard 是模拟器 SD 映像的创建工具;emulator 是 Android 模拟器主程序,不过从 Android 1.5 开始,需要输入合适的参数才能启动模拟器;traceview 是 Android 平台上重要的调试工具。
Android.jar
作为一个 Java 项目,通常情况下都会引入要用到的工具类,也就是 JAR 包。
Android API 扩展包
核心的 Android API 在每部手机上都可以使用,但仍然有一些 API 接口有各自特别的适用范围,这就是所谓的“可选API”。这些 API 之所以是“可选的”,主要是因为一个手持设备并不一定要完全支持这类 API,甚至可以完全不支持。
安装
安装的话就直接使用官方推荐的Android Studio(SDK在软件内安装)进行安装,不过安装过程会出现以下问题
https://developer.android.google.cn/studio
SDK无法安装(无法勾选)
- 关闭代理
- 跳过第一次SDK检查
3. 4.即可开始下载
UI控件
TextView
layout_width
自定义TextView
Button
StateListDrawable(状态效果)
事件处理
第一种实现方式(事件监听)
click事件是在手指抬起时触发;
那么touch为什么会触发那么多次呢?
因为touch事件触发条件有三种
- 点击时触发
- 在按钮上不抬起,移动触发
- 从按钮上抬起触发
注意!!!,如果onTouch或LongClick回调函数为true,那么就不会执行click的事件,这是因为安卓系统的判断就是如此,事件会被覆盖(消费)
第二种实现方式(标签与方法关联)
EditText
ImageView
缩放类型
ProcessBar
Notification(系统通知)
一个通知的常见属性
简单的通知创建流程
- 创建通知管理
- 根据安卓版本创建消息频道
-
代码实现
/**
* @author LinXiPeng
*/
public class MainActivity extends AppCompatActivity {
private NotificationManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获得通知管理
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//如果安卓版本大于8,那么就可以创建通知频道
NotificationChannel notificationChannel = new NotificationChannel("TestChannel", "自定义测试通知通道", NotificationManager.IMPORTANCE_HIGH);
//将通知频道服务注册到通知管理种
manager.createNotificationChannel(notificationChannel);
}
/**
* 发送系统消息
*
* @param view 视图
*/
public void sendMsg(View view) {
//创建消息点击后的跳转意图
Intent intent = new Intent(this,NotificationTest.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this, "TestChannel")
//设置通知标题
.setContentTitle("自定义的通知标题")
//设置通知内容
.setContentText("你好啊,这是你的通知正文内容")
//设置小图标
.setSmallIcon(R.drawable.ic_baseline_chat_bubble_24)
//设置小图标颜色
.setColor(Color.parseColor("#00ff00"))
//设置大图标,通知右侧图片
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.test))
//点击是否自动关闭
.setAutoCancel(true)
//设置点击后的意图
.setContentIntent(pendingIntent)
.build();
manager.notify(1, notification);
}
/**
* 取消通知
*
* @param view
*/
public void cancelMsg(View view) {
manager.cancel(1);
}
}
效果
设置通知重要性
ToolBar(导航栏)
!!!注意,这里需要提前将values中themes的actionBar设置为NoActionBar,即取消自带的导航栏 在layout中,使用的Androidx中的toolbar,而不是第二个控件
标题居中效果
AlertDialog(提示框)
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="openAlertDialog"
android:text="点击显示对话框" />
</LinearLayout>
MainActivity.java
public void openAlertDialog(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//获得自定义布局
View inflate = getLayoutInflater().inflate(R.layout.dialog__view, null);
builder
//设置图标
.setIcon(R.drawable.ic_baseline_account_box_24)
//设置标题
.setTitle("对话框标题")
//设置对话内容
.setMessage("对话框弹出来啦")
//设置确认按钮的监听事件
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.e("dialog", "onClick: 确定按钮点击了");
}
})
//设置取消按钮的监听事件
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.e("dialog", "onClick: 取消按钮点击了");
}
})
//设置中间按钮的监听事件,不同版本会有不同的位置
.setNeutralButton("知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.e("dialog", "onClick: 中间按钮点击了");
}
})
//设置自定义布局
.setView(inflate)
//创建
.create()
//然后再显示,一定要先创建再显示
.show();
}
自定义布局
效果
PopupWindow(弹窗)
/**
* 打开弹窗
*/
public void openPopWindow(View view) {
//获取视图对象
View inflate = getLayoutInflater().inflate(R.layout.popup_view, null);
//通过id找到视图中的按钮
Button popupViewBtn = inflate.findViewById(R.id.btn);
//创建弹窗, 参数1:弹窗内的视图,宽度,高度
PopupWindow popupWindow = new PopupWindow(inflate, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//设置是否聚焦,点击空白处可关闭弹窗
popupWindow.setFocusable(true);
//通过弹窗视图中的按钮关闭弹窗
popupViewBtn.setOnClickListener(v -> popupWindow.dismiss());
//显示弹窗,参数1:相对哪个控件,x偏移,y偏移
popupWindow.showAsDropDown(view, view.getWidth(), view.getHeight());
}
布局
LinearLayout
常见属性
weight:是分配屏幕的剩余空间,在原本元素的基础上增加
注意: 如果其中一个元素使用了match_parent,那么这个元素占父元素的所有 如果linearLayOut容器中的元素全是match_parent,那么这个时候就会根据权重进行计算,显示对应的元素 例: 为什么红色不显示呢?它的权重不是更多吗? 这样每个元素占用了一个屏幕,也就是三个屏幕 1(显示屏幕空间)-3(需要屏幕空间)=-2(剩余空间) 1(显示空间)-2(剩余空间)*(2/4)(红块权重空间)=0 即红块不显示 不理解也没有关系,没有人会在开发中这样用,解决办法,就是高度统一使用0dp
如果父级容器设置为垂直的,那么权重就在高度上生效,反之如果是水平的,那么就是在宽度上生效
RelativeLayout
常见属性
FremeLayout
TableLayout
常见属性
缺点:只能列合并,无法行合并,而gridLayout解决了这个问题
GridLayout
常用属性
子控件属性
ConstraintLayout(约束布局)
使用Android studio拖拽控件实现,并在控件之间添加约束来实现布局,即约束布局
ListView
Listview布局页面
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp" />
ListViewItem页面布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/vi"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
模拟实体类
public class Bean {
private String name;
public Bean() {
}
public Bean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
页面逻辑层面
public class MainActivity extends AppCompatActivity {
/*
* 模拟数据集合
*/
private List<Bean> items = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//循环模拟listview中的数据---页面创建时加载数据
for (int i = 0; i < 100; i++) {
Bean bean = new Bean();
bean.setName("我是第" + i);
items.add(bean);
}
//通过id拿到listview
ListView listView = findViewById(R.id.listview);
listView.setAdapter(new ListViewAdapter(items, this));
//单项点击监听器
listView.setOnItemClickListener((parent, view, position, id) -> Log.e("listview的单项", position + "被点击"));
}
}
Adapter(item的视图适配类)
public class ListViewAdapter extends BaseAdapter {
private List<Bean> data;
private Context context;
public ListViewAdapter(List<Bean> data, Context context) {
this.data = data;
this.context = context;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return this.data.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//由于item是复用的,避免重复创建,加个判断,只有视图为空的时候才会获取布局
if (convertView == null) {
//拿到item的布局
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
}
//在布局中拿到对应的控件
TextView textView = convertView.findViewById(R.id.vi);
//将当前下标的对象数据取出赋到控件上
textView.setText(this.data.get(position).getName());
//返回item视图
return convertView;
}
}
效果
问题:每渲染一项,都要去查找对应的控件,那如果item布局中控件比较繁杂,消耗的时间很多怎么办呢?
解决办法
RecyclerView
导入依赖
使用注意事项 RecyclerView并非SDK自带需要提前引入依赖包
//添加RecyclerView依赖包
implementation 'androidx.recyclerview:recyclerview:1.1.0'
注意目录结构
recycleView跟listview一样都需要设置一个Adapter 不同的是,recycleView的自定义Adapter继承的RecyclerView.Adapter在泛型上必须编写一个视图处理的类,目的是为了统一,避免部分开发者没有采用这个方法 同时这个视图处理也必须继承RecyclerView.ViewHolder并重写单参构造器,而这个参数就是item的视图布局对象
MainActivity
public class MainActivity extends AppCompatActivity {
private NotificationManager manager;
private List<Bean> items = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获得通知管理
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//如果安卓版本大于8,那么就可以创建通知频道
NotificationChannel notificationChannel = new NotificationChannel("TestChannel", "自定义测试通知通道", NotificationManager.IMPORTANCE_HIGH);
//将通知频道服务注册到通知管理种
manager.createNotificationChannel(notificationChannel);
//循环模拟listview中的数据
for (int i = 0; i < 100; i++) {
Bean bean = new Bean();
bean.setName("我是第" + i);
items.add(bean);
}
//通过id拿到recyclerView
RecyclerView recyclerView = findViewById(R.id.rv);
/*由于recyclerView的灵活性,原本的listview只能为垂直滑动列表的形式渲染,
而recyclerView考虑到布局的灵活性,将布局形式抽取出来让开发者自定义,所以这里需要给recyclerView创建一个布局*/
//线性
/*LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);*/
//网格
/*GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 4);
recyclerView.setLayoutManager(gridLayoutManager);*/
//瀑布
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
RecyclerMyAdapter recyclerMyAdapter = new RecyclerMyAdapter(items, this);
//设置点击监听事件
recyclerMyAdapter.setOnItemClickListener(position -> {
Log.i(String.valueOf(position), "被点击啦");
});
recyclerView.setAdapter(recyclerMyAdapter);
}
}
MyAdapter
public class RecyclerMyAdapter extends RecyclerView.Adapter<RecyclerMyAdapter.MyViewHolder> {
private List<Bean> data;
private Context context;
public RecyclerMyAdapter(List<Bean> data, Context context) {
this.data = data;
this.context = context;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//直接拿到item的整个布局,并通过单参构造放入viewHolder
View view = View.inflate(context, R.layout.list_item, null);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
//给控件赋值
holder.textView.setText(this.data.get(position).getName());
}
/**
* 获得数据的长度
*
* @return 数据长度
*/
@Override
public int getItemCount() {
return this.data == null ? 0 : this.data.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
/*
* 控件成员变量
*/
private TextView textView;
/**
* 在这里通过传入的item布局拿到控件信息,并保存起来
*
* @param itemView 布局视图
*/
public MyViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.vi);
//通过itemView的点击监听事件触发adapter的点击事件
itemView.setOnClickListener(v -> {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(getBindingAdapterPosition());
}
});
}
}
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnItemClickListener {
/**
* 当单项被点击的时候触发的方法
*
* @param position 单项的下标
*/
void onItemClick(int position);
}
}