Jeg leste om AsyncTask
, og jeg prøvde det enkle programmet nedenfor. Men det ser ikke ut til å fungere. Hvordan kan jeg få det til å fungere?
public class AsyncTaskActivity extends Activity {
Button btn;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener((OnClickListener) this);
}
public void onClick(View view){
new LongOperation().execute("");
}
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
return null;
}
@Override
protected void onPostExecute(String result) {
}
@Override
protected void onPreExecute() {
}
@Override
protected void onProgressUpdate(Void... values) {
}
}
}
Jeg prøver bare å endre etiketten etter 5 sekunder i bakgrunnsprosessen.
Dette er min main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="false"
android:max="10"
android:padding="10dip">
</ProgressBar>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Progress" >
</Button>
<TextView android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Replace"/>
</LinearLayout>
Ok, du prøver å få tilgang til brukergrensesnittet via en annen tråd. Dette er i hovedsak ikke god praksis.
AsyncTask utfører alt i doInBackground()
inne i en annen tråd, som ikke har tilgang til GUI der visningene dine er.
preExecute()
og postExecute()
gir deg tilgang til GUI før og etter at det tunge løftet skjer i denne nye tråden, du kan til og med sende resultatet av den lange operasjonen til postExecute()
for deretter å vise eventuelle resultater av behandlingen.
Se disse linjene der du senere oppdaterer TextView:
TextView txt = findViewById(R.id.output);
txt.setText("Executed");
legg dem i onPostExecute()
Du vil da se TextView-teksten oppdatert etter at doInBackground
er fullført.
EDIT: Jeg la merke til at onClick-lytteren din ikke sjekker for å se hvilken visning som er valgt. Jeg finner den enkleste måten å gjøre dette på er via bytte uttalelser. Jeg har en komplett klasse redigert nedenfor med alle forslag for å spare forvirring.
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.System;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.view.View.OnClickListener;
public class AsyncTaskActivity extends Activity implements OnClickListener {
Button btn;
AsyncTask<?, ?, ?> runningTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = findViewById(R.id.button1);
// because we implement OnClickListener we only have to pass "this"
// (much easier)
btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
// detect the view that was "clicked"
switch (view.getId()) {
case R.id.button1:
if (runningTask != null) runningTask.cancel(true);
runningTask = new LongOperation();
runningTask.execute();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// cancel running task(s) to avoid memory leaks
if (runningTask != null) runningTask.cancel(true);
}
private final class LongOperation extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// we were cancelled, stop sleeping!
}
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed"); // txt.setText(result);
// might want to change "executed" for the returned string passed
// into onPostExecute() but that is upto you
}
}
}
Jeg er sikker på at den kjører riktig, men du prøver å endre brukergrensesnittelementene i bakgrunnstråden, og det vil ikke gjøre det.
Endre anropet og AsyncTask som følger:
Calling Class
Note: Jeg personlig foreslår at du bruker onPostExecute()
uansett hvor du utfører AsyncTask-tråden din og ikke i klassen som utvider AsyncTask selv. Jeg tror det gjør koden lettere å lese, spesielt hvis du trenger AsyncTask på flere steder som håndterer resultatene litt annerledes.
new LongThread() {
@Override public void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}.execute("");
LongThread klassen (utvider AsyncTask):
@Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Executed";
}
Jeg har laget et enkelt eksempel for bruk av AsyncTask av Android. Det starter med onPreExecute(), doInBackground(), publishProgress() og til slutt
onProgressUpdate().
I dette fungerer doInBackground() som en bakgrunnstråd, mens andre fungerer i UI-tråden. Du kan ikke få tilgang til et UI-element i doInBackground(). Sekvensen er den samme som jeg har nevnt.
Men hvis du trenger å oppdatere en widget fra doInBackground
kan du publishProgress
fra doInBackground
som vil kalle onProgressUpdate
for å oppdatere din UI widget.
class TestAsync extends AsyncTask<Void, Integer, String> {
String TAG = getClass().getSimpleName();
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG + " PreExceute","On pre Exceute......");
}
protected String doInBackground(Void...arg0) {
Log.d(TAG + " DoINBackGround", "On doInBackground...");
for (int i=0; i<10; i++){
Integer in = new Integer(i);
publishProgress(i);
}
return "You are at PostExecute";
}
protected void onProgressUpdate(Integer...a) {
super.onProgressUpdate(a);
Log.d(TAG + " onProgressUpdate", "You are in progress update ... " + a[0]);
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.d(TAG + " onPostExecute", "" + result);
}
}
Kall den slik i aktiviteten din:
new TestAsync().execute();
Flytt disse to linjene:
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
ut av AsyncTask-metoden doInBackground
og legg dem i onPostExecute
-metoden. Din AsyncTask
skal se omtrent slik ut:
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep(5000); // no need for a loop
} catch (InterruptedException e) {
Log.e("LongOperation", "Interrupted", e);
return "Interrupted";
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}
Ikke gjør det
Hvis du er ny i AsyncTask, er det veldig vanlig å bli forvirret mens du skriver en AsyncTask. De viktigste synderne er parametrene som brukes i AsyncTask, dvs. AsyncTask<A,B,C>
. Basert på A,B,C (argumenter) signaturen til metodene er forskjellig, noe som gjør ting enda mer forvirrende.
Nøkkelen er Ikke pugge. Hvis du kan visualisere hva oppgaven din virkelig trenger å gjøre, vil det være enkelt å skrive AsyncTask med riktig signatur ved første forsøk. Bare finn ut hva Input, Progress og Output er, så er du i gang.
AsyncTask er en bakgrunnsoppgave som kjører i bakgrunnstråden. Den tar en Input, utfører Progress og gir Output.
ie AsyncTask<input,Progress,output>
.
For eksempel:
]1.
Mellom
AsyncTask
ogdoInBackground()
.
]2
doInBackground()
ogonPostExecute(),
onProgressUpdate()` er også relaterte
Hvordan skriver jeg det i koden?
DownloadTask extends AsyncTask<String,Integer,String>{
// Always same signature
@Override
public void onPreExecute()
{}
@Override
public String doInbackGround(String... params)
{
// Download code
int downloadPerc = // calculate that
publish(downloadPerc);
return "Download Success";
}
@Override
public void onPostExecute(String result)
{
super.onPostExecute(result);
}
@Override
public void onProgressUpdate(Integer... params)
{
// show in spinner, access UI elements
}
}
}
DownloadTask extends AsyncTask<String,Integer,String>{
// Always same signature
@Override
public void onPreExecute()
{}
@Override
public String doInbackGround(String... params)
{
// Download code
int downloadPerc = // calculate that
publish(downloadPerc);
return "Download Success";
}
@Override
public void onPostExecute(String result)
{
super.onPostExecute(result);
}
@Override
public void onProgressUpdate(Integer... params)
{
// show in spinner, access UI elements
}
}
Hvordan vil du kjøre denne oppgaven?
new DownLoadTask().execute("Paradise.mp3");
new DownLoadTask().execute("Paradise.mp3");