Невозможно обновить пользовательский интерфейс из IntentService

На первый взгляд простое требование - обновлять пользовательский интерфейс с IntentService, На снимке ниже, когда Start Service кнопка нажата, мне нужно показать ProgressBar выше единственного TextView и после задержки удалите ProgressBar,

Скриншот AVD

Нашел много ответов на SO, но как-то так и не смог его взломать. Я понимаю из этого, что LocalBroadcastManager это хороший путь. Я также пытался следовать этому (подход с Handler s), но он не может показать ProgressBar тоже. Наконец, основываясь на этом ответе, вот что я закончил. Результат, которым я управлял до сих пор, - это просто Toast Появляется последовательно, после того, как все записи сделаны.

Очень признателен, если бы вы могли указать, где я иду не так, боролся уже довольно давно. Спасибо заранее!

MainActivity обновленный

public class MainActivity extends AppCompatActivity
{
    private static final String TAG = "MainActivity";
    private ProgressBar pb;
    private MyBroadcastReceiver myBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pb = (ProgressBar) findViewById(R.id.pb);
        myBroadcastReceiver = new MyBroadcastReceiver();

        LocalBroadcastManager.getInstance(this).registerReceiver(myBroadcastReceiver, new IntentFilter("ACTION"));
    }

    private void updateUI(boolean show)
    {
        if (show)
            pb.setVisibility(View.VISIBLE);
        else
            pb.setVisibility(View.GONE);
//        Toast.makeText(this, "UI Updated...", Toast.LENGTH_LONG).show();
    }

    public void startIt(View view)
    {
        Intent intent = new Intent(this, NanisIntentService.class);
        startService(intent);
    }

    public class MyBroadcastReceiver extends BroadcastReceiver
    {
        @Override
        public void onReceive(final Context context, Intent intent)
        {
            String action = intent.getAction();
            Log.e(TAG, "In onReceive(): " + action);
            if (action.equals("ACTION"))
            {
                updateUI(true);
            } // of if (action = "ACTION")
            else if (action.equals("NOITCA"))
            {
                updateUI(false);
            } // of else of if (action = "ACTION")
        } // of onReceive()
    } // of class MyBroadcastReceiver
}

IntentService обновленный

public class NanisIntentService extends IntentService
{
    private static final String TAG = "NanisIntentService";

    public NanisIntentService()
    {
        super("NanisIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent)
    {
        Log.e(TAG, "In onHandleIntent(): Intent is being serviced");
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("ACTION"));
        int i = 0;
        while (i <= 50)
        {
            try
            {
                Thread.sleep(50);
                i++;
                Log.e("", "" + i);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("NOITCA"));
        Log.e(TAG, "In onDestroy(): The service has been destroyed");
    }
}

    @Override
    public void onStart(Intent intent, int startId)
    {
        super.onStart(intent, startId);
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("ACTION"));
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("NOITCA"));
        Log.e(TAG, "In onDestroy(): The service has been destroyed");
    }
}

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("NOITCA"));
        Log.e(TAG, "In onDestroy(): The service has been destroyed");
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="testing.com.myintentservice.MainActivity">

    <ProgressBar
        android:id="@+id/pb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/tv"
        android:layout_centerHorizontal="true"
        android:indeterminate="true"
        android:visibility="gone"/>

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Hello IntentService!"
        android:textColor="#1298CE"
        android:textSize="32sp"/>

    <Button
        android:id="@+id/bt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Start Service"
        android:onClick="startIt"/>
</RelativeLayout>

AndroidManifest

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:name="testing.com.myintentservice.NanisIntentService"/>
    </application>

2 ответа

Решение

Во-первых, вы связываете основной поток приложения на ~2,5 секунды. Это заморозит ваш пользовательский интерфейс в течение этого периода времени. Не делай этого.

Во-вторых, вы звоните updateUI() один раз до ~2,5 секунд и один раз после. Поскольку в течение этого времени вы связываете основной поток приложения, это будет иметь тот же визуальный эффект, что и вызов updateUI() дважды подряд после задержки. updateUI() переключает ProgressBar видимость, поэтому два вызова отменят друг друга, оставив вас в том же состоянии, в котором вы начали.

Мне нужно показать ProgressBar над единственным TextView и после задержки удалить ProgressBar.

Показаны ProgressBar в течение 2,5 секунд, независимо от выполняемой работы, довольно произвольно.

Как говорится, звоните updateUI() один раз, затем используйте pb.postDelayed() запланировать Runnable запустить 2500 миллисекунд спустя, где run() метод в Runnable звонки updateUI() второй раз. Это позволяет избежать блокировки основного потока приложения, поэтому позволяет updateUI() вызов для вступления в силу, продолжая при этом 2,5-секундную длительность.

Большое спасибо, @CommonsWare! Наконец-то получил его на работу. Потребовалось два приемника для двух разных действий, которые я обнаружил здесь.

Для тех, кто ищет окончательный рабочий код...

Экраны результатов:- после нажатия и после обработки

Прошу прощения за любых копий-вставок в коде, это ведь PoC, хотя и функциональный.

Другие вопросы по тегам