尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究!
炮兵镇楼
看到这样的标题……估计N多人会说我是逗比…………因为很多盆友在学习Android(特别是从4.0之后开始入门的)的时候都会常看见或听到别人说我们更新UI呢要在UI线程(或者说主线程)中去更新UI,不要在子线程中更新UI,而Android官方呢也建议我们不要在非UI线程直接更新UI,为什么呢?借助Android官方的一句话来说就是:
“The Android UI toolkit is not thread-safe and the view must always be manipulated on the UI thread.”
因此,很多童鞋会有这么一个惯性思维:在非UI线程中不能更新UI!既然Android不建议我们这么做,那其必定会对我们在code时做一些限制,比如当我们尝试运行如下代码时:
- /**
- * 主界面
- *
- * @author Aige {@link http://blog.csdn.net/aigestudio}
- * @since 2014/11/17
- */
- public class MainActivity extends Activity {
- private TextView tvText;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- tvText = (TextView) findViewById(R.id.main_tv);
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- tvText.setText("OtherThread");
- }
- }).start();
- }
- }
- <!-- http://blog.csdn.net/aigestudio -->
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:background="#ffffff"
- android:layout_height="match_parent" >
- <TextView
- android:id="@+id/main_tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
这句话非常简单,而且……我相信每个做Android开发到一定时间的盆友都碰到过,Android通过检查我们当前的线程是否为UI线程从而抛出一个自定义的AndroidRuntimeException来提醒我们“Only the original thread that created a view hierarchy can touch its views”并强制终止程序运行,具体的实现在ViewRootImpl类的checkThread方法中:
- @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
- public final class ViewRootImpl implements ViewParent,
- View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
- // 省去海量代码…………………………
- void checkThread() {
- if (mThread != Thread.currentThread()) {
- throw new CalledFromWrongThreadException(
- "Only the original thread that created a view hierarchy can touch its views.");
- }
- }
- // 省去巨量代码……………………
- }
- 究竟TM到底能不能在非UI线程中更新UI呢?答案:能、当然可以
- View的运行和Activity的生命周期有什么必然联系吗?答案:没有、或者隐晦地说没有必然联系
- 除了Handler外是否还有更简便的方式在非UI线程更新UI呢?答案:有、而且还不少
OK,这里我们再来看一下上面的一段代码,在线程中我调用了Thread.sleep(200);来让我们的匿名线程暂停了200ms,如果……假如……我们去掉它的话……………………会发生什么?来试试:
- /**
- * 主界面
- *
- * @author Aige {@link http://blog.csdn.net/aigestudio}
- * @since 2014/11/17
- */
- public class MainActivity extends Activity {
- private TextView tvText;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- tvText = (TextView) findViewById(R.id.main_tv);
- new Thread(new Runnable() {
- @Override
- public void run() {
- tvText.setText("OtherThread");
- }
- }).start();
- }
- }
也就是说一旦我们尝试去对我们的控件进行生成,这些方法其中一个必然会被调用,这时候很多朋友就会蛋疼了…………但是,请不要被checkThread方法的思维所束缚,这时候你该扩大你的思维范畴,既然checkThread方法属于ViewRootImpl的成员方法,那么会不会是此时我们的ViewRootImpl根本就没被创建呢?怀着这个出发点,我们再度审视ActivtyThread调度Activity生命周期的各个环节,首先看看performLaunchActivity方法中的处理:
- public final class ActivityThread {
- // 省去海量代码…………………………
- private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- ActivityInfo aInfo = r.activityInfo;
- // 省去对packageInfo的逻辑处理
- // 省去对ComponentName的逻辑处理
- Activity activity = null;
- try {
- java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
- // 通过Instrumentation对象生成Activity类的实例
- activity = mInstrumentation.newActivity(
- cl, component.getClassName(), r.intent);
- // 省去三行代码…………
- } catch (Exception e) {
- // 省去对异常的捕获处理
- }
- try {
- Application app = r.packageInfo.makeApplication(false, mInstrumentation);
- // 省去多行无关代码
- if (activity != null) {
- Context appContext = createBaseContextForActivity(r, activity);
- CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
- Configuration config = new Configuration(mCompatConfiguration);
- // 省去多行无关代码
- if (customIntent != null) {
- activity.mIntent = customIntent;
- }
- r.lastNonConfigurationInstances = null;
- activity.mStartedActivity = false;
- int theme = r.activityInfo.getThemeResource();
- if (theme != 0) {
- activity.setTheme(theme);
- }
- /*
- * 调用callActivityOnCreate方法处理Create逻辑
- */
- activity.mCalled = false;
- mInstrumentation.callActivityOnCreate(activity, r.state);
- if (!activity.mCalled) {
- // 省去多行无关代码
- }
- r.activity = activity;
- r.stopped = true;
- /*
- * 调用performStart方法处理Start逻辑
- */
- if (!r.activity.mFinished) {
- activity.performStart();
- r.stopped = false;
- }
- // 省去多行无关代码
- }
- // 省去两行无关代码
- } catch (SuperNotCalledException e) {
- // 省去对异常的捕获处理
- } catch (Exception e) {
- // 省去对异常的捕获处理
- }
- return activity;
- }
- // 省去巨量代码……………………
- }
- public class Instrumentation {
- // 省去海量代码…………………………
- public void callActivityOnCreate(Activity activity, Bundle icicle) {
- // 省去某些逻辑……
- activity.performCreate(icicle);
- // 省去某些逻辑……
- }
- // 省去巨量代码……………………
- }
- public class Activity extends ContextThemeWrapper
- implements LayoutInflater.Factory2,
- Window.Callback, KeyEvent.Callback,
- OnCreateContextMenuListener, ComponentCallbacks2 {
- // 省去海量代码…………………………
- final void performCreate(Bundle icicle) {
- onCreate(icicle);
- mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
- com.android.internal.R.styleable.Window_windowNoDisplay, false);
- mFragments.dispatchActivityCreated();
- }
- // 省去巨量代码……………………
- }
- public class Activity extends ContextThemeWrapper
- implements LayoutInflater.Factory2,
- Window.Callback, KeyEvent.Callback,
- OnCreateContextMenuListener, ComponentCallbacks2 {
- // 省去海量代码…………………………
- final void performStart() {
- mFragments.noteStateNotSaved();
- mCalled = false;
- mFragments.execPendingActions();
- mInstrumentation.callActivityOnStart(this);
- if (!mCalled) {
- throw new SuperNotCalledException(
- "Activity " + mComponent.toShortString() +
- " did not call through to super.onStart()");
- }
- mFragments.dispatchStart();
- if (mAllLoaderManagers != null) {
- final int N = mAllLoaderManagers.size();
- LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
- for (int i=N-1; i>=0; i--) {
- loaders[i] = mAllLoaderManagers.valueAt(i);
- }
- for (int i=0; i<N; i++) {
- LoaderManagerImpl lm = loaders[i];
- lm.finishRetain();
- lm.doReportStart();
- }
- }
- }
- // 省去巨量代码……………………
- }
- public class Instrumentation {
- // 省去海量代码…………………………
- public void callActivityOnStart(Activity activity) {
- activity.onStart();
- }
- // 省去巨量代码……………………
- }
- public final class ActivityThread {
- // 省去海量代码…………………………
- final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
- boolean reallyResume) {
- unscheduleGcIdler();
- ActivityClientRecord r = performResumeActivity(token, clearHide);
- if (r != null) {
- final Activity a = r.activity;
- // 省去无关代码…………
- final int forwardBit = isForward ?
- WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
- boolean willBeVisible = !a.mStartedActivity;
- if (!willBeVisible) {
- try {
- willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
- a.getActivityToken());
- } catch (RemoteException e) {
- }
- }
- if (r.window == null && !a.mFinished && willBeVisible) {
- 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);
- }
- } else if (!willBeVisible) {
- // 省去无关代码…………
- r.hideForNow = true;
- }
- cleanUpPendingRemoveWindows(r);
- if (!r.activity.mFinished && willBeVisible
- && r.activity.mDecor != null && !r.hideForNow) {
- if (r.newConfig != null) {
- // 省去无关代码…………
- performConfigurationChanged(r.activity, r.newConfig);
- freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));
- r.newConfig = null;
- }
- // 省去无关代码…………
- WindowManager.LayoutParams l = r.window.getAttributes();
- if ((l.softInputMode
- & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
- != forwardBit) {
- l.softInputMode = (l.softInputMode
- & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
- | forwardBit;
- if (r.activity.mVisibleFromClient) {
- ViewManager wm = a.getWindowManager();
- View decor = r.window.getDecorView();
- wm.updateViewLayout(decor, l);
- }
- }
- r.activity.mVisibleFromServer = true;
- mNumVisibleActivities++;
- if (r.activity.mVisibleFromClient) {
- r.activity.makeVisible();
- }
- }
- if (!r.onlyLocalRequest) {
- r.nextIdle = mNewActivities;
- mNewActivities = r;
- // 省去无关代码…………
- Looper.myQueue().addIdleHandler(new Idler());
- }
- r.onlyLocalRequest = false;
- // 省去与ActivityManager的通信处理
- } else {
- // 省略异常发生时对Activity的处理逻辑
- }
- }
- // 省去巨量代码……………………
- }
- public class Activity extends ContextThemeWrapper
- implements LayoutInflater.Factory2,
- Window.Callback, KeyEvent.Callback,
- OnCreateContextMenuListener, ComponentCallbacks2 {
- // 省去海量代码…………………………
- void makeVisible() {
- if (!mWindowAdded) {
- ViewManager wm = getWindowManager();
- wm.addView(mDecor, getWindow().getAttributes());
- mWindowAdded = true;
- }
- mDecor.setVisibility(View.VISIBLE);
- }
- // 省去巨量代码……………………
- }
- public final class WindowManagerGlobal {
- public void addView(View view, ViewGroup.LayoutParams params,
- Display display, Window parentWindow) {
- // 省去很多代码
- ViewRootImpl root;
- // 省去一行代码
- synchronized (mLock) {
- // 省去无关代码
- root = new ViewRootImpl(view.getContext(), display);
- // 省去一行代码
- // 省去一行代码
- mRoots.add(root);
- // 省去一行代码
- }
- // 省去部分代码
- }
- }
- /**
- * 主界面
- *
- * @author Aige {@link http://blog.csdn.net/aigestudio}
- * @since 2014/11/17
- */
- public class MainActivity extends Activity {
- private TextView tvText;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- tvText = (TextView) findViewById(R.id.main_tv);
- }
- @Override
- protected void onResume() {
- super.onResume();
- new Thread(new Runnable() {
- @Override
- public void run() {
- tvText.setText("OtherThread");
- }
- }).start();
- }
- }