Невозможно обновить пользовательский интерфейс из IntentService
На первый взгляд простое требование - обновлять пользовательский интерфейс с IntentService
, На снимке ниже, когда Start Service
кнопка нажата, мне нужно показать ProgressBar
выше единственного TextView
и после задержки удалите ProgressBar
,
Нашел много ответов на 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, хотя и функциональный.