1.Androidä¸Viewçå建è¿ç¨
2.android的自定义View的实现原理?哪位能给我个思路呢。谢谢。
3.Android UIç»å¶ä¹Viewç»å¶çå·¥ä½åç
4.❤️ Android 源码解读-从setContentView深入了解 Window|Activity|View❤️
5.Android 14 HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
6.最简最全,一文搞定Android WebView编译+AOSP集成
Androidä¸Viewçå建è¿ç¨
æ们ç¥éå¨onCreateéé¢Viewè¿æ¯æ²¡ææµç»å®æçãé£ä¹ä»ä¹æ¶åæµç»å®æäºï¼çæ¡æ¯onResumeãéè¿æ¥çæºç æ们å¯ä»¥çå°å¨onCreateæ¹æ³éé¢è°ç¨äºgetWindow()æ¹æ³ç¶åå¨å°æ们ç页é¢å¡å°è¿ä¸ªwindowéé¢ãè¿ä¸ªwindowä¹å°±æ¯PhonwWindow.
é£PhoneWindowæ¯ä»ä¹æ¶å被å建çï¼
è¿å°±å¼åºäºActivityçå建æµç¨ã
é£Activityæ¯æä¹è¢«å建çå¢ï¼
ç±äºActivityæ¯ä¸ä¸ªç»ä»¶ä»æ¯ç±ç³»ç»ä½¿ç¨ActivityThreadæ¹æ³å»å建çã
ç°å¨ææ¥åæä¸ï¼
å æ¥å°ActivityThreadç±»çhandleLaunchActivityæ¹æ³ã
å¯ä»¥çå°ä»å»è°ç¨äºActivityçperformCreateæ¹æ³ã
ç°å¨æ们ç»äºçå°onCreateæ¹æ³è¢«è°ç¨äºã
è¿éè¿æ个éç¹ï¼å¨performLaunchActivityéé¢å»è°ç¨ActivityçonCreateæ¹æ³ä¹åè¿å»åäºä¸ä»¶å¾éè¦çäºæ ï¼è¿ä¸ªäºæ å¨ç¬¬è¡ï¼è°ç¨äºActivityçattachæ¹æ³ã
ç°å¨è·å°Activityçattachæ¹æ³ï¼æ¾å°äºæ们ä¸ç´æ¾çPhoneWindowçå建ã
android的自定义View的实现原理?哪位能给我个思路呢。谢谢。
如果说要按类型来划分的主控源码话,自定义View的实现方式大概可以分为三种,自绘控件、组合控件、以及继承控件。那么下面我们就来依次学习一下,每种方式分别是如何自定义View的。一、自绘控件
自绘控件的意思就是,这个View上所展现的内容全部都是我们自己绘制出来的。绘制的代码是写在onDraw()方法中的,而这部分内容我们已经在 Android视图绘制流程完全解析,带你一步步深入了解View(二) 中学习过了。
下面我们准备来自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了多少次。新建一个CounterView继承自View,代码如下所示:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="dp"
android:background="#ffcb" >
<Button
android:id="@+id/button_left"
android:layout_width="dp"
android:layout_height="dp"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:background="@drawable/back_button"
android:text="Back"
android:textColor="#fff" />
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="This is Title"
android:textColor="#fff"
android:textSize="sp" />
</RelativeLayout>
在这个布局文件中,我们首先定义了一个RelativeLayout作为背景布局,然后在这个布局里定义了一个Button和一个TextView,Button就是标题栏中的返回按钮,TextView就是标题栏中的显示的文字。
接下来创建一个TitleView继承自FrameLayout,代码如下所示:
public class TitleView extends FrameLayout {
private Button leftButton;
private TextView titleText;
public TitleView(Context context,android源码下载清华 AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.title, this);
titleText = (TextView) findViewById(R.id.title_text);
leftButton = (Button) findViewById(R.id.button_left);
leftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((Activity) getContext()).finish();
}
});
}
public void setTitleText(String text) {
titleText.setText(text);
}
public void setLeftButtonText(String text) {
leftButton.setText(text);
}
public void setLeftButtonListener(OnClickListener l) {
leftButton.setOnClickListener(l);
}
}
TitleView中的代码非常简单,在TitleView的构建方法中,我们调用了LayoutInflater的inflate()方法来加载刚刚定义的title.xml布局,这部分内容我们已经在 Android LayoutInflater原理分析,带你一步步深入了解View(一) 这篇文章中学习过了。
接下来调用findViewById()方法获取到了返回按钮的实例,然后在它的onClick事件中调用finish()方法来关闭当前的Activity,也就相当于实现返回功能了。
另外,为了让TitleView有更强地扩展性,我们还提供了setTitleText()、setLeftButtonText()、setLeftButtonListener()等方法,分别用于设置标题栏上的文字、返回按钮上的文字、以及返回按钮的点击事件。
到了这里,一个自定义的标题栏就完成了,那么下面又到了如何引用这个自定义View的部分,其实方法基本都是相同的,在布局文件中添加如下代码:
<RelativeLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.customview.TitleView
android:id="@+id/title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</com.example.customview.TitleView>
</RelativeLayout>
这样就成功将一个标题栏控件引入到布局文件中了,运行一下程序。
现在点击一下Back按钮,就可以关闭当前的Activity了。如果你想要修改标题栏上显示的内容,或者返回按钮的默认事件,只需要在Activity中通过findViewById()方法得到TitleView的实例,然后调用setTitleText()、setLeftButtonText()、亚马逊钓鱼邮件源码setLeftButtonListener()等方法进行设置就OK了。
Android UIç»å¶ä¹Viewç»å¶çå·¥ä½åç
è¿æ¯AndroidUIç»å¶æµç¨åæç第äºç¯æç« ï¼ä¸»è¦åæçé¢ä¸Viewæ¯å¦ä½ç»å¶å°çé¢ä¸çå ·ä½è¿ç¨ãViewRoot 对åºäº ViewRootImpl ç±»ï¼å®æ¯è¿æ¥ WindowManager å DecorView ç纽带ï¼Viewçä¸å¤§æµç¨åæ¯éè¿ ViewRoot æ¥å®æçãå¨ ActivityThread ä¸ï¼å½ Activity 对象被å建å®æ¯åï¼ä¼å° DecorView æ·»å å° Window ä¸,åæ¶ä¼å建 ViewRootImpl 对象ï¼å¹¶å° ViewRootImpl 对象å DecorView 建ç«å ³èã
measure è¿ç¨å³å®äº View ç宽/é«ï¼ Measure å®æ以åï¼å¯ä»¥éè¿ getMeasuredWidth å getMeasuredHeight æ¹æ³æ¥è·å View æµéåç宽/é«ï¼å¨å ä¹ææçæ åµä¸ï¼å®çåäºViewçæç»ç宽/é«ï¼ä½æ¯ç¹æ®æ åµé¤å¤ã Layout è¿ç¨å³å®äº View çå个顶ç¹çåæ åå®é ç宽/é«ï¼å®æ以åï¼å¯ä»¥éè¿ getTopãgetBottomãgetLeft å getRight æ¥æ¿å°Viewçå个顶ç¹çä½ç½®ï¼å¯ä»¥éè¿ getWidth å getHeight æ¹æ³æ¿å°Viewçæç»å®½/é«ã Draw è¿ç¨å³å®äº View çæ¾ç¤ºï¼åªæ draw æ¹æ³å®æå View çå 容æè½åç°å¨å±å¹ä¸ã
DecorView ä½ä¸ºé¡¶çº§ View ï¼ä¸è¬æ åµä¸ï¼å®å é¨ä¼å å«ä¸ä¸ªç«ç´æ¹åç LinearLayout ï¼å¨è¿ä¸ª LinearLayout éé¢æä¸ä¸ä¸¤ä¸ªé¨åï¼ä¸é¢æ¯æ é¢æ ï¼ä¸é¢æ¯å 容æ ãå¨Activityä¸ï¼æ们éè¿ setContentView æ设置çå¸å±æä»¶å ¶å®å°±æ¯è¢«å å°å 容æ ä¸çï¼èå 容æ id为 content ãå¯ä»¥éè¿ä¸é¢æ¹æ³å¾å° content:ViewGroup content = findViewById(R.android.id.content) ãéè¿ content.getChildAt(0) å¯ä»¥å¾å°è®¾ç½®ç view ã DecorView å ¶å®æ¯ä¸ä¸ª FrameLayout , View å±çäºä»¶é½å ç»è¿ DecorView ï¼ç¶åæä¼ éç»æ们ç View ã
MeasureSpec 代表ä¸ä¸ªä½çintå¼ï¼é«2ä½ä»£è¡¨ SpecMode ,ä½ä½ä»£è¡¨ SpecSize , SpecMode æ¯ææµé模å¼ï¼è SpecSize æ¯æå¨æç§æµé模å¼ä¸çè§æ ¼å¤§å°ã
SpecMode æä¸ç±»ï¼å¦ä¸æ示ï¼
UNSPECIFIED
EXACTLY
AT_MOST
LayoutParamséè¦åç¶å®¹å¨ä¸èµ·æè½å³å®ViewçMeasureSpecï¼ä»èè¿ä¸æ¥å³å®Viewç宽/é«ã
对äºé¡¶çº§Viewï¼å³DecorViewåæ®éViewæ¥è¯´ï¼MeasureSpecç转æ¢è¿ç¨ç¥æä¸åã对äºDecorViewï¼å ¶MeasureSpecç±çªå£ç尺寸åå ¶èªèº«çLayoutParamså ±åç¡®å®ï¼
对äºæ®éViewï¼å ¶MeasureSpecç±ç¶å®¹å¨çMeasureSpecåèªèº«çLayoutparamså ±åå³å®ï¼
MeasureSpecä¸æ¦ç¡®å®ï¼onMeasureå°±å¯ä»¥ç¡®å®Viewçæµé宽/é«ã
å°ç»ä¸ä¸
å½å View ç宽é«éç¨ wrap_content æ¶ï¼ä¸ç®¡ç¶å®¹å¨ç模å¼æ¯ç²¾ç¡®æ¨¡å¼è¿æ¯æ大模å¼ï¼å View ç模å¼æ»æ¯æ大模å¼+ç¶å®¹å¨çå©ä½ç©ºé´ã
View çå·¥ä½æµç¨ä¸»è¦æ¯æ measure ã layout ã draw ä¸å¤§æµç¨ï¼å³æµéãå¸å±ãç»å¶ãå ¶ä¸ measure ç¡®å® View çæµé宽/é«ï¼ layout ç¡®å® view çæç»å®½/é«åå个顶ç¹çä½ç½®ï¼è draw åå° View ç»å¶å¨å±å¹ä¸ã
measure è¿ç¨è¦åæ åµï¼å¦æåªæ¯ä¸ä¸ªåå§ç view ï¼åéè¿ measure æ¹æ³å°±å®æäºå ¶æµéè¿ç¨ï¼å¦ææ¯ä¸ä¸ª ViewGroup ï¼é¤äºå®æèªå·±çæµéè¿ç¨å¤ï¼è¿ä¼éåè°ç¨ææåå ç´ ç measure æ¹æ³ï¼å个åå ç´ åéå½å»æ§è¡è¿ä¸ªæµç¨ã
å¦ææ¯ä¸ä¸ªåå§ç Viewï¼é£ä¹éè¿ measure æ¹æ³å°±å®æäºæµéè¿ç¨ï¼å¨ measure æ¹æ³ä¸ä¼å»è°ç¨ View ç onMeasure æ¹æ³ï¼View ç±»éé¢å®ä¹äº onMeasure æ¹æ³çé»è®¤å®ç°:
å çä¸ä¸ getSuggestedMinimumWidth å getSuggestedMinimumHeight æ¹æ³çæºç ï¼
å¯ä»¥çå°ï¼ getMinimumWidth æ¹æ³è·åçæ¯ Drawable çåå§å®½åº¦ãå¦æåå¨åå§å®½åº¦ï¼å³æ»¡è¶³ intrinsicWidth > 0ï¼ï¼é£ä¹ç´æ¥è¿ååå§å®½åº¦å³å¯ï¼å¦æä¸åå¨åå§å®½åº¦ï¼å³ä¸æ»¡è¶³ intrinsicWidth > 0ï¼ï¼é£ä¹å°±è¿å 0ã
æ¥ççæéè¦ç getDefaultSize æ¹æ³ï¼
å¦æ specMode 为 MeasureSpec.UNSPECIFIED å³æªæå®æ¨¡å¼ï¼é£ä¹è¿åç±æ¹æ³åæ°ä¼ éè¿æ¥ç尺寸ä½ä¸º View çæµé宽度åé«åº¦ï¼
å¦æ specMode ä¸æ¯ MeasureSpec.UNSPECIFIED å³æ¯æ大模å¼æè 精确模å¼ï¼é£ä¹è¿åä» measureSpec ä¸ååºç specSize ä½ä¸º View æµéåç宽度åé«åº¦ã
çä¸ä¸åæçè¡¨æ ¼ï¼
å½ specMode 为 EXACTLY æè AT_MOST æ¶ï¼View çå¸å±åæ°ä¸º wrap_content æè match_parent æ¶ï¼ç» View ç specSize é½æ¯ parentSize ãè¿ä¼æ¯å»ºè®®çæå°å®½é«è¦å¤§ãè¿æ¯ä¸ç¬¦åæ们çé¢æçãå 为æä»¬ç» View 设置 wrap_content æ¯å¸æViewç大å°å好å¯ä»¥å 裹å®çå 容ã
å æ¤ï¼
å¦ææ¯ä¸ä¸ª ViewGroupï¼é¤äºå®æèªå·±ç measure è¿ç¨ä»¥å¤ï¼è¿ä¼éåå»è°ç¨ææåå ç´ ç measure æ¹æ³ï¼å个åå ç´ åéå½å»æ§è¡ measure è¿ç¨ã
ViewGroup 并没æéå View ç onMeasure æ¹æ³ï¼ä½æ¯å®æä¾äº measureChildrenãmeasureChildãmeasureChildWithMargins è¿å 个æ¹æ³ä¸é¨ç¨äºæµéåå ç´ ã
å¦ææ¯ View çè¯ï¼é£ä¹å¨å®ç layout æ¹æ³ä¸å°±ç¡®å®äºèªèº«çä½ç½®ï¼å ·ä½æ¥è¯´æ¯éè¿ setFrame æ¹æ³æ¥è®¾å® View çå个顶ç¹çä½ç½®ï¼å³åå§å mLeft ï¼ mRight ï¼ mTop ï¼ mBottom è¿å个å¼ï¼ï¼ layout è¿ç¨å°±ç»æäºã
å¦ææ¯ ViewGroup çè¯ï¼é£ä¹å¨å®ç layout æ¹æ³ä¸åªæ¯ç¡®å®äº ViewGroup èªèº«çä½ç½®ï¼è¦ç¡®å®åå ç´ çä½ç½®ï¼å°±éè¦éå onLayout æ¹æ³ï¼å¨ onLayout æ¹æ³ä¸ï¼ä¼è°ç¨åå ç´ ç layout æ¹æ³ï¼åå ç´ å¨å®ç layout æ¹æ³ä¸ç¡®å®èªå·±çä½ç½®ï¼è¿æ ·ä¸å±ä¸å±å°ä¼ éä¸å»å®ææ´ä¸ª View æ ç layout è¿ç¨ã
layout æ¹æ³çä½ç¨æ¯ç¡®å® View æ¬èº«çä½ç½®ï¼å³è®¾å® View çå个顶ç¹çä½ç½®ï¼è¿æ ·å°±ç¡®å®äº View å¨ç¶å®¹å¨ä¸çä½ç½®ï¼
onLayout æ¹æ³çä½ç¨æ¯ç¶å®¹å¨ç¡®å®åå ç´ çä½ç½®ï¼è¿ä¸ªæ¹æ³å¨ View ä¸æ¯ç©ºå®ç°ï¼å 为 View 没æåå ç´ äºï¼å¨ ViewGroup ä¸åè¿è¡æ½è±¡åï¼å®çåç±»å¿ é¡»å®ç°è¿ä¸ªæ¹æ³ã
1.ç»å¶èæ¯ï¼ background.draw(canvas); ï¼ï¼
2.ç»å¶èªå·±ï¼ onDraw ï¼ï¼
3.ç»å¶ childrenï¼ dispatchDraw(canvas) ï¼ï¼
4.ç»å¶è£ é¥°ï¼ onDrawScrollBars ï¼ã
dispatchDraw æ¹æ³çè°ç¨æ¯å¨ onDraw æ¹æ³ä¹åï¼ä¹å°±æ¯è¯´ï¼æ»æ¯å ç»å¶èªå·±åç»å¶å View ã
å¯¹äº View ç±»æ¥è¯´ï¼ dispatchDraw æ¹æ³æ¯ç©ºå®ç°çï¼å¯¹äº ViewGroup ç±»æ¥è¯´ï¼ dispatchDraw æ¹æ³æ¯æå ·ä½å®ç°çã
éè¿ dispatchDraw æ¥ä¼ éçã dispatchDraw ä¼éåè°ç¨åå ç´ ç draw æ¹æ³ï¼å¦æ¤ draw äºä»¶å°±ä¸å±ä¸å±ä¼ éäºä¸å»ãdispatchDraw å¨ View ç±»ä¸æ¯ç©ºå®ç°çï¼å¨ ViewGroup ç±»ä¸æ¯çæ£å®ç°çã
å¦æä¸ä¸ª View ä¸éè¦ç»å¶ä»»ä½å 容ï¼é£ä¹å°±è®¾ç½®è¿ä¸ªæ 记为 trueï¼ç³»ç»ä¼è¿è¡è¿ä¸æ¥çä¼åã
å½å建çèªå®ä¹æ§ä»¶ç»§æ¿äº ViewGroup 并ä¸ä¸å ·å¤ç»å¶åè½æ¶ï¼å°±å¯ä»¥å¼å¯è¿ä¸ªæ è®°ï¼ä¾¿äºç³»ç»è¿è¡åç»çä¼åï¼å½æç¡®ç¥éä¸ä¸ª ViewGroup éè¦éè¿ onDraw ç»å¶å 容æ¶ï¼éè¦å ³éè¿ä¸ªæ è®°ã
åèï¼ãAndroidå¼åèºæ¯æ¢ç´¢ã
❤️ Android 源码解读-从setContentView深入了解 Window|Activity|View❤️
Android系统中,Window、Activity、View之间的关系是紧密相连且相互作用的。了解这三者之间的关系,有助于深入理解Android应用的渲染和交互机制。
在Android中,通常在创建Activity时会调用`setContentView()`方法,以指定显示的布局资源。这个方法主要作用是将指定的布局添加到一个名为`DecorView`的容器中,并最终将其显示在屏幕上。这一过程涉及到多个组件的交互,下面分步骤解析。
在`Activity`类中,`setContentView()`方法调用`getWindow()`方法获取`Window`对象,而`Window`对象在`Activity`的`attach()`方法中被初始化。`Window`对象是一个抽象类,其默认实现为`PhoneWindow`,这是Android特定的窗口实现。
`PhoneWindow`在创建时会通过`setWindowManager()`方法与`WindowManager`进行关联。`WindowManager`是系统级组件,用于管理所有的窗口,包括窗口的创建、更新、删除等操作。`WindowManager`的管理最终由`WindowManagerService`(WMS)执行,这是发彩app源码一个运行在系统进程中的服务。
在`PhoneWindow`中,`installDecor()`方法会初始化`DecorView`和`mContentParent`。`mContentParent`是一个`ViewGroup`,用于存放`setContentView()`传入的布局。通过`mLayoutInflater`的`inflate()`方法,将指定的布局资源添加到`mContentParent`中。
`DecorView`是一个特殊的`FrameLayout`,包含了`mContentParent`。在完成布局的添加后,`DecorView`本身并没有直接与`Activity`建立联系,也没有被绘制到屏幕上显示。`DecorView`的绘制和显示发生在`Activity`的`onResume()`方法执行后,这时`Activity`中的内容才真正可见。
当`Activity`执行到`onCreate()`阶段时,其内容实际上并没有显示在屏幕上,直到执行到`onResume()`阶段,`Activity`的内容才被真正显示。这一过程涉及到`ActivityThread`中的`handleResumeActivity()`方法,该方法会调用`WindowManager`的`addView()`方法,将`DecorView`添加到`WindowManagerService`中,完成`DecorView`的绘制和显示。
`WindowManagerService`通过`addView()`方法将`DecorView`添加到显示队列中,并且在添加过程中,会创建关键的`ViewRootImpl`对象,进一步管理`DecorView`的布局、测量和绘制。`ViewRootImpl`会调用`mWindowSession`的`addToDisplay()`方法,将`DecorView`添加到真正的laychat移动端源码显示队列中。
`mWindowSession`是`WindowManagerGlobal`中的单例对象,其内部实际上是一个`IWindowSession`类型,通过`AIDL`接口与系统进程中的`Session`对象进行通信,最终实现`DecorView`的添加和显示。
通过`setView()`方法的实现,可以看到除了调用`IWindowSession`进行跨进程添加`View`之外,还会设置输入事件处理。当触屏事件发生时,这些事件首先通过驱动层的优化计算,通过`Socket`跨进程通知`Android Framework`层,最终触屏事件会通过输入管道传送到`DecorView`处理。
在`DecorView`内部,触屏事件会通过`onProcess`方法传递给`mView`,即`PhoneWindow`中的`DecorView`。最终,事件传递到`PhoneWindow`中的`View.java`实现的`dispatchPointerEvent()`方法,并调用`Window.Callback`的`dispatchTouchEvent(ev)`方法。对于`Activity`来说,`dispatchTouchEvent()`方法最终还是会调用`PhoneWindow`的`superDispatchTouchEvent()`,然后传递给`DecorView`的`superDispatchTouchEvent()`方法,完成事件的分发和处理。
综上所述,通过`setContentView()`的过程,我们可以清晰地看到`Activity`、`Window`、`View`之间的交互关系。整个过程主要由`PhoneWindow`组件主导,而`Activity`主要负责提供要显示的布局资源,其与屏幕的直接交互则通过`WindowManager`和`WindowManagerService`实现。
Android HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
HUWUI是Android系统中负责应用可视化元素绘制的核心组件,其架构主要在C++层实现,从Java层接收View绘制信息,通过唯一的渲染线程使用skia技术完成渲染任务。整体上,从应用程序到UI线程,再到渲染线程,形成了清晰的层级关系。
HUWUI的构建主要包括三个核心类,它们分别是:RecordingCanvas、Canvas、RenderNode、RenderProxy、RenderThread、CanvasContext、IRenderPipeline。在Java层,主要涉及两类Canvas,RecordingCanvas用于记录绘制指令,Canvas则是直接用于渲染。RecordingCanvas在构造时创建,而Canvas在调用时创建。这两个类在C++层分别对应SkiaRecordingCanvas和SkiaCanvas,后者直接引用SkCanvas。
在全局循环中,UI线程与渲染线程之间的协同操作至关重要。具体流程包括:新创建Activity后,附着到对应的PhoneWindow,然后调用PhoneWindow的setContentView方法,将View添加到DecorView作为子节点。接着,DecorView与ViewRootImpl对接,完成View的更新与渲染。整个过程包含了measure、layout和draw等复杂子流程。
渲染线程创建与核心对象紧密关联,主要包括RenderProxy、RenderThread和DrawFrameTask。RenderProxy负责Java层信息的衔接,RenderThread作为进程唯一的渲染线程,持有DrawFrameTask和CanvasContext,完成一帧的绘制任务。指令记录流程的核心在于使用C++层的RecordingCanvas将View属性和绘制信息记录到DisplayList中,进而完成指令的渲染。
Surface、ANativeWindow、EGLSurface的创建流程在ViewRootImpl的performTraversals函数中初始化。ReliableSurface的封装和EGL与Skia环境的创建主要在RenderThread的requireGlContext函数中实现。从源码分析,这一过程通常在三个地方调用。
View树与RenderNode树之间的协作关系明确,一个Application进程对应多个Activity,每个Activity与一个PhoneWindow绑定,PhoneWindow持有DecorView,DecorView对应一个ViewRootImpl,而ViewRootImpl与ThreadedRender模块对接。ThreadedRender与C++层的RenderProxy一一对应,RenderProxy持有关键对象,如RenderThread、CanvasContext、DrawFrameTask等。RenderThread是单例模式,进程唯一,负责一帧绘制的逻辑。
在RenderPipeline模块中,关键操作包括makeCurrent、draw和swapBuffers。Native Canvas在这一过程中扮演了桥梁角色,接收Java API调用,而RecordingCanvas完成Op记录,最终DisplayListData存储这些Op。
skia的核心资源主要在三个使用场景中发挥作用,具体细节需深入分析,这些资源对于实现高效、稳定的渲染效果至关重要。
最简最全,一文搞定Android WebView编译+AOSP集成
对于Android开发者来说,Android WebView是不可或缺的内置组件,它提供了一键可用的网页浏览功能。然而,WebView作为系统组件,其版本更新受限于系统级别的开发,可能导致HTML5、ES、CSS特性支持不足。本文将详细介绍如何从Chromium源码编译定制WebView,以及如何集成到AOSP系统中。
首先,确保你已经下载并配置好Chromium源码。编译时,使用gn命令生成args.gn文件,其中需新增system_webview_package_name选项来设置自定义APK包名,特别注意不同Android版本的WebView包名差异。编译目标有三种:system_webview_apk(适用于5.0及以上,独立APK)、monochrome_public_apk(包含WebView和Chrome,适用于自开发系统)和trichrome_webview_apk(适用于Android +,采用aab拆分)。
编译完成后,根据目标选择对应的APK,如system_webview_apk将生成一个SystemWebview.apk,包内包含WebView DevTools,用于调试。通过修改args.gn文件中的包名,确保与系统预装WebView的版本一致。如果在非AOSP系统中,可能需要使用adb或其他工具检查并修改包名。
在编译过程中,还需注意在系统中卸载预装的WebView以避免签名冲突。使用adb脚本进行一键卸载,然后将编译好的APK安装到设备,可能还需修改WebView提供者以指向新安装的版本。
对于AOSP集成,虽然预编译的WebView在AOSP中可用,但建议使用自编译的最新稳定版。根据目标Android版本选择合适的Chromium稳定版代码,并注意兼容性问题。编译正式发布版本时,需设置is_official_build和proprietary_codecs等选项,同时考虑视频编解码的许可证问题。
最后,对于私有签名、包名修改、系统镜像集成以及Android框架的修改,都有详细的步骤和注意事项。编译WebView并成功集成到AOSP后,可以确保为用户提供最新、定制化的浏览器体验。
androidèªå®ä¹view
androidæä¹èªå®ä¹viewå¢ï¼ä¸ç¥éçå°ä¼ä¼´æ¥ççå°ç¼ä»å¤©çå享å§!
androidå¯ä»¥éè¿ç»åæ§ä»¶æ¥å®ç°èªå®ä¹viewãç»åæ§ä»¶å°±æ¯å°ç³»ç»åæçæ§ä»¶è¿è¡ç»åï¼ææä¸ä¸ªæ°çæ§ä»¶ãè¿ç§æ¹å¼ä¸ï¼ä¸éè¦å¼åè èªå·±å»ç»å¶å¾ä¸æ¾ç¤ºçå 容ï¼ä¹ä¸éè¦å¼åè éåonMeasureï¼onLayoutï¼onDrawæ¹æ³æ¥å®ç°æµéãå¸å±ä»¥ådrawæµç¨ã
å ·ä½æä½ï¼
1ãå®ä¹æ é¢æ å¸å±æ件
å®ä¹æ é¢æ çå¸å±æ件custom_title_view.xmlï¼å°è¿åæé®åæ é¢ææ¬è¿è¡ç»åãè¿ä¸æ¥ç¨äºç¡®å®æ é¢æ çæ ·åï¼ä»£ç å¦ä¸æ示ï¼
xmlversion="1.0"encoding="utf-8"?RelativeLayoutxmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
Button
android:id="@+id/btn_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:text="Back"
android:textColor="@android:color/white"/
TextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Title"
android:textColor="@android:color/white"
android:textSize="sp"/
/RelativeLayout
2ãæ ¹æ®ç»å®å¸å±å®ç°èªå®ä¹View
publicclassCustomTitleViewextendsFrameLayoutimplementsView.OnClickListener{
privateView.OnClickListenermLeftOnClickListener;
privateButtonmBackBtn;
privateTextViewmTittleView;
publicCustomTitleView(@NonNullContextcontext,@NullableAttributeSetattrs){
super(context,attrs);
LayoutInflater.from(context).inflate(R.layout.custom_title_view,this);
mBackBtn=findViewById(R.id.btn_left);
mBackBtn.setOnClickListener(this);
mTittleView=findViewById(R.id.title_tv);
}
@Override
publicvoidonClick(Viewv){
switch(v.getId()){
caseR.id.btn_left:
if(mLeftOnClickListener!=null){
mLeftOnClickListener.onClick(v);
}
break;
}
}
publicvoidsetLeftOnClickListener(View.OnClickListenerleftOnClickListener){
mLeftOnClickListener=leftOnClickListener;
}
publicvoidsetTittle(Stringtitle){
mTittleView.setText(title);
}
}
说æï¼
ï¼1ï¼ä»£ç ä¸å¯¹å¤æä¾äºä¸¤ä¸ªæ¥å£ï¼ä¸æ¯å¨æ设置æ é¢ï¼äºæ¯ä½¿ç¨è å¯ä»¥èªå®ä¹è¿åæé®çç¹å»äºä»¶ã
ï¼2ï¼CustomTitleViewçæé å½æ°ï¼è¦éæ©ä¸¤ä¸ªåæ°çï¼éæ©å ¶å®åæ°çæé å½æ°ä¼æ¥éãè¿ä¸ç¹æ¯ç¬è å¼åæºæµè¯çç»æï¼ææ¶ä¸æ¸ æ¥æ¯ä¸æ¯ææææºä¸é½æ¯è¿æ ·ã
ï¼3ï¼è¿éæ¯ç»§æ¿çFrameLayoutï¼ä½æ¯ç»§æ¿LinearLayoutï¼RelativeLayoutçç³»ç»å¸å±æ§ä»¶é½å¯ä»¥ãä¹æ以è¦ç»§æ¿è¿äºç³»ç»ç°æçViewGroupï¼æ¯å 为è¿æ ·å¯ä»¥ä¸ç¨åéåonMeasureï¼onLayoutçï¼è¿æ ·çäºå¾å¤ãç±äºè¿éæ¯ä¸ä¸ªå¸å±æ§ä»¶ï¼è¦ç¨LayoutInflateræ¥å¡«å ï¼æ以éè¦ç»§æ¿ViewGroupï¼å¦æ继æ¿Viewçç´æ¥åç±»ï¼ç¼è¯ä¼ä¸éè¿ãæ以ï¼CustomTitleViewèªå·±å°±æ¯ä¸ä¸ªå®¹å¨ï¼å®å ¨å¯ä»¥å½æ容å¨ä½¿ç¨ï¼æ¤æ¶CustomTitleViewèªèº«çå 容ä¼åå ¶ä½ä¸ºç¶å¸å±æ·»å çåæ§ä»¶ï¼ææä¼å å ï¼å ·ä½çå å æææ¯æ ¹æ®ç»§æ¿ç容å¨ç¹æ§å³å®çã
3ãå¨Activityçå¸å±æ件ä¸æ·»å CustomTitleViewã
å¨Activityçå¸å±æ件activity_custom_view_compose_demo.xmlä¸ï¼å使ç¨ç³»ç»æ§ä»¶ä¸æ ·ä½¿ç¨CustomTitleViewå³å¯ãCustomTitleViewèªå·±å°±æ¯ç»§æ¿çç°æçç³»ç»å¸å±ï¼æ以å®ä»¬æ¥æçå±æ§ç¹æ§ï¼CustomTitleViewä¸æ ·æ¥æã
xmlversion="1.0"encoding="utf-8"?RelativeLayoutxmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
com.example.demos.customviewdemo.CustomTitleView
android:id="@+id/customview_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/com.example.demos.customviewdemo.CustomTitleView
/RelativeLayout
4ãå¨Activityä¸æä½CustomTitleViewï¼ä»£ç å¦ä¸ï¼
1publicclassCustomViewComposeDemoActivityextendsAppCompatActivity{ privateCustomTitleViewmCustomTitleView;4@Override5protectedvoidonCreate(BundlesavedInstanceState){ 6super.onCreate(savedInstanceState);7setContentView(R.layout.activity_custom_view_compose_demo);8mCustomTitleView=findViewById(R.id.customview_title);9mCustomTitleView.setTittle("ThisisTitle");mCustomTitleView.setLeftOnClickListener(newView.OnClickListener(){ @OverridepublicvoidonClick(Viewv){ finish();}});}}
å¨ç¬¬8è¡ä¸ï¼è·åå°CustomTitleViewå®ä¾ï¼ç¬¬9è¡è®¾ç½®æ é¢æåï¼ç¬¬è¡èªå®ä¹âBackâæé®ç¹å»äºä»¶ã
5ãææå¾
æç §å¦ä¸ç4æ¥ï¼å°±éè¿ç»åæ§ä»¶å®æäºä¸ä¸ªæ¯è¾ç®åçèªå®ä¹æ é¢æ ã
以ä¸å°±æ¯å°ç¼ä»å¤©çå享äºï¼å¸æå¯ä»¥å¸®å©å°å¤§å®¶ã
Android èªå®ä¹Viewï¼ä¸ºä»ä¹ä½ 设置çwrap_contentä¸èµ·ä½ç¨ï¼
å¨ä½¿ç¨èªå®ä¹Viewæ¶ï¼View宽 / é«ç wrap_content å±æ§ä¸èµ·èªèº«åºæçä½ç¨ï¼èä¸æ¯èµ·å°ä¸ match_parent ç¸åä½ç¨ãå ¶å®è¿éæ两个é®é¢ï¼
请åæ & 解å³é®é¢ä¹åï¼è¯·å çèªå®ä¹Viewåçä¸ ï¼2ï¼èªå®ä¹View Measureè¿ç¨ - æææçèªå®ä¹Viewåçç³»å
é®é¢åºç°å¨Viewç宽 / é«è®¾ç½®ï¼é£æ们ç´æ¥æ¥çèªå®ä¹Viewç»å¶ä¸ç¬¬ä¸æ¥å¯¹View宽 / é«è®¾ç½®çè¿ç¨ï¼measureè¿ç¨ä¸ç onMeasureï¼ï¼ æ¹æ³
继ç»å¾ä¸ç getDefaultSizeï¼ï¼
ä»ä¸é¢åç°ï¼
é£ä¹æ人ä¼é®ï¼wrap_contentåmatch_parentå ·æç¸åçææï¼ä¸ºä»ä¹æ¯å¡«å ç¶å®¹å¨çææå¢ï¼
æ们ç¥éï¼åViewçMeasureSpecå¼æ¯æ ¹æ®åViewçå¸å±åæ°ï¼LayoutParamsï¼åç¶å®¹å¨çMeasureSpecå¼è®¡ç®å¾æ¥ï¼å ·ä½è®¡ç®é»è¾å°è£ å¨getChildMeasureSpec()éã
æ¥ä¸æ¥ï¼æ们ççæåView MeasureSpecçæ¹æ³: getChildMeasureSpec() çæºç åæï¼
getChildMeasureSpec()
ä»ä¸é¢å¯ä»¥çåºï¼å½åViewçå¸å±åæ°ä½¿ç¨ match_parent æ wrap_content æ¶ï¼
æä»¥ï¼ wrap_content èµ·å°äºå match_parent ç¸åçä½ç¨ï¼çäºç¶å®¹å¨å½åå©ä½ç©ºé´å¤§å°
å½èªå®ä¹Viewçå¸å±åæ°è®¾ç½®æwrap_contentæ¶æ¶ï¼æå®ä¸ä¸ªé»è®¤å¤§å°ï¼å®½ / é«ï¼ã
è¿æ ·ï¼å½ä½ çèªå®ä¹Viewç宽 / é«è®¾ç½®æwrap_contentå±æ§æ¶å°±ä¼çæäºã
ç½ä¸æµä¼ çè¿ä¹ä¸ä¸ªè§£å³æ¹æ¡ï¼
çï¼æ¯ï¼å½ç¶View为 AT_MOST ãView为 match_parent æ¶ï¼è¯¥Viewç match_parent çææå°±çäº wrap_content ãä¸è¿°æ¹æ³åå¨é»è¾é误ï¼ä½ç±äºè¿ç§æ åµé常ç¹æ®çï¼æ以导è´æç»çç»æ没æé误ãå ·ä½åæ请çä¸é¢ä¾åï¼
ä»ä¸é¢çææå¯ä»¥çåºï¼Viewå¤§å° = é»è®¤å¼
æåå°åViewçå±æ§æ¹ä¸º wrap_content ï¼
ä»ä¸é¢çææå¯ä»¥çåºï¼View大å°è¿æ¯çäºé»è®¤å¼ã
ç¸ä¿¡çå°è¿éä½ å·²ç»çæäºï¼
为äºæ´å¥½ç表示å¤æé»è¾ï¼æå»ºè®®ä½ ä»¬ç¨æ¬ææä¾ç解å³æ¹æ¡ï¼å³æ ¹æ®å¸å±åæ°å¤æé»è®¤å¼ç设置
ä¸å®æåäº«å ³äºå®åå¼åç干货ï¼è¿½æ±çãå¹³ãå¿«ï¼ä½å´ä¸ç¼ºæ·±åº¦ã