- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
自从 AsyncTask
自2009年在Cupcake(API 3,Android 1.5)中引入以来,它一直被Android团队一如既往地推广:
AsyncTask
非常吸引人。
AsyncTask
的开发人员。这通常是由于
Activity
正在运行
destruction and recreation on runtime configuration change而导致的
AsyncTask
doInBackground(Params...)
(尤其是方向/旋转)引起的;当调用
onPostExecute(Result)
时,
Activity
已经被破坏,使UI引用处于无法使用的状态(甚至是
null
)。
AsyncTask
可以在许多情况下使用,所以没有一种方法可以解决此问题。但是,我的问题是关于
选项。
AsyncTask
与
Activity
/
Fragment
生命周期集成并在运行时配置更改后自动重启的规范(由Android团队认可)的最佳实践是什么?
最佳答案
提供的建议(适用于任何方法)
不要保留对特定于UI的对象的引用
从Memory & Threading. (Android Performance Patterns Season 5, Ep. 3):
You've got some threading object that's declared as an inner class of an
Activity
. The problem here is that theAsyncTask
object now has an implicit reference to the enclosingActivity
, and will keep that reference until the work object has been destroyed... Until this work completes, theActivity
stays around in memory... This type of pattern also leads to common types of crashes seen in Android apps...The takeaway here is that you shouldn't hold references to any types of UI-specific objects in any of your threading scenarios.
AsyncTask
进行的配置更改重启:
WeakReference
到UI对象Activity
或
Fragment
中进行管理
To see how you can persist your task during one of these restarts and how to properly cancel the task when the activity is destroyed, see the source code for the Shelves sample application.
Activity
中,以便可以在
Activity
的生命周期方法中进行管理。但是,在看一下代码之前,需要注意一些重要的事情。
AsyncTask
添加到平台之前编写的。源代码中包含一个与
AsyncTask
稍后发布的类非常相似的类,称为
UserTask
。对于此处的讨论,
UserTask
在功能上等效于
AsyncTask
。
UserTask
的子类被声明为
Activity
的内部类。如前所述,这种方法现在被视为反模式(请参见上面的“不保存对UI特定对象的引用”)。幸运的是,此实现细节不会影响生命周期方法中管理正在运行的任务的整体方法。但是,如果您选择将此示例代码用于自己的应用程序,请在其他位置声明
AsyncTask
的子类。
onDestroy()
,取消任务,并将任务引用设置为null
。 (我不确定在此处设置对null
的引用是否会产生影响;如果您有更多信息,请发表评论,我将相应地更新答案)。 AsyncTask#onCancelled(Object)
返回后需要清理或执行任何其他需要的工作,则覆盖 AsyncTask#doInBackground(Object[])
。 AddBookActivity.java
public class AddBookActivity extends Activity implements View.OnClickListener,
AdapterView.OnItemClickListener {
// ...
private SearchTask mSearchTask;
private AddTask mAddTask;
// Tasks are initialized and executed when needed
// ...
@Override
protected void onDestroy() {
super.onDestroy();
onCancelAdd();
onCancelSearch();
}
// ...
private void onCancelSearch() {
if (mSearchTask != null && mSearchTask.getStatus() == UserTask.Status.RUNNING) {
mSearchTask.cancel(true);
mSearchTask = null;
}
}
private void onCancelAdd() {
if (mAddTask != null && mAddTask.getStatus() == UserTask.Status.RUNNING) {
mAddTask.cancel(true);
mAddTask = null;
}
}
// ...
// DO NOT DECLARE YOUR TASK AS AN INNER CLASS OF AN ACTIVITY
// Instances of this class will hold an implicit reference to the enclosing
// Activity as long as the task is running, even if the Activity has been
// otherwise destroyed by the system. Declare your task where you can be
// sure it holds no implicit references to UI-specific objects (Views,
// etc.), and do not hold explicit references to them in your own
// implementation.
private class AddTask extends UserTask<String, Void, BooksStore.Book> {
// ...
@Override
public void onCancelled() {
enableSearchPanel();
hidePanel(mAddPanel, false);
}
// ...
}
// DO NOT DECLARE YOUR TASK AS AN INNER CLASS OF AN ACTIVITY
// Instances of this class will hold an implicit reference to the enclosing
// Activity as long as the task is running, even if the Activity has been
// otherwise destroyed by the system. Declare your task where you can be
// sure it holds no implicit references to UI-specific objects (Views,
// etc.), and do not hold explicit references to them in your own
// implementation.
private class SearchTask extends UserTask<String, ResultBook, Void>
implements BooksStore.BookSearchListener {
// ...
@Override
public void onCancelled() {
enableSearchPanel();
hidePanel(mSearchPanel, true);
}
// ...
}
onSaveInstanceState(Bundle, PersistableBundle)
,取消任务,并保存有关任务的状态,以便在恢复实例状态后可以重新启动它们。 onRestoreInstanceState(Bundle, PersistableBundle)
,检索有关已取消任务的状态,并使用已取消任务状态中的数据启动新任务。 AddBookActivity.java
public class AddBookActivity extends Activity implements View.OnClickListener,
AdapterView.OnItemClickListener {
// ...
private static final String STATE_ADD_IN_PROGRESS = "shelves.add.inprogress";
private static final String STATE_ADD_BOOK = "shelves.add.book";
private static final String STATE_SEARCH_IN_PROGRESS = "shelves.search.inprogress";
private static final String STATE_SEARCH_QUERY = "shelves.search.book";
// ...
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// ...
restoreAddTask(savedInstanceState);
restoreSearchTask(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (isFinishing()) {
// ...
saveAddTask(outState);
saveSearchTask(outState);
}
}
// ...
private void saveAddTask(Bundle outState) {
final AddTask task = mAddTask;
if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
final String bookId = task.getBookId();
task.cancel(true);
if (bookId != null) {
outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
outState.putString(STATE_ADD_BOOK, bookId);
}
mAddTask = null;
}
}
private void restoreAddTask(Bundle savedInstanceState) {
if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
final String id = savedInstanceState.getString(STATE_ADD_BOOK);
if (!BooksManager.bookExists(getContentResolver(), id)) {
mAddTask = (AddTask) new AddTask().execute(id);
}
}
}
private void saveSearchTask(Bundle outState) {
final SearchTask task = mSearchTask;
if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
final String bookId = task.getQuery();
task.cancel(true);
if (bookId != null) {
outState.putBoolean(STATE_SEARCH_IN_PROGRESS, true);
outState.putString(STATE_SEARCH_QUERY, bookId);
}
mSearchTask = null;
}
}
private void restoreSearchTask(Bundle savedInstanceState) {
if (savedInstanceState.getBoolean(STATE_SEARCH_IN_PROGRESS)) {
final String query = savedInstanceState.getString(STATE_SEARCH_QUERY);
if (!TextUtils.isEmpty(query)) {
mSearchTask = (SearchTask) new SearchTask().execute(query);
}
}
}
Activity
生命周期的初学者也应该有意义。它还具有不需要任务类本身之外的最少代码的优点,根据需要可以使用一到三个生命周期方法。
onDestroy()
javadoc的“用法”部分中的一个简单的7行
AsyncTask
代码段可以为我们省去很多麻烦。也许某些后代可以幸免。
AsyncTask
的构造函数。将对这些对象的弱引用存储为WeakReference
中的 AsyncTask
字段。 onPostExecute()
中,检查UI对象WeakReference
是否不是null
,然后直接对其进行更新。 class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
The
WeakReference
to theImageView
ensures that theAsyncTask
does not prevent theImageView
and anything it references from being garbage collected. There’s no guarantee theImageView
is still around when the task finishes, so you must also check the reference inonPostExecute()
. TheImageView
may no longer exist, if for example, the user navigates away from the activity or if a configuration change happens before the task finishes.
Activity
重新启动时自动重新启动,则第一种方法可能是一个更好的选择。
...force the top-level
Activity
orFragment
to be the sole system responsible for updating the UI objects.For example, when you'd like to kick off some work, create a "work record" that pairs a
View
with some update function. When that block of work is finished, it submits the results back to theActivity
using anIntent
or arunOnUiThread(Runnable)
call.The
Activity
can then call the update function with the new information, or if theView
isn't there, just drop the work altogether. And, if theActivity
that issued the work was destroyed, then the newActivity
won't have a reference to any of this, and it will just drop the work, too.
WorkRecord.java
public class WorkRecord {
public static final String ACTION_UPDATE_VIEW = "WorkRecord.ACTION_UPDATE_VIEW";
public static final String EXTRA_WORK_RECORD_KEY = "WorkRecord.EXTRA_WORK_RECORD_KEY";
public static final String EXTRA_RESULT = "WorkRecord.EXTRA_RESULT";
public final int viewId;
public final Callback callback;
public WorkRecord(@IdRes int viewId, Callback callback) {
this.viewId = viewId;
this.callback = callback;
}
public interface Callback {
boolean update(View view, Object result);
}
public interface Store {
long addWorkRecord(WorkRecord workRecord);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements WorkRecord.Store {
// ...
private final Map<Long, WorkRecord> workRecords = new HashMap<>();
private BroadcastReceiver workResultReceiver;
// ...
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
initWorkResultReceiver();
registerWorkResultReceiver();
}
@Override protected void onDestroy() {
super.onDestroy();
// ...
unregisterWorkResultReceiver();
}
// Initializations
private void initWorkResultReceiver() {
workResultReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
doWorkWithResult(intent);
}
};
}
// Result receiver
private void registerWorkResultReceiver() {
final IntentFilter workResultFilter = new IntentFilter(WorkRecord.ACTION_UPDATE_VIEW);
LocalBroadcastManager.getInstance(this).registerReceiver(workResultReceiver, workResultFilter);
}
private void unregisterWorkResultReceiver() {
if (workResultReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(workResultReceiver);
}
}
private void doWorkWithResult(Intent resultIntent) {
final long key = resultIntent.getLongExtra(WorkRecord.EXTRA_WORK_RECORD_KEY, -1);
if (key <= 0) {
Log.w(TAG, "doWorkWithResult: WorkRecord key not found, exiting:"
+ " intent=" + resultIntent);
return;
}
final Object result = resultIntent.getExtras().get(WorkRecord.EXTRA_RESULT);
if (result == null) {
Log.w(TAG, "doWorkWithResult: Result not found, exiting:"
+ " key=" + key
+ ", intent=" + resultIntent);
return;
}
final WorkRecord workRecord = workRecords.get(key);
if (workRecord == null) {
Log.w(TAG, "doWorkWithResult: matching WorkRecord not found, exiting:"
+ " key=" + key
+ ", workRecords=" + workRecords
+ ", result=" + result);
return;
}
final View viewToUpdate = findViewById(workRecord.viewId);
if (viewToUpdate == null) {
Log.w(TAG, "doWorkWithResult: viewToUpdate not found, exiting:"
+ " key=" + key
+ ", workRecord.viewId=" + workRecord.viewId
+ ", result=" + result);
return;
}
final boolean updated = workRecord.callback.update(viewToUpdate, result);
if (updated) workRecords.remove(key);
}
// WorkRecord.Store implementation
@Override public long addWorkRecord(WorkRecord workRecord) {
final long key = new Date().getTime();
workRecords.put(key, workRecord);
return key;
}
}
MyTask.java
public class MyTask extends AsyncTask<Void, Void, Object> {
// ...
private final Context appContext;
private final long workRecordKey;
private final Object otherNeededValues;
public MyTask(Context appContext, long workRecordKey, Object otherNeededValues) {
this.appContext = appContext;
this.workRecordKey = workRecordKey;
this.otherNeededValues = otherNeededValues;
}
// ...
@Override protected void onPostExecute(Object result) {
final Intent resultIntent = new Intent(WorkRecord.ACTION_UPDATE_VIEW);
resultIntent.putExtra(WorkRecord.EXTRA_WORK_RECORD_KEY, workRecordKey);
resultIntent.putExtra(WorkRecord.EXTRA_RESULT, result);
LocalBroadcastManager.getInstance(appContext).sendBroadcast(resultIntent);
}
}
// ...
private WorkRecord.Store workRecordStore;
private MyTask myTask;
// ...
private void initWorkRecordStore() {
// TODO: get a reference to MainActivity and check instanceof WorkRecord.Store
workRecordStore = (WorkRecord.Store) activity;
}
private void startMyTask() {
final long key = workRecordStore.addWorkRecord(key, createWorkRecord());
myTask = new MyTask(getApplicationContext(), key, otherNeededValues).execute()
}
private WorkRecord createWorkRecord() {
return new WorkRecord(R.id.view_to_update, new WorkRecord.Callback() {
@Override public void update(View view, Object result) {
// TODO: update view using result
}
});
}
Activity
重新启动时自动重新启动,则您需要修改此方法以适应取消,选择保存和重新启动任务的需要。或者只是坚持第一种方法;罗曼(Romain)对此有明确的愿景,并很好地实现了。
关于android - AsyncTask和运行时配置使用简洁的代码示例更改: what approaches,,Android团队认可吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35971070/
我只是不喜欢 Logback 的 XML 或 Groovy 配置,而更喜欢用 Java 进行配置(这也是因为我将在初始化后的不同时间在运行时更改配置)。 似乎对 Logback 进行 Java 配置的
我的 sphinx 配置是: ================================ config/sphinx.yml development: bin_path: "/usr/loc
我们计划在生产服务器中部署我们的系统。我有兴趣了解更多有关优化网站性能的信息。 Sitecore 有哪些优化建议? (缓存,网络配置中的其他设置) 我们可以在 IIS 中做哪些优化? 找不到关于这些主
我有一个 Django 应用程序,可以处理网站的两个(或更多)部分,例如网站的“admin”和“api”部分。我还为网站的其余部分提供了普通的 html 页面,其中不需要 Django。 例如,我希望
我刚刚开始研究Docker。我有一个 Node 应用程序,可以调整大小和图像,然后在完成后向 aws 发送 SQS 消息。我已成功创建应用程序的 docker 镜像,并从本地计算机复制它,但遇到了无法
如何配置 checkstyle(在 Ant nt Maven 中)任务?我尝试了一点,但没有正确收到报告。这是我的 Ant 脚本。
我正在使用 Quartz 和 Spring 框架重写一个遗留项目。原始配置是 XML 格式,现在我将其转换为 Java Config。 xml 配置使用 jobDetail 设置触发器 bean 的作
tl;rd: 使用主键对数据库进行分区 索引大小问题。 数据库大小每天增长约 1-3 GB 突袭设置。 您有使用 Hypertable 的经验吗? 长版: 我刚刚建立/购买了一个家庭服务器: 至强 E
在安装 gcp 应用程序后,我们尝试使用 GCP 的图形 api 配置 Azure Active Directory saml 配置。我们正在遵循相同的 AWS graph api saml 设置 U
我刚刚了解了 spring security 并想使用 java hibernate 配置连接到数据库,但我发现的示例或教程很少。我通过使用 xml 配置找到了更多。我在这里使用 Spring 4.0
我们最近切换到 Java 8 以使用 java.time API(LocalDate、LocalDateTime,...)。因此,我们将 Hibernate 依赖项更新到版本 4.3.10。我们编写了
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是《quarkus实战》系列的第六篇,咱
我是 NGINX 的新手,我正在尝试对我们的 ERP 网络服务器进行负载平衡。我有 3 个网络服务器在由 websphere 提供支持的端口 80 上运行,这对我来说是一个黑盒子: * web01.e
我们想使用 gerrit 进行代码审查,但我们在 webview 中缺少一些设置。 是否可以禁止提交者审查/验证他们自己的 提交? 是否有可能两个审稿人给 +1 一个累积它 到+2,以便可以提交? 谢
配置根据运行模式应用于 AEM 实例。在多个运行模式和多个配置的情况下,AEM 如何确定要选择的配置文件?假设以下配置在 AEM 项目中可用, /apps /myproject - con
我正在使用 Neo4j 服务器。我遇到了负载相对较低的问题。但是,响应时间相当长。我认为为请求提供服务的线程数太少了。有没有办法调整为 HTTP 请求提供服务的线程池的大小。那可能吗? 最佳答案 线程
我在/etc/default/celeryd 中有以下配置 CELERYD_NODES = "worker1 worker2 worker3" CELERYD_CHDIR = "path to pro
Plone 在其页面中显示来 self 的母语(巴西葡萄牙语)的特殊字符。但是,当我使用我创建的 spt 页面时,它会显示转义序列,例如: Educa\xc3\xa7\xc3\xa3o 代替 Educ
我正在尝试开始使用 Emacs/Clojure。安装 emacs 扩展的正确方法是什么。我正在尝试安装以下插件: https://bitbucket.org/kotarak/vimclojure 我已
我有一个简单的 C 项目结构: proj/ src/ docs/ build/ tests/ lib/ 尝试编写合适的 CMake 文件。 到目前为止我的尝试:http://pas
我是一名优秀的程序员,十分优秀!