Androidの中性子は本当にUIがそれをスレッド更新することはできませんか?

複数のスレッドでUIへのアクセスが安全ではないので、AndroidのUIがロックされていない訪問。だから、Androidの規定は、UIスレッドでUIにのみアクセスすることができます。

しかし、極端なケースではありませんか?また、プログラムを構成し、それを実行することができます私たちは、サブスレッドでUIへのアクセスを許可しますか?次に、我々はそれを証明するために例を使用します。

次のように新しいプロジェクト、activity_main.xmlのレイアウトを作成します。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <TextView
        android:id="@+id/main_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:layout_centerInParent="true"
        />

</RelativeLayout>

非常にシンプルな、ちょうど中心のTextViewを追加

MainActivityコードは次のよう:

public class MainActivity extends AppCompatActivity {

    private TextView main_tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        main_tv = (TextView) findViewById(R.id.main_tv);

        new Thread(new Runnable() {

            @Override
            public void run() {
                main_tv.setText("子线程中访问");
            }
        }).start();
    }
}

それはのonCreateメソッド、およびUIアクセス操作で子スレッドを作成し、非常にシンプルなラインです。

[実行]をクリックします。あなたも、UIスレッドへの子のアクセスで、同じプログラムが駆けていることがわかります。:結果を以下に示す
ここに画像を挿入説明
ねえ、子供にUIスレッドを更新する前にそのことがそれを与えられていますか?サブスレッドでUIにアクセスすることが本当に可能ですか?

以下のようではない心配する、これは極端なケースである、MainActivityを変更します。

public class MainActivity extends AppCompatActivity {

    private TextView main_tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        main_tv = (TextView) findViewById(R.id.main_tv);

        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                main_tv.setText("子线程中访问");
            }
        }).start();
    }
}

スリープ200ミリ秒に子スレッドを聞かせて、その後、UIへのアクセスを覚まします。

結果はあなたがプログラムの崩壊ことがわかります。まあ、これは正常な現象です。彼は非常に精通し、次の例外がスローされます。

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6581)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:924)

開発者として、我々は慎重にこれらの例外についての情報をお読みください、なぜこれらの異常がUIにアクセスすることができなかったような場合に見つけることができます。その後、我々は例外情報を分析します:

まず、次の例外からの情報を知っているかもしれません

at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6581)

この例外はandroid.view.ViewRootImplのcheckThreadメソッドからスローされます。

そしてViewRootImplルックアップフォローする今checkThread方法は、ソースコードは次の通り:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

アプリケーションが起動されたときだけにして数行のコード、およびmThreadはメインスレッドで、それが初期化されています。

このことから、我々は結論付けることができます:
UIを訪れている間、メインスレッドが、それは次の例外をスローしない場合は、ViewRootImplは、スレッドがアクセス可能である現在のUIをチェックします:

Only the original thread that created a view hierarchy can touch its views

これは何を説明していないようですか?異常を見続けます

at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:924)

そして今requestLayout方法を見て、

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

これは、えっ、現在のスレッドをチェックするcheckThread()メソッドと呼ばれていますか?スレッドをチェックすることに加えて何の情報もないように思われます。次に参照する方法)(scheduleTraversalsに指し示します

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

PostCallbackは、二番目のパラメータの方法は、バックグラウンドタスクのような非常に渡されたことを指摘しました。それはまた行き指します

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

見つかり、その後、doTraversal()メソッドをフォローアップし続けています。

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

あなたは、performTraversalsコール()メソッドの内部で見るこのperformTraversals方法の初めからである描画処理を見ることができます。それはビューを描画するために学ぶことです一緒に行くし続けた場合、コードのPerformTraversals方法は、少し長くて掲載されていません。そして、我々は今、知っているあなたはUIにアクセスするたびに、Androidは再描画表示されます分析は、ここでは、実際に私たちを助けるために異常な情報が、それは私たちだけ例外がスローされたUIスレッドに子のアクセスを伝え、大きくはありません。
そして、我々は考えてます:UIにアクセスすると、ViewRootImplはそれがない場合は、UIスレッドが例外をスローします、UIスレッドへの現在のアクセスをチェックするためにcheckThreadの道を呼び出し、これは問題ありません。しかし、なぜ一緒にサブスレッドのアクセスUIでMainActivityを作成するためのonCreateメソッド、または通常のプログラム実行をしましたか?
唯一の説明は、時間ViewRootImplは作成しないことを、実行のonCreateの方法は、現在のスレッドをチェックすることはないということです。

そして、あなたはとても深くに行くことができます。ルッキングViewRootImplは創造の、どのような時間です。さて、上の移動

次のようにActivityThreadでは、我々は、handleResumeActivity方法を見つけます:

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    // TODO Push resumeArgs into the activity for consideration
    ActivityClientRecord r = performResumeActivity(token, clearHide);

    if (r != null) {
        final Activity a = r.activity;

        //代码省略

            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }

      //代码省略    
}

確かに、我々は表情で行く、コールバックメソッドonResume入り口の名前が表示された内部コールperformResumeActivity方法を、見ることができます。

public final ActivityClientRecord performResumeActivity(IBinder token,
        boolean clearHide) {
    ActivityClientRecord r = mActivities.get(token);
    if (localLOGV) Slog.v(TAG, "Performing resume of " + r
            + " finished=" + r.activity.mFinished);
    if (r != null && !r.activity.mFinished) {
    //代码省略
            r.activity.performResume();

    //代码省略

    return r;
}

次のように参照してくださいr.activity.performResume()このコード行は、performResume方法に従ってください。

final void performResume() {
    performRestart();

    mFragments.execPendingActions();

    mLastNonConfigurationInstances = null;

    mCalled = false;
    // mResumed is set by the instrumentation
    mInstrumentation.callActivityOnResume(this);

    //代码省略
}

次のように計装はcallActivityOnResume法、callActivityOnResumeソースコードを呼び出します。

public void callActivityOnResume(Activity activity) {
    activity.mResumed = true;
    activity.onResume();

    if (mActivityMonitors != null) {
        synchronized (mSync) {
            final int N = mActivityMonitors.size();
            for (int i=0; i<N; i++) {
                final ActivityMonitor am = mActivityMonitors.get(i);
                am.match(activity, activity, activity.getIntent());
            }
        }
    }
}

発見、activity.onResume()。これが確認され、performResumeActivity方法は、実際に入学onResumeコールバックメソッドです。

今、私たちが戻ってhandleResumeActivity方法、コールバックメソッドperformResumeActivity onResumeメソッドを実行するように見えるので、後は、
このコードブロックに来るでしょう。

r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
    r.activity.makeVisible();
}

makeVisibleメソッド呼び出しのアクティビティ、これが鼓動して行くために、それを作るために何を示すべきです。

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

ウィンドウマネージャにDecorViewを追加し、懸念は今それが必要という方法ウィンドウマネージャのaddViewです。そして、ウィンドウマネージャがインターフェイスである、我々はウィンドウマネージャの実装クラスの仕事やウィンドウマネージャの実装クラスを見つける必要がありWindowManagerImplです。

次のようにWindowManagerImplは、addView方法を見つけました:

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

これは今、ロック、addViewメソッドを呼び出すWindowManagerGlobal
addViewメソッドのWindowManagerGlobalを:

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {

    //代码省略  


    ViewRootImpl root;
    View panelParentView = null;

    //代码省略

        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }

    // do this last because it fires off messages to start doing things
    try {
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        // BadTokenException or InvalidDisplayException, clean up.
        synchronized (mLock) {
            final int index = findViewLocked(view, false);
            if (index >= 0) {
                removeViewLocked(index, true);
            }
        }
        throw e;
    }
}

最後に壊れ、ViewRootImplはにaddView方法WindowManagerGlobalに作成されます。

:合計する、以前の分析を想起
onResumeメソッドのコールバック後ViewRootImplを作成し、我々は開口部が子スレッドとアクセスUIでのonCreateメソッドで作成され、その瞬間に、ViewRootImplが作成されていない、現在のスレッドがあるかどうかを検出することができませんそれは、同じアプリケーションがクラッシュしていない実行されますが、後でプログラム、200ミリ秒後にスリープ状態にスレッドを改訂することができますので、UIスレッドは、プログラムが崩壊します。明らかにViewRootImplが作成された200ミリ秒後に、あなたは現在のスレッドをチェックするcheckThreadメソッドを実行することができます。

このブログなど話題の分析、Androidの中性子は本当にUIスレッドにそれを更新することはできませんか?アクセスUIへのonCreateメソッドで作成された子スレッドが、これは知られていないソースコードの慎重な分析ではなく、極端なケースです。私は最近だけのことを見つけるために、インタビューの質問をお読みください。

また、私は答えを見つけるために異常な情報源からのフォローアップから学んだ、あなたはどう?

公開された81元の記事 ウォン称賛37 ビュー50000 +

おすすめ

転載: blog.csdn.net/gaolh89/article/details/104012167