EndInvoke изменяет текущий CallContext - почему?

Еще одно решение: 1) Сначала установите groupIndicator в ExpandableListView равным @null:

<ExpandableListView [...] 
    android:groupIndicator="@null" />

2) Затем создайте файл group_indicator.xml со следующими подробностями:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/down_icon" android:state_selected="false"></item>
    <item android:drawable="@drawable/up_icon" android:state_selected="true"></item>
    <item android:drawable="@drawable/down_icon"></item>
</selector>

3) Затем создайте макет group_header.xml со следующими деталями и накачайте этот макет в методе getGroupView () ExpandableListAdapter.java:

<?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="wrap_content"
    android:padding="10dp">

    <TextView
        android:id="@+id/tvHeader"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:textColor="@color/white"
        android:layout_centerVertical="true"
        android:textSize="16sp"/>

    <ImageView
        android:id="@+id/ivGroupIndicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/group_indicator"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"/>
</RelativeLayout>

3) В методе getGroupView () вашего класса ExpandableListAdapter.java просто установите следующее:

ivGroupIndicator.setSelected(isExpanded);

При таком подходе ваши down_icon и up_icon будут работать правильно. Надеюсь, это поможет.

6
задан Community 23 May 2017 в 12:22
поделиться

2 ответа

Это потому, что вы смешиваете SetData / GetData с LogicalSetData / LogicalGetData. Есть статья , в которой вы можете узнать больше о различиях между этими двумя методами. Практическое правило здесь - всегда использовать SetData вместе с GetData и LogicalSetData вместе с LogicalGetData.

Эта модификация сделает ваш тест пройденным:

[Test]
public void aaa()
{
    CallContext.SetData("aa", "1");
    Action parallelMethod = () => CallContext.SetData("aa", "2");
    var r = parallelMethod.BeginInvoke(null, null);
    Assert.That(CallContext.GetData("aa"), Is.EqualTo("1"));
    parallelMethod.EndInvoke(r);
    Assert.That(CallContext.GetData("aa"), Is.EqualTo("1"));
}
0
ответ дан 17 December 2019 в 00:14
поделиться

Поведение, проиллюстрированное вашим примером, действительно задумано. LogicalCallContext может передаваться в двух направлениях через асинхронный вызов или удаленный вызов .net. Когда вы вызываете EndInvoke, LogicalCallContext дочернего контекста снова объединяется с родительским, как вы заметили. Это сделано намеренно, чтобы вызывающие удаленные методы могли получить доступ к любым значениям, установленным удаленным методом. Вы можете использовать эту функцию для передачи данных обратно от дочернего, если хотите.

Отлаживая это с помощью степпинга исходного кода .NET Framework, есть явные комментарии на этот счет:

в System.Runtime.Remoting.Proxies.RemotingProxy.Invoke:

    case Message.EndAsync: 
         // This will also merge back the call context
         // onto the thread that called EndAsync
         RealProxy.EndInvokeHelper(m, false);

в System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper:

    // Merge the call context back into the thread that
    // called EndInvoke 
    CallContext.GetLogicalCallContext().Merge(
         mrm.LogicalCallContext);

Если вы хотите избежать слияния данных, его довольно легко пропустить, просто избегайте вызова EndInvoke из основного потока. Вы можете, например, использовать ThreadPool.QueueUserWorkItem, который передает LogicalCallContext в , но не выводится, или вызывать EndInvoke из AsyncCallback.

Если посмотреть на пример на сайте Microsoft Connect, причина того, что вы: Если вы не видите, что значение LogicalSetData возвращается из вызова RunWorkerCompleted, это означает, что BackgroundWorker не передает контекст обратно. Кроме того, помните, что LogicalSetData - это не то же самое, что и локальное хранилище потока, поэтому не имеет значения, что RunWorkerCompleted выполняется в потоке пользовательского интерфейса - у LogicalCallContext все еще есть дочерний контекст, и если родитель явно не вернет его обратно, вызвав EndInvoke из порождающего потока, он будет оставлен. Если вам нужно локальное хранилище потока, вы можете получить к нему доступ из потока, например:

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Thread.SetData(Thread.GetNamedDataSlot("foo"), "blah!!");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var val = (string)Thread.GetData(Thread.GetNamedDataSlot("foo"));
        MessageBox.Show(val ?? "no value");
    }

В этом примере появляется MessageBox с отображением «blah !!». Причина в том, что оба обратных вызова выполняются в потоке пользовательского интерфейса, поэтому имеют доступ к одному и тому же локальному хранилищу потока.

Надеюсь, это поможет прояснить ситуацию.

7
ответ дан 17 December 2019 в 00:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: