8.Paging

Paging概述

1
Paging 库可帮助您加载和显示来自本地存储或网络中更大的数据集中的数据页面。此方法可让您的应用更高效地利用网络带宽和系统资源;

使用 Paging 库的优势

1
2
3
4
5
- 分页数据的内存中缓存。该功能有助于确保您的应用在处理分页数据时高效使用系统资源。
- 内置的请求去重功能,可确保您的应用高效利用网络带宽和系统资源。
- 可配置的RecyclerView适配器,会在用户滚动到已加载数据的末尾时自动请求数据。
- 对 Kotlin 协程和数据流以及LiveData和 RxJava 的一流支持。
- 内置对错误处理功能的支持,包括刷新和重试功能。
1
implementation 'androidx.paging:paging-runtime:2.1.0'

//没有认真研究,写真实项目时再认真学习下,以下是简单的使用案例,真实项目可能不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* PagedList: 数据源获取的数据最终靠PagedList来承载。
* 对于PagedList,我们可以这样来理解,它就是一页数据的集合。
* 每请求一页,就是新的一个PagedList对象。
*/
public class StudentViewModel extends ViewModel {

// 看源码:@1 listLiveData 数据怎么来的
private final LiveData<PagedList<Student>> listLiveData;

public StudentViewModel() {
StudentDataSourceFactory factory = new StudentDataSourceFactory();

// 初始化 ViewModel,如何生产一页数据出来给我!
this.listLiveData = new LivePagedListBuilder<Integer, Student>(factory, Flag.SIZE)
.setBoundaryCallback(new MyBoundaryCallback())
.build();
}
// TODO 暴露数据出去
public LiveData<PagedList<Student>> getListLiveData() {
return listLiveData;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

public class RecyclerPagingAdapter extends PagedListAdapter<Student, RecyclerPagingAdapter.MyRecyclerViewHolder> {
// TODO 比较的行为
private static DiffUtil.ItemCallback<Student> DIFF_STUDNET = new
DiffUtil.ItemCallback<Student>() {
// 一般是比较 唯一性的内容, ID
@Override
public boolean areItemsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
return oldItem.getId().equals(newItem.getId());
}

// 对象本身的比较
@Override
public boolean areContentsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
return oldItem.equals(newItem);
}
};

protected RecyclerPagingAdapter() {
super(DIFF_STUDNET);
}

@NonNull
@Override
public MyRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null);
return new MyRecyclerViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull MyRecyclerViewHolder holder, int position) {
Student student = getItem(position);

// item view 出来了, 分页库还在加载数据中,我就显示 Id加载中
if (null == student) {
holder.tvId.setText("Id加载中");
holder.tvName.setText("Name加载中");
holder.tvSex.setText("Sex加载中");
} else {
holder.tvId.setText(student.getId());
holder.tvName.setText(student.getName());
holder.tvSex.setText(student.getSex());
}
}

// Item 优化的 ViewHolder
public static class MyRecyclerViewHolder extends RecyclerView.ViewHolder {

TextView tvId;
TextView tvName;
TextView tvSex;

public MyRecyclerViewHolder(View itemView) {
super(itemView);
tvId = itemView.findViewById(R.id.tv_id); // ID
tvName = itemView.findViewById(R.id.tv_name); // 名称
tvSex = itemView.findViewById(R.id.tv_sex); // 性别
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
RecyclerPagingAdapter recyclerPagingAdapter;
StudentViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

recyclerView = findViewById(R.id.recycle_view);
recyclerPagingAdapter = new RecyclerPagingAdapter();

// 最新版本初始化 viewModel
viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory())
.get(StudentViewModel.class);

// LiveData 观察者 感应更新
viewModel.getListLiveData().observe(this, new Observer<PagedList<Student>>() {
@Override
public void onChanged(PagedList<Student> students) {
// 再这里更新适配器数据
recyclerPagingAdapter.submitList(students);
}
});
recyclerView.setAdapter(recyclerPagingAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
}
7.Hilt

HILT 是什么

1
2
3
4
5
6
7
8
9
Hilt 提供了一种合并 Dagger 的标准方法 依赖项注入到 Android 应用程序中。

Hilt 的目标是:
1.简化 Android 应用的 Dagger 相关基础架构。
2.要创建一组标准的组件和示波器以简化设置, 可读性/理解力,以及应用程序之间的代码共享。
3.提供一种简单的方法来为各种生成提供不同的绑定 类型(例如测试、调试或发布)。

Hilt 通过代码为您生成 Dagger 设置代码。这带走了 大多数使用 Dagger 的样板,实际上只留下了 定义如何创建对象以及注入对象的位
置。Hilt 将生成 Dagger 组件和用于自动注入 Android 类的代码

添加依赖项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//1. 配置Hilt的插件路径(project->build.gradle)
buildscript {
...
dependencies {
...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}

buildscript {//需要放在plugins之前
dependencies {
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
plugins {
id 'com.android.application' version '7.1.3' apply false
id 'com.android.library' version '7.1.3' apply false

}
//2. 引入Hilt的插件(app->build.gradle )
apply plugin: 'com.android.application'
apply plugin: 'dagger.hilt.android.plugin'

plugins {
id 'com.android.application'
id 'dagger.hilt.android.plugin'
}
//3. 添加Hilt的依赖库及java8
android{
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
...
implementation "com.google.dagger:hilt-android:2.28-alpha"
annotationProcessor "com.google.dagger:hilt-android-compiler:2.28-alpha"
}

Hilt 目前支持以下 Android 类:

1
2
3
4
5
6
1.Application
2.Activity
3.Fragment
4.View
5.Service
6.BroadcastReceiver

最简单的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1.提供一个对象
public class HttpObject {
}
2.编写Module
@InstallIn(ActivityComponent.class)
@Module
public class HttpModule {
@Provides
public HttpObject getHttpObject(){
return new HttpObject();
}
}
3.注入到Activity
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Inject
HttpObject httpObject2;

4.Application中注册
@HiltAndroidApp
public class MyApplication extends Application {
}
5.全局单例使用
@InstallIn(ApplicationComponent.class)
@Module
public class HttpModule {

@Provides
@Singleton
public HttpObject getHttpObject(){
return new HttpObject();
}
}
注意:Hilt注入的字段是不可以声明成private

-为接口注入实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//1
public interface AnalyticsService {
void analyticsMethods();
}
//2
public class AnalyticsServiceImpl implements AnalyticsService {
...
@Inject
AnalyticsServiceImpl(...) {
...
}
}
//3
@Module
@InstallIn(ActivityComponent.class)
public abstract class AnalyticsModule {

@Binds
public abstract AnalyticsService bindAnalyticsService(
AnalyticsServiceImpl analyticsServiceImpl
);
}

HILT 常用的注解的含义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@HiltAndroidApp
@HiltAndroidApp 将会触发 Hilt 的代码生成,作为程序依赖项容器的基类
生成的 Hilt 依附于 Application 的生命周期,他是 App 的父组件,提供访问其他组件的依赖
在 Application 中配置好后,就可以使用 Hilt 提供的组件了;组件包含 Application,Activity,Fragment,View,Service 等。
@HiltAndroidApp
创建一个依赖容器,该容器遵循 Android 的生命周期类,目前支持的类型是: Activity, Fragment, View, Service, BroadcastReceiver.
@Inject
使用 @Inject 来告诉 Hilt 如何提供该类的实例,常用于构造方法,非私有字段,方法中。
Hilt 有关如何提供不同类型的实例信息也称之为绑定
@Module
module 是用来提供一些无法用 构造@Inject 的依赖,如第三方库,接口,build 模式的构造等。
使用 @Module 注解的类,需要使用 @InstallIn 注解指定 module 的范围
增加了 @Module 注解的类,其实代表的就是一个模块,并通过指定的组件来告诉在那个容器中可以使用绑定安装。
@InstallIn
使用 @Module 注入的类,需要使用 @InstallIn 注解指定 module 的范围。
例如使用 @InstallIn(ActivityComponent::class) 注解的 module 会绑定到 activity 的生命周期上。
@Provides
常用于被 @Module 注解标记类的内部方法上。并提供依赖项对象。
@EntryPoint
Hilt 支持最常见的 Android 类 Application、Activity、Fragment、View、Service、BroadcastReceiver 等等,但是您可能需要
在Hilt 不支持的类中执行依赖注入,在这种情况下可以使用 @EntryPoint 注解进行创建,Hilt 会提供相应的依赖。
image-20231116112108212 image-20231116112129629 image-20231116112154771
6.Room

Room是什么?

1
2
Room 是一个轻量级 orm 数据库,本质上是一个SQLite抽象层,但是使用起来会更加简单,类似于Retrofit库。Room 在开发阶段通过注解
的方式标记相关功能,编译时自动生成响应的 impl 实现类

ORM映射关系设计与详解

1
2
3
对象关系映射(英语:Object Relational Mapping,简称ORM,或0/RM,或0/R mapping),是一种程序设计技术,用于实现面向对象编程
语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。如今已有很多免费和
付费的ORM产品,而有些程序员更倾向于创建自己的ORM工具。
image-20231110141607880

Room使用:

1
2
implementation "androidx.room:room-runtime:2.2.3"
annotationProcessor "androidx.room:room-compiler:2.2.3"
1
2
3
4
官网的文档也指出,我们需要创建这三个文件,才能正式的使用Room:
1.Entity:普通Java Bean类
2.DAO:定义一个接口类
3.Room Database:定义一个抽象类,并继承Room Database

1.定义Entity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* 1.Entity类需要使用@Entity注解标注,并且其中的tableName即数据表名,可以改为自己想取的名字
* 2.构造函数:构造函数可以允许多个,但只允许只有一个不加@Ignore注解,其它的都得加上@Ignore注解,实际运行发现,若多个构造函数不
* 加@Ignore,将无法通过编译。至于多个构造函数的存在是因为CRUD的特性决定的,例如删除,只需要id主键就行了。因此提供了多个构造
* 函数
* 3.Getter和Setter方法:这些方法是必须要的,否则无法对对象属性进行读取或修改
*/

//Entity类需要使用@Entity注解标注,并且其中的tableName即数据表名,可以改为自己想取的名字
@Entity(tableName = "StudentDao")
public class StudentEntity {
/**
* 使用@PrimaryKey声明为主键,并且允许自动生成
* 使用@ColumnInfo表明这个属性是表中的一列列名,并可以指明列的名称
*/
@PrimaryKey(autoGenerate = true)//autoGenerate = true 使得主键的值自动生成
@ColumnInfo(name = "id")
private int id;
@ColumnInfo
private String name;
@ColumnInfo
private String pwd;
@ColumnInfo
private int age;


public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}
}

2.定义 Dao:

DAO类主要是提供对数据库的访问方法,是一个接口类。通过将SQL语句与方法结合的方式,降低结构的复杂程度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* DAO层接口,需要添加@Dao注解声明
* 所有的操作都以主键为依托进行
*/
@Dao
public interface StudentDao {

/**
* 查询所有的数据,返回List集合
* @return
*/
@Query("Select * from StudentDao")
List<StudentEntity> getAllStudentList();

/**
* 传递参数的集合,注意 Room通过参数名称进行匹配,若不匹配,则编译出现错误
* @param personId
* @return
*/
@Query("select * from StudentDao where id in (:personId)")
List<StudentEntity> getStudentById(int[] personId);

/**
* 返回一定条件约束下的数据,注意参数在查询语句中的写法
* @param minAge
* @param maxAge
*/
@Query("select * from StudentDao where age between :minAge and :maxAge")
List<StudentEntity> getStudentByAgeRange(int minAge, int maxAge);

/**
* 插入数据,onConflict = OnConflictStrategy.REPLACE表明若存在主键相同的情况则直接覆盖
* 返回的long表示的是插入项新的id
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insertStudent(StudentEntity studentEntity);

/**
* 更新数据,这意味着可以指定id然后传入新的person对象进行更新
* 返回的long表示更新的行数
*/
@Update
int updatePerson(StudentEntity studentEntity);


/**
* 删除数据,根据传入实体的主键进行数据的删除。
* 也可以返回long型数据,表明从数据库中删除的行数
*/
@Delete
int deletePerson(StudentEntity studentEntity);
}

3.Room Database类构建

得益于Room的良好封装,这个类的构建步骤只有两步,非常的简单。

  • 创建一个StudentDatabase抽象类并继承自RoomDatabase类
  • 声明一个StudentDao()的抽象方法并返回StudentDao的对象引用
1
2
3
4
5
6
7
/**
* 指明是需要从那个class文件中创建数据库,并必须指明版本号
*/
@Database(entities = {StudentEntity.class}, version = 1)
public abstract class StudentDataBase extends RoomDatabase {
public abstract StudentDao studentDao();
}

这样就完成了对RoomDatabase的构建,这里要注意引入@Database注解,它表明需要与哪一个Entity类产生联系,生成对应的数据库表。并且version版本号是一个必填字段,在这里,我们定义为1即可。

4.在Activity中操作Room

这里要特别注意的是:虽然Room可以通过调用.allowMainThreadQueries()方法允许在主线程中进行查询,但是实际操作中禁止在主线程操作数据库,避免出现ANR问题。需要开启子线程进行数据的操作!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class RoomActivity extends AppCompatActivity {
private StudentDataBase studentDataBase;
private StudentDao studentDao;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room);
studentDataBase = Room.databaseBuilder(
getApplicationContext(),
StudentDataBase.class,
"StudentDao"
).build();
studentDao = studentDataBase.studentDao();
}

private int defaultClick=1;
public void addData(View view) {
new Thread(new Runnable() {
@Override
public void run() {
StudentEntity studentEntity = new StudentEntity();
studentEntity.setName("name:"+defaultClick);
studentEntity.setAge(defaultClick);
studentEntity.setPwd("pwd:"+defaultClick);
long l = studentDao.insertStudent(studentEntity);
defaultClick++;
if (l!=0){
Log.e("tyl","添加成功");
}else {
Log.e("tyl","添加失败");
}
}
}).start();
}

public void deleteData(View view) {
new Thread(new Runnable() {
@Override
public void run() {
List<StudentEntity> allStudentList = studentDao.getAllStudentList();
if (allStudentList!=null&&allStudentList.size()!=0){
studentDao.deletePerson(allStudentList.get(allStudentList.size()-1));
}
}
}).start();
}

public void changeData(View view) {
new Thread(new Runnable() {
@Override
public void run() {
List<StudentEntity> allStudentList = studentDao.getAllStudentList();
if (allStudentList!=null&&allStudentList.size()!=0){
StudentEntity studentEntity = allStudentList.get(allStudentList.size() - 1);
studentEntity.setName("upData");
studentEntity.setAge(0);
studentDao.updatePerson(studentEntity);
}
}
}).start();
}

public void queryData(View view) {
new Thread(new Runnable() {
@Override
public void run() {
List<StudentEntity> allStudentList = studentDao.getAllStudentList();
if (allStudentList!=null&&allStudentList.size()!=0){
for (int i = 0; i <allStudentList.size() ; i++) {
StudentEntity studentEntity = allStudentList.get(i);
Log.e("tyl","id:"+studentEntity.getId());
Log.e("tyl","name:"+studentEntity.getName());
Log.e("tyl","pwd:"+studentEntity.getPwd());
Log.e("tyl","age:"+studentEntity.getAge());
}
}
}
}).start();
}
}
5.ViewModle

什么是ViewModel

1
2
3
4
它是介于View(视图)和Model(数据模型)之间的一个东西。它起到了桥梁的作用,使视图和数据既能分离,也能保持通信。即ViewModel 
是以生命周期的方式存储与管理UI相关数据。
用于分离 UI 逻辑与 UI 数据。在发生 Configuration Changes 时,它不会被销毁。在界面重建后,方便开发者呈现界面销毁前
的 UI 状态。

ViewModel的特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.数据持久化
2.异步回调问题,不会造成内存泄漏
3.隔离Model层与View层
4.Fragments间共享数据

ViewModel出现所解决的问题:
1.Activity的具体生命周期管理,每一次反转都是去重新创建了一个Activity
2.Activity的使用,正常onDestory都会清空数据,每次onCreate都会重新加载进来
(手机旋转时,activity会销毁后新建1个activity,viewmodel相当于在做了数据恢复,onCreate数据恢复)

业务目的:
是为了达成数据持久化,去规避Activity在设计当中对于旋转处理造成的实例Activity重置下的数据保存问题
1.ViewModel = Boudle使用!
2.只不过是作了上层的封装!
3.利用了jetpack组件套当中的应用!
4. 在上一个被销毁的Activity与新建的这个Activity之间建立一个Boudle数据传递!

1.依赖库

1
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

2.使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//1.model类
public class DataModel extends ViewModel {
private int age=0;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//2.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>

<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_display"
android:gravity="center"
android:textColor="@color/black"
android:layout_width="match_parent"
android:layout_height="40dp"/>
<Button
android:id="@+id/bn_change"
android:onClick="change"
android:text="change"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/bn_rote"
android:onClick="rote"
android:text="rote"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
//3.使用类
public class ViewModelActivity extends AppCompatActivity {
private ActivityViewModelBinding binding;
private DataModel dataModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_view_model);
//不能直接 new DataModel(),这种方式无法自动恢复数据
dataModel = new ViewModelProvider(this).get(DataModel.class);
Log.e("tyl","age="+ dataModel.getAge());
}

public void change(View view) {
dataModel.setAge(dataModel.getAge()+1);
binding.tvDisplay.setText(dataModel.getAge()+"");
}

//旋转后activity销毁后重新onCreate,但是会恢复age在销毁前的值
private Boolean isLand=false;
public void rote(View view) {
setScreenOrientation(isLand);
}
public void setScreenOrientation(boolean isLandScape){
if (isLandScape){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 横屏
}else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);// 横屏
}
isLand=!isLand;
}
}
4.Dagger2

什么是Dagger2

1
2
Dagger 2是由Google开发的 依赖注入框架,它利用Java和Java注解处理器的强大功能,提供了一种优雅的方式来进行依赖注入。
Dagger 2基于一组注解和代码生成器,可以在编译时自动生成依赖注入的代码,从而提高性能和类型安全性。

dagger2 简单理解

1
2
3
4
5
1.一般我们使用一个实体类或者工具或者请求,比如在MainActivity中使用UerInfo.class,我们会在new Userinfo(),去使用
而dagger帮我们省略了这一步,dagger去管理new UserInfo(),我们直接在Activity中使用。
2.dagger 理解其实就是相当于买了快递后,快递送货流程
实现此流程需要四个注解 @Module @Component @Provides @Inject
如下图:

img

dagger2 的使用

1.在build.gradle中引入插件

1
2
3
4
// dagger2 的功能支持
implementation 'com.google.dagger:dagger:2.4'
// dagger2 自己的注解处理器
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'

2.简单使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//1.HttpObject 对象-->快递
public class HttpObject {
private String HttpClient="client";
public String getHttpClient() {
return HttpClient;
}
public void setHttpClient(String httpClient) {
HttpClient = httpClient;
}
}
//2.HttpModule-->包裹
@Module// 包裹注解
public class HttpModule {
@Provides // 暴露出去注解
public HttpObject providerHttpObject(){
//........
return new HttpObject();
}
}
//3.Component 类似于快递员,拿到所有包裹->根据地址配给用户
//存放module的组件
@Component(modules = {HttpModule.class, DataModule.class})
public interface MyComponent {
//注入的位置就写在参数上 不能用多态
void injectMainActivity(Dagger2Activity mainActivity);
}
//4.用户使用-->接收快递
public class Dagger2Activity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
//rebuild项目后才有DaggerMyComponent文件,Dagger+自定义的Component名字
// 方式1: 注入
// DaggerMyComponent.create().injectMainActivity(this);
// 方式2:
DaggerMyComponent.builder().httpModule(new HttpModule()).build().injectMainActivity(this);
Log.e("tyl",httpObject.getHttpClient());
}
}

3.dagger2 单例使用

1
2
3
1.局域单例:存在于当前类中,只有一个实例,需要增加一个单例注解 @Singleton。
2.全局单例:存在于整个项目,只有一个实例,需要增加一个单例注解 @Singleton,全局单例需要配合Application使用,否则只能是
当前activity单例。

局域单例示例:(在moudle和Component类中添加@Singleton注解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//1.moudle
@Module
public class HttpModule {
@Singleton
@Provides // 暴露出去注解
public HttpObject providerHttpObject(){
//........
return new HttpObject();
}
}
//2.Compinent类
@Singleton
@Component(modules = {HttpModule.class})
public interface MyComponent {
void injectMainActivity(Dagger2Activity dagger2Activity);
}
//3.使用示例:
public class Dagger2Activity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Inject
HttpObject httpObject2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
//rebuild项目后才有DaggerMyComponent文件,Dagger+自定义的Component名字
// 方式1: 注入
// DaggerMyComponent.create().injectMainActivity(this);
// 方式2:
DaggerMyComponent.builder().httpModule(new HttpModule()).build().injectMainActivity(this);
Log.e("tyl",httpObject.hashCode()+"");
Log.e("tyl",httpObject2.hashCode()+"");
//得出结果值一样
}
}

全局单例示例:(在moudle和Component类中添加@Singleton注解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//1.Component中添加次级要调用的类
@Singleton
@Component(modules = {HttpModule.class})
public interface MyComponent {
//多少个类就自定义多少个方法
void injectMainActivity(Dagger2Activity dagger2Activity);
void injectSecActivity(Dagger2Activity2 dagger2Activity2);
}
//2.application中注入
public class MyApplication extends Application {
private MyComponent myComponent;
private static MyApplication context;
@Override
public void onCreate() {
super.onCreate();
context = this;
myComponent = DaggerMyComponent.builder()
.httpModule(new HttpModule())
.build();
}
public static MyApplication getInstance() {
return context;
}
public MyComponent getMyComponent() {
return myComponent;
}
}
//3.主activity中执行调用
public class Dagger2Activity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
MyApplication.getInstance().getMyComponent().injectMainActivity(this);
Log.e("tyl",httpObject.hashCode()+"");
}
public void jump(View view) {
startActivity(new Intent(this,Dagger2Activity2.class));
}
}
//4.次activity中执行调用
public class Dagger2Activity2 extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApplication.getInstance().getMyComponent().injectSecActivity(this);
Log.e("tyl",httpObject.hashCode()+"");
}
}

4.多个Component组合依赖

dagger2不能使用多个Component同时注入同一个类中 这种情况需要进行Component的组合;

先确定使用哪个 Component 作为主 Component,确定后,主 Component 仍然执行注入操作,而其他 Component 作为依赖项,不再执行注入,转而提供 Module 提供的对象。一般我们会选择 ApplicationComponent 作为主 Component。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//1.新建一个对象DataObject
public class DataObject {
private String data="data";
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
//2.新建一个moudle
@Module
public class DataModule {
@Provides
public DataObject providerDatabaseObject(){
//........
return new DataObject();
}
}
//3.新建一个Component
@Component(modules = {DataModule.class})
public interface SecondComponent {
//使用依赖关系,就不再使用这种语法
//void injectMainActivity(Dagger2Activity dagger2Activity);
//直接提供object对象及自定义方法名
DataObject providerDatabaseObject();
}
//4.在ApplicationComponent 的 @Component 注解值中增加 dependencies,指定依赖的 Component:
@Singleton
@Component(modules = {HttpModule.class},dependencies = {SecondComponent.class})
public interface MyComponent {
void injectMainActivity(Dagger2Activity dagger2Activity);
void injectSecActivity(Dagger2Activity2 dagger2Activity2);
}
//5.application中新增指定的实现类
public class MyApplication extends Application {
private MyComponent myComponent;
private static MyApplication context;
@Override
public void onCreate() {
super.onCreate();
context = this;
myComponent = DaggerMyComponent.builder()
.httpModule(new HttpModule())
//指定 PresenterComponent 的实现类
.secondComponent(DaggerSecondComponent.create())
.build();
}
public static MyApplication getInstance() {
return context;
}
public MyComponent getMyComponent() {
return myComponent;
}
}
//6.使用
public class Dagger2Activity extends AppCompatActivity {
@Inject
HttpObject httpObject;
@Inject
DataObject dataObject;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
MyApplication.getInstance().getMyComponent().injectMainActivity(this);
Log.e("tyl",httpObject.hashCode()+"");
Log.e("tyl2",dataObject.getData()+"");
}
}
  1. @Scope

假如现在有个需求,想让 DataObject 也变成个单例对象,按照我们之前的做法,给 PresenterModule 中的 Provides 方法和 PresenterComponent 加上 @Singleton 之后,会发现编译报错了:

1
com.demo.dagger.component.ApplicationComponent also has @Singleton

它说 ApplicationComponent 中已经有 @Singleton 了。显然,Dagger2 要求 @Singleton 不能用在多个组件上。

1
2
3
4
5
6
使用 @Scope 的原则:

1.多个 Component 上面的 Scope 不能相同
2.没有 Scope 的 Component 不能依赖有 Scope 的组件
3.使用作用域注解的模块只能在带有相同作用域注解的组件中使用
4.使用构造方法注入(通过 @Inject)时,应在类中添加作用域注解;使用 Dagger 模块时,应在 @Provides 方法中添加作用域注解

@Scope 注解表示作用域,被其修饰的类或提供对象的方法会被做成单例,所以我们可以用 @Scope 自定义一个作用域注解:

1
2
3
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface MyScope {}//自定义名字

实际上它和 @Singleton 一样只是名字不同:

1
2
3
4
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

以下示例编译报错,未查明原因

Component使用

1
2
3
4
5
6
7
@MyScope
@Component(modules = {DataObject.class})
public interface SecondComponent {
//使用依赖关系,就不再使用这种语法
// void inject(MainActivity activity);
DataObject providerDatabaseObject();
}

moudle使用

1
2
3
4
5
6
7
8
9
10
@Module
@MyScope
public class DataModule {
@MyScope
@Provides
public DataObject providerDatabaseObject(){
//........
return new DataObject();
}
}
3.DataBinding

什么是DataBinding

1
2
3
4
DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的
耦合性,使代码逻辑更加清晰。
DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件
中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常。

启用 DataBinding 的方法是在对应 Model 的 build.gradle 文件里加入以下代码,同步后就能引入对 DataBinding 的支持

1
2
3
4
5
6
android {
...
buildFeatures {
dataBinding true
}
}

简单使用

启用 DataBinding 后,这里先来看下如何在布局文件中绑定指定的变量

打开布局文件,选中根布局的 ViewGroup,按住 Alt + 回车键,点击 “Convert to data binding layout”,就可以生成 DataBinding 需要的布局规则

img

这里先来声明一个 Modle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class User {
private String name;
private String pwd;
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}
}

和原始布局的区别在于多出了一个 layout 标签将原布局包裹了起来,data 标签用于声明要用到的变量以及变量类型,要实现 MVVM 的 ViewModel 就需要把数据(Model)与 UI(View)进行绑定,data 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type= "com.tyl.mystudy2.User"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:textSize="50dp"
android:id="@+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}"
/>
<TextView
android:textSize="50dp"
android:id="@+id/tv2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.pwd}"
/>
</LinearLayout>
</layout>

通过 @{viewModel.name} 使 TextView 引用到相关的变量,DataBinding 会将之映射到相应的 getter 方法 之后可以在 Activity 中通过 DataBindingUtil 设置布局文件,省略原先 Activity 的 setContentView() 方法,并为变量 userInfo 赋值

如果 User 类型要多处用到,也可以直接将之 import 进来,这样就不用每次都指明整个包名路径了,而 java.lang.* 包中的类会被自动导入,所以可以直接使用

1
2
3
4
5
6
<data>
<import type="com.tyl.mystudy2.User"/>
<variable
name="user"
type= "User"/>
</data>

由于 @{user.name}在布局文件中并没有明确的值,所以在预览视图中什么都不会显示,不便于观察文本的大小和字体颜色等属性,此时可以为之设定默认值(文本内容或者是字体大小等属性都适用),默认值将只在预览视图中显示,且默认值不能包含引号

1
android:text="@{user.name,default=defaultValue}"

在 Activity 中通过 DataBindingUtil 设置布局文件,省略原先 Activity 的 setContentView() 方法,并为变量 userInfo 赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DataBindingActivity extends AppCompatActivity {
private ActivityDatabindingBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//注意DataBindingUtil.setContentView()的返回值是DataBindingUtil工具生成的Binding类,
//布局文件名来生成,将之改为首字母大写的驼峰命名法来命名,并省略布局文件名包含的下划线
binding = DataBindingUtil.setContentView(this, R.layout.activity_databinding);
//数据是从网络或是数据库拿来的
User user=new User("jett","123");
binding.setUser(user);//view.setText(text);
binding.setVariable(BR.name,"jett");
}
}

此外,也可以通过 ActivityMain2Binding 直接获取到指定 ID 的控件

1
binding.tv1.setText("tyl");

Databinding 同样是支持在 FragmentRecyclerView 中使用 。例如,可以看 Databinding 在 Fragment 中的使用

1
2
3
4
5
6
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
FragmentBlankBinding fragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false);
fragmentBlankBinding.setHint("Hello");
return fragmentBlankBinding.getRoot();
}

以上实现数据绑定的方式,每当绑定的变量发生变化的时候,都需要重新向 ViewDataBinding 传递新的变量值才能刷新 UI 。接下来看如何实现自动刷新 UI

驱动 UI 刷新

实现数据变化自动驱动 UI 刷新的方式有三种:BaseObservableObservableFieldObservableCollection

BaseObservable

一个纯净的 ViewModel 类被更新后,并不会让 UI 自动更新。而数据绑定后,我们自然会希望数据变更后 UI 会即时刷新,Observable 就是为此而生的概念

BaseObservable 提供了 notifyChange()notifyPropertyChanged() 两个方法,前者会刷新所有的值域,后者则只更新对应 BRflag,该 BR 的生成通过注释 @Bindable 生成,可以通过 BR notify 特定属性关联的视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class User extends BaseObservable {
//如果是 public 修饰符,则可以直接在成员变量上方加上 @Bindable 注解
@Bindable
public String name;
//如果是 private 修饰符,则在成员变量的 get 方法上添加 @Bindable 注解
private String pwd;
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
@Bindable
public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
//更新所有字段
this.pwd = pwd;
notifyChange();
}
}

setName() 方法中更新的只是本字段,而 setPwd() 方法中更新的是所有字段

2.LiveData

MyJetpackStudy: jetpack学习 (gitee.com)

1.什么是LiveData

1
2
3
4
5
LiveData是Jetpack组件的一部分,更多的时候是搭配ViewModel来使用,相对于Observable,LiveData的最大优势是其具有生命感知的,
换句话说,LiveData可以保证只有在组件( Activity、Fragment、Service)处于活动生命周期状态的时候才会更新数据。
LiveData是一个可观察的数据持有者类,与常规的Observable不同,LiveData可感知Activity、Fragment、Service的生命周期,确保
LiveData仅更新处于活动生命周期状态的组件观察者。如果应用程序组件观察者处于started或者resumed,则LiveData认为该组件处于
活跃状态,该组件会收到LiveData的数据更新,而其他注册的组件观察者将不会收到任何数据更新。

LiveData的优点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.确保界面符合数据状态
LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中
更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。
2.不会发生内存泄漏
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
3.不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回堆栈中的 activity),它便不会接收任何 LiveData 事件。
4.不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
5.数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的
数据。
6.适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 activity 或 fragment,它会立即接收最新的可用数据。
7.共享资源
您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资
源的任何观察者只需观察 LiveData 对象。

使用 LiveData 对象

1
2
3
4
5
6
7
8
9
10
11
12
请按照以下步骤使用LiveData对象:

1. 创建 LiveData的实例以存储某种类型的数据。这通常在ViewModel类中完成。

2. 创建可定义 onChanged()对象,该方法可以控制当LiveData对象存储的数据更改时会发生什么。通常情况下,您可以在界面控制器
(如 activity 或 fragment)中创建 Observer对象。

3. 使用 observe()方法将Observer对象附加到 LiveData对象。observe()方法会采用LifecycleOwner对象。这样会使Observer对象
订阅LiveData对象,以使其收到有关更改的通知。通常情况下,您可以在界面控制器(如 activity 或 fragment)中附加Observer对象。

当您更新存储在LiveData对象中的值时,它会触发所有已注册的观察者(只要附加的LifecycleOwner处于活跃状态)。
LiveData允许界面控制器观察者订阅更新。当LiveData对象存储的数据发生更改时,界面会自动更新以做出响应。

LiveData的使用

常用方法:

方法名 作用
setValue(T value): 设置 LiveData 的值,并通知所有活跃的观察者进行更新。此方法应在主线程中调用
postValue(T value) 与setValue()类似,但可以在任何线程中调用。内部使用 Handler 来确保更新操作在主线程执行。
getValue() 获取当前 LiveData 实例的值
observe(LifecycleOwner owner, Observer<? super T> observer) 将观察者(Observer)添加到 LiveData。当 LiveData 的值发生变化时,会通知观察者。该方法传入一个 LifecycleOwner 对象(如 Activity 或 Fragment),LiveData 将观察者与该 LifecycleOwner 的生命周期关联起来,以自动管理观察者的注册和取消注册。
hasObservers() 检查 LiveData 是否有活跃的观察者。
removeObserver(Observer<? super T> observer) 从 LiveData 中移除观察者。
observeForever(Observer<? super T> observer) 与 observe()类似,但观察者不会自动注销。需要手动调用 removeObserver()方法来取消观察。

简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class NameViewModel extends ViewModel {
// 使用字符串创建LiveData
private MutableLiveData<String> currentName;
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
}

public class LiveDataActiviry extends BaseActivity {
private NameViewModel model;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=findViewById(R.id.button);
Button button2=findViewById(R.id.button2);
model = new ViewModelProvider(this).get(NameViewModel.class);
// 创建用于更新UI的观察者
Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable String newName) {
// 更新UI
button.setText(newName);
}
};
// 观察LiveData,将此活动作为LifecycleOwner和observer传入
model.getCurrentName().observe(this, nameObserver);
//更新LiveData对象的值
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
//主线程传值
model.getCurrentName().setValue(anotherName);
//子线程传值
// new Thread(new Runnable() {
// @Override
// public void run() {
// model.getCurrentName().postValue("anotherName");
// }
// }).start();
}
});
}
}
1.LifeCycle

MyJetpackStudy: jetpack学习 (gitee.com)

Lifecycle是什么?

1
2
3
4
lifecycle 是属于Android Jetpack(官方开发工具包)—— Architecture(架构组件)中的一员。

官方介绍:构建生命周期感知型组件,这些组件可以根据 Activity 或 Fragment 的当前生命周期状态调整行为。
白话:lifecycle可以和Activity或Fragment生命周期绑定,方便我们做一些跟生命周期相关的业务逻辑。

Lifecycle的特点

1
2
3
4
5
6
7
1.生命周期感知:Lifecycle 可以感知应用组件的生命周期状态,包括活动状态、暂停状态、停止状态等。
2.简化生命周期回调:使用 Lifecycle,可以避免在应用组件中手动实现繁琐的生命周期回调。
3.灵活性:Lifecycle 可以与其他 Jetpack 组件(如 ViewModel、LiveData 等)结合使用,提供更灵活的生命周期管理方案。
4.生命周期安全性:Lifecycle 能够确保在正确的生命周期状态下执行相应的操作,避免了可能导致崩溃或内存泄漏的问题。

通过 Lifecycle,开发者可以更好地管理和控制应用组件的生命周期,执行与生命周期相关的操作,如初始化资源、释放资源、注册/注销观
察者等。它可以帮助开发者编写更健壮、可维护的代码,并提供更好的用户体验。

Lifecycle使用

1.先看看可以引用哪些?

1
2
//androidx
implementation 'androidx.appcompat:appcompat:1.3.0'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   //非androidx
dependencies {
def lifecycle_version = "1.1.1"
// 包含ViewModel和LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// 仅仅包含ViewModel
implementation "android.arch.lifecycle:viewmodel:$lifecycle_version" // For Kotlin use viewmodel-ktx
// 仅仅包含LiveData
implementation "android.arch.lifecycle:livedata:$lifecycle_version"
// 仅仅包含Lifecycles
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // For Kotlin use kapt instead of annotationProcessor
// 如果用Java8, 用于替代compiler
implementation "android.arch.lifecycle:common-java8:$lifecycle_version"
// 可选,ReactiveStreams对LiveData的支持
implementation "android.arch.lifecycle:reactivestreams:$lifecycle_version"
// 可选,LiveData的测试
testImplementation "android.arch.core:core-testing:$lifecycle_version"
}

除了Activity和Fragment外,还可以绑定Service和Application的生命周期。只要引入支持相关的可选库即可;官方提到最多的是Activity和Fragment,是因为平时主要用于这两个组件;其实只要有生命周期的组件都可以跟它绑定。而在Android中大多数的组件都是有生命周期的。

2.简单使用案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.lifecycledemo1;

import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getLifecycle().addObserver(new MyObserver());//1
}
public class MyObserver implements LifecycleObserver{
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume(){
Log.d(TAG, "Lifecycle call onResume");
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onPause(){
Log.d(TAG, "Lifecycle call onPause");
}
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
}

工具类封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//使用:被观察者继承BaseActivity或者添加getLifecycle().addObserver(new BaseLifeCycle());即可
public abstract class BaseActivity <T extends BaseLifeCycle> extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLifecycle().addObserver(new BaseLifeCycle());
}
}

//观察者类
public class BaseLifeCycle implements LifecycleObserver{
private String TAG="tyl";
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreate(LifecycleOwner owner) {
Log.e(TAG, "BaseLifeCycle-onCreate:"+owner.getClass().toString());
}

@OnLifecycleEvent(Lifecycle.Event.ON_START)
void onStart(LifecycleOwner owner) {
Log.e(TAG, "BaseLifeCycle-onStart:"+owner.getClass().toString());
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onStop(LifecycleOwner owner) {
Log.e(TAG, "BaseLifeCycle-onStop:"+owner.getClass().toString());
}

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume(LifecycleOwner owner) {
Log.e(TAG, "BaseLifeCycle-onResume:"+owner.getClass().toString());
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onPause(LifecycleOwner owner) {
Log.e(TAG, "BaseLifeCycle-onPause:"+owner.getClass().toString());
}

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestory(LifecycleOwner owner) {
Log.e(TAG, "BaseLifeCycle-onDestory:"+owner.getClass().toString());
}

@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
void onAny(LifecycleOwner owner) {//无论当前活动处于何种生命周期状态,都会触发相应的生命周期方法
// Log.e(TAG, "BaseLifeCycle-onAny:"+owner.getClass().toString());
}

}

Wine介绍:

1
2
3
4
5
6
7
Wine是一个允许在Linux和其他类Unix操作系统上运行Windows应用程序的兼容层。它通过将Windows API转换为本地系统调用来实现这
一目标。使用Wine,用户可以在Linux系统上运行许多Windows应用程序,而无需安装Windows操作系统。
要使用Wine,用户需要首先安装Wine软件包。然后,他们可以使用“wine”命令来运行Windows应用程序。

官网主页:https://wiki.winehq.org/%E4%B8%BB%E9%A1%B5
官网:https://wiki.winehq.org/Download
官网 ubuntu 安装步骤链接:https://wiki.winehq.org/Ubuntu_zhcn

安装 Wine 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1、如果您使用的是 64 位系统,请开启 32 bit 架构支持(如果您之前没有开启的话):
sudo dpkg --add-architecture i386

2、下载添加仓库密钥:
sudo mkdir -pm755 /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key

3、并添加仓库:
Ubuntu 22.04:
sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/jammy/winehq-jammy.sources

4、更新安装包:
sudo apt update

5、然后安装 以下任一一个安装包:
稳定分支:
sudo apt install --install-recommends winehq-stable
开发分支:
sudo apt install --install-recommends winehq-devel
Staging 分支:
sudo apt install --install-recommends winehq-staging

如果 apt-get 提示缺少依赖,请先安装缺少的依赖,然后重复以上两步(update 和 install)。

//遇到依赖问题:
0468:err:mscoree:CLRRuntimeInfo_GetRuntimeHost Wine Mono is not installed
解决方案:
1.将所有软件及系统更新到最新;
2.sudo apt-get install mono-complete
3.上面还是没有安装成功的话则: sudo aptitude install mono-complete //aptitude可能需要安装

安装winetricks

1
2
3
4
5
1.在软件商城里面搜索安装winetricks。安装后在终端输入winetricks,会打开图形界面设置。
 在winetricks选择一些常用的元件保证常用windows程式的运行。
2.推荐选择的Windows部件主要有:riched20,riched30,ie8,vcrun6,flash11,gdiplus,msls31,msxml6,vcrun2005,vcrun2008,
 winhttp元件。
//有些部件没有,可不安装

字体乱码

1
winetricks上可以安装字体。也可手动将 Windows 下面的字体复制到~/.wine/drive_c/windows/Fonts/ 。

安装/卸载软件

1
2
3
4
5
6
7
安装:在exe文件目录,执行:wine xx.exe
卸载:
  运行 wine uninstall.exe往往没用。直接手动删除。
1、进入 ~/.wine/drive_c(wine的虚拟C盘) 在Program Files 和 Program Files (x86) 找到相应目录删除
2、进入 ~/.local/share/applications/wine/Programs/ 删除
3、进入~/.config/menus/applications-merged删除对应文件(这里对应显示的图标)
  如果同时使用 deepin-wine 和 wine,wine下安装的软件目录可能跑到deepin-wine的目录下。

wine的常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
wine  [程序名]      (运行Windows程序)
winecfg (配置Wine的图形界面设置,可以设置Windows版本、驱动程序、DLL文件等)
wine control (控制面板)
wine taskmgr (任务管理器)
wine uninstaller (卸载安装的Windows应用程序,稍等即可出现添加-删除程序的界面)
wine regedit / regedit (打开wine中Windows注册表编辑器。)
wine notepad (记事本)
wineboot ( 模拟Windows启动和关闭过程)
winefile (打开模拟的Windows文件管理器;)
wineserver 启动wine服务器进程,用于管理wine进程。
winemine 运行Windows版的扫雷游戏;
winetricks 安装特定的Windows应用程序;
winepath [路径] 转换Windows路径到Unix路径;
winemaker 自动生成Makefiles;
regsvr32 [DLL文件名] 注册DLL文件。
wineconsole [程序名] 在控制台中运行Windows程序。


wine msiexec /i install.msi //有些windows的安装程序是使用msi来安装的.
wine start install.msi //使用msi来安装window应用
wine eject d: //如果安装程序需要你换CD,而系统却说不能推出CD,那就打开一个新终端
wine uninstaller //使用命令“uninstaller”来卸载安装的程序,稍等即可出现添加-删除程序的界面

所有命令列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
1、这些子程序通常可通过运行以下程序访问:
wine wineprogram arg1 arg2 ...

2、只要Wine安装在用户的$PATH中)。
这些程序的完整列表可以在Wine的源代码中的programs/下找到。

cacls - 编辑ACL
clock - 显示基本时钟
cmd - 命令提示符实现
cmdlgtst - (用于开发人员)commdlg.dll测试夹具
control - 控制面板实施
eject - 弹出光盘(请注意,wine eject与正常的弹出命令不同)
expand - 展开cabinet(.cab)文件
explorer - explorer.exe实现
hh - HTML帮助(.chm文件)查看器
icinfo - 显示已安装的Wine视频压缩器
iexplore - Internet Explorer实现
lodctr - 加载性能监视器计数器
msiexec - miexec.exe安装.msi文件的实现
net - 启动和停止服务
notepad - 记事本,一个简单的文本编辑器
oleview - 允许浏览和探索COM对象以及配置DCOM
progman - 程序管理器实现
reg - 基于reg控制台的注册表编辑器
regedit - 注册表编辑器实现
regsvr32 - 在注册表中注册OLE组件
rpcss - rpcss.exe的准实现
rundll32"] - 加载DLL并使用指定参数运行入口点
secedit - 安全配置编辑命令
services - 管理服务
spoolsv - 打印包装
start - 启动程序或打开程序中的文档,该程序通常用于带有该后缀的文件
svchost - 服务的(内部)主机进程
taskmgr - 任务管理器实现
uninstaller - 基本程序卸载程序
unlodctr - 卸载性能监视器计数器
view - 图元文件查看器
wineboot - “重新启动”(restarts)Wine,用于Windows应用程序需要真正重新启动时。
winebrowser - 启动本地OS浏览器或邮件客户端
winecfg - Wine的GUI配置工具
wineconsole - 显示Windows控制台
winedbg - Wine调试器内核
winedevice - (内部)管理设备
winefile - 文件资源管理器实现
winemenubuilder - 帮助构建Unix菜单项
winemine - 经典扫雷游戏
winepath - 在Windows和Unix路径格式之间转换
winetest - 适用于无人值守测试和报告提交的所有DLL一致性测试程序
winevdm - Wine虚拟DOS程序
winhelp - 帮助查看器
winhlp32 - 帮助查看器(32位)
winver - 显示“关于葡萄酒”窗口
wordpad - wordpad.exe实现
write - 启动字板(用于Win16兼容性)
xcopy - 与Wine兼容的xcopy程序

这些程序直接运行(即不是“葡萄酒程序”,只是“程序”):

1
2
3
winelauncher-尝试智能地管理使用Wine启动程序的程序。

wineserver-为Wine提供与Windows内核在Windows上提供的服务大致相同的守护进程。

工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
这些工具是帮助Wine开发的脚本和可执行文件的集合。在工具目录(例如~/wine git/tools/wineinstall)中,它们作为独立命令调用。

这些命令的完整列表可以在wine-<version>/tools的源代码中找到/

buildimage - 从SVG文件生成位图和图标文件
c2man.pl - 生成API文档
config.guess - 尝试猜测规范系统名称
findfunc - 尝试查找指定的Wine程序/工具
install-sh - 安装程序、脚本或数据文件
makedep - 在构建Wine之前生成makefile和依赖项
make_fir - 生成dsound使用的fir滤波器
make_makefiles - 根据对源树的更改更新configure和Makefile.in文件
make_requests - 更新Wine服务器协议描述文件
make_specfiles - 在发生更改时更新.spec文件
make_unicode - 基于unicode.org中的文件重建unicode表
make_xftmpl - 从.x源文件生成二进制标头
runtest - wapper脚本,用于从构建树内部运行Wine回归测试之一
sfnt2fon - 仅将位图ttf转换为窗口字体文件
wineapploader - 安装Winelib应用程序后启动该应用程序的包装器脚本
wineinstall - Wine安装脚本(上次更新于2009年;不推荐)
winemaker - 帮助将Windows源代码转换为Winelib程序

环境:android13.0.0_r6

1、SystemUI介绍

1.1、SystemUI摘要

1
2
在Android系统中SystemUI是以应用的形式运行在Android系统当中,即编译SystemUI模块会生产APK文件,源代码路径
在frameworks/base/packages/SystemUI/,安装路径system/priv-app/-SystemUI。

1.2、什么是SystemUI

1
2
3
4
5
6
7
8
9
在前文1.1章节中可知,SystemUI是一个普通的APK文件,即是一个普通的APP,但是,手机使用者看见的所有SystemUI的内容都不像是
一个APP,为什么?既然是一个应用,就是完成特定的功能,SystemUI主要完成的功能有:
(1)、Status bars
(2)、Navigation bars
(3)、Notification
(4)、Lockscreen
(5)、Quick settings
(6)、Overview(recent task switcher)
(7)、VolumeUI

img
img

2、SystemUI的架构

SystemUI的整体架构:

Android SystemUI采用了MVC(Model-View-Controller)架构,以实现界面元素的管理和交互。
它由多个关键组件组成,包括状态栏、导航栏、通知管理器、快捷设置等。
各个组件的功能和相互关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
状态栏(Status Bar):显示系统状态信息,如时间、电池电量、信号强度等,并提供快速访问通知和快捷设置的入口。

导航栏(Navigation Bar):提供设备导航和操作控制,包括返回、主页和最近任务等按钮。

通知管理器(Notification Manager):负责接收、管理和展示通知,包括应用推送的通知和系统事件的通知。

快捷设置(Quick Settings):提供快速访问常用系统设置的面板,如Wi-Fi、蓝牙、亮度等,以方便用户进行快速调整。

锁屏界面(Lock Screen):显示在设备锁定状态下的界面,提供时间、日期、通知预览和快速启动应用等功能。

系统UI控制器(System UI Controller):作为整个SystemUI的控制中心,负责协调各个组件之间的交互和管理。

其他辅助组件:还有一些辅助组件,如电源管理器、音量控制器等,用于管理设备的电源和音量控制。

这些组件相互配合,构成了SystemUI的整体架构。每个组件负责不同的功能和交互,并通过事件触发和消息传递等机制相互关联。理解SystemUI的架构和组件之间的相互关系,有助于我们深入了解系统界面的实现原理,并进行自定义和扩展。

3、SystemUI的启动过程

SystemUI任何内容都不像一个APP,自然它的启动也不像大多APP一样启动一个Activity。SystemUI顾名思义就是全局UI,必须在开机过程中完成启动,并不可退出。
回顾Android系统开机的过程,会创建server进程维护系统各种服务,如下:

1
2
3
4
5
6
7
Android系统启动流程:
1.按下电源,系统上电
2.从固定地址启动固化在ROM的BootLoader程序
3.启动Linux内核,加载init进程
4.启动init进程,fork出zygote进程
5.启动SystemServer,启动Binder线程池以及各种服务
6.AMS启动Launcher, 然后你就看到该看到的了

SystemUI作为系统主要进程,就是在SystemServer启动的服务其中之一

SystemServer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
   public static void main(String[] args) {
new SystemServer().run();
}

private void run() {
......
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
......
}

private void startOtherServices() {
......
mActivityManagerService.systemReady(() -> {
Slog.i(TAG, "Making services ready");
traceBeginAndSlog("StartActivityManagerReadyPhase");
......
try {
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
......
}
}

static final void startSystemUi(Context context, WindowManagerService windowManager) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
Intent intent = new Intent();
intent.setComponent(pm.getSystemUiServiceComponent());//从本地获取systemuiService的包名
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}

总结下代码流程:SystemServermain()方法会调用SystemServer.run()方法,在run方法中,会启动bootStrapService,coreService,以及其他服务otherService,在startOtherServices()中,会注册AMS的systemReady回调,在回调中启动SystemUI,其实就是启动了SystemUIService。

SystemUIService.java

1
2
3
4
5
6
7
//frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
.....
}

SystemUIApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
//frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
public void startServicesIfNeeded() {
final String vendorComponent = SystemUIFactory.getInstance()
.getVendorComponent(getResources());
// Sort the startables so that we get a deterministic ordering.
// TODO: make #start idempotent and require users of CoreStartable to call it.
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
Comparator.comparing(Class::getName));
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents());
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
startServicesIfNeeded(
sortedStartables, "StartServices", vendorComponent);
}

SystemUIApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
private void startServicesIfNeeded(
Map<Class<?>, Provider<CoreStartable>> startables,
String metricsPrefix,
String vendorComponent) {
if (mServicesStarted) {//如果服务已经启动,则直接返回。
return;
}
//初始化一个 CoreStartable 类型的数组 mServices,它的长度为 startables 元素的数量加上(如果有)一个 vendorComponent 的长度。
mServices = new CoreStartable[startables.size() + (vendorComponent == null ? 0 : 1)];
//如果检测到系统还未启动完成,则检查系统属性 “sys.boot_completed” 是否为 1。如果是 1,则设置启动完成标志,并记录日志
if (!mBootCompleteCache.isBootComplete()) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleteCache.setBootComplete();
if (DEBUG) {
Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
}
//使用 mSysUIComponent(也就是 SystemUI 的组件)创建一个 DumpManager 对象,用于管理系统 UI 相关的 dump 文件
mDumpManager = mSysUIComponent.createDumpManager();
//记录一条日志,表示正在启动 SystemUI 服务。
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin(metricsPrefix);

int i = 0;
//对于 startables 中的每个元素,获取它的类名和 CoreStartable 实现类的提供者,然后初始化一个 int 类型的变量 j,
//并将其赋值为 i,以便接下来在lambda 函数中使用。接着调用 timeInitialization 方法,其作用是计时和执行初始化
for (Map.Entry<Class<?>, Provider<CoreStartable>> entry : startables.entrySet()) {
String clsName = entry.getKey().getName();
int j = i; // Copied to make lambda happy.
timeInitialization(
clsName,
() -> mServices[j] = startStartable(clsName, entry.getValue()),
log,
metricsPrefix);
i++;
}
//如果 vendorComponent 不为 null,则调用 timeInitialization 方法,初始化包含 vendorComponent 的 CoreStartable 实现类并将其存储在 \
// mServices 数组的最后一个位置。
if (vendorComponent != null) {
timeInitialization(
vendorComponent,
() -> mServices[mServices.length - 1] =
startAdditionalStartable(vendorComponent),
log,
metricsPrefix);
}
//对于 mServices 数组中的每个元素,如果系统已经启动完成,则调用其 onBootCompleted 方法,表示启动完成了。然后注册每个
// CoreStartable 实现类的 dump,以便可以进行 dumpsys 命令后的分析

for (i = 0; i < mServices.length; i++) {
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
}
//执行 SystemUI 组件的 InitController 中注册的所有后置初始化任务
mSysUIComponent.getInitController().executePostInitTasks();
//使用 TimingsTraceLog 停止记录启动时间的追踪,记录所有记录的时间,并结束执行 startServicesIfNeeded 方法的过程。
log.traceEnd();

mServicesStarted = true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.Dependency</item>
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.stackdivider.Divider</item>
<item>com.android.systemui.SystemBars</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
<item>com.android.systemui.keyboard.KeyboardUI</item>
<item>com.android.systemui.pip.PipUI</item>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
</string-array>

<!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
<string name="config_systemUIVendorServiceComponent" translatable="false">com.android.systemui.VendorServices</string>

SystemUIService的onCreate()方法中,直接调用SystemUIApplication.startServicesIfNeeded()方法,最终会将所有服务一一加载并通过start()方法启动起来。

至此,SystemUI基本启动流程就结束了。