นี่เป็นอีกตัวอย่างหนึ่งของ AsyncTask ที่ใช้ในFragment
การจัดการการเปลี่ยนแปลงการกำหนดค่ารันไทม์ (เช่นเมื่อผู้ใช้หมุนหน้าจอ) ด้วยsetRetainInstance(true)
ด้วย แถบความคืบหน้าในการตัดสินใจ (อัพเดทเป็นประจำ) จะแสดงให้เห็นเช่นกัน
ตัวอย่างส่วนหนึ่งขึ้นอยู่กับเอกสารอย่างเป็นทางการรักษาวัตถุในระหว่างการกำหนดค่าเปลี่ยน
ในตัวอย่างนี้งานที่ต้องการเธรดพื้นหลังเป็นการโหลดรูปภาพจากอินเทอร์เน็ตไปยัง UI
Alex Lockwood ดูเหมือนจะถูกต้องว่าเมื่อพูดถึงการจัดการการเปลี่ยนแปลงการกำหนดค่ารันไทม์ด้วย AsyncTasks โดยใช้ "Retained Fragment" เป็นแนวปฏิบัติที่ดีที่สุด onRetainNonConfigurationInstance()
ได้รับการคัดค้านใน Lint ใน Android Studio เอกสารอย่างเป็นทางการเตือนให้เราปิดการใช้android:configChanges
งานจากการจัดการการเปลี่ยนแปลงการตั้งค่าตัวเอง ...
การจัดการการเปลี่ยนแปลงการกำหนดค่าด้วยตัวคุณเองอาจทำให้ยากต่อการใช้ทรัพยากรทางเลือกมากขึ้นเนื่องจากระบบไม่ได้ปรับใช้ให้คุณโดยอัตโนมัติ เทคนิคนี้ควรได้รับการพิจารณาเป็นทางเลือกสุดท้ายเมื่อคุณต้องหลีกเลี่ยงการเริ่มต้นใหม่เนื่องจากการเปลี่ยนแปลงการกำหนดค่าและไม่แนะนำสำหรับแอปพลิเคชันส่วนใหญ่
แล้วมีปัญหาว่าควรใช้ AsyncTask เลยสำหรับเธรดพื้นหลังหรือไม่
การอ้างอิงอย่างเป็นทางการสำหรับ AsyncTaskเตือน ...
AsyncTasks ควรใช้อย่างเหมาะสมสำหรับการดำเนินการสั้น ๆ (ไม่เกินสองสามวินาที) หากคุณต้องการให้เธรดทำงานต่อเนื่องเป็นเวลานานขอแนะนำอย่างยิ่งให้คุณใช้ API ต่าง ๆ ที่มีให้โดย java.util.concurrent pacakge เช่น ผู้บริหาร, 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");
}
}
เพิ่มคลาสที่ซ้อนกัน RetainedFragment ซึ่งขยายคลาส Fragement และไม่มี UI ของตัวเอง เพิ่ม setRetainInstance (จริง) ไปยังเหตุการณ์ onCreate ของ Fragment นี้ ให้ขั้นตอนการตั้งค่าและรับข้อมูลของคุณ
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> {
....
ในคลาสกิจกรรมนอกสุดของ onCreate () จัดการ RetainedFragment: อ้างอิงถ้ามันมีอยู่แล้ว (ในกรณีที่กิจกรรมเริ่มต้นใหม่); สร้างและเพิ่มหากไม่มีอยู่ จากนั้นหากมีอยู่แล้วให้รับข้อมูลจาก 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());
}
}
เริ่มต้น AsyncTask จาก UI
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
เพิ่มและรหัสแถบความคืบหน้าแน่นอน:
- เพิ่มแถบความคืบหน้าในเค้าโครง UI;
- รับการอ้างอิงถึงในกิจกรรม oncreate ();
- ทำให้มองเห็นและ Invisble ที่จุดเริ่มต้นและจุดสิ้นสุดของกระบวนการ
- กำหนดความคืบหน้าในการรายงานต่อ UI ใน onProgressUpdate
- เปลี่ยนพารามิเตอร์ AsyncTask 2nd Generic จาก Void เป็นประเภทที่สามารถจัดการกับการอัพเดตความคืบหน้า (เช่น Integer)
- publishProgress ที่จุดปกติใน doInBackground ()
รหัสทั้งหมดสำหรับขั้นตอนข้างต้น
เค้าโครงกิจกรรม
<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>
The Activity with: subclassed 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);
}
}
ในตัวอย่างนี้ฟังก์ชั่นห้องสมุด (อ้างอิงข้างต้นด้วยคำนำหน้าแพคเกจที่ชัดเจน 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;
}
เพิ่มสิทธิ์ใด ๆ ที่งานพื้นหลังของคุณต้องการกับ AndroidManifest.xml ...
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
เพิ่มกิจกรรมของคุณใน 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>