caw*_*caw 149 multithreading android exception
AsyncTask 在另一个线程中运行复杂任务是一件好事.
但是当AsyncTask仍在运行时有方向更改或其他配置更改时,将Activity消除并重新启动电流.并且当实例AsyncTask连接到该活动时,它会失败并导致"强制关闭"消息窗口.
所以,我正在寻找某种"最佳实践"来避免这些错误并防止AsyncTask失败.
到目前为止我看到的是:
onRetainNonConfigurationInstanceActivity并Activity在再次创建时重新启动它.一些代码示例:
屏幕旋转期间的Android AsyncTasks,第一部分和第二部分
你能帮我找到最好的方法来解决问题并且易于实现吗?代码本身也很重要,因为我不知道如何正确解决这个问题.
Ale*_*ood 135
千万不要使用android:configChanges来解决这个问题.这是非常糟糕的做法.
千万不要使用Activity#onRetainNonConfigurationInstance()两种.这种模块化程度较低,不适合Fragment基于应用程序.
您可以阅读我的文章,描述如何使用保留的Fragments 处理配置更改.它解决了保持AsyncTask旋转变化很好的问题.你基本上需要托管AsyncTask里面Fragment,叫setRetainInstance(true)上Fragment,报告AsyncTask的进度/结果返回给它Activity通过保留Fragment.
Zso*_*agy 34
我通常通过让我的AsyncTasks在.onPostExecute()回调中触发广播Intent来解决这个问题,因此他们不会修改直接启动它们的Activity.活动使用动态BroadcastReceivers监听这些广播,并采取相应措施.
这样,AsyncTasks就不必关心处理其结果的特定Activity实例.他们只是在他们完成时"大喊",如果一个活动在那个时间(活跃并且专注/处于恢复状态),这对任务的结果感兴趣,那么它将被处理.
这涉及更多的开销,因为运行时需要处理广播,但我通常不介意.我认为使用LocalBroadcastManager而不是默认的系统范围可以加快速度.
Joh*_*ley 24
下面是AsyncTask的另一个示例,它使用a Fragment来处理运行时配置更改(如用户旋转屏幕时)setRetainInstance(true).还演示了确定(定期更新)的进度条.
该示例部分基于官方文档,在配置更改期间保留对象.
在这个例子中,需要后台线程的工作仅仅是将图像从互联网加载到UI中.
Alex Lockwood似乎是正确的,当使用"保留片段"使用AsyncTasks处理运行时配置更改时,这是最佳做法.onRetainNonConfigurationInstance()在Android Studio的Lint中被弃用.官方文档警告我们使用android:configChanges,从处理配置改变自己,...
自己处理配置更改会使使用备用资源变得更加困难,因为系统不会自动为您应用它们.当您必须避免因配置更改而重新启动时,此技术应被视为最后的手段,并且不建议用于大多数应用程序.
那么是否应该使用AsyncTask作为后台线程的问题.
理想情况下,AsyncTasks应该用于短操作(最多几秒钟.)如果需要保持线程长时间运行,强烈建议您使用java.util.concurrent pacakge提供的各种API,例如Executor,ThreadPoolExecutor和FutureTask.
或者,可以使用服务,加载器(使用CursorLoader或AsyncTaskLoader)或内容提供程序来执行异步操作.
我打破了帖子的其余部分:
从一个基本的AsyncTask开始,作为一个活动的内部类(它不需要是一个内部类,但它可能很方便).在此阶段,AsyncTask不处理运行时配置更改.
public class ThreadsActivity extends ActionBarActivity {
private ImageView mPictureImageView;
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mPictureImageView.setImageBitmap(bitmap);
}
}
/**
* Requires in AndroidManifext.xml
* <uses-permission android:name="android.permission.INTERNET" />
*/
private Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream)
new URL(url).getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
}
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask()
.execute("http://i.imgur.com/SikTbWe.jpg");
}
}
Run Code Online (Sandbox Code Playgroud)添加一个嵌套类RetainedFragment,它扩展了Fragement类,但没有自己的UI.将setRetainInstance(true)添加到此Fragment的onCreate事件中.提供设置和获取数据的过程.
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
...
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive
// runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Integer,Bitmap> {
....
Run Code Online (Sandbox Code Playgroud)在最外层的Activity类的onCreate()处理RetainedFragment:如果它已经存在则引用它(如果Activity正在重新启动); 创建并添加它,如果它不存在; 然后,如果它已经存在,则从RetainedFragment获取数据并使用该数据设置UI.
public class ThreadsActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar =
(ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must
// reference it with a tag.
mRetainedFragment =
(RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction()
.add(mRetainedFragment, retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView
.setImageBitmap(mRetainedFragment.getData());
}
}
Run Code Online (Sandbox Code Playgroud)从UI启动AsyncTask
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
Run Code Online (Sandbox Code Playgroud)添加并编码确定的进度条:
活动布局.
<ScrollView 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"
tools:context="com.example.mysecondapp.ThreadsActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
android:id="@+id/imageView_picture"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@android:color/black" />
<Button
android:id="@+id/button_get_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@id/imageView_picture"
android:onClick="getPicture"
android:text="Get Picture" />
<Button
android:id="@+id/button_clear_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/button_get_picture"
android:layout_toEndOf="@id/button_get_picture"
android:layout_toRightOf="@id/button_get_picture"
android:onClick="clearPicture"
android:text="Clear Picture" />
<ProgressBar
android:id="@+id/progressBar_loading"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/button_get_picture"
android:progress="0"
android:indeterminateOnly="false"
android:visibility="invisible" />
</RelativeLayout>
</ScrollView>
Run Code Online (Sandbox Code Playgroud)
Activity with:子类化AsyncTask内部类; 子类化RetainedFragment内部类,用于处理运行时配置更改(例如,当用户旋转屏幕时); 以及定期更新的确定进度条....
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
private ProgressBar mLoadingProgressBar;
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
Integer, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
// Simulate a burdensome load.
int sleepSeconds = 4;
for (int i = 1; i <= sleepSeconds; i++) {
SystemClock.sleep(1000); // milliseconds
publishProgress(i * 20); // Adjust for a scale to 100
}
return com.example.standardapplibrary.android.Network
.loadImageFromNetwork(
urls[0]);
}
@Override
protected void onProgressUpdate(Integer... progress) {
mLoadingProgressBar.setProgress(progress[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
publishProgress(100);
mRetainedFragment.setData(bitmap);
mPictureImageView.setImageBitmap(bitmap);
mLoadingProgressBar.setVisibility(View.INVISIBLE);
publishProgress(0);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must reference it with a tag.
mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction().add(mRetainedFragment,
retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView.setImageBitmap(mRetainedFragment.getData());
}
}
public void getPicture(View view) {
mLoadingProgressBar.setVisibility(View.VISIBLE);
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
public void clearPicture(View view) {
mRetainedFragment.setData(null);
mPictureImageView.setImageBitmap(null);
}
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,库函数(上面引用了显式包前缀com.example.standardapplibrary.android.Network)可以实现真正的工作......
public static Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
.getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
Run Code Online (Sandbox Code Playgroud)
将后台任务所需的任何权限添加到AndroidManifest.xml ...
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
Run Code Online (Sandbox Code Playgroud)
将您的活动添加到AndroidManifest.xml ...
<manifest>
...
<application>
<activity
android:name=".ThreadsActivity"
android:label="@string/title_activity_threads"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.mysecondapp.MainActivity" />
</activity>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
66764 次 |
| 最近记录: |