Android应用开发特色
四大组件:活动(Activity),服务(Service),广播接收器(Broadcast Receiver)和内容提供器(Content Provider)。
引入布局
1 |
|
1 | R.string.app_name//字符串引用 |
1 | @string/app_name//字符串引用 |
Log日志打印
1 | 级别由高到低 |
设置主活动
1 | <intent-filter> |
方法重写
快捷键:Ctrl+O
Toast
1 | Toast.makeText(LoginActivity.this, "登录成功啦!", Toast.LENGTH_SHORT).show(); |
菜单设置(menu)
1 | 重写onCreateOptionsMenu和onOptionsItemSelected(if else)方法 |
修改themes.xml文件
1 | <resources xmlns:tools="http://schemas.android.com/tools"> |
intent跳转活动
1.显式intent
1 | private Button login; |
2.隐式intent
AndroidManifest.xml
1 | <activity |
要同时匹配action和category才能响应该Intent
FirstActivity.java
1 | button1.setOnClickListener(new View.OnClickListener(){//注册监听器并调用onClick()方法 |
传递数据
1 | private EditText editText; |
1.传出数据
1 | holder.rlContainer.setOnClickListener(v -> { |
2.获取数据
1 | Intent intent = getIntent(); // 获取启动该活动时传递的 Intent |
返回数据给上一个活动
startActivityForResult方法(FirstActivity)
1 | Intent intent = new IntentFirstActivity.this, SecondActivity.class); |
1.点击按钮返回数据:setResult方法(SecondActivity)
1 | holder.rlContainer.setOnClickListener(v -> { |
2.back返回数据:重写onBackPressed方法(SecondActivity)
1 |
|
重写onActivityResult方法(FirstActivity)
1 |
|
活动的生命周期
注:如果活动是对话框式的,要设置AndroidManifest.xml的主题
1 | <activity android:name=".DialogActivity" |
活动被回收前的历史数据保存
使用onSaveInstanceState方法
ProgressBar进度条
1 | <ProgressBar |
1 | // 显示 ProgressBar |
Visibility(控件属性)
visibility 的使用场景
1. visible
- 当需要正常显示视图时。
- 默认状态下所有视图都是
visible。
2. invisible
- 当需要临时隐藏视图,但又需要保留其空间时。
- 常用于占位布局或切换内容时。
- 示例:隐藏未加载的数据占位布局,避免布局重新排列。
3. gone
-
当需要完全移除视图,并让其他视图调整布局时。
-
常用于条件显示的内容,例如动态菜单、可选功能等。
-
示例:表单中某些字段仅在用户选择某个选项时显示。
布局
线性布局(LinearLayout)
1 | android:orientation="vertical" //垂直排列 |
相对布局(RelativeLayout)
1 | 相对于父布局 |
帧布局(Framelayout)
默认情况下控件都位于左上角
1 | android:layout_gravity="left" |
注:所有控件都是直接或间接继承自View的,所有的布局都是直接或间接继承自ViewGroup的,ViewGroup是一种特殊的View.
引入布局与自定义控件
看书!!!
RecyclerView(牛逼控件)
1.app/build.gradle文件的dependencies闭包添加依赖库
1 | implementation("androidx.recyclerview:recyclerview:1.3.2") |
2.在布局中添加RecyclerView控件
1 | <androidx.recyclerview.widget.RecyclerView |
3.准备适配器
新建适配器MyAdapter并继承自RecyclerView.Adapter,泛型为RecyclerView.ViewHolder,ViewHolder是内部类
1.MyViewHolder继承RecyclerView.ViewHolder,在里面的MyViewHolder构造函数中传入参数view(布局)
2.MyAdapter构造函数赋值
3.重写onCreateViewHolder,onBindViewHolder,getItemCount()三个方法
1 | public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { |
4.使用RecyclerView
1 | public class MainActivity extends AppCompatActivity { |
5.RecyclerView的点击事件(MyAdapter)
1 | // 设置点击事件,跳转到编辑页面 |
碎片(Fragment)
简单用法
新建left_fragment.xml和right_fragment_xml(和普通的一样)
新建LeftFragment和RightFragment类,继承自Fragment
1 | public class LeftFragment extends Fragment { |
修改activity_main.xml
1 |
|
动态加载碎片
新建another_right_fragment.xml
1 |
|
修改activity_main_xml
1 |
|
修改MainActivity
1 | public class MainActivity extends AppCompatActivity implements View.OnClickListener{ |
碎片与活动之间进行通信
在活动中调用碎片里的方法
1 | // 从布局文件中获取碎片的实例 |
在碎片中调用活动里的方法
1 | MainActivity activity = (MainActivity)getActivity(); |
碎片的生命周期
1 | 左: //不提供返回栈 |
限定符
根据设备的分辨率或屏幕的大小来决定加载哪个布局
广播
注册广播(动态:在代码中注册):程序启动后才能接受广播
1.新建NetworkChangeReceiver继承BroadcastReceiver,并重写onReceive
2.在onCreate中添加广播并注册
3.取消注册
1 | public class MainActivity extends AppCompatActivity { |
添加权限(AndroidManifest.xml)
1 |
|
注册广播(静态:在AndroidManifest.xml中注册):程序启动前可以接受广播
1.用快捷注册自动生成(包-New-Other-Broadcast Receiver-全选)
2.修改该生成的广播接收器的代码
3.在AndroidManifest.xml添加相应的action
1 | public class BootCompleteReceiver extends BroadcastReceiver { |
1 | <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> |
发送标准广播
1.新建MyBroadcastReceiver
2.在AndroidManifest.xml添加相应的action
3.在MainActivity发送该广播
1 | public class MyBroadcastReceiver extends BroadcastReceiver { |
1 | <receiver |
1 |
|
发送有序广播
1.把MainActivity中的 sendBroadcast方法改成sendOrderedBroadcast方法
2.在AndroidManifest.xml里面设置广播接收器的优先级
3.允许广播继续传递abortBroadcast();
1 |
|
1 | <receiver |
1 | public class MyBroadcastReceiver extends BroadcastReceiver { |
使用本地广播(在应用程序的内部传递)
注:不能静态注册
1 | public class MainActivity extends AppCompatActivity { |
数据储存-持久化技术
文件储存
注:所有的文件都是默认储存到/data/data/
储存数据
1.新建save方法(可不记),运用openFileOutput接收(“文件名”,保存模式(强制覆盖:MODE_PRIVATE,增添追加:MODE_APPEND)),通过BufferedWriter将其写入到文件.
2.重写onDestroy(),确保活动销毁前一定会调用save方法.
1 | public class MainActivity extends AppCompatActivity { |
读取数据
新建load方法(可不记),运用openFileInput接收(“文件名”),通过BufferedReader一行一行读取对象.
1 | public String load() { |
1 | public class MainActivity extends AppCompatActivity { |
SharedPreferences存储(应用于偏好设置功能)
注:所有的文件都是默认储存到/data/data/
储存数据
1.用getSharedPreferences设置存放的文件名与模式(唯一).
2.调用edit()获取对象.
2.存入数据并调用apply()进行提交.
1 |
|
读取数据
1 | // 获取布局文件中的 Button 控件,用于恢复数据 |
注:get( 键 , 默认值 ):键找不到对应值时就会返回默认值
SQL数据库储存(结构化查询语言)
注:所有的文件都是默认储存到/data/data/
SQL语句
1.整型:integer
2.浮点型:real
3.文本类型:text
4.二进制型:blob
1 | // 定义一个常量 CREATE_BOOK,表示创建 Book 表的 SQL 语句 |
创建数据库
-
新建帮助类NoteDbOpenHelper继承自SQLiteOpenHelper
-
重写构造方法
-
重写onCreate调用execSQL执行建表语句
-
重写onUpgrade
-
之后可以调用getWritableDatabase()或getReadableDatabase()获取数据库
1 | // 构造方法,初始化数据库 |
升级数据库
如果你在原先已经创建好数据库的基础上还想增添几类数据就需要升级数据库
1 | public class MyDatabaseHelper extends SQLiteOpenHelper { |
CRUD: Create(创建)、Read(读取)、Update(更新) 和 Delete(删除)
添加数据
insert() 方法通常用于向数据库表中插入新的记录。在 Android 开发中,通常是通过 SQLiteDatabase 类的 insert() 方法来完成数据库操作。这个方法用于将一条新的数据插入到表中。
基本语法:
1 | public long insert(String table, String nullColumnHack, ContentValues values) |
参数说明:
table:表的名称,表示你要插入数据的目标表。nullColumnHack:如果提供了该值并且values中的某个列值为null,则该列将会插入null。通常如果表中允许某些列为NULL,可以传入null。如果没有这种需求,通常传入null。values:ContentValues对象,它存储了要插入的数据。每个键值对代表一列和该列的值。
insert() 方法会返回一个 long 类型的值,表示插入数据后的行 ID。如果插入失败,则返回 -1。
1 | public long insertData(Note note) { |
更新数据
在 Android 中,使用 SQLiteDatabase 类的 update() 方法来更新数据库中的记录。这个方法允许你根据一定的条件(通过 WHERE 子句)更新指定表格中的一条或多条记录。
基本语法:
1 | public int update(String table, ContentValues values, String whereClause, String[] whereArgs) |
参数说明:
table:要更新的表格的名称。values:一个ContentValues对象,包含了要更新的列和值。每个键值对代表一列和对应的值。whereClause:指定更新的条件,类似于 SQL 中的WHERE子句。如果不指定该参数,所有记录都将被更新。whereArgs:whereClause中的占位符(?)的实际值。是一个字符串数组,用来替代whereClause中的参数。
update() 方法返回一个 int 值,表示受影响的行数。如果没有满足条件的记录,则返回 0。
1 | public int updateData(Note note) { |
删除数据
在 Android 中,使用 SQLiteDatabase 类的 delete() 方法可以删除数据库表中的一条或多条记录。这个方法允许你根据条件删除特定的记录,或者删除整个表中的所有记录。
基本语法
1 | public int delete(String table, String whereClause, String[] whereArgs) |
参数说明:
table:要删除数据的表名。whereClause:删除的条件(类似于 SQL 中的WHERE子句),只有符合条件的记录才会被删除。如果为null,将删除表中所有记录。whereArgs:whereClause中占位符(?)的实际值。它是一个字符串数组,用来替代whereClause中的参数。如果没有条件需要传入,通常可以传入null。
delete() 方法返回一个 int 值,表示实际删除的行数。如果没有符合条件的记录被删除,返回 0。
1 | public int deleteFromDbById(String id) { |
查询数据
在 Android 中,SQLiteDatabase 类的 query() 方法用于从数据库表中检索数据。通过 query() 方法,我们可以指定查询的表、要查询的列、查询的条件、排序方式等参数,以便灵活地从数据库中获取所需的数据。
基本语法:
1 | public Cursor query(String table, String[] columns, String selection, |
参数说明:
table:要查询的表名。columns:要返回的列名数组。可以指定要查询的具体列,若为null,表示查询所有列。selection:查询条件,相当于 SQL 中的WHERE子句。可以为null,表示没有条件(即返回所有记录)。selectionArgs:selection中占位符(?)的实际值,必须与selection中的?占位符一一对应。如果没有占位符,传入null。groupBy:对查询结果进行分组的列名数组,通常与GROUP BY子句结合使用。如果不需要分组,可以传入null。having:分组后筛选的条件,相当于 SQL 中的HAVING子句。如果不需要筛选分组后的数据,可以传入null。orderBy:排序方式,相当于 SQL 中的ORDER BY子句。如果为null,则不进行排序。
返回值:
query() 方法返回一个 Cursor 对象,它包含了查询的结果。通过 Cursor,你可以逐行遍历查询结果并获取每一行的数据。
1 | // 根据标题模糊查询笔记 |
SQL语法
1 | SQLiteDatabase db = this.getWritableDatabase(); |
内容提供器
Android的内容提供器(Content Provider)是四大组件之一,用于在不同应用间共享数据。它提供统一的接口,允许应用访问或修改其他应用的数据,同时保护数据安全。
主要功能
- 数据共享:支持应用间共享数据,如联系人、短信等。
- 数据访问:通过URI标识数据,使用ContentResolver进行查询、插入、更新和删除操作。
- 数据安全:通过权限控制保护数据访问。
核心类
- ContentProvider:基类,需实现增删改查等方法。
- ContentResolver:用于与ContentProvider交互。
- Uri:标识数据,格式为
content://authority/path/id。
使用步骤
- 定义ContentProvider:继承并实现必要方法。
- 声明Provider:在
AndroidManifest.xml中注册。 - 访问数据:通过ContentResolver操作数据。
在程序运行时申请权限(危险权限)
1.在activity_main.xml设置一个按钮
2.在MainActivity运用隐式intent(导入android.Manifest)
3.在AndroidManifest.xml添加权限
1 | import android.Manifest; |
1 | <uses-feature |
访问其他程序中的数据
ContentResolver用法
ContentResolver 是 Android 中用于访问内容提供器(Content Provider)数据的核心类。它充当应用程序与内容提供器之间的桥梁,允许应用程序通过 URI 查询、插入、更新和删除其他应用程序提供的数据。
核心功能
- 查询数据:通过
query()方法从内容提供器中获取数据。 - 插入数据:通过
insert()方法向内容提供器中添加数据。 - 更新数据:通过
update()方法修改内容提供器中的数据。 - 删除数据:通过
delete()方法从内容提供器中删除数据。
使用步骤
- 获取 ContentResolver 实例
通过getContentResolver()方法获取ContentResolver实例。 - 定义 URI
URI 是访问内容提供器中数据的唯一标识符。格式通常为:
content://<authority>/<path>/<id>
例如:- 访问联系人数据:
content://com.android.contacts/contacts - 访问媒体文件:
content://media/external/images/media
- 访问联系人数据:
- 操作数据
使用ContentResolver的方法(如query()、insert()、update()、delete())操作数据。
示例代码
1. 查询数据
以下示例演示如何查询联系人数据:
1 | import android.database.Cursor; |
2. 插入数据
以下示例演示如何向媒体库插入一张图片:
1 | import android.content.ContentValues; |
3. 更新数据
以下示例演示如何更新联系人名称:
1 | import android.content.ContentValues; |
4. 删除数据
以下示例演示如何删除联系人:
1 | import android.net.Uri; |
关键点
-
URI
URI 是访问内容提供器中数据的唯一标识符。不同的内容提供器有不同的 URI。 -
权限
访问某些内容提供器(如联系人、媒体库)需要在AndroidManifest.xml中声明权限。例如:1
2
3
4<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />运行 HTML
-
游标管理
使用Cursor查询数据后,务必调用cursor.close()关闭游标,避免内存泄漏。 -
线程安全
ContentResolver的操作通常是同步的,建议在子线程中执行耗时操作(如查询大量数据)。
创建内容提供器
1.新建Myprovider继承自ContentProvider,并重写6个方法
2.借助UriMatcher匹配内容内容URI
3.AndroidManifest.xml会自动注册
以下是 ContentProvider 的 6 个核心方法及其作用:
1. onCreate()
-
作用:在
ContentProvider创建时调用,用于执行初始化操作(如打开数据库连接)。 -
调用时机:当
ContentProvider第一次被访问时调用。 -
返回值:
boolean,表示ContentProvider是否初始化成功。 -
示例:
1
2
3
4
5
6
public boolean onCreate() {
// 初始化数据库连接
dbHelper = new MyDatabaseHelper(getContext());
return true; // 返回 true 表示 ContentProvider 初始化成功
}
2. query()
-
作用:查询数据,返回一个
Cursor对象。 -
参数:
uri:数据的 URI,用于指定要查询的数据源。projection:要查询的列(字段)数组。selection:查询条件,类似于 SQL 中的WHERE子句。selectionArgs:查询条件的参数值。sortOrder:排序方式,类似于 SQL 中的ORDER BY子句。
-
返回值:
Cursor,包含查询结果。 -
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// 获取一个可读的SQLiteDatabase实例
SQLiteDatabase db = dbHelper.getReadableDatabase();
// 执行查询操作,查询"my_table"表,并根据传入的参数筛选、排序结果
Cursor cursor = db.query("my_table", projection, selection, selectionArgs, null, null, sortOrder);
// 设置内容通知URI,允许内容观察者在数据发生变化时收到通知
cursor.setNotificationUri(getContext().getContentResolver(), uri);
// 返回查询结果的Cursor对象
return cursor;
}
3. insert()
-
作用:插入新数据,返回新插入数据的 URI。
-
参数:
uri:数据的 URI,用于指定要插入的数据源。values:要插入的数据,封装在ContentValues对象中。
-
返回值:
Uri,表示新插入数据的 URI。 -
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Uri insert(Uri uri, ContentValues values) {
// 获取可写的SQLiteDatabase实例
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 插入数据到数据库表"my_table"中,并获取插入的行ID
long id = db.insert("my_table", null, values);
// 如果插入成功(id大于0),则构造新插入数据的URI
if (id > 0) {
// 使用ContentUris.withAppendedId()为插入的记录生成一个唯一的URI
Uri newUri = ContentUris.withAppendedId(uri, id);
// 通知内容观察者该URI的数据已经发生变化
getContext().getContentResolver().notifyChange(newUri, null);
// 返回新插入数据的URI
return newUri;
}
// 如果插入失败,抛出异常
throw new SQLException("Failed to insert row into " + uri);
}
4. update()
-
作用:更新数据,返回受影响的行数。
-
参数:
uri:数据的 URI,用于指定要更新的数据源。values:要更新的数据,封装在ContentValues对象中。selection:更新条件,类似于 SQL 中的WHERE子句。selectionArgs:更新条件的参数值。
-
返回值:
int,表示受影响的行数。 -
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 获取可写的SQLiteDatabase实例,以便对数据库进行更新操作
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 执行更新操作,将数据表"my_table"中符合条件的行更新为新的值
int rowsUpdated = db.update("my_table", values, selection, selectionArgs);
// 如果更新操作影响了至少一行数据,通知内容观察者数据已变化
if (rowsUpdated > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
// 返回更新的行数
return rowsUpdated;
}
5. delete()
-
作用:删除数据,返回被删除的行数。
-
参数:
uri:数据的 URI,用于指定要删除的数据源。selection:删除条件,类似于 SQL 中的WHERE子句。selectionArgs:删除条件的参数值。
-
返回值:
int,表示被删除的行数。 -
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 获取可写的SQLiteDatabase实例,以便对数据库进行删除操作
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 执行删除操作,删除表"my_table"中符合条件的行
int rowsDeleted = db.delete("my_table", selection, selectionArgs);
// 如果删除操作影响了至少一行数据,通知内容观察者数据已变化
if (rowsDeleted > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
// 返回删除的行数
return rowsDeleted;
}
6. getType()
-
作用:返回指定 URI 对应的 MIME 类型。
-
参数:
uri:数据的 URI。
-
返回值:
String,表示 MIME 类型。 -
MIME 类型格式:
- 单条数据:
vnd.android.cursor.item/vnd.<authority>.<path> - 多条数据:
vnd.android.cursor.dir/vnd.<authority>.<path>
- 单条数据:
-
示例:
1
2
3
4
5
6
7
8
9
10
11
public String getType(Uri uri) {
// 如果 URI 的最后一个路径段为空,返回表示多条数据的 MIME 类型
if (uri.getLastPathSegment() == null) {
return "vnd.android.cursor.dir/vnd.com.example.provider.my_table";
} else {
// 否则,返回表示单条数据的 MIME 类型
return "vnd.android.cursor.item/vnd.com.example.provider.my_table";
}
}
完整示例
以下是一个简单的 ContentProvider 实现示例:
1 | import android.content.ContentProvider; |
总结
ContentProvider 的 6 个核心方法分别是:
onCreate():初始化。query():查询数据。insert():插入数据。update():更新数据。delete():删除数据。getType():返回 MIME 类型。
通过实现这些方法,可以创建一个功能完整的 ContentProvider,用于在 Android 应用中共享数据。
手机多媒体
通知(进阶)
1.创建通知通道并请求通知权限
2.用getSystemService()获取一个NotificationManager对通知进行管理
3.创建Notification对象并进行一些基础设置
4.用notify()显示通知
1 | /** |
1 | public class NotificationActivity extends AppCompatActivity { |
1 | <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> |
调用摄像头拍照
1.设置Button和ImageView控件
2.申请相机权限,创建File对象存放在SD卡的应用关联缓存目录下
3.把File转换为Uri,被内容提供器FileProvider通过getUriForFile()接收
4.构建intent启动相机
5.重写onActivityResult处理结果
6.在AndroidManifest.xml注册内容提供器,声明权限
7.在xml目录下创建file_paths.xml共享路径
1 |
|
1 | /** |
1 | <uses-permission android:name="android.permission.CAMERA" /> |
1 |
|
从相册中选择图片
1.申请权限
2.创建并调用openAlum()方法
3.重写onActivityResult处理结果
4.针对不同图片格式进行不同操作
1 | /** |
播放音频
1.创造一个MediaPlayer对象
2.动态申请权限
3.调用initMediaPlayer()进行初始化

1 |
|
1 | /** |
播放视频
1.创造一个VideoView对象
2.动态申请权限
3.调用initVideoPath()进行初始化
1 |
|
1 | /** |
WebView的用法
可以用WebView嵌入一个游览器
1.在activity_main.xml导入WebView控件
2.在MainActivity获取实例,并用getSettings设置属性
3.用setWebViewClient使目标网页在WebView显示
4.加入权限并加开http的读取(也可以直接使用更稳定的https连接)
1 |
|
1 | public class MainActivity extends AppCompatActivity { |
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
运用HTTP协议访问网络
使用HttpURLConnection
1.调用sendRequestWithHttpURLConnection()方法开启子线程,并使用HttpURLConnection发出HTTP请求
2.利用BufferedReader对服务器返回的流进行读取,并将结果传到showResponse方法中
3.在showResponse里通过runOnUiThread将线程切换到主线程,然后更新UI元素
1 | /** |
服务(Service)
Android多线程编程
1 | new Thread(new Runnable(){ |
在子线程中更新UI
如果想要更新程序里的UI元素必须在主线程中进行(UI线程不安全)
1.在主线程进行操作
2.在子线程进行回调
1 | <?xml version="1.0" encoding="utf-8"?> |
1 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { |
异步消息处理机制
-
Handler
- 定义:Handler是一个接口,用于在不同线程之间发送和处理消息。
- 功能:通过Handler,可以将消息或Runnable对象发送到与之绑定的Looper所在的线程,处理消息或执行任务。
- 使用场景:常用于更新UI,因为Android不允许在子线程直接修改UI,需要通过Handler将UI操作发送到主线程执行。
-
Message
- 定义:Message是Handler处理的消息对象,用于封装需要传递的数据。
- 功能:Message可以包含数据和标识信息,通过Handler发送到目标线程,供相应的处理逻辑使用。
- 使用场景:当需要在不同线程之间传递数据时,使用Message来封装数据,并通过Handler发送。
-
Runnable
- 定义:Runnable是一个接口,表示可以被执行的任务。
- 功能:通过实现Runnable的run方法,可以定义需要在不同线程执行的任务。Handler可以通过post方法执行Runnable。
- 使用场景:当需要在子线程执行某些任务,并通过Handler在主线程更新UI时,使用Runnable定义任务。
-
Looper
- 定义:Looper是消息队列的管理者,负责不断地从消息队列中取出消息,并分发给相应的Handler处理。
- 功能:每个线程如果需要处理消息,必须有一个Looper,它会运行一个无限循环,处理消息队列中的消息。
- 使用场景:主线程默认有一个Looper,子线程如果需要使用Handler处理消息,必须手动创建Looper并启动消息循环。
服务的基本用法
1.启动服务(onCreate()在第一次执行,onStartCommand()每次启动都执行)
2.停止服务(在服务里的任何位置调用stopSelf()也可以停止服务)
3.绑定服务与解绑服务 :
在服务里创建一个新的Binder对象(新建一个内部类继承自Binder).
在活动中创建一个ServiceConnection的匿名类,并重写onServiceConnected(绑定调用)与onServiceDisconnected(解绑调用)方法
调用bindService和unbindService方法
1 | /** |
1 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { |
IntentService(简单创建异步的,会自动停止的服务)
1 | public class MyIntentService extends IntentService { |
1 | if (v.getId() == R.id.start_intent_service) { |
1 | <service android:name=".MyIntentService" /> |
服务的最佳实践—完整版的下载实例
1.添加一个OKHttp的依赖
2.定义一个DownloadListener回调接口
3.新建一个DownloadTask继承自AsyncTask
doInBackground()用于后台执行具体的下载逻辑
onProgressUpdate()用于在界面上更新当前的下载进度
onPostExecute()用于通知最终的下载结果
4.创建DownloadService服务(记得创建通道,用DownloadBinder实现服务与活动之间的通信)
5.修改activity_main.xml
6.修改MainActivity(活动销毁了,一定要记得对服务进行解绑)
7.注册权限
1 | implementation("com.squareup.okhttp3:okhttp:4.9.0") |
1 | public interface DownloadListener { |
1 | public class DownloadTask extends AsyncTask<String, Integer, Integer> { |
1 | public class DownloadService extends Service { |
1 | // 主活动类,继承自AppCompatActivity,并实现了View.OnClickListener接口 |
1 | <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> |
Material Design设计原则
Toolbar(标题栏)
1.设置主题为Theme.AppCompat.Light.NoActionBar(淡色主题)
2.在activity_main.xml用Toolbar替代ActionBar
3.在menu下创建一个toolbar.xml文件,并设计相应item
4.在Mainactivity获取toolbar实例并设置点击事件
1 | <resources xmlns:tools="http://schemas.android.com/tools"> |
1 | <androidx.appcompat.widget.Toolbar |
1 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); |
1 | <?xml version="1.0" encoding="utf-8"?> |
滑动菜单
DrawerLayout控件
1.在activity_main.xml添加DrawerLayout
2.在MainActivity添加一个导航按钮
1 |
|
1 | // DrawerLayout对象 |
NavigationView(滑动菜单页面)
1.导入依赖库
2.在menu下创建nav_menu.xml文件,并设计相应item
3.在layout下创建一个nav_header.xml文件(头像区)
4.在activity_main.xml使用NavigationView
5.创建点击事件
1 | implementation("de.hdodenhof:circleimageview:2.1.0") |
1 |
|
1 |
|
1 | <com.google.android.material.navigation.NavigationView |
1 | // 获取NavigationView对象 |
悬浮按钮和可交互显示
FloatingActionButton
1.在activity_main.xml
2.设置点击事件
1 | <com.google.android.material.floatingactionbutton.FloatingActionButton |
1 | // 获取FloatingActionButton对象 |
Snackbar(可交互的Toast)
1 | // 设置FloatingActionButton的点击事件 |
CoordinatorLayout(加强版FrameLayout)
1 | <androidx.coordinatorlayout.widget.CoordinatorLayout |
卡片式布局
CardView(提供圆角与阴影等效果)
1.添加依赖库(CardView与RecyclerView)
2.在activity_main.xml调用RecyclerView
3.定义一个实体类(构造方法Fruit)与一个自定义布局(Fruit_item.xml)
4.创建RecyclerView适配器
5.在MainActivity展示卡片
1 | implementation("androidx.recyclerview:recyclerview:1.3.2") |
1 | <androidx.recyclerview.widget.RecyclerView |
1 | package com.example.materialtest; |
1 |
|
1 | public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { |
1 | private Fruit[] fruits = new Fruit[]{ |
AppBarLayout(垂直方向上的Linearlayout,滚动事件的封装)
1.将Toolbar嵌套到AppBarLayout
2.给RecyclerView指定一个布局行为
3.在activity_main.xml添加app:layout_scrollFlags=“scroll|enterAlways|snap”(滑动时隐藏Toolbar)
1 | <com.google.android.material.appbar.AppBarLayout |
1 | // androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
1 | app:layout_scrollFlags="scroll|enterAlways|snap" |
下拉刷新
1.在activity_main.xml中实现SwipeRefreshLayout核心类
2.在MainActivity处理刷新逻辑
1 | <androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
1 | private SwipeRefreshLayout swipeRefresh; |
可折叠式标题栏
CollapsingToolbarLayout(作用于Toolbar之上的布局)
1.设置FruitActivity作为详情展示界面
2.修改activity_fruit.xml
3.在AppBarLayout嵌套一个CollapsingToolbarLayout
4.在FruitActivity编写功能逻辑
1 |
|
1 | public class FruitActivity extends AppCompatActivity { |
充分利用系统空间状态栏
1.使用android:fitsSystemWindows=“true”
2.在values-v21创建一个styles.xml
3.创建values的styles.xml
4.在AndroidManifest.xml应用FruitActivityTheme主题
1 | android:fitsSystemWindows="true" |
1 |
|
1 |
|
使用Intent传递对象
一、Serializable(Java 原生接口)
1. 核心特点
- 简单易用:仅需实现接口,无需额外代码。
- 性能一般:依赖反射机制,序列化和反序列化速度较慢。
- 适用场景:数据存储到文件、网络传输等对性能要求不高的场景。
2. 实现步骤
1 | import java.io.Serializable; |
3. 序列化与反序列化
1 | // 发送方 |
二、Parcelable(Android 专用接口)
1. 核心特点
- 高效:基于内存的二进制流操作,速度极快。
- 复杂实现:需要手动编写序列化/反序列化逻辑。
- 适用场景:内存中数据传输(如 Activity 间传递对象)。
2. 实现步骤
1 | import android.os.Parcel; |
3. 在 Activity 间传递对象
1 | // 发送方 |
三、对比与选型建议
| 特性 | Serializable | Parcelable |
|---|---|---|
| 实现复杂度 | 简单(自动) | 复杂(手动编写代码) |
| 性能 | 低(反射机制) | 高(直接内存操作) |
| 适用场景 | 存储/网络传输 | 内存中数据传输 |
| Android 系统支持 | 支持 | 仅限 Android |
- 选型原则:
- 需要跨平台或持久化存储 → Serializable。
- Android 内存高效传输(如 Intent/Bundle) → Parcelable。
- 优化技巧:
- 使用 Kotlin 的
@Parcelize注解可自动生成 Parcelable 代码。 - 对复杂对象考虑使用
Bundle或第三方库(如 Gson + Serializable)。
- 使用 Kotlin 的
全局获取Context
1.定制一个Application类MyApplication
2.在AndroidManifest.xml对其进行指定
3.在需要的地方调用
1 | public class MyApplication extends Application { |
1 | <application |
1 | Toast.makeText(MyApplication.getContext(), "You clicked Backup", Toast.LENGTH_SHORT).show(); |
定制日志工具
1.新建一个LogUtil类
2.用LogUtil打印日志
1 | public class LogUtil { |
1 | LogUtil.d("MainActivity", "You clicked Delete"); |
多窗口模式
1.取消进入多窗口时活动的重新创建
1 | android:configChanges="orientation|keyboardHidden|screenSize|screenLayout"> |
2.禁用多窗口模式
1 | android:resizeableActivity="false"> |
说些什么吧!