AsyncTask Androidi näide

Ma lugesin AsyncTask kohta ja proovisin alljärgnevat lihtsat programmi. Aga tundub, et see ei tööta. Kuidas ma saan seda tööle panna?

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) {
        }
    }
}

Ma üritan lihtsalt muuta sildi 5 sekundi pärast taustaprotsessi.

See on minu 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>
Lahendus

Okei, sa üritad juurdepääsu GUI-le teise niidi kaudu. See ei ole üldjuhul hea tava.

AsyncTask täidab kõike doInBackground() teise niidi sees, millel ei ole ligipääsu GUI-le, kus teie vaated on.

preExecute() ja postExecute() pakuvad teile juurdepääsu GUI-le enne ja pärast seda, kui raske töö toimub selles uues niidis, te võite isegi edastada pika operatsiooni tulemuse postExecute()-le, et seejärel näidata kõiki töötlemise tulemusi.

Vaata neid ridu, kus te hiljem oma TextView'i uuendate:

TextView txt = findViewById(R.id.output);
txt.setText("Executed");

pane need onPostExecute() sisse.

Seejärel näete oma TextView teksti uuendatud pärast doInBackground lõpetamist.

EDIT: Märkasin, et teie onClick-kuulaja ei kontrolli, milline vaade on valitud. Minu arvates on kõige lihtsam viis seda teha switch-avalduste kaudu. Mul on täielik klass redigeeritud allpool kõik ettepanekud, et säästa segadust.

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 {

        @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
        }
    }
}
Kommentaarid (11)

Ma olen kindel, et see käivitub korralikult, kuid sa üritad muuta kasutajaliidese elemente taustaliinis ja see ei õnnestu.

Muutke oma üleskutse ja AsyncTask järgmiselt:

Kutseklass

Märkus: Mina isiklikult soovitan kasutada onPostExecute() kõikjal, kus te täidate oma AsyncTask niiti ja mitte klassis, mis laiendab AsyncTaski ennast. Ma arvan, et see muudab koodi kergemini loetavaks, eriti kui teil on vaja AsyncTaski mitmes kohas, mis käitlevad tulemusi veidi erinevalt.

new LongThread() {
    @Override public void onPostExecute(String result) {
        TextView txt = (TextView) findViewById(R.id.output);
        txt.setText(result);
    }
}.execute("");

LongThread klass (laiendab 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";
}      
Kommentaarid (2)

Liigutage need kaks rida:

TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");

välja oma AsyncTask'i meetodist doInBackground ja pane need meetodisse onPostExecute. Teie AsyncTask peaks välja nägema umbes nii:

private class LongOperation extends AsyncTask {

    @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);
    }
}
Kommentaarid (2)