andriod 개발 의 Activity 렌 더 링 메커니즘
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Activity 의 원본 코드 로 바로 이동 하면 Activity.set ContentView 가 실제로 Phone Window.set ContentView 를 호출 한 것 을 볼 수 있 습 니 다.
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) {
...
mWindow = new PhoneWindow(this, window);
...
}
public Window getWindow() {
return mWindow;
}
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
우 리 는 Phone Window 의 소스 코드 를 계속 추적 하면 최종 layoutResID 가 inflate 에 나 온 후에 mDecor 라 는 DecorView 의 하위 view 가 된 것 을 발견 할 수 있 습 니 다.DecorView 는 사실상 FrameLayout 입 니 다.
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
...
}
if (mContentParent == null) {
//mContentParent mDecor view
mContentParent = generateLayout(mDecor);
...
}
...
}
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
...
}
이 곳 의 generateLayout 는 비교적 중요 합 니 다.이것 은 실제로 window 의 각종 속성 인 inflate 에 따라 다른 layot 를 DecorView 아래 에 걸 고 mContentParent 는 이 layot 의 키 ViewGroup 입 니 다.window 의 속성 을 설정 하지 않 으 면 기본 com.android.internal.R.layot.screen 을 사용 합 니 다.simple 이 레이아웃:
protected ViewGroup generateLayout(DecorView decor) {
...
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
...
layoutResource = com.android.internal.R.layout.screen_title_icons;
...
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = com.android.internal.R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
...
layoutResource = com.android.internal.R.layout.screen_custom_title;
...
} ... else{
layoutResource = com.android.internal.R.layout.screen_simple;
}
...
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
우 리 는 AndroidSdk 루트 디 렉 터 리/platforms/android-19/data/res/layout/아래 에서 이 layot xml 를 찾 을 수 있 습 니 다.예 를 들 어 screenSimple,이것 은 수직 적 인 LinearLayout 로 위의 Action Bar 와 아래 의 content FrameLayout 로 구성 되 어 있 습 니 다.이것 은 바로 우리 가 가장 흔히 볼 수 있 는 Action Bar 를 가 진 activity 스타일 입 니 다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
Activity 가 레이아웃 을 어떻게 관리 하 는 지 그림 으로 정리 할 수 있 습 니 다.(여기 서 DecorView 에 screen 이 추가 되 었 다 고 가정 합 니 다.simple 이 레이아웃):Activity 의 레이아웃 은 어떻게 시스템 에 렌 더 링 됩 니까?
지난 절 에서 우 리 는 Activity 가 레이아웃 을 어떻게 관리 하 는 지 이미 알 게 되 었 다.이어서 Activity 의 레이아웃 이 시스템 에 어떻게 렌 더 링 되 는 지 살 펴 보 자.
Activity Thread 는 Activity 의 성명 주 기 를 관리 하 는 데 사 용 됩 니 다.그 후에 저 는 전문 적 으로 글 을 써 서 이야기 하 겠 습 니 다.Activity Thread.handle ResumeActivity 방법 을 직접 보 겠 습 니 다.
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
...
//performResumeActivity Activity.onResume
ActivityClientRecord r = performResumeActivity(token, clearHide);
...
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
...
}
Activity.onResume 에 이 어 Activity 에서 Window 를 가 져 온 다음 window 에서 DecorView 를 가 져 온 것 을 볼 수 있 습 니 다.마지막 으로 Window Manager.addView 를 사용 하여 DecorView 를 Window Manager 에 추가 하 였 습 니 다.이렇게 해서 DecorView 를 휴대 전화 에 렌 더 링 했다.Window Manager.addView 방법 은 하나의 view 를 모 바 일 인터페이스 에 렌 더 링 할 수 있 습 니 다.여러분 이 부유 구 와 유사 한 응용 을 해 본 적 이 있 는 지 모 르 겠 지만,바로 Window Manager.addView 로 이 루어 진 것 입 니 다.여 기 는 더 이상 펼 쳐 지지 않 습 니 다.관심 이 있 으 시 면 직접 찾 아 보 세 요.
왜 하위 스 레 드 에서 view 를 조작 할 수 없 습 니까?
안 드 로 이 드 에서 ui 스 레 드 에서 ui 를 조작 해 야 한 다 는 것 을 잘 알 고 있 습 니 다.하위 스 레 드 에서 view 를 조작 할 수 없습니다.그렇지 않 으 면 Called FromWrong ThreadException 이상 을 던 질 수 있 습 니 다.그런데 서브 스 레 드 에서 view 를 조작 하면 꼭 이상 이 생기 지 않 을까요?다음 코드 를 실행 합 니 다:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
((TextView)findViewById(R.id.textView)).setText(" view");
}
}).start();
}
}
실제 onCreate 에서 하위 스 레 드 를 직접 시작 하여 TextView 의 문 자 를 수정 하면 정상적으로 실 행 될 수 있 고 문자 도 정상 적 인 것 을 볼 수 있 습 니 다.우리 집의 1 초 지연 을 다시 시도 합 시다.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
((TextView)findViewById(R.id.textView)).setText(" view");
}
}).start();
}
}
실행 하면 익숙 한 충돌 로 그 를 볼 수 있 습 니 다:
02-28 22:36:48.550 3780 3817 E AndroidRuntime: FATAL EXCEPTION: Thread-5
02-28 22:36:48.550 3780 3817 E AndroidRuntime: Process: com.example.linjw.myapplication, PID: 3780
02-28 22:36:48.550 3780 3817 E AndroidRuntime: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6987)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1104)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.view.View.requestLayout(View.java:19807)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.view.View.requestLayout(View.java:19807)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.view.View.requestLayout(View.java:19807)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.view.View.requestLayout(View.java:19807)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.support.constraint.ConstraintLayout.requestLayout(ConstraintLayout.java:874)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.view.View.requestLayout(View.java:19807)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.widget.TextView.checkForRelayout(TextView.java:7375)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.widget.TextView.setText(TextView.java:4487)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.widget.TextView.setText(TextView.java:4344)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at android.widget.TextView.setText(TextView.java:4319)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at com.example.linjw.myapplication.MainActivity$1.run(MainActivity.java:20)
02-28 22:36:48.550 3780 3817 E AndroidRuntime: at java.lang.Thread.run(Thread.java:760)
왜 1 초 만 늦 추 면 이상 이 던 져 지 는 걸 볼 수 있 을까요?꼬치 꼬치 캐 묻 는 정신 에 따라 ViewRootImpl 의 소스 코드 를 직접 잠 그 고 Called From Wrong ThreadException 이상 이 어떻게 던 져 졌 는 지 살 펴 보 겠 습 니 다.
public ViewRootImpl(Context context, Display display) {
...
mThread = Thread.currentThread();
...
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
View.requestLayout 방법 에 서 는 ViewRootImpl.requestLayout 를 호출 한 다음 ViewRootImpl.requestLayout 에서 ViewRootImpl.checkThread 를 호출 하여 현재 스 레 드 와 ViewRootImpl 을 만 드 는 스 레 드 가 같은 스 레 드 인지 아 닌 지 판단 합 니 다.아니면 Called From Wrong ThreadException 이상 을 던 져.그럼 ViewRootImpl 은 또 어느 스 레 드 에서 만 들 어 졌 습 니까?지난 절 에서 말 한 Activity Thread.handle ResumeActivity 방법 에서 DecorView 를 Window Manager 에 추가 한 것 을 기억 하 십 니까?Window Manager 는 실제로 Window Manager Impl 인 스 턴 스 입 니 다.
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
...
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
...
}
Window Manager Impl.addView 가 실제로 Window Manager Global.addView 로 바 뀌 었 음 을 볼 수 있 습 니 다.
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
...
ViewRootImpl root;
...
root = new ViewRootImpl(view.getContext(), display);
...
}
}
그래서 ViewRootImpl 은 handle ResumeActivity 의 스 레 드 에서 만 들 어 졌 습 니 다.우 리 는 모두 onResume 이 메 인 스 레 드 에서 호출 되 었 다 는 것 을 알 고 있 기 때문에 ViewRootImpl 은 메 인 스 레 드 에서 호출 되 었 습 니 다.따라서 비 메 인 스 레 드 에서 ViewRootImpl.requestLayout 를 호출 하면 Called FromWrong ThreadException 이상 을 던 집 니 다.그러면 최초의 문제 로 돌아 갑 니 다.왜 우 리 는 onCreate 에서 하위 스 레 드 를 직접 시작 하여 TextView 의 문 자 를 수정 합 니까?Called From Wrong ThreadException 이상 을 던 지지 않 습 니까?ViewRootImpl 은 onResume 에서 만 들 어 졌 기 때문에 onCreate 에서 만 들 지 않 았 기 때문에 Called From Wrong ThreadException 이상 을 던 지지 않 습 니 다.
onResume 을 기 다 렸 을 때 ViewRootImpl 이 생 성 되 고 첫 번 째 layot 가 진 행 됩 니 다.이 럴 때 주 스 레 드 에서 ui 를 작 동 하 는 지 확인 할 수 있 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Andriod 대화 상자 FLAGNOT_TOUCH_MODAL 활용 정보1. 대화상자가 표시된 상태에서 대화상자 이외의 구역에 단추가 존재하면 단추를 누르면 대화상자를 삭제하는 동시에 단추의 클릭 이벤트도 반응한다. 2. 대화 상자 이외의 영역에 ListView와 같은 스크롤 가능한 U...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.