0xA05 Android 10 源码分析:Dialog加载绘制流程以及在Kotlin、DataBinding中的使用

简介: 0xA05 Android 10 源码分析:Dialog加载绘制流程以及在Kotlin、DataBinding中的使用

image.png


引言



  • 这是 Android 10 源码分析系列的第 5 篇
  • 分支:android-10.0.0_r14
  • 全文阅读大概 10 分钟


通过这篇文章你将学习到以下内容,将在文末总结部分会给出相应的答案


  • Dialog的的创建流程?
  • Dialog的视图怎么与Window做关联了?
  • 自定义CustomDialog的view的是如何绑定的?
  • 如何使用Kotlin具名可选参数构造类,实现构建者模式?
  • 相比于Java的构建者模式,通过具名可选参数构造类具有以下优点?
  • 如何在Dialog中使用DataBinding?


阅读本文之前,如果之前没有看过 Apk加载流程之资源加载一Apk加载流程之资源加载二 点击下方链接前去查看,这几篇文章都是互相关联的



本文主要来主要围绕以下几个方面来分析:


  • Dialog加载绘制流程
  • 如何使用Kotlin具名可选参数构造类,实现构建者模式
  • 如何在Dialog中使用DataBinding


源码分析



在开始分析Dialog的源码之前,需要了解一下Dialog加载绘制流程,涉及到的数据结构与职能


在包 android.app 下:


  • Dialog:Dialog是窗口的父类,主要实现Window对象的初始化和一些共有逻辑


  • AlertDialog:继承自Dialog,是具体的Dialog的操作实现类
  • AlertDialog.Builder:是AlertDialog的内部类,主要用于构造AlertDialog


  • AlertController:是AlertDialog的控制类


  • AlertController.AlertParams:是AlertController的内部类,负责AlertDialog的初始化参数


了解完相关的数据结构与职能,接下来回顾一下Dialog的创建流程


AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.mipmap.ic_launcher);
builder.setMessage("Message部分");
builder.setTitle("Title部分");
builder.setView(R.layout.activity_main);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        alertDialog.dismiss();
    }
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        alertDialog.dismiss();
    }
});
alertDialog = builder.create();
alertDialog.show();


上面代码都不会很陌生,主要使用了设计模式当中-构建者模式,


  1. 构建AlertDialog.Builder对象
  2. builder.setXXX 系列方法完成Dialog的初始化
  3. 调用builder.create()方法创建AlertDialog
  4. 调用AlertDialog的show()完成View的绘制并显示AlertDialog


主要通过上面四步完成Dialog的创建和显示,接下来根据源码来分析每个方法的具体实现,以及Dialog的视图怎么与Window做关联


1 构建AlertDialog.Builder对象


AlertDialog.Builder builder = new AlertDialog.Builder(this);


AlertDialog.Builder是AlertDialog的内部类,用于封装AlertDialog的构造过程,看一下Builder的构造方法frameworks/base/core/java/android/app/AlertDialog.java


// AlertController.AlertParams类型的成员变量
private final AlertController.AlertParams P;
public Builder(Context context) {
    this(context, resolveDialogTheme(context, Resources.ID_NULL));
}
public Builder(Context context, int themeResId) {
    // 构造ContextThemeWrapper,ContextThemeWrapper 是 Context的子类,主要用来处理和主题相关的
    // 初始化成为变量 P
    P = new AlertController.AlertParams(new ContextThemeWrapper(
            context, resolveDialogTheme(context, themeResId)));
}


  • ContextThemeWrapper 继承自ContextWrapper,Application、Service继承自ContextWrapper,Activity继承自ContextThemeWrapper

image.png


  • P是AlertDialog.Builder中的AlertController.AlertParams类型的成员变量
  • AlertParams中包含了与AlertDialog视图中对应的成员变量,调用builder.setXXX系列方法之后,我们传递的参数就保存在P中了


1.1 AlertParams封装了初始化参数


AlertController.AlertParams 是AlertController的内部类,负责AlertDialog的初始化参数frameworks/base/core/java/com/android/internal/app/AlertController.java


public AlertParams(Context context) {
mContext = context;
// mCancelable 用来控制点击外部是否可取消,默认可以取消
mCancelable = true;
// LayoutInflater 主要来解析layout.xml文件
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}



2 调用AlertDialog.Builder的setXXX系列方法


AlertDialog.Builder初始化完成之后,调用它的builder.setXXX 系列方法完成Dialog的初始化frameworks/base/core/java/android/app/AlertDialog.java


// ... 省略了很多builder.setXXX方法
public Builder setTitle(@StringRes int titleId) {
    P.mTitle = P.mContext.getText(titleId);
    return this;
}
public Builder setMessage(@StringRes int messageId) {
    P.mMessage = P.mContext.getText(messageId);
    return this;
}
public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
    P.mPositiveButtonText = P.mContext.getText(textId);
    P.mPositiveButtonListener = listener;
    return this;
}
public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
    P.mPositiveButtonText = text;
    P.mPositiveButtonListener = listener;
    return this;
}
// ... 省略了很多builder.setXXX方法


上面所有setXXX方法都是给Builder的成员变量P赋值,并且他们的返回值都是Builder类型,因此可以通过消息琏的方式调用


builder.setTitle().setMessage().setPositiveButton()...


PS: 在Kotlin应该尽量避免使用构建者模式,使用Kotlin中的具名可选参数,实现构建者模式,代码更加简洁,为了不影响阅读的流畅性,将这部分内容放到了文末扩展阅读部分


3 builder.create方法


builder.setXXX 系列方法之后调用builder.create方法完成AlertDialog构建,接下来看一下create方法frameworks/base/core/java/android/app/AlertDialog.java


public AlertDialog create() {
    // P.mContext 是ContextWrappedTheme 的实例
    final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
    // Dialog的参数其实保存在P这个类里面
    // mAler是AlertController的实例,通过这个方法把P中的变量传给AlertController.AlertParams
    P.apply(dialog.mAlert);
    // 用来控制点击外部是否可取消,mCancelable 默认为true
    dialog.setCancelable(P.mCancelable);
    // 如果可以取消设置回调监听
    if (P.mCancelable) {
        dialog.setCanceledOnTouchOutside(true);
    }
    // 设置一系列监听
    dialog.setOnCancelListener(P.mOnCancelListener);
    dialog.setOnDismissListener(P.mOnDismissListener);
    if (P.mOnKeyListener != null) {
        dialog.setOnKeyListener(P.mOnKeyListener);
    }
    // 返回 AlertDialog 对象
    return dialog;
}


  • 根据P.mContex 构建了一个AlertDialog
  • mAler是AlertController的实例,调用apply方法把P中的变量传给AlertController.AlertParams
  • 设置是否可以点击外部取消,默认可以取消,同时设置回调监听
  • 最后返回AlertDialog对象


3.1 如何构建AlertDialog


我们来分析一下AlertDialog是如何构建的,来看一下它的造方法具体实现frameworks/base/core/java/android/app/AlertDialog.java


AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
            createContextThemeWrapper);
    mWindow.alwaysReadCloseOnTouchAttr();
    // getContext() 返回的是ContextWrapperTheme
    // getWindow() 返回的是 PhoneWindow
    // mAlert 是AlertController的实例
    mAlert = AlertController.create(getContext(), this, getWindow());
}


PhoneWindows是什么时候创建的?AlertDialog继承自Dialog,首先调用了super的构造方法,来看一下Dialog的构造方法frameworks/base/core/java/android/app/Dialog.java


Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    ...
    // 获取WindowManager对象
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    // 构建PhoneWindow
    final Window w = new PhoneWindow(mContext);
    // mWindow 是PhoneWindow实例
    mWindow = w;
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setOnWindowSwipeDismissedCallback(() -> {
        if (mCancelable) {
            cancel();
        }
    });
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);
    // 继承 Handler
    mListenersHandler = new ListenersHandler(this);
}


  • 获取WindowManager对象,构建了PhoneWindow,到这里我们知道了PhoneWindow是在Dialog构造方法创建的
  • 初始化了Dialog的成员变量mWindow,mWindow 是PhoneWindow的实例
  • 初始化了Dialog的成员变量mListenersHandler,mListenersHandler继承Handler


我们回到AlertDialog构造方法,在AlertDialog构造方法内,调用了 AlertController.create方法,来看一下这个方法


public static final AlertController create(Context context, DialogInterface di, Window window) {
    final TypedArray a = context.obtainStyledAttributes(
            null, R.styleable.AlertDialog, R.attr.alertDialogStyle,
            R.style.Theme_DeviceDefault_Settings);
    int controllerType = a.getInt(R.styleable.AlertDialog_controllerType, 0);
    a.recycle();
    // 根据controllerType 使用不同的AlertController
    switch (controllerType) {
        case MICRO:
            // MicroAlertController 是matrix风格 继承自AlertController
            return new MicroAlertController(context, di, window);
        default:
            return new AlertController(context, di, window);
    }
}


根据controllerType 返回不同的AlertController,到这里分析完了AlertDialog是如何构建的


4 调用Dialog的show方法显示Dialog


调用AlertDialog.Builder的create方法之后返回了AlertDialog的实例,最后调用了AlertDialog的show方法显示dialog,但是AlertDialog是继承自Dialog的,实际上调用的是Dialog的show方法frameworks/base/core/java/android/app/Dialog.java


public void show() {
    // mShowing变量用于表示当前dialog是否正在显示
    if (mShowing) {
        if (mDecor != null) {
            if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
            }
            mDecor.setVisibility(View.VISIBLE);
        }
        return;
    }
    mCanceled = false;
    // mCreated这个变量控制dispatchOnCreate方法只被执行一次
    if (!mCreated) {
        dispatchOnCreate(null);
    } else {
        // Fill the DecorView in on any configuration changes that
        // may have occured while it was removed from the WindowManager.
        final Configuration config = mContext.getResources().getConfiguration();
        mWindow.getDecorView().dispatchConfigurationChanged(config);
    }
    // 用于设置ActionBar
    onStart();
    // 获取DecorView
    mDecor = mWindow.getDecorView();
    if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
        final ApplicationInfo info = mContext.getApplicationInfo();
        mWindow.setDefaultIcon(info.icon);
        mWindow.setDefaultLogo(info.logo);
        mActionBar = new WindowDecorActionBar(this);
    }
    // 获取布局参数
    WindowManager.LayoutParams l = mWindow.getAttributes();
    boolean restoreSoftInputMode = false;
    if ((l.softInputMode
            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
        l.softInputMode |=
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        restoreSoftInputMode = true;
    }
    // 将DecorView和布局参数添加到WindowManager中,完成view的绘制
    mWindowManager.addView(mDecor, l);
    if (restoreSoftInputMode) {
        l.softInputMode &=
                ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
    }
    mShowing = true;
    // 向Handler发送一个Dialog的消息,从而显示AlertDialog
    sendShowMessage();
}


  • 判断dialog是否已经显示,如果显示了直接返回
  • 判断dispatchOnCreate方法是否已经调用,如果没有则调用dispatchOnCreate方法
  • 获取布局参数添加到WindowManager,调用addView方法完成view的绘制
  • 向Handler发送一个Dialog的消息,从而显示AlertDialog


4.1 dispatchOnCreate


在上面代码中,根据mCreated变量,判断dispatchOnCreate方法是否已经调用,如果没有则调用dispatchOnCreate方法frameworks/base/core/java/android/app/Dialog.java


void dispatchOnCreate(Bundle savedInstanceState) {
    if (!mCreated) {
        // 调用 onCreate 方法
        onCreate(savedInstanceState);
        mCreated = true;
    }
}


在dispatchOnCreate方法中主要调用Dialog的onCreate方法, Dialog的onCreate方法是个空方法,由于我们创建的是AlertDialog对象,AlertDialog继承于Dialog,所以调用的是AlertDialog的onCreate方法frameworks/base/core/java/android/app/AlertDialog.java


protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mAlert.installContent();
}


在这方法里面调用了AlertController的installContent方法,来看一下具体的实现逻辑frameworks/base/core/java/com/android/internal/app/AlertController.java


public void installContent() {
    // 获取相应的Dialog布局文件
    int contentView = selectContentView();
    // 调用setContentView方法解析布局文件
    mWindow.setContentView(contentView);
    // 初始化布局文件中的组件
    setupView();
}


  • 调用selectContentView方法获取布局文件,来看一下具体的实现

frameworks/base/core/java/com/android/internal/app/AlertController.java


private int selectContentView() {
    if (mButtonPanelSideLayout == 0) {
        return mAlertDialogLayout;
    }
    if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
        return mButtonPanelSideLayout;
    }
    return mAlertDialogLayout;
}


返回的布局是mAlertDialogLayout,布局文件是在AlertController的构造方法初始化的frameworks/base/core/java/com/android/internal/app/AlertController.java


mAlertDialogLayout = a.getResourceId(
                R.styleable.AlertDialog_layout, R.layout.alert_dialog);


  • 调用Window.setContentView方法解析布局文件,Activity的setContentView最后也是调用了Window.setContentView这个方法,具体的解析流程,可以参考之前的文章Activity布局加载流程 0xA03 Android 10 源码分析:Apk加载流程之资源加载
  • 调用setupView方法初始化布局文件中的组件, 到这里dispatchOnCreate方法分析结束


4.2 调用mWindowManager.addView完成View的绘制


回到我们的Dialog的show方法,在执行了dispatchOnCreate方法之后,又调用了onStart方法,这个方法主要用于设置ActionBar,然后初始化WindowManager.LayoutParams对象,最后调用mWindowManager.addView()方法完成界面的绘制,绘制完成之后调用sendShowMessage方法frameworks/base/core/java/android/app/Dialog.java


private void sendShowMessage() {
    if (mShowMessage != null) {
        // Obtain a new message so this dialog can be re-used
        Message.obtain(mShowMessage).sendToTarget();
    }
}


向Handler发送一个Dialog的消息,从而显示AlertDialog,该消息最终会在ListenersHandler中的handleMessage方法中被执行,ListenersHandler是Dialog的内部类,继承Handlerframeworks/base/core/java/android/app/Dialog.java


public void handleMessage(Message msg) {
    switch (msg.what) {
        case DISMISS:
            ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
            break;
        case CANCEL:
            ((OnCancelListener) msg.obj).onCancel(mDialog.get());
            break;
        case SHOW:
            ((OnShowListener) msg.obj).onShow(mDialog.get());
            break;
    }
}


如果msg.what = SHOW,会执行OnShowListener.onShow方法,msg.what的值和OnShowListener调用setOnShowListener方法赋值的frameworks/base/core/java/android/app/Dialog.java


public void setOnShowListener(@Nullable OnShowListener listener) {
    if (listener != null) {
        mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
    } else {
        mShowMessage = null;
    }
}


mListenersHandler构造了Message对象,当我们在Dialog中发送showMessage的时候,被mListenersHandler所接收


4.3 自定义Dialog的view的是如何绑定的


在上文分析中根据mCreated变量,判断dispatchOnCreate方法是否已经调用,如果没有则调用dispatchOnCreate方法,在dispatchOnCreate方法中主要调用Dialog的onCreate方法,由于创建的是AlertDialog对象,AlertDialog继承于Dialog,所以实际调用的是AlertDialog的onCreate方法,来完成布局文件的解析,和布局文件中控件的初始化

同理我们自定义CustomDialog继承自Dialog,所以调用的是自定义CustomDialog的onCreate方法,代码如下


public class CustomDialog extends Dialog {
    Context mContext;
    // ... 省略构造方法
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.custom_dialog, null);
        setContentView(view);
    }
}


在onCreate方法中调用了 Dialog的setContentView 方法, 来分析setContentView方法frameworks/base/core/java/android/app/Dialog.java


public void setContentView(@NonNull View view) {
    mWindow.setContentView(view);
}


mWindow是PhoneWindow的实例,最后调用的是PhoneWindow的setContentView解析布局文件,Activity的setContentView最后也是调用了PhoneWindow的setContentView方法,具体的解析流程,可以参考之前的文章Activity布局加载流程 0xA03 Android 10 源码分析:Apk加载流程之资源加载


总结



Dialog和Activity的显示逻辑是相似的都是内部管理这一个Window对象,用WIndow对象实现界面的加载与显示逻辑


Dialog的的创建流程?


  1. 构建AlertDialog.Builder对象
  2. builder.setXXX 系列方法完成Dialog的初始化
  3. 调用builder.create()方法创建AlertDialog
  4. 调用AlertDialog的show()初始化Dialog的布局文件,Window对象等,然后执行mWindowManager.addView方法,开始执行绘制View的操作,最终将Dialog显示出来


Dialog的视图怎么与Window做关联了?


  • 在Dialog的构造方法中初始化了Window对象


Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    ...
    // 获取WindowManager对象
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    // 构建PhoneWindow
    final Window w = new PhoneWindow(mContext);
    // mWindow 是PhoneWindow实例
    mWindow = w;
   ...
}


  • 调用Dialog的show方法,完成view的绘制和Dialog的显示


public void show() {
    // 获取DecorView
    mDecor = mWindow.getDecorView();
    // 获取布局参数
    WindowManager.LayoutParams l = mWindow.getAttributes();
    // 将DecorView和布局参数添加到WindowManager中
    mWindowManager.addView(mDecor, l);
}


最终会通过WindowManager将DecorView添加到Window之中,用WIndow对象实现界面的加载与显示逻辑


自定义CustomDialog的view的是如何绑定的?


  • 调用Dialog的show方法,在该方法内部会根据mCreated变量,判断dispatchOnCreate方法是否已经调用,如果没有则调用dispatchOnCreate方法,在dispatchOnCreate方法中主要调用Dialog的onCreate方法
  • 自定义CustomDialog继承自Dialog,所以调用的是自定义CustomDialog的onCreate方法,在CustomDialog的onCreate方法中调用setContentView方法,最后调用的是PhoneWindow的setContentView解析布局文件,解析流程参考0xA03 Android 10 源码分析:Apk加载流程之资源加载


如何使用Kotlin具名可选参数构造类,实现构建者模式?


这部分内容参考扩展阅读部分


相比于Java的构建者模式,通过具名可选参数构造类具有以下优点?


  • 代码非常的简洁
  • 每个参数名都可以显示的,声明对象时无须按照顺序书写,非常的灵活
  • 构造函数中每个参数都是val声明的,在多线程并发业务场景中更加的安全
  • Kotlin的require方法,让我们在参数约束上更加的友好


如何在Dialog中使用DataBinding?


这部分内容参考扩展阅读部分


扩展阅读



1. Kotlin实现构建者模式


刚才在上文中提到了,在Kotlin中应该尽量避免使用构建者模式,使用Kotlin的具名可选参数构造类,实现构建者模式,代码更加简洁


在 "Effective Java" 书中介绍构建者模式时,是这样子描述它的:本质上builder模式模拟了具名的可算参数,就像Ada和Python中的一样


关于Java用构建者模式实现自定义dialog,这里就不展示了,可以百度、Google搜索一下,代码显得很长........幸运的是,Kotlin是一门拥有具名可选参数的变成语言,Kotlin中的函数和构造器都支持这一特性,接下里我们使用具名可选参数构造类,实现构建者模式,点击JDataBinding前往查看,核心代码如下:


class AppDialog(
    context: Context,
    val title: String? = null,
    val message: String? = null,
    val yes: AppDialog.() -> Unit
) : DataBindingDialog(context, R.style.AppDialog) {
    init {
        requireNotNull(message) { "message must be not null" }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(root)
        display.text = message
        btnNo.setOnClickListener { dismiss() }
        btnYes.setOnClickListener { yes() }
    }
}


调用方式也更加的简单


AppDialog(
        context = this@MainActivity,
        message = msg,
        yes = {
            // do something
        }).show()


相比于Java的构建者模式,通过具名可选参数构造类具有以下优点:


  • 代码非常的简洁
  • 每个参数名都可以显示的,声明对象时无须按照顺序书写,非常的灵活
  • 构造函数中每个参数都是val声明的,在多线程并发业务场景中更加的安全
  • Kotlin的require方法,让我们在参数约束上更加的友好


2. 如何在Dialog中使用DataBinding


DataBinding是什么?查看Google官网,会有更详细的介绍


DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源


在使用Kotlin的具名可选参数构造类实现Dailog构建者模式的基础上,用DataBinding进行二次封装,加上DataBinding数据绑定的特性,使Dialog变得更加简洁、易用


Step1: 定义一个基类DataBindingDialog


abstract class DataBindingDialog(@NonNull context: Context, @StyleRes themeResId: Int) :
    Dialog(context, themeResId) {
    protected inline fun <reified T : ViewDataBinding> binding(@LayoutRes resId: Int): Lazy<T> =
        lazy {
            requireNotNull(
                DataBindingUtil.bind<T>(LayoutInflater.from(context).inflate(resId, null))
            ) { "cannot find the matched view to layout." }
        }
}


Step2:  改造AppDialog


class AppDialog(
    context: Context,
    val title: String? = null,
    val message: String? = null,
    val yes: AppDialog.() -> Unit
) : DataBindingDialog(context, R.style.AppDialog) {
    private val mBinding: DialogAppBinding by binding(R.layout.dialog_app)
    init {
        requireNotNull(message) { "message must be not null" }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        mBinding.apply {
            setContentView(root)
            display.text = message
            btnNo.setOnClickListener { dismiss() }
            btnYes.setOnClickListener { yes() }
        }
    }
}


同理DataBinding在Activity、Fragment、Adapter中的使用也是一样的,利用Kotlin的inline、reified、DSL等等语法,可以设计出更加简洁并利于维护的代码


关于基于DataBinding封装的DataBindingActivity、DataBindingFragment、DataBindingDialog基础库相关代码,后续也会陆续完善基础库,点击JDataBinding前往查看,欢迎start


结语



致力于分享一系列 Android 系统源码、逆向分析、算法、翻译、Jetpack 源码相关的文章,正在努力写出更好的文章,如果这篇文章对你有帮助给个 star,文章中有什么没有写明白的地方,或者有什么更好的建议欢迎留言,欢迎一起来学习,在技术的道路上一起前进。


计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,可以前去查看:AndroidX-Jetpack-Practice, 如果这个仓库对你有帮助,请帮我点个赞,我会陆续完成更多 Jetpack 新成员的项目实践。


算法


由于 LeetCode 的题库庞大,每个分类都能筛选出数百道题,由于每个人的精力有限,不可能刷完所有题目,因此我按照经典类型题目去分类、和题目的难易程度去排序。


  • 数据结构: 数组、栈、队列、字符串、链表、树……
  • 算法: 查找算法、搜索算法、位运算、排序、数学、……


每道题目都会用 Java 和 kotlin 去实现,并且每道题目都有解题思路、时间复杂度和空间复杂度,如果你同我一样喜欢算法、LeetCode,可以关注我 GitHub 上的 LeetCode 题解:Leetcode-Solutions-with-Java-And-Kotlin,一起来学习,期待与你一起成长。


Android 10 源码系列


正在写一系列的 Android 10 源码分析的文章,了解系统源码,不仅有助于分析问题,在面试过程中,对我们也是非常有帮助的,如果你同我一样喜欢研究 Android 源码,可以关注我 GitHub 上的 Android10-Source-Analysis,文章都会同步到这个仓库。



工具系列



逆向系列




目录
相关文章
|
22天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
24天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
24天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
2天前
|
移动开发 Java Android开发
构建高效Android应用:采用Kotlin协程优化网络请求
【4月更文挑战第24天】 在移动开发领域,尤其是对于Android平台而言,网络请求是一个不可或缺的功能。然而,随着用户对应用响应速度和稳定性要求的不断提高,传统的异步处理方式如回调地狱和RxJava已逐渐显示出局限性。本文将探讨如何利用Kotlin协程来简化异步代码,提升网络请求的效率和可读性。我们将深入分析协程的原理,并通过一个实际案例展示如何在Android应用中集成和优化网络请求。
|
2天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第24天】随着移动开发技术的不断演进,提升应用性能和用户体验已成为开发者的核心任务。在Android平台上,Kotlin语言凭借其简洁性和功能性成为主流选择之一。特别是Kotlin的协程功能,它为异步编程提供了一种轻量级的解决方案,使得处理并发任务更加高效和简洁。本文将深入探讨Kotlin协程在Android开发中的应用,通过实际案例分析协程如何优化应用性能,以及如何在项目中实现协程。
|
8天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
12 0
|
18天前
|
移动开发 API Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第7天】 在移动开发领域,性能优化和应用响应性的提升一直是开发者追求的目标。近年来,Kotlin语言因其简洁性和功能性在Android社区中受到青睐,特别是其对协程(Coroutines)的支持,为编写异步代码和处理并发任务提供了一种更加优雅的解决方案。本文将探讨Kotlin协程在Android开发中的应用,揭示其在提高应用性能和简化代码结构方面的潜在优势,并展示如何在实际项目中实现和优化协程。
|
5天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
27天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
14 0
|
1天前
|
数据库 Android开发 开发者
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第24天】 在竞争激烈的移动应用市场中,一个流畅且响应迅速的用户界面(UI)是吸引和保留用户的关键。针对安卓平台,开发者面临着多样化的设备和系统版本,这增加了构建高效UI的复杂性。本文将深入分析安卓平台上构建高效用户界面的最佳实践,包括布局优化、资源管理和绘制性能的考量,旨在为开发者提供实用的技术指南,帮助他们创建更流畅的用户体验。