JNI層
上面兩個類的JNI實現(xiàn)都在framework/base/core/jni/android_view_Surface.cpp中。
[---->SurfaceSession:: SurfaceSession()]
- public class SurfaceSession {
- /** Create a new connection with the surface flinger. */
- public SurfaceSession() {
- init();
- }
它的init函數對應為:
[--->SurfaceSession_init]
- static void SurfaceSession_init(JNIEnv* env, jobject clazz)
- {
- //SurfaceSession對應為SurfaceComposerClient
- sp client = new SurfaceComposerClient;
- client->incStrong(clazz);
- //Google常用做法,在JAVA對象中保存C++對象的指針。
- env->SetIntField(clazz, sso.client, (int)client.get());
- }
Surface的init對應為:
[--->Surface_init]
- static void Surface_init(
- JNIEnv* env, jobject clazz,
- jobject session,
- jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
- {
- SurfaceComposerClient* client =
- (SurfaceComposerClient*)env->GetIntField(session, sso.client);
- sp surface;
- if (jname == NULL) {
- //client是SurfaceComposerClient,返回的surface是一個SurfaceControl
- //真得很復雜!
- surface = client->createSurface(pid, dpy, w, h, format, flags);
- } else {
- const jchar* str = env->GetStringCritical(jname, 0);
- const String8 name(str, env->GetStringLength(jname));
- env->ReleaseStringCritical(jname, str);
- surface = client->createSurface(pid, name, dpy, w, h, format, flags);
- }
- //把surfaceControl信息設置到Surface對象中
- setSurfaceControl(env, clazz, surface);
- }
- static void setSurfaceControl(JNIEnv* env, jobject clazz,
- const sp& surface)
- {
- SurfaceControl* const p =
- (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
- if (surface.get()) {
- surface->incStrong(clazz);
- }
- if (p) {
- p->decStrong(clazz);
- }
- env->SetIntField(clazz, so.surfaceControl, (int)surface.get());
- }
[--->Surface_copyFrom]
- static void Surface_copyFrom(
- JNIEnv* env, jobject clazz, jobject other)
- {
- const sp& surface = getSurfaceControl(env, clazz);
- const sp& rhs = getSurfaceControl(env, other);
- if (!SurfaceControl::isSameSurface(surface, rhs)) {
- setSurfaceControl(env, clazz, rhs);
- //把本地那個surface的surfaceControl對象轉移到outSurface上
- }
- }
這里僅僅是surfaceControl的轉移,但是并沒有看到Surface相關的信息。
那么Surface在哪里創(chuàng)建的呢?為了解釋這個問題,我使用了終極武器,aidl。
1 終極武器AIDL
aidl可以把XXX.aidl文件轉換成對應的java文件。我們剛才調用的是WindowSession的
relayOut函數。如下:
- sWindowSession.relayout(
- mWindow, params,
- (int) (mView.mMeasuredWidth * appScale + 0.5f),
- (int) (mView.mMeasuredHeight * appScale + 0.5f),
- viewVisibility, insetsPending, mWinFrame,
- mPendingContentInsets, mPendingVisibleInsets,
- mPendingConfiguration, mSurface);
它的aidl文件在framework/base/core/java/android/view/IWindowSession.aidl中
- interface IWindowSession {
- int add(IWindow window, in WindowManager.LayoutParams attrs,
- in int viewVisibility, out Rect outContentInsets);
- void remove(IWindow window);
- //注意喔,這個outSurface前面的是out,表示輸出參數,這個類似于C++的引用。
- int relayout(IWindow window, in WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewVisibility,
- boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
- out Rect outVisibleInsets, out Configuration outConfig,
- out Surface outSurface);
剛才說了,JNI及其JAVA調用只是copyFrom了SurfaceControl對象到outSurface中,但是沒看到哪里創(chuàng)建Surface。這其中的奧秘就在aidl文件編譯后生成的java文件中。
你在命令行下可以輸入:
aidl -Id:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java\ -Id:\android-2.2-froyo-20100625-source\source\frameworks\base\Graphics\java d:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java\android\view\IWindowSession.aidl test.java
以生成test.java文件。-I參數指定include目錄,例如aidl有些參數是在別的java文件中指定的,那么這個-I就需要把這些目錄包含進來。
先看看ViewRoot這個客戶端生成的代碼是什么。
- public int relayout(
- android.view.IWindow window,
- android.view.WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight,
- int viewVisibility, boolean insetsPending,
- android.graphics.Rect outFrame,
- android.graphics.Rect outContentInsets,
- android.graphics.Rect outVisibleInsets,
- android.content.res.Configuration outConfig,
- android.view.Surface outSurface) ---->outSurface是第11個參數
- throws android.os.RemoteException
- {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- int _result;
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));
- if ((attrs!=null)) {
- _data.writeInt(1);
- attrs.writeToParcel(_data, 0);
- }
- else {
- _data.writeInt(0);
- }
- _data.writeInt(requestedWidth);
- _data.writeInt(requestedHeight);
- _data.writeInt(viewVisibility);
- _data.writeInt(((insetsPending)?(1):(0)));
- //奇怪,outSurface的信息沒有寫到_data中。那.....
- mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
- _reply.readException();
- _result = _reply.readInt();
- if ((0!=_reply.readInt())) {
- outFrame.readFromParcel(_reply);
- }
- ....
- if ((0!=_reply.readInt())) {
- outSurface.readFromParcel(_reply); //從Parcel中讀取信息來填充outSurface
- }
- }
- finally {
- _reply.recycle();
- _data.recycle();
- }
- return _result;
- }
真奇怪啊,Binder客戶端這頭竟然沒有把outSurface的信息發(fā)過去。我們趕緊看看服務端。
服務端這邊處理是在onTranscat函數中。
- @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
- {
- switch (code)
- {
- case TRANSACTION_relayout:
- {
- data.enforceInterface(DESCRIPTOR);
- android.view.IWindow _arg0;
- android.view.Surface _arg10;
- //剛才說了,Surface信息并沒有傳過來,那么我們在relayOut中看到的outSurface是怎么
- //出來的呢?看下面這句,原來在服務端這邊竟然new了一個新的Surface!!!
- _arg10 = new android.view.Surface();
- int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);
- reply.writeNoException();
- reply.writeInt(_result);
- //_arg10是copyFrom了,那怎么傳到客戶端呢?
- if ((_arg10!=null)) {
- reply.writeInt(1);//調用Surface的writeToParcel,把信息加入reply
- _arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- }
- return true;
- }
太詭異了!竟然有這么多花花腸子。我相信如果沒有aidl的幫助,我無論如何也不會知道這其中的奧妙。
那好,我們的流程明白了。
◆客戶端雖然傳了一個surface,但其實沒傳遞給服務端
◆服務端調用writeToParcel,把信息寫到Parcel中,然后數據傳回客戶端
◆客戶端調用Surface的readFromParcel,獲得surface信息。
那就去看看writeToParcel吧。
[---->Surface_writeToParcel]
- static void Surface_writeToParcel(
- JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
- {
- Parcel* parcel = (Parcel*)env->GetIntField(
- argParcel, no.native_parcel);
- const sp& control(getSurfaceControl(env, clazz));
- //還好,只是把數據序列化到Parcel中
- SurfaceControl::writeSurfaceToParcel(control, parcel);
- if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
- setSurfaceControl(env, clazz, 0);
- }
- }
那看看客戶端的Surface_readFromParcel吧。
[----->Surface_readFromParcel]
- static void Surface_readFromParcel(
- JNIEnv* env, jobject clazz, jobject argParcel)
- {
- Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
- //客戶端這邊還沒有surface呢
- const sp& control(getSurface(env, clazz));
- //不過我們看到希望了,根據服務端那邊Parcel信息來構造一個新的surface
- sp rhs = new Surface(*parcel);
- if (!Surface::isSameSurface(control, rhs)) {
- setSurface(env, clazz, rhs); //把這個新surface賦給客戶端。終于我們有了surface!
- }
- }
到此,我們終于七拐八繞的得到了surface,這其中經歷太多曲折了。下一節(jié),我們將精簡這其中復雜的操作,統(tǒng)一歸到Native層,以這樣為切入點來了解Surface的工作流程和原理。
好,反正你知道ViewRoot調用了relayout后,Surface就真正從WindowManagerService那得到了。繼續(xù)回到ViewRoot,其中還有一個重要地方是我們知道卻不了解的。
- private void performTraversals() {
- // cache mView since it is used so much below...
- final View host = mView;
- boolean initialized = false;
- boolean contentInsetsChanged = false;
- boolean visibleInsetsChanged;
- try {
- relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
- // relayoutWindow完后,我們得到了一個無比寶貴的Surface
- //那我們畫界面的地方在哪里?就在這個函數中,離relayoutWindow不遠處。
- ....
- boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
- if (!cancelDraw && !newSurface) {
- mFullRedrawNeeded = false;
- draw(fullRedrawNeeded); //draw?draw什么呀?
- }
[--->ViewRoot::draw()]
- private void draw(boolean fullRedrawNeeded) {
- Surface surface = mSurface; //嘿嘿,不擔心了,surface資源都齊全了
- if (surface == null || !surface.isValid()) {
- return;
- }
- if (mAttachInfo.mViewScrollChanged) {
- mAttachInfo.mViewScrollChanged = false;
- mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
- }
- int yoff;
- final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
- if (scrolling) {
- yoff = mScroller.getCurrY();
- } else {
- yoff = mScrollY;
- }
- if (mCurScrollY != yoff) {
- mCurScrollY = yoff;
- fullRedrawNeeded = true;
- }
- float appScale = mAttachInfo.mApplicationScale;
- boolean scalingRequired = mAttachInfo.mScalingRequired;
- Rect dirty = mDirty;
- if (mUseGL) { //我們不用OPENGL
- ...
- }
- Canvas canvas;
- try {
- int left = dirty.left;
- int top = dirty.top;
- int right = dirty.right;
- int bottom = dirty.bottom;
- //從Surface中鎖定一塊區(qū)域,這塊區(qū)域是我們認為的需要重繪的區(qū)域
- canvas = surface.lockCanvas(dirty);
- // TODO: Do this in native
- canvas.setDensity(mDensity);
- }
- try {
- if (!dirty.isEmpty() || mIsAnimating) {
- long startTime = 0L;
- try {
- canvas.translate(0, -yoff);
- if (mTranslator != null) {
- mTranslator.translateCanvas(canvas);
- }
- canvas.setScreenDensity(scalingRequired
- ? DisplayMetrics.DENSITY_DEVICE : 0);
- //mView就是之前的decoreView,
- mView.draw(canvas);
- }
- } finally {
- //我們的圖畫完了,告訴surface釋放這塊區(qū)域
- surface.unlockCanvasAndPost(canvas);
- }
- if (scrolling) {
- mFullRedrawNeeded = true;
- scheduleTraversals();
- }
- }
看起來,這個surface的用法很簡單嘛:
l lockSurface,得到一個畫布Canvas
l 調用View的draw,讓他們在這個Canvas上盡情繪圖才。另外,這個View會調用所有它的子View來畫圖,最終會進入到View的onDraw函數中,在這里我們可以做定制化的界面美化工作。當然,如果你想定制化整個系統(tǒng)畫圖的話,完全可以把performTranvsal看懂,然后再修改。
l unlockCanvasAndPost,告訴Surface釋放這塊畫布
當然,這幾個重要函數調用干了具體的活。這些重要函數,我們最終會精簡到Native層的。
2 總結
到這里,你應該知道了一個Activity中,調用setContentView后它如何從系統(tǒng)中獲取一塊Surface,以及它是如何使用這個Surface的了。不得不說,關于UI這塊,Android絕對是夠復雜的。難怪2.3把UI這塊代碼基本重寫一遍,希望能夠簡單精煉點。
【編輯推薦】
第 1 頁:Activity是如何顯示的 | 第 2 頁:JNI層 |