androidx库是谷歌在两年前推出的用于取代support库的一个库,最低兼容API 28,也就是说28以上的默认使用androidx库,之前的support库都不再适用了。如果你不懂support库有哪些内容,以及如何迁移到androidx?可以看看我之前写的文章 《AndroidX库和一般库的详细对比以及迁移中遇到的坑总结》。
言归正传,下面我带领大家来看看androidx库是如何做到兼容低版本的。
首先我们这里有一个MainActivity
,它是继承自,但是
AppCompatActivity
源码太多,从头到尾去看会一脸懵逼,所以我们可以换一种最简易的思路去看源码。我们先看看我们创建项目之后默认的MainActivity
里面的代码是什么样的:
package com.lzw.androidx_demo;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
我们看到默认的代码重写了onCreate
函数,调用了父类(AppCompatActivity
)的onCreate
函数,我们点击去看看:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
首先它调用了getDelegate()
方法,返回一个AppCompatDelegate
对象,调用了delegate的onCreate
方法,最后再调用父类的onCreate
方法。
我们就从生命周期方法开始看,再看看其它的相关方法,我们会发现都首先调用了getDelegate()
,返回一个AppCompatDelegate
对象。源码摘抄如下:
@Override
protected void onStart() {
super.onStart();
getDelegate().onStart();
}
@Override
protected void onStop() {
super.onStop();
getDelegate().onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
getDelegate().onDestroy();
}
当然如果你去看其他的方法,内部第一行就是调用了``getDelegate方法,返回一个
AppCompatDelegate对象。下面重点看一下这个
getDelegate`方法。
的getDelegate()方法
()方法
它的返回值是AppCompatDelegate
类,它有一个成员变量mDelegate
,它持有的AppCompatDelegate
类的引用,如果为空,就调用(this, this)
创建一个AppCompatDelegate
类的对象,否则就用mDelegate
。源码如下:
/**
* @return The {@link AppCompatDelegate} being used by this Activity.
*/
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
2.接下来我们看看create(xxx)
方法内部的流程,先附上源码:
@NonNull
public static AppCompatDelegate create(@NonNull Activity activity,
@Nullable AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, callback);
}
我们可以看到create
方法内部直接创建了AppCompatDelegateImpl
类的对象。我们看源码得知AppCompatDelegate
是一个抽象类。这里面的创建对象的过程实际上是使用的AppCompatDelegate
的子类AppCompatDelegateImpl
来实现的。
3.接下来我们看看AppCompatDelegateImpl
,看它做了什么事情:
它有几个构造函数,这里选取重要的两个:
AppCompatDelegateImpl(Activity activity, AppCompatCallback callback) {
this(activity, null, callback, activity);
}
private AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback,
Object host) {
mContext = context;
mAppCompatCallback = callback;
mHost = host;
if (mLocalNightMode == MODE_NIGHT_UNSPECIFIED && mHost instanceof Dialog) {
final AppCompatActivity activity = tryUnwrapContext();
if (activity != null) {
// This code path is used to detect when this Delegate is a child Delegate from
// an Activity, primarily for Dialogs. Dialogs use the Activity as it's Context,
// so we want to make sure that the this 'child' delegate does not interfere
// with the Activity config. The simplest way to do that is to match the
// outer Activity's local night mode
mLocalNightMode = activity.getDelegate().getLocalNightMode();
}
}
if (mLocalNightMode == MODE_NIGHT_UNSPECIFIED) {
// Try and read the current night mode from our static store
final Integer value = sLocalNightModes.get(mHost.getClass().getName());
if (value != null) {
mLocalNightMode = value;
// Finally remove the value
sLocalNightModes.remove(mHost.getClass().getName());
}
}
if (window != null) {
attachToWindow(window);
}
// Preload appcompat-specific handling of drawables that should be handled in a special
// way (for tinting etc). After the following line completes, calls from AppCompatResources
// to ResourceManagerInternal (in appcompat-resources) will handle those internal drawable
// paths correctly without having to go through AppCompatDrawableManager APIs.
AppCompatDrawableManager.preload();
}
两个参数的构造函数调用了4个参数的构造函数。
构造函数内部第一行代码是将传进来的context
赋值给本类的成员变量mContext
,我们都只到context是核心,它可以操作很多内容,比如四大组件,资源文件等都需要用到context,所以这个mContext
我们重点关注一下。
看来这个AppCompatDelegate
类非常关键。
类的findViewById方法
看看我们这
里面有一个TextView
控件,一般情况下我们首先要findViewById
找到它的id。代码如下:
package com.lzw.androidx_demo;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.tv_title);
}
}
说明一下:我这里使用的编译版本是29,所以后文看到的源码API版本都是基于29的,但是大部分源码都是差不多的,只有少量的是差异化处理(如果你使用了其它的版本,可能源码有些许不同):
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId ".androidx_demo"
minSdkVersion 28
targetSdkVersion 29
}
}
那么我们废话少说,就直接从findViewById
开始看源码,我们点击去看看源码(Ctrl+鼠标左键
),这时候我们打开了AppconpatActivity
的findViewById
方法:
@SuppressWarnings("TypeParameterUnusedInFormals")
@Override
public <T extends View> T findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
我们把可以看到这里面调用了getDelegate()
这个方法,它的返回值是AppCompatDelegate
,然后再通过AppCompatDelegate
这个类调用其静态方法findViewById
。我们这里面就开始分两步来讲解: