无法从 IntentService 更新 UI
Unable to update UI from IntentService
一个看似简单的要求 - 从 IntentService
更新 UI。在下面的镜头中,当单击 Start Service
按钮时,我需要在唯一的 TextView
上方显示 ProgressBar
并在延迟后删除 ProgressBar
.
在SO上找到了很多答案,但不知为何还是破解不了。我从 this that LocalBroadcastManager
is a good way to go. I have also tried following this, (an approach with Handler
s), but it fails to show the ProgressBar
too. Finally, based on this 的回答中了解到,这就是我最终得到的结果。到目前为止,我管理的输出只是连续出现的 Toast
s,在完成所有日志记录之后。
如果你能指出我哪里出错了,我将不胜感激,现在我已经苦苦挣扎了一段时间。非常感谢!
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.5 秒。这将在这段时间内冻结您的 UI。 不要这样做。
其次,您在 ~2.5 秒之前和之后调用了一次 updateUI()
。由于您在那段时间占用了主应用程序线程,因此这与在延迟后连续调用两次 updateUI()
具有相同的视觉效果。 updateUI()
切换 ProgressBar
可见性,因此两个调用会相互抵消,使您处于与开始时相同的状态。
I need to show a ProgressBar above the only TextView and after a delay, remove the ProgressBar.
显示 ProgressBar
2.5 秒,而不考虑正在完成的任何实际工作,这是相当随意的。
也就是说,调用 updateUI()
一次,然后使用 pb.postDelayed()
安排 Runnable
到 运行 2500 毫秒后,其中 run()
方法在 Runnable
中第二次调用 updateUI()
。这避免了阻塞主应用程序线程,因此它允许第一个 updateUI()
调用生效,同时仍然为您提供 2.5 秒的持续时间。
非常感谢@CommonsWare!终于让它工作了。我发现 here.
需要 两个 两个不同的 动作 接收器
为了那些寻找最终工作代码的人的利益...
结果屏幕:- 点击后和处理后
请原谅代码中的任何复制粘贴幽灵,它毕竟是一个 PoC,尽管是功能性的。
一个看似简单的要求 - 从 IntentService
更新 UI。在下面的镜头中,当单击 Start Service
按钮时,我需要在唯一的 TextView
上方显示 ProgressBar
并在延迟后删除 ProgressBar
.
在SO上找到了很多答案,但不知为何还是破解不了。我从 this that LocalBroadcastManager
is a good way to go. I have also tried following this, (an approach with Handler
s), but it fails to show the ProgressBar
too. Finally, based on this 的回答中了解到,这就是我最终得到的结果。到目前为止,我管理的输出只是连续出现的 Toast
s,在完成所有日志记录之后。
如果你能指出我哪里出错了,我将不胜感激,现在我已经苦苦挣扎了一段时间。非常感谢!
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.5 秒。这将在这段时间内冻结您的 UI。 不要这样做。
其次,您在 ~2.5 秒之前和之后调用了一次 updateUI()
。由于您在那段时间占用了主应用程序线程,因此这与在延迟后连续调用两次 updateUI()
具有相同的视觉效果。 updateUI()
切换 ProgressBar
可见性,因此两个调用会相互抵消,使您处于与开始时相同的状态。
I need to show a ProgressBar above the only TextView and after a delay, remove the ProgressBar.
显示 ProgressBar
2.5 秒,而不考虑正在完成的任何实际工作,这是相当随意的。
也就是说,调用 updateUI()
一次,然后使用 pb.postDelayed()
安排 Runnable
到 运行 2500 毫秒后,其中 run()
方法在 Runnable
中第二次调用 updateUI()
。这避免了阻塞主应用程序线程,因此它允许第一个 updateUI()
调用生效,同时仍然为您提供 2.5 秒的持续时间。
非常感谢@CommonsWare!终于让它工作了。我发现 here.
需要 两个 两个不同的 动作 接收器为了那些寻找最终工作代码的人的利益...
结果屏幕:- 点击后和处理后
请原谅代码中的任何复制粘贴幽灵,它毕竟是一个 PoC,尽管是功能性的。