Content

    • Android开发环境的配置与准备
    • Android 概述
    • Android UI 开发
    • Activity
    • 数据存储
    • UI进阶
    • 内容提供者
    • 广播接收者
    • 服务

  • 什么是 Android?

安卓是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的操作系统
主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发
----百度

Android开发环境的配置与准备


采用 Java 语言开发 Android 应用

  • 链接 老师的视频.
    链接 安装Android Studio.
    链接 AndroidDevTools.

SDK

  • 更新 sdk 通过 SDK Manager
    NDK 是底层开发用的,暂时不管

Genymotion

  • Genymotion 是一套完整的工具,它提供了 Android 虚拟环境
    链接 Genymotion下载及安装.

Virtural Box

  • VirtualBox 是一款开源虚拟机软件,号称最强免费虚拟机软件,
    可以在当前运行的系统上构建出一台虚拟电脑,
    在这台虚拟电脑上可以安装系统和软件,就像真实的电脑一样操作
    20.2.14

链接 联想thinkpad笔记本电脑怎么开vt虚拟化.
(我并没有开BIOS的VT的虚化,竟也能用虚拟机…可能是以前开过不知道?)

  • 查看是否已经开虚拟化,打开任务管理器 > 性能 > CPU > 虚拟化 : 已启用

build.gradle 文件

  • 完成上述的许多步骤以后,如果 AndroidStudio 中仍会有许多 Error 的出现,
    根本原因可能是顶级 build.gradle 文件中的问题
    可更改为如下:
buildscript {    repositories {        maven { url 'https://maven.aliyun.com/repository/google' }        maven { url 'https://maven.aliyun.com/repository/jcenter'}        mavenLocal()        mavenCentral()        //google()        //jcenter()//我的并没有将这两条语句注释,不然会出问题            }    dependencies {        classpath 'com.android.tools.build:gradle:3.5.2'    }}allprojects {    repositories {        maven { url 'https://maven.aliyun.com/repository/google' }        maven { url 'https://maven.aliyun.com/repository/jcenter'}        mavenLocal()        mavenCentral()        //google()        //jcenter()//还有这条            }}

遇到的问题1:

Gradle sync failed: Cause: invalid type code: 85 Consult IDE log for more details (Help | Show Log) (22 s 73 ms)

解决1 :
链接 下载新的Gradle.zip文件替换.

问题2 插件无法更新
AndroidStudio Firebase services 3.6更新报错.
用浏览器打开报错给的网址,直接下载插件压缩包
然后在 Installed 右边的设置 Install Plugin from Disk,导入压缩包即可
网址 链接.

http://plugins.jetbrains.com/pluginManager/?action=download&id=com.google.services.firebase&build=AI-192.7142.36&uuid=325b0776-a0e1-4765-8883-2a3dfe7cb602

问题3 删除后的应用无法安装
原因: 已经安装过了
解决: cmd 中

adb uninstall 应用程序包名

链接 Android开发环境的设置与准备_问答. 20.2.17

Android 概述


  • 什么是 API?
    Application Programing Interface (应用程序编程接口)
    简单来说就是封装了复杂操作的函数
    链接 什么是API?.

  • 什么是MSDN?
    操作系统的API有很多,需要一个说明文档
    MSDN就是Windows API 的说明文档

Android 版本

  • Android 版本,目前
    平台版本10,API 级别 29,版本号 (VERSION_CODE) Q
    Android 主要用于移动设备

Android平台结构
Android 平台结构,基于 Linux 内核 (Kernel) 的分层结构
五层
① System Apps--------------------------------------------(系统应用)
② Java API FrameWork ---------------------------------(Java API 框架)
③Native C/C++ Libraries、Android Runtime ------(C/C++原生库、ART)
④Hardware Abstraction Layer -------------------------(HAL 硬件抽象层)
⑤Linux Kernel----------------------------------------------(Linux 内核)

上层功能可以依靠 Linux 内核来执行底层功能
硬件抽象层 (HAL),提供标准界面,显示硬件设备功能

Android 5.0 以上,
每个应用都在其自己的进程中运行,都有其自己的 Android Runtime (ART) 实例
DEX (Dalvik Executable) 文件是一种专为 Android 设计的字节码格式
编译工具链 (如 Jack) ,
将 Java 源代码 编译为 DEX 字节码;
删1

  • 原生 C/C++ 库
    许多核心 Android 系统组件和服务(例如 ART 和 HAL)构建自 原生代码
    代码在 以 C 和 C++ 编写的原生库中

  • Java API 框架
    以Java语言编写的API
    以此使用 Android OS 的整个功能集

  • 系统应用
    Android 系统随附的应用,
    和第三方应用一样,没有特殊的状态

APK

  • Android SDK 工具将你的 代码 + 所有数据资源 等 编译到一个APK中
    Android 软件包 (Android application package),.apk
    一个 APK 文件包含一个 Android 应用的所有内容,
    它是 基于 Android 系统的设备 用来安装应用的文件

Android 应用的运行方式和运行权限

  • Android 操作系统是一种多用户 Linux 系统,其中 每个应用都是一个不同的用户
    系统 分配 Linux 用户 ID(该 ID 仅由系统使用,应用并不知晓)
    系统为应用中的 所有文件 设置权限,只有 用户 ID 匹配才能访问
  • 每个进程 都具有自己的虚拟机 (VM)
    因此应用代码是在 与其他应用隔离 的环境中运行
  • 默认情况,每个应用都 在其自己的 Linux 进程 内运行
  • Android 会在需要执行 任何应用组件 时启动相应进程,
    然后在不需要时关闭该线程,实现 最小权限原则
  • 可以安排两个应用共享同一 Linux 用户 ID,这样,
    它们能够相互访问彼此的文件,可在同一 Linux 进程中运行;
    应用可以请求访问设备数据的权限

组件

组件 (Component)
组成 Android 应用
可以单独调用,每个组件都是一个不同的入口点
共有四种不同的应用组件类型:
Activity、服务 Service、广播接收器 BroadcastReceiver、内容提供程序 ContentProvider

  • Activity 表示具有用户界面的 单一屏幕
    一个应用可有多个 Activity,它们之间互相独立

  • 服务是一种在 后台运行 的组件,用于执行长时间运行的操作或为远程进程执行作业
    其他组件可以启动服务,让其运行或与其绑定以便与其进行交互

  • 广播接收器,用于响应系统范围广播通知
    不显示用户界面,会创建状态栏通知;作为通向其他组件的“通道”;
    广播接收器作为 BroadcastReceiver 的子类实现,并且每条广播都作为 Intent 对象进行传递

  • 内容提供程序管理一组 共享 的应用数据
    其他应用可以通过内容提供程序查询、修改数据 (需要权限)
    内容提供程序作为 ContentProvider 的子类实现,并且必须实现让其他应用能够执行事务的一组标准 API

启动组件
Android 系统设计的独特之处,任何应用都可通过 Android 系统间接启动其他应用的组件
向系统传递一则消息,说明想启动特定组件的 Intent,系统随后便会启动该组件
当系统启动某个组件时,会启动该应用的进程 (如果尚未运行),并实例化该组件所需的类

  • Activity、服务、广播接收器,这三种组件类型通过名为 Intent 的异步消息进行启动
    Intent,异步消息机制,一个载体
    Intent 使用 Intent 对象创建,它定义的消息用于启动特定组件或特定类型的组件
    可以指定要执行操作的数据的 URI(对于广播组件,只能定义要广播的通知)
  • 内容提供程序 组件,只会在成为 ContentResolver 的请求目标时启动

清单

应用清单文件
用来确认组件存在,声明组件
AndroidManifast.xml,是核心配置文件
包含一个应用的所有组件的声明

  • 声明组件
    ___Activity
    ____服务
    ___广播接收器
    ___内容提供程序
    源代码中有,但是未在清单中声明的组件,系统看不见
    不过,广播接收器可以在清单文件中声明或在代码中动态创建
    (如 BroadcastReceiver 对象) 并通过调用 registerReceiver() 在系统中注册

  • 声明组件功能
    Intent 的真正强大之处 在于隐式 Intent 概念
    作用: 描述要执行的 操作 类型 (可选择描述要执行的操作所针对的数据类型),
    让系统 能够 在设备上找到可执行该操作的组件,并启动该组件
    若有多个组件可以执行 Intent 所描述的操作,则由用户选择使用哪一个组件
    系统如何找?
    系统通过将接收到的 Intent 与设备上的 其他应用的清单文件中 提供的 Intent 过滤器 进行比较来确定可以响应 Intent 的组件
    为组件声明 Intent 过滤器
    元素作为组件声明元素的子项进行添加 (意图接收器)

  • 声明应用要求
    在清单中声明,为应用支持的设备类型明确定义一个配置文件
    android:required = "true" ,声明应用要求,例如:没有相机不能安装
    若不要求,则改为 false
    建议API级别 19,对应Android版本 4.4

<manifest ... >    <uses-feature android:name="android.hardware.camera.any"                  android:required="true" />    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />    ...</manifest>

R类

  • Android 项目中包括的每一项资源,SDK 构建工具都会定义一个唯一的整型 ID
    R.java 生成一个系统常量,R.目录.文件名,即资源 ID
    这里的 R类 是系统自动生成的,程序员 不能修改 此类中的内容

  • 限定符
    是一种加入到资源目录名称中,用来定义这些 资源适用的设备配置 的简短字符串

开发者模式

在搭载 Android 4.2 及更高版本的设备上,“开发者选项”默认情况下处于隐藏状态
开发者模式 > 打开USB调试 > USB连接方式选择 传输文件 >
打开Android Studio > 选择Troubleshoot Devices Connections

可以通过 WLAN 连接到设备,不过用USB比用WLAN方便一些

  • Android 调试桥 (Android Debug Bridge) 是一种功能多样的命令行工具
    客户端,发送adb命令给服务器
    守护进程 (adbd),接收abd命令的转接,在设备 (手机) 上运行命令
    服务器,接收abd命令,确定手机上有守护进程,发给手机

日志

  • Logcat,日志,View > Tool Windows > Logcat
    Log level:Verbose (所有日志),Debug,Info,Warn,Error,Assert (级别递增)
    写日志Log.v(String, String),两个参数分别为 项名(键),值 20.3.2
    可通过 过滤器 限制日志的输出

链接 Android Basic_Activity Empty_Activity Bottom_Navigation_Activity Fragment + ViewModel etc.
链接 Android 概述_问答.
链接 Android 概述_习题.

Android UI 开发


  • UI 就是用户界面

ViewGroup 和 View

  • ViewGroup 继承自 View
    View 对象是一块矩形区域,用于在屏幕上绘制可供用户交互的内容
    ViewGroup 对象是一种不可见的容器

布局声明

  • 在 XML 中声明 UI 的优点
    将应用的 外观 与控制应用行为的 代码 分离
    UI 描述位于应用代码外部,在修改或调整外观的描述时,无需修改源代码

加载 XML 资源
启动 Activity 时,Android 框架调用其 onCreate 方法加载布局资源
on开头,通常都是回调方法 (当系统中发生某种事件时,由系统调用的方法)

  • 每个布局文件都必有且只能有一个根元素 (可为 View 或 ViewGroup对象)
    小部件 (微件 widget) 中不能再嵌入其他元素

  • ID 用于在结构树中对 View 对象进行唯一标识
    XML 标记内部的 ID 语法

android:id="@+id/my_button"// + 号表示创建该名称并将其添加到资源内 (R.java中)android:id="@android:id/empty"//引用 Android 资源 ID
  • 创建视图并从应用中引用
//在布局文件中定义一个视图/小部件,并为其分配一个唯一的 ID:<Button android:id="@+id/my_button"/>        //然后创建一个 view 对象实例,并从布局中捕获它(通常在 onCreate() 方法中):Button myButton = (Button) findViewById(R.id.my_button);在 Java 代码中通过 findViewById()方法,获取该 View 的引用,并强制转型为 Button 类型,此后,代码中就可以使用 myButton 来引用该按钮了

布局参数

  • 所有视图组都包括宽度和高度 (layout_width 和 layout_height),并且每个视图都必须定义它们
    wrap_content 同内容一样的尺寸
    match_parent 同父视图几近一样的尺寸

布局位置

  • 每个 view 的形状为一个矩形,left 和 top 表示它的位置,width 和 height 表示它的大小
    下图 利用各种方法获取 View 坐标

度量单位

显示度量单位

  • 长度单位
    in,英寸 (inch),每英寸 2.54 厘米,屏幕物理尺寸
    pt,表示一个点 (Points),1 / 72 英寸,在 72dpi 屏幕上 1pt = 1px,屏幕物理尺寸
  • 像素单位
    px,对应屏幕上实际像素点
    dip = dp,密度无关的像素,一种基于屏幕密度的抽象单位
    sp,和dp的概念相似,用于字体大小的单位
  • 下面六种密度集
    ldpi (low) ~120dpi
    mdpi (medium) ~160dpi
    hdpi (high) ~240dpi
    xhdpi (extra-high) ~320dpi
    xxhdpi (extra-extra-high) ~480dpi
    xxxhdpi (extra-extra-extra-high) ~640dpi
    若把 mdpi 作为基准密度,其他密度的实际像素px = dp * (dpi / 160)

视图 View 的尺寸通过宽度和高度表示
测量宽度和测量高度,视图想要在其父项内具有的大小
绘制宽度和绘制高度,简称宽度高度,视图在绘制时和布局后在屏幕上的实际尺寸
因为程序代码中可能在真正显示布局的时候,
修改了它的大小,最终使得其显示大小和定义大小不一致

常见布局

常见布局
布局的嵌套布局越少,绘制速度越快(扁平的视图层次结构优于深层的视图层次结构)
布局用 LinearLayout (线性布局)足够,约束布局 (ConstraintLayout) 比较高级比较难

  • 相对布局,RelativeLayout
    每个 view 的位置通过相对它的兄弟 view 或父 view 来进行定位
  • 线性布局,LinearLayout
    使用单个水平行或垂直行来组织子项的布局
  • 表格布局,TableLayout 是 LinearLayout 的子类
    行由 TableRow 表示,表列的个数由包含最多子元素的 TableRow 所决定
  • 网格布局,GridLayout 在 Android4.0之后
    用一组没有宽度的线将屏幕区域划分为纵横交错的格子,将子控件依次放在格子 (cell) 里
    网格线用下标表示
  • 帧布局,FrameLayout
    为每个加入其中的控件创建一个空白区域,只在左上角显示
  • 绝对布局,AbsoluteLayout (不推荐使用)
    需要通过指定 layout_x、layout_y 坐标来控制每一个控件的位置

视图可以定义内边距 (Padding),但不支持外边距 (Margin)
在表示视图组中的视图之间时,可用 margin,别的不可
width 设为 0dp 后,再改 weight 权重,比例关系 (因为是 剩余空间 按权重比例分配)
gravity (重力) 指内容
layout_gravity 指相对父控件的位置
下去自己学 20.3.9

链接 Android UI 开发_问答_1.
链接 Android UI 开发_问答_2.
链接 Android UI 开发_问答_3.
链接 【Android】Android UI 开发_习题.

Activity


链接 【Android】Activity.(占篇幅较大)
链接 【Android】Activity_问答.
链接 【Android】Activity_习题.

数据存储


Internal 内部存储区 只能app访问
External 外部存储区 世界可读,不具有保密性
卸载app时,Internal 中的都删,External public 的文件保留 private 的文件删除
app默认安装到 Internal 存储区

  • 内部存储

获取合适的目录作为File对象
getFilesDir() 为你的app返回一个代表internal目录的File对象
getDir(name,mode) 你的app的内部存储目录中创建或者打开一个目录
getCacheDir() 返回一个用于存放你的app 临时 缓存文件的internal目录

  • 写入文件 (注 openFileOutput 方法默认打开内部存储区目录)
String filename = "myfile";String string = "Hello world!";FileOutputStream outputStream;try {  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);  outputStream.write(string.getBytes());//转换为字节流  outputStream.close();} catch (Exception e) {  e.printStackTrace();}
  • 写一个缓存文件
public File getTempFile(Context context, String url) {    File file;    try {        String fileName = Uri.parse(url).getLastPathSegment();        //createTempFile(String prefix, String suffix, File directory)        //在指定目录中创建空文件, 用给定的前缀名prefix和后缀名suffix        file = File.createTempFile(fileName, null, context.getCacheDir());    } catch (IOException e) {        // Error while creating file    }    return file;}

fileList()方法获取你的app的所有文件名的字符数组

保存静态文件
在项目的 res/raw/ 目录中保存该文件
可以使用 openRawResource() 打开该资源并传递 R.raw. 资源 ID
创建 raw :res右击 > New > Android Resource Directory

  • 外部存储

  • 申请权限 获取外部存储的访问权限
    写权限本身是包含读权限的

<manifest ...>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    ...</manifest>
  • 测试状态 校验外部存储是否可用
/* Checks if external storage is available for read and write */public boolean isExternalStorageWritable() {    String state = Environment.getExternalStorageState();    if (Environment.MEDIA_MOUNTED.equals(state)) {        return true;    }    return false;}//是否可写/* Checks if external storage is available to at least read */public boolean isExternalStorageReadable() {    String state = Environment.getExternalStorageState();    if (Environment.MEDIA_MOUNTED.equals(state) ||        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {        return true;    }    return false;}//是否可读

getExternalStoragePublicDirectory() 获取外部存储的公共区域的目录
getExternalFilesDir() 获取外部存储的私有区域的目录

  • XML序列化 模板
1.设置文件编码方式:serializer.setOutput(fos,"utf-8");//fos为文件输出流对象2.写入xml文件的开始标签:serializer.startDocument("utf-8",true); 第一个参数设置文档的编码格式,第二个参数设置是否是一个独立的文档,一般设置为true3.依次写入各元素(如果有多个元素则可以使用迭代的方式写入,如果标签是嵌套的,则在写入顺序上也是嵌套的):a) 写入开始标签:serializer.startTag(null,"Persons"); 这里第一个参数为xml的命名空间,没有可以用null,第二个参数为标签名b) 如果该标签有属性:serializer.attribute(null,"id",1);其中第一个参数为命名空间,第二个参数是属性名,第三个参数为属性值c) 写入元素内容:serializer.text(person.getName());该参数为实例对象中的某个属性值d) 写入结束标签:serializer.endTag(null,"Persons");第一个参数为命名空间,一般为null即可,第二个参数为结束标签的标签名4.以这个语句表示文档的写入结束:serializer.endDocument()5.通过serializer.flush()将流写入文件中最后,关闭输出流:fos.close()

XML解析

  • DOM解析 只能解析较小的文件
  • SAX解析 逐行扫描 可以解析超大的
  • PULL解析 和SAX差不多

SQLite,是一款轻型的数据库
设计目标 嵌入式设备 用的资源比较少
点命令中的命令名同点之间没有空白符
点命令必须在同一行
点命令不能在普通SQL语句中
点命令不接受注释

.open 数据库名称,如果该数据库在磁盘中不存在,则创建并打开,如果存在,则直接打开.save 数据库名称,如果sqlite3的启动是通过双击windows中的sqlite3.exe的图标打开的,系统会在内存中创建一个数据库,这个数据库如果需要存放到磁盘,则需要使用本命令进行存放。注意如果磁盘上如有同名的数据库,会覆盖。.databases,用于列出数据库.tables,用于列出数据库中的数据表

控制台中输入:chcp 65001,把控制台的字符编码由GBK切换为utf-8
contract类 协约类 伙伴类
定义表名和单个表的列名

public final class FeedReaderContract {    // To prevent someone from accidentally instantiating the contract class,    // give it an empty constructor.    public FeedReaderContract() {}    /* Inner class that defines the table contents */    public static abstract class FeedEntry implements BaseColumns {        public static final String TABLE_NAME = "entry";        public static final String COLUMN_NAME_ENTRY_ID = "entryid";        public static final String COLUMN_NAME_TITLE = "title";        public static final String COLUMN_NAME_SUBTITLE = "subtitle";        ...    }}

创建和删除表的语句

private static final String TEXT_TYPE = " TEXT";private static final String COMMA_SEP = ",";private static final String SQL_CREATE_ENTRIES =    "CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +    FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +    FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +    FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +    ... // Any other options for the CREATE command    " )";private static final String SQL_DELETE_ENTRIES =    "DROP TABLE IF EXISTS " + FeedReaderContract.FeedEntry.TABLE_NAME;

少用 execSQL,因SQL注入

链接 【Android】数据存储.
链接 数据存储_问答.
链接 数据存储_习题.

UI进阶


使用 AdapterView 的三个步骤

  • 1.设置数据源
  • 2.创建 Adapter,并将数据源和 Adapter 绑定在一起
  • 3.将装载好的 Adapter 绑定到我们想要展示的控件上面

AdapterView 的两个子类,ListView、GridView

ListView 是一个可滚动的条目列表,是一个 ViewGroup
通过 Adapter 将数据从数据源自动加载到表中
常用方法 setAdapter 将控件和适配器联系起来

public void setAdapter(ListAdapter adapter)ListAdapter 是 Adapter 的一个子接口主要方法abstract View getView(int position, View convertView, ViewGroup parent)参数position 显示数据项的位置,若数据集是数组,该参数即为下标convertView 被复用的老的 view,若为 null,则回收器中没有,需新建这是防止数据项(表项)过多 设计者对控件进行复用,调用 setTag getTag 指定 ViewHolder 对应的控件//滚出页面的数据条目,放在回收器里parent 绑定的父 view

Adapter 的实现子类 主要:BaseAdapter ArrayAdapter SimpleAdapter

  • BaseAdapter 抽象类
    继承时需要覆写 getView 例如
public View getView(int position, View convertView, ViewGroup parent) {    //ViewHolder用于保存对子view的引用    ViewHolder holder;    if (convertView == null) {        convertView = View.inflate(MainActivity.this,R.layout.list_item, null);//新建        holder = new ViewHolder();        holder.text = (TextView) convertView.findViewById(R.id.tv_list);        holder.icon = (ImageView) convertView.findViewById(R.id.image);        convertView.setTag(holder);    } else {        holder = (ViewHolder) convertView.getTag();//若非空 取出来 设置相应的值    }//通过holder来 绑定数据    holder.text.setText(names[position]);    holder.icon.setImageResource(icons[position]);    return convertView;}static class ViewHolder {//内嵌亦可    TextView text;    ImageView icon;//此处一个表项里包含一个文本和一个图片}
  • ArrayAdapter
    继承自 BaseAdapter,用于显示数据源为任意对象的数组

构造方法

  • 1.public ArrayAdapter (Context context, int resource)
    resource:布局文件的 ID,有且只能有一个 TextView (R.layout.)
    也可用系统给的android.R.layout.simple_expandable_list_item_1
  • 2.public ArrayAdapter (Context context, int resource, int textViewResourceId)
    resource 限制解除
    textViewResourceId:把布局文件中的 TextView id 写上即可 (R.id.)
  • 3.public ArrayAdapter (Context context, int resource, T[] objects)
    比之1,不用 add,直接在第三个参数放入数组
  • 4.public ArrayAdapter (Context context, int resource, int textViewResourceId, T[] objects)
    2 + 3,结合一下

后面就是用表,不说了

  • SimpleAdapter
    用来将静态数据显示在XML文件所定义的视图中

构造方法

public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)
  • from 对应 to,to 数组包含的是布局的所有控件 id,from 数组包含的是 data 取值对应的字符串
public class MainActivity extends ListActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //setContentView(R.layout.activity_main);        SimpleAdapter adapter = new SimpleAdapter(this,getData(),R.layout.activity_main,                new String[]{"img","title"},                new int[]{R.id.iv_img,R.id.tv_title});        setListAdapter(adapter);    }    private List<Map<String,Object>> getData(){        List<Map<String,Object>> list = new ArrayList<>();        Map<String,Object> map = new HashMap<>();        map.put("img",R.drawable.jd);        map.put("title","京东商城");        map.put("info", "no.1");//因 from 数组中不包含 info,故 no.1 不会被写入,也无处可写        list.add(map);        return list;    }}

Fragment 分段

Fragment 类和 Activity 类很像,它包含的回调方法同 activity 相似
其中 Stoppd 状态:移除并且在 back 栈中时

f 与 a 生命周期显著不同在于:

  • activity 停止时,被系统自动放入 系统管理的 back 栈
  • Fragment 停止时,放入宿主 activity 所管理的 back 栈中,
    而且是在事务中移除 Fragment 时,调用 addToBackStack 显式放入的

别的都类似,而且 Activity 的生命周期直接影响 Fragment 的,
只有 Activity 在 Resumed 状态时,Fragment 的生命周期能独立变化
多的回调方法

  • onAttach,Fragment 和 Activity 关联时调用
  • onCreateView,创建同 Fragment 关联的视图层次时调用
  • onDestoryView,视图层次被移除的时候
  • onDetach,解除关联的时候

引入布局,需要 LayoutInflater

public class ArticleFragment extends Fragment {    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        // 把布局应用到fragment        return inflater.inflate(R.layout.article_view, container, false);//false 未绑定 ViewGroup    }}

可以把一个 Fragment 当作一个 view 一样
1.静态,布局文件
Fragment 三种给 id 的方法

  • 1.id 属性
  • 2.tag 属性
  • 3.用其容器 view 的 id

2.编程加入 viewGroup

FragmentManager fragmentManager = getFragmentManager()//管理 FragmentFragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();//开始一个事务 ExampleFragment fragment = new ExampleFragment();fragmentTransaction.add(R.id.fragment_container, fragment);//对 Fragment 进行 添加 移除 替换等都可fragmentTransaction.commit();//记得提交

添加没有 UI 的 Fragment,用来为 activity 提供后台行为
不用实现 onCreateView,没有 id 属性,只能用 tag

放入 back 栈是人工调用 addToBackStack 方法,不会自动放

Fragment 通过 getActivity() 获取 activity 的实例

View listView = getActivity().findViewById(R.id.list);//在 Fragment 调用 getActivity 时,必须已绑定一起,否则返回 null

activity 通过 FragmentManager 中的findFragmentById/Tag 方法来获取 Fragment 的引用

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

Fragment 之间传递信息需要借助 其宿主 Activity

public static class FragmentA extends ListFragment {    // Container Activity must implement this interface    public interface OnArticleSelectedListener {        public void onArticleSelected(Uri articleUri);    }        //判断 宿主 activity 是否实现接口    public void onAttach(Activity activity) {// Fragment 和 Activity 关联时调用        super.onAttach(activity);        try {            mListener = (OnArticleSelectedListener) activity;        } catch (ClassCastException e) {            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");        }    }        public void onListItemClick(ListView l, View v, int position, long id) {//id 点击项        // Append the clicked item's row ID with the content provider Uri        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);        // Send the event and Uri to the host activity        mListener.onArticleSelected(noteUri);//调用该方法    }}

内容提供者


ContentProvider 四大组件之一,管理对结构化数据集的访问

  • 是一个进程同另一个进程连接数据的标准接口
    应用程序之间共享数据的手段之一
  • 以 URI 的形式对外提供数据存取途径
  • 增删改查

ContentResolver,作为客户端,自动与 Provider 暴露的数据进行进程间通信 (可以有多个)

  • ContentProvider 封装数据,并通过 ContentResolver 向其他应用提供数据
  • 通过 Uri 的 authority 找到对应的 ContentProvider

URI 统一资源标识符(Uniform Resource Identifier),标识某一资源名称的字符串

  • URI = scheme + authority + path
    authority = host + port
    path 分层,称片段
  • 给定字符串转换为 URI 用 parse 方法

UriMatcher 用于匹配content provider中的Uri,建立 URI 树

public static final int NO_MATCH//值 -1public UriMatcher (int code)//参数值为 NO_MATCH  创建 URI 树根节点public void addURI (String authority, String path, int code)//添加 uri 到树中,同一个树中 code 不重复int match = sURIMatcher.match(uri);//取其 code 值

ContentUris 关于Content uri的工具类
语法 content://authority/path/id

  • 可用 parseId 方法取其 id 值
  • 用 withAppendedId (Uri contentUri, long id) 方法 在uir 后追加 id

MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型
ContentProvider 有两个方法返回 MIME 类型

  • getType() 返回一个MIME格式的字符串
    你必须实现的一个方法
    若某个 provider 的 authority 是 com.example.app.provider,
    且有一个表的名字为 table1,table1 的多行MIME类型是
vnd.android.cursor.dir/vnd.com.example.provider.table1单行就是将 dir 改为 item
  • getStreamTypes() 返回一个MIME类型的字符串数组
    如果你的 provider 提供文件的话,你需要实现的方法

关于访问权限
默认情况下,无权限,都可访问

  • 设置权限: 元素的 android:permission 属性 假设A
    为了让权限只针对你的provider,使用android:name属性 假设B
  • 访问者 需 元素的 name 属性与 A 匹配
    元素的 name 属性与 B 匹配

Message 类
常用域

  • public int arg1,arg2 传两个 int,多了只能用 setData
    替代 setData 方法的低成本方式
  • public Object obj 传一个 obj,多了同上
  • public int what 消息代码,类似于 requestCode (隐式意图里的)
    每个 Handler 都有自己的消息代码的命名空间,无需担心冲突

常用方法

  • setData
  • getData
  • obtain
    public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj)
    无参数是传给默认的
    参数为 Message orig 复制参数消息,消息复制
  • setTarget
    然后 sendToTarget

Looper
常用方法

  • loop 启动线程中的消息队列
  • prepare 将当前线程转换为 looper 线程
class LooperThread extends Thread {    public Handler mHandler;    public void run() {      Looper.prepare();      mHandler = new Handler() {//新建的 Handler 和所在的线程的 looper 及消息队列相关联 此过程在构造方法中完成          public void handleMessage(Message msg) {              // process incoming messages here          }      };      Looper.loop();    }}

Handler
Handler 使得你可以发送和处理线程的消息队列中的消息
每个handler对象同某个线程及它的消息队列相关

  • 一个线程可有多个,若和消息对象则只能有一个
  • handler 同创建其的线程关联,在哪个线程创建,就和哪个线程的 looper 相关联
  • 可向消息对象发送 消息 和 Runable 对象 (也是个线程对象)

主要用途
1.安排消息 并且在未来的某个时候执行某个 Runable 对象
post 发送的是 Runable 对象,send 发送的才是 Message
2.排队 在其他线程要执行的动作

构造方法

  • Handler(),无参,关联当前
  • Handler(Looper looper),指定关联的 looper

常用方法

  • void handleMessage(Message msg),必须覆写

广播接收者


BroadcastReceiver 组件,用于监听并处理系统中的广播
需要创建并进行注册 (组件都需要注册,在清单文件中注册)
注册的两种方式

  • 静态注册 ,常驻型广播接收者
<receiver android:name = ".MyBroadcastReceiver">//java 文件   <intent-filter android:priority = "1000">//优先级 -1000 ~ 1000 默认为0       <action android:name = "android.provider.Telephony.SMS_RECEIVED" />//接收广播的类型   </intent-filter ></receiver >
  • 动态注册 Context.registerReceiver(),非常驻广播接收者,依赖其所在的组件
    若组件被销毁,该广播也不再启动
1.定义广播接收者private BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {    @Override    public void onReceive(Context context, Intent intent) {       // 相关处理,如收短信,监听电量变化信息    }};2.创建IntentFilter对象IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED " );//指定接收类型优先级通过 setPriority 方法设置3.注册registerReceiver( mBatteryInfoReceiver , intentFilter);注册建议放在 onResume 回调方法中注销建议放在 onPause 中

生命周期
广播接收者对象只在 onReceiver 方法被调用期间有效,方法结束就销毁,所以不能执行异步
所以勿考虑比较耗时的操作,耗时的可用 service

广播的类型

  • 普通,通过 Context.sendBroadcast (intent, receiverPermission) 进行广播,异步,
    接收者以无序方式同时接收,处理结果不能传递,广播不能被截断
    有时系统会让接收者一个一个来,但还是普通广播
    参数
    intent,广播通过意图发送,载体是意图
    receiverPermission,指定权限,可省
  • 有序,通过 Context.sendOrderBroadcast (intent, string) 广播,接收者轮流接收,处理结果可传给下一个
    优先级高的先接收
    public abstract void sendOrderedBroadcast (Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
    BroadcastReceiver ,不管是否中断,指定最终接收者
  • 注,广播用的 intent 与 activity 用的 intent 机制不同

判断广播是否有序 isOrderedBroadcast

有序广播常用方法

  • 中断 abortBroadcast
  • 取消中断 clearAbortBroadcast
  • 获取结果状态和数据
    getResultCode getResultData getResultExtras 结果码,结果数据,附加数据
  • 设置结果状态和数据
    总 public final void setResult (int code, String data, Bundle extras)
    setResultCode setResultData setResultExtras 与上对应

服务


服务的两种形式

  • 1.启动 其它组件调用 startService,启动后无限期运行 需实现 onStartCommand onBind
  • 2.绑定 其它组件调用 bindService,所有绑定取消,服务销毁 需实现 onBind

密集型建议另开单独线程
可以降低发生“应用无响应”(ANR) 错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互

onBind 必须实现,不允许绑定返回 null

service 必须在 清单 中声明
只能通过显示 intent 来启动,所以发布后最好不要改 name

<service android:name=".ExampleService" />android:exported //false 则服务只能用于你的 app

启动服务
调用 startService() 方法并传递 Intent 对象
服务通过 onStartCommand() 方法接收此 Intent

Intent intent = new Intent(this, HelloService.class);startService(intent);//后面系统调用各种方法

intent 是组件和服务之间的唯一通信模式

停止服务:服务调用 stopSelf,组件调用 stopService 方法
只要调用 onStartCommand,就是启动服务,不会自动销毁

继承类

  • Service
    默认使用 主线程,尽量创建一个用于执行所有服务工作的新线程
  • IntentService
    逐一处理所有启动请求,需实现 onHandleIntent

IntentService

创建一个 worker 线程来执行提交到 onStartCommand() 方法的意图,该线程区别于你的 app 的主线程创建一个工作队列,每次向 onHandleIntent() 传递一个 intent,所以你从来不必担心多线程当所有的请求处理完毕以后,你从来不必调用 stopSelf()提供一个返回 null 的 onBind() 的实现提供一个 onStartCommand() 的默认实现,该实现把intent提交到工作队列及你的 onHandleIntent() 实现

通过广播的形式显示结果

Service
实现代码示例

public class HelloService extends Service {  private Looper mServiceLooper;  private ServiceHandler mServiceHandler;  // Handler that receives messages from the thread  private final class ServiceHandler extends Handler {      public ServiceHandler(Looper looper) {          super(looper);      }      @Override      public void handleMessage(Message msg) {          // Normally we would do some work here, like download a file.          // For our sample, we just sleep for 5 seconds.          try {              Thread.sleep(5000);          } catch (InterruptedException e) {              // Restore interrupt status.              Thread.currentThread().interrupt();          }          // Stop the service using the startId, so that we don't stop          // the service in the middle of handling another job          stopSelf(msg.arg1);      }  }  @Override  public void onCreate() {    // Start up the thread running the service.  Note that we create a    // separate thread because the service normally runs in the process's    // main thread, which we don't want to block.  We also make it    // background priority so CPU-intensive work will not disrupt our UI.    HandlerThread thread = new HandlerThread("ServiceStartArguments",            Process.THREAD_PRIORITY_BACKGROUND);    thread.start();    // Get the HandlerThread's Looper and use it for our Handler    mServiceLooper = thread.getLooper();    mServiceHandler = new ServiceHandler(mServiceLooper);  }  @Override  public int onStartCommand(Intent intent, int flags, int startId) {      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();      // For each start request, send a message to start a job and deliver the      // start ID so we know which request we're stopping when we finish the job      Message msg = mServiceHandler.obtainMessage();      msg.arg1 = startId;      mServiceHandler.sendMessage(msg);//发送给线程      // If we get killed, after returning from here, restart      return START_STICKY;  }  @Override  public IBinder onBind(Intent intent) {      // We don't provide binding, so return null      return null;  }  @Override  public void onDestroy() {    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();  }}

START_NOT_STICKY
不会重建,除非有挂起的 intent 需处理

START_STICK
服务重新启动的时候无需提交 intent,除非有挂起的 intent 需处理

START_REDELIVER_INTENT
再次提交 intent

绑定服务
生成 IBinder 接口
解绑 unbindService

实现 onBind
多个client连接到service,系统只执行第一个 client 的 onBind() 方法,返回 IBinder 对象
绑定调用

public abstract boolean bindService (Intent service, ServiceConnection conn, int flags)两个方法public abstract void onServiceConnected (ComponentName name, IBinder service)public abstract void onServiceDisconnected (ComponentName name)

定义 IBinder 接口的方法:

  • 继承Binder类,同一个进程
  • Messenger,不同进程

cast 类型转换

public class LocalService extends Service {    // Binder given to clients    private final IBinder mBinder = new LocalBinder();    // Random number generator    private final Random mGenerator = new Random();//nothing    /**     * Class used for the client Binder.  Because we know this service always     * runs in the same process as its clients, we don't need to deal with IPC.     */    public class LocalBinder extends Binder {        LocalService getService() {            // Return this instance of LocalService so clients can call public methods            return LocalService.this;        }    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    /** method for clients */    public int getRandomNumber() {      return mGenerator.nextInt(100);//只是一个例子    }}

下面的 Activity 绑定上面的 LocalService 并调用其中方法

public class BindingActivity extends Activity {    LocalService mService;    boolean mBound = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);    }    @Override    protected void onStart() {        super.onStart();        // Bind to LocalService        Intent intent = new Intent(this, LocalService.class);        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        // Unbind from the service        if (mBound) {            unbindService(mConnection);            mBound = false;        }    }    /** Called when a button is clicked (the button in the layout file attaches to      * this method with the android:onClick attribute) */    public void onButtonClick(View v) {        if (mBound) {            // Call a method from the LocalService.            // However, if this call were something that might hang, then this request should            // occur in a separate thread to avoid slowing down the activity performance.            int num = mService.getRandomNumber();            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();        }    }    /** Defines callbacks for service binding, passed to bindService() */    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,                IBinder service) {            // We've bound to LocalService, cast the IBinder and get LocalService instance            LocalBinder binder = (LocalBinder) service;            mService = binder.getService();            mBound = true;        }        @Override        public void onServiceDisconnected(ComponentName arg0) {            mBound = false;        }    };}

更多相关文章

  1. android监听键盘
  2. Android(安卓)Activity 详解
  3. Android开发艺术探索知识回顾——第2章 IPC机制:1、IPC 的基础知
  4. Android(安卓)Dagger2 初学笔记
  5. Android菜单Menu的创建
  6. Android(安卓)ViewPager多页面滑动切换以及动画效果
  7. Android两种序列化方式详解(一):Serializable
  8. Android(安卓)使用Parcelable序列化对象
  9. Android(安卓)初始化Setup Wizard——Provision

随机推荐

  1. android用jdbc多线程操作sqlite小结
  2. C语言的一些练习以及自己写一个猜数字小
  3. 算法面试专题课(Java版)
  4. centos LVM(逻辑卷管理)
  5. Android(安卓)NDK开发之旅(5):Android(安
  6. Unity3D之坐标系的转换
  7. 数据结构之哈希表
  8. 基于业务和平台理解数字营销概念
  9. 打卡学习
  10. Plotly中4种文本类型设置详解