Android异步和线程

代码仓库:https://github.com/MADMAX110/Starbuzz

Android应用打开数据库时首先要搜索数据库文件,如果没有找到数据库文件就要创建一个空的数据库。然后它要运行所有SQL命令,在数据库中创建数据库表和需要的所有初始数据。最后还要执行一些查询从数据库得到数据。

线程合作让生活更美好

访问一个很慢的数据库会让你的应用看起来好像没有响应。
线程主要有三种:
1、主事件线程
2、呈现线程
3、你创建的所有其他线程
如果你不细心,应用几乎所有的工作都可能在主事件线程中完成,因为这个线程运行你的事件方法,如果把数据库代码放在onCreate方法中,主事件线程就会忙于与数据库交互,而不会迅速应对来自屏幕或其他应用的事件。
如果你的数据库代码要花很长时间,用户就会觉得自己被忽略,或者担心应用是否崩溃了。
所以这里的技巧时将数据库代码从主事件线程移出来,在后台的一个定制线程中运行。

AsyncTask完成异步任务

AsyncTask类允许你在后台完成操作。这些操作运行结束时,就可以在主事件线程中更新视图。
如果任务是重复的,甚至可以利用这个类发布任务运行的进度。
要创建AsyncTask,需要扩展AsyncTask类,并实现它的doInBackground方法。这个方法中的代码会在后台线程中运行,所以把数据库代码放在这里非常合适。AsyncTask类还有一个onPreExecute方法,这个方法在doInBackground之后运行。如果需要发布任务进度还可以使用一个onProgressUpdate方法。

private class MyAsyncTask extends AsyncTask{

//可选,在后台运行的代码之前运行
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

//必须实现这个方法
        @Override
        protected Object doInBackground(Object[] objects) {
            return null;
        }

//允许你发布在后台运行的代码的进度
        @Override
        protected void onProgressUpdate(Object[] values) {
            super.onProgressUpdate(values);
        }

//在后台中的代码结束运行
        @Override
        protected void onPostExecute(Object o) {
            super.onPostExecute(o);
        }
    }

onPreExecute方法

这个方法会在后台任务开始之前调用,用来建立任务。
onPreExecute方法在主事件线程调用,所以它可以访问用户界面中的视图。
在这里使用onPreExecute方法得到favorite复选框的值,把它放在drinkValues ContentValues对象中。
这是因为我们需要访问这个复选框视图才能得到它的值,而且这个工作必须在运行数据库代码之前完成。
我们要在这个方法之外使用另一个属性表示drinkValues ContentValues对象,使得这个类的其他方法也能访问这个ContentValues对象。

    private class UpdateDrinkTask extends AsyncTask<Integer, Void, Boolean>{
        private ContentValues drinkValues;
        
        protected void onPreExecute(){
            CheckBox favorite = (CheckBox) findViewById(R.id.favorite);
            drinkValues = new ContentValues();
            drinkValues.put("FAVORITE", favorite.isChecked());
        }
        
    }

doInBackground方法

        @Override
        protected Boolean doInBackground(Integer[] drinks) {
            int drinkId = drinks[0];
            SQLiteOpenHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(DrinkActivity.this);
            try {
                SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
                db.update("DRINK",
                        drinkValues,
                        "_id = ?",
                        new String[] {Integer.toString(drinkId)});
                db.close();
                return true;
            }catch (SQLiteException e){
                return false;
            }
        }

onProgressUpdate方法

该方法在主事件线程调用,所以可以访问用户界面中的视图。可以使用这个方法更新屏幕上的视图向用户显示进度。要定义这个方法接受什么类型的参数。
如果由doInBackground方法调用publishProgress,就会运行onProgressUpdate方法,如下所示

        protected Boolean doInBackground(Integer[] count) {
            for (int i = 0; i < count; ++i){
                publishProgress(i);
            }
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            super.onProgressUpdate(progress[0]);
        }

onPostExecute方法

后台任务完成后调用onPostExecute()方法。它在主事件线程中调用,所以可以访问用户界面中的视图。可以使用这个方法为用户呈现任务的结果。要把doInBackground方法的结果传入onPostExecute方法,所以它的参数必须与doInBackground的返回类型一致

我们要使用onPostExecute方法检查doInBackground方法中的数据库代码是否成功运行。如果没有,就要向用户显示一个消息。这个工作在onPostExecute方法中完成,因为这个方法可以更新用户界面。doInBackground方法在后台线程中运行,所以不能更新视图。

    protected void onPostExecute(Boolean success) {
        if (!success) {
            Toast toast = Toast.makeText(DrinkActivity.this, "Darabase unavailable", Toast.LENGTH_SHORT);
            toast.show();
        }
    }

AsyncTask类

AsyncTask是一个抽象类,它定义了三个泛型参数:Params、Progress和Result。这三个参数在创建AsyncTask的子类并实现其方法时使用,以便更好地处理异步任务。
Params:这个参数类型是在任务开始执行时传递给AsyncTask的。具体传递的内容根据任务的需要而定,例如,可能是要加载的图片的URL,或者是需要从网络上获取数据的特定字段名称等。
Progress:这个参数类型用于异步任务执行过程中的进度更新。当任务执行过程中需要更新进度时,可以在任务线程中调用publishProgress()方法,传递当前的进度值,然后在主线程中通过重写onProgressUpdate(Progress…)方法来接收并处理这些进度值。
Result:这个参数类型是在异步任务执行完成后返回的结果。当任务执行完毕后,可以在任务线程中调用onPostExecute(Result result)方法,传递计算得到的结果,然后在主线程中通过重写此方法来接收并处理这些结果。
这三个参数在AsyncTask中是非常重要的,它们使得异步任务的执行过程变得更加清晰、有条理。

完整的DrinkActivity

package com.hfad.starbuzz;

import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class DrinkActivity extends AppCompatActivity {

    public static final String EXTRA_DRINKID = "drinkId";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);

        int drinkId = (Integer)getIntent().getExtras().get(EXTRA_DRINKID);

        SQLiteOpenHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(this);
        try {
            SQLiteDatabase db = starbuzzDatabaseHelper.getReadableDatabase();
            Cursor cursor = db.query("DRINK",
                    new String[]{"NAME", "DESCRIPTION", "IMAGE_RESOURCE_ID", "FAVORITE"},
                    "_id = ?",
                    new String[] {Integer.toString(drinkId)},
                    null, null, null);
            if (cursor.moveToFirst()) {
                String nameText = cursor.getString(0);
                String descriptionText = cursor.getString(1);
                int photoId = cursor.getInt(2);
                boolean isFavorite = (cursor.getInt(3) == 1);

                TextView name = (TextView) findViewById(R.id.name);
                name.setText(nameText);

                TextView description = (TextView) findViewById(R.id.description);
                description.setText(descriptionText);

                ImageView photo = (ImageView) findViewById(R.id.photo);
                photo.setImageResource(photoId);
                photo.setContentDescription(nameText);

                CheckBox favorite = (CheckBox) findViewById(R.id.favorite);
                favorite.setChecked(isFavorite);
            }
            cursor.close();
            db.close();
        }catch (SQLException e){
            Toast toast = Toast.makeText(this,
                    "Database unavailable",
                    Toast.LENGTH_SHORT);
            toast.show();
        }
    }

    public void onFavoriteClicked(View view){
        int drinkId = (Integer)getIntent().getExtras().get(EXTRA_DRINKID);

        CheckBox favorite = (CheckBox) findViewById(R.id.favorite);
        ContentValues drinkValues = new ContentValues();
        drinkValues.put("FAVORITE", favorite.isChecked());

        SQLiteOpenHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(this);
        try{
            SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
            db.update("DRINK",
                    drinkValues,
                    "_id = ?",
                    new String[] {Integer.toString(drinkId)});
            db.close();
        }catch(SQLiteException e) {
            Toast toast = Toast.makeText(this, "Database unavailable", Toast.LENGTH_SHORT);
            toast.show();
        }
    }

    private class UpdateDrinkTask extends AsyncTask<Integer, Void, Boolean> {
        private ContentValues drinkValues;

        protected void onPreExecute(){
            CheckBox favorite = (CheckBox) findViewById(R.id.favorite);
            drinkValues = new ContentValues();
            drinkValues.put("FAVORITE", favorite.isChecked());
        }

        @Override
        protected Boolean doInBackground(Integer[] drinks) {
            int drinkId = drinks[0];
            SQLiteOpenHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(DrinkActivity.this);
            try {
                SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
                db.update("DRINK",
                        drinkValues,
                        "_id = ?",
                        new String[] {Integer.toString(drinkId)});
                db.close();
                return true;
            }catch (SQLiteException e){
                return false;
            }
        }
    }

    protected void onPostExecute(Boolean success) {
        if (!success) {
            Toast toast = Toast.makeText(DrinkActivity.this, "Darabase unavailable", Toast.LENGTH_SHORT);
            toast.show();
        }
    }

}

你可能感兴趣的:(Android实践,android,数据库)