欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin by lazy關(guān)鍵字深入探究實(shí)現(xiàn)原理

 更新時間:2022年11月14日 14:00:56   作者:devnn  
這篇文章主要介紹了by lazy,在kotlin中使用是很常見的,用于實(shí)現(xiàn)懶加載某個數(shù)據(jù)。而這兩個單詞不是一體的,其中by是kotlin中的關(guān)鍵字,用于實(shí)現(xiàn)委托;lazy是一個方法,他的返回值是委托的具體對象

前言

kotlin的by lazy關(guān)鍵字是很常用的,它表示延時初始化變量,只在第一次使用時才給它初始化。那么它是如何實(shí)現(xiàn)這種功能的呢?這篇文章從字節(jié)碼和Java語言的角度揭密它的實(shí)現(xiàn)原理。

ViewModel和ViewBinding變量初始化過程

先舉兩個項目中最常見的例子:ViewModel和ViewBinding,了解一下為什么需要延時初始化。

看一段代碼:

class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by lazy {
        ViewModelProviders.of(this).get(MainViewModel::class.java)
    }
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.i("MainActivity", "onCreate")
     }
}

Jetpack庫中的ViewModel和ViewBinding的使用是非常常見的,ViewModel和ViewBinding類型的變量都是需要延時初始化,不能在聲明時初始化。ViewModel是因?yàn)閮?nèi)部需要依賴Activity的成員變量mApplication,而mApplication是在attach時給賦值的。ViewBinding的初始化需要依賴Window的layoutInflater變量,而Window變量也是在attach時賦值的。

先看ViewModel是如何初始化的,在以下ViewModelProviders.of方法里會調(diào)用checkApplication判斷application是否為空,為空則拋出異常:

public class ViewModelProviders {
    /**
     * @deprecated This class should not be directly instantiated
     */
    @Deprecated
    public ViewModelProviders() {
    }
    private static Application checkApplication(Activity activity) {
        Application application = activity.getApplication();
        if (application == null) {
            throw new IllegalStateException("Your activity/fragment is not yet attached to "
                    + "Application. You can't request ViewModel before onCreate call.");
        }
        return application;
    }
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }

mApplication是Activity的成員變量,它是在attach時賦值的:

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);
		...
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
      	...
        mApplication = application;
        ...
   }

layoutInflater變量同理,它需要通過mWindow變量獲取,而mWindow也是在attach里賦值的:

 public LayoutInflater getLayoutInflater() {
        return getWindow().getLayoutInflater();
    }

Activity的attach方法是早于onCreate方法執(zhí)行的,所以在onCreate方法里是可以訪問這兩個變量。

所以,ViewModel和ViewBinding類型變量都需要延時初始化。

下面開始進(jìn)入正題,by lazy關(guān)鍵字是如何實(shí)現(xiàn)延時初始化。

by lazy關(guān)鍵字的字節(jié)碼實(shí)現(xiàn)

查看以上MainActivity的字節(jié)碼內(nèi)容如下:

public final class com/devnn/demo/MainActivity extends androidx/appcompat/app/AppCompatActivity {
  ...省略無關(guān)字節(jié)碼
  // access flags 0x12
  private final Lkotlin/Lazy; viewModel$delegate
  @Lorg/jetbrains/annotations/NotNull;() // invisible
  // access flags 0x12
  private final Lkotlin/Lazy; binding$delegate
  @Lorg/jetbrains/annotations/NotNull;() // invisible
  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 27 L0
    ALOAD 0
    INVOKESPECIAL androidx/appcompat/app/AppCompatActivity.<init> ()V
   L1
    LINENUMBER 28 L1
    ALOAD 0
    NEW com/devnn/demo/MainActivity$viewModel$2
    DUP
    ALOAD 0
    INVOKESPECIAL com/devnn/demo/MainActivity$viewModel$2.<init> (Lcom/devnn/demo/MainActivity;)V
    CHECKCAST kotlin/jvm/functions/Function0
    INVOKESTATIC kotlin/LazyKt.lazy (Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;
    PUTFIELD com/devnn/demo/MainActivity.viewModel$delegate : Lkotlin/Lazy;
   L2
    LINENUMBER 32 L2
    ALOAD 0
    NEW com/devnn/demo/MainActivity$binding$2
    DUP
    ALOAD 0
    INVOKESPECIAL com/devnn/demo/MainActivity$binding$2.<init> (Lcom/devnn/demo/MainActivity;)V
    CHECKCAST kotlin/jvm/functions/Function0
    INVOKESTATIC kotlin/LazyKt.lazy (Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;
    PUTFIELD com/devnn/demo/MainActivity.binding$delegate : Lkotlin/Lazy;
   L3
    LINENUMBER 27 L3
    RETURN
   L4
    LOCALVARIABLE this Lcom/devnn/demo/MainActivity; L0 L4 0
    MAXSTACK = 4
    MAXLOCALS = 1
  // access flags 0x12
  private final getViewModel()Lcom/devnn/demo/MainViewModel;
   L0
    LINENUMBER 28 L0
    ALOAD 0
    GETFIELD com/devnn/demo/MainActivity.viewModel$delegate : Lkotlin/Lazy;
    ASTORE 1
    ALOAD 1
    INVOKEINTERFACE kotlin/Lazy.getValue ()Ljava/lang/Object; (itf)
    CHECKCAST com/devnn/demo/MainViewModel
   L1
    LINENUMBER 28 L1
    ARETURN
   L2
    LOCALVARIABLE this Lcom/devnn/demo/MainActivity; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 2
  // access flags 0x12
  private final getBinding()Lcom/devnn/demo/databinding/ActivityMainBinding;
   L0
    LINENUMBER 32 L0
    ALOAD 0
    GETFIELD com/devnn/demo/MainActivity.binding$delegate : Lkotlin/Lazy;
    ASTORE 1
    ALOAD 1
    INVOKEINTERFACE kotlin/Lazy.getValue ()Ljava/lang/Object; (itf)
    CHECKCAST com/devnn/demo/databinding/ActivityMainBinding
   L1
    LINENUMBER 32 L1
    ARETURN
   L2
    LOCALVARIABLE this Lcom/devnn/demo/MainActivity; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 2

觀察字節(jié)碼可以發(fā)現(xiàn)幾點(diǎn)變化:

(1)、viewModel變量的類型被換成了kotlin.Lazy類型,變量名字也換成了viewModel$delegate??疵忠仓朗怯玫搅宋兴枷搿?/p>

(2)、在MainActivity的init方法即構(gòu)造方法中使用LazyKt的靜態(tài)方法lazy,給viewModel$delegate變量賦值了。by lazy后面{}內(nèi)初始化實(shí)現(xiàn)邏輯封裝在了Function0類型變量MainActivity$viewModel$2中。

INVOKESTATIC kotlin/LazyKt.lazy (Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy

注:LazyKt.lazy這個靜態(tài)方法的入?yún)㈩愋褪荈unction0,它代表零個參數(shù)(即沒有參數(shù))的回調(diào):

package kotlin.jvm.functions
public interface Function0<out R> : kotlin.Function<R> {
    public abstract operator fun invoke(): R
}

(3)、給MainActivity生成了一個get方法:getViewModel(),這個方法的返回類型正是我們需要的類型:com/devnn/demo/MainViewModel。

通過字節(jié)碼可以看到這個getViewModel()方法內(nèi)部實(shí)現(xiàn):

調(diào)用了viewModel$delegate(類型是kotlin.Lazy)變量的getValue()方法返回一個Object,強(qiáng)轉(zhuǎn)成com/devnn/demo/MainViewModel再將其返回。

玄機(jī)就在這個Lazy的getValue方法。

然后繼續(xù)看kotlin.Lazy的getValue的實(shí)現(xiàn):

internal class UnsafeLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    private var _value: Any? = UNINITIALIZED_VALUE
    override val value: T
        get() {
            if (_value === UNINITIALIZED_VALUE) {
                _value = initializer!!()
                initializer = null
            }
            @Suppress("UNCHECKED_CAST")
            return _value as T
        }
    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
    private fun writeReplace(): Any = InitializedLazyImpl(value)
}

可以看到,當(dāng)value是UNINITIALIZED_VALUE即未初始化時,就通過入?yún)nitializer(即Function0)初始化,并給value賦值,然后返回這個value。

這里有點(diǎn)類似于Java里的單例模式的懶漢模式。

到這時已經(jīng)分析完了by lazy的字節(jié)碼原理,大致過程就是將變量類型替換成了Lazy類型,然后通過Lazy類的getValue方法返回真實(shí)類型,getValue方法里通過判空來判斷是否是首次訪問。

關(guān)鍵還是通過委托的思想將變量初始化委托給了通用類型Lazy類。

ViewBinding延時初始化跟ViewModel是一樣的,就不再分析了。

by lazy關(guān)鍵字的Java實(shí)現(xiàn)

kotlin的代碼是可以轉(zhuǎn)成Java代碼的,我們查看一下它的Java代碼,驗(yàn)證是否跟上面分析的一樣:

public final class MainActivity extends AppCompatActivity {
   @NotNull
   private final Lazy viewModel$delegate = LazyKt.lazy((Function0)(new Function0() {
      @NotNull
      public final MainViewModel invoke() {
         ViewModel var1 = ViewModelProviders.of((FragmentActivity)MainActivity.this).get(MainViewModel.class);
         Intrinsics.checkNotNullExpressionValue(var1, "of(this).get(MainViewModel::class.java)");
         return (MainViewModel)var1;
      }
      // $FF: synthetic method
      // $FF: bridge method
      public Object invoke() {
         return this.invoke();
      }
   }));
   @NotNull
   private final Lazy binding$delegate = LazyKt.lazy((Function0)(new Function0() {
      @NotNull
      public final ActivityMainBinding invoke() {
         ActivityMainBinding var1 = ActivityMainBinding.inflate(MainActivity.this.getLayoutInflater());
         Intrinsics.checkNotNullExpressionValue(var1, "inflate(layoutInflater)");
         return var1;
      }
      // $FF: synthetic method
      // $FF: bridge method
      public Object invoke() {
         return this.invoke();
      }
   }));
   private final MainViewModel getViewModel() {
      Lazy var1 = this.viewModel$delegate;
      return (MainViewModel)var1.getValue();
   }
   private final ActivityMainBinding getBinding() {
      Lazy var1 = this.binding$delegate;
      return (ActivityMainBinding)var1.getValue();
   }

可以看到,跟上面的分析是一模一樣的,它就是將字節(jié)碼反編譯成了Java代碼而已。

Java的成員變量初始化是在構(gòu)造方法(init方法)中完成的,有興趣可以查看我的另一個篇文章: Kotlin字節(jié)碼層探究構(gòu)造函數(shù)與成員變量和init代碼塊執(zhí)行順序

關(guān)于Kotlin的by lazy關(guān)鍵字實(shí)現(xiàn)原理就介紹到此。

到此這篇關(guān)于Kotlin by lazy關(guān)鍵字深入探究實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Kotlin by lazy內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論