Android Studio错误: "方法getText()必须从UI线程调用,当前推断的线程是worker"

7
我正在Android Studio中创建一个CRUD操作,但一直出现错误。当我检查LogCat时,它们向我展示的错误如下: line 156-158 1907-1931/com.example.casquejo.loginadmin E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #2 Process: com.example.casquejo.loginadmin, PID: 1907 java.lang.RuntimeException: An error occurred while executing doInBackground() Caused by: java.lang.NullPointerException atcom.example.casquejo.loginadmin.NewProductActivity$CreateNewProduct.doInBackground(NewProductActivity.java:85) at com.example.casquejo.loginadmin.NewProductActivity$CreateNewProduct.doInBackground(NewProductActivity.java:58) atcom.example.casquejo.loginadmin.NewProductActivity$CreateNewProduct.onPreExecute(NewProductActivity.java:67) atcom.example.casquejo.loginadmin.NewProductActivity$1.onClick(NewProductActivity.java:53)
有人可以帮我解决这个问题吗?或者有人可以给我一个修复这个问题的思路吗?以下是我的Java类EditProductActivity.class的代码。
       package com.example.casquejo.loginadmin;

        import java.util.ArrayList;
        import java.util.List;
        import org.apache.http.NameValuePair;
        import org.apache.http.message.BasicNameValuePair;
        import org.json.JSONException;
        import org.json.JSONObject;
        import android.app.Activity;
        import android.app.ProgressDialog;
        import android.content.Intent;
        import android.os.AsyncTask;
        import android.os.Bundle;
        import android.util.Log;
        import android.view.View;
        import android.widget.Button;
        import android.widget.EditText;

        /**
        * Created by Casquejo on 9/14/2015.
        */
        public class NewProductActivity extends Activity {
    private ProgressDialog pDialog;

    JSONParser jsonParser = new JSONParser();
    EditText inputName;
    EditText inputPrice;
    EditText inputDesc;

    private static String url_create_product = "http://10.0.2.2/android_connect/create_product.php";

    private static final String TAG_SUCCESS = "success";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.add_product);

        inputName = (EditText) findViewById(R.id.inputName);
        inputPrice = (EditText) findViewById(R.id.inputPrice);
        inputDesc = (EditText) findViewById(R.id.inputDesc);

        Button btnCreateProduct = (Button) findViewById(R.id.btnCreateProduct);
        btnCreateProduct.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                String name = inputName.getText().toString();
                String price = inputPrice.getText().toString();
                String description = inputDesc.getText().toString();
                new CreateNewProduct().execute(name, price,description);
            }
        });
    }

    class CreateNewProduct extends AsyncTask<String, String, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pDialog = new ProgressDialog(NewProductActivity.this);
            pDialog.setMessage("Creating Product..");
            pDialog.setIndeterminate(false);
            pDialog.setCancelable(true);
            pDialog.show();

        }

        protected String doInBackground(String... args) {

            String name = args[0],
                    price = args[1],
                    description = args[2];

            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("name", name));
            params.add(new BasicNameValuePair("price", price));
            params.add(new BasicNameValuePair("description", description));

            JSONObject json = jsonParser.makeHttpRequest(url_create_product,
                    "POST", params);

            Log.d("Create Response", json.toString());

            try {
                int success = json.getInt(TAG_SUCCESS);

                if (success == 1) {
                    Intent i = new Intent(getApplicationContext(), AllProductsActivity.class);
                    startActivity(i);
                    finish();
                }
                else {

                }
            } catch (JSONException e) {
                e.printStackTrace();
            }

            return null;
        }

        protected void onPostExecute(String file_url) {
            pDialog.dismiss();
        }

    }
}

我会非常小心这段代码,因为你正在从在AsyncTask上运行的后台线程中调用findViewByID(...)来获取UI元素的引用(这本身就没有意义!),而且你没有防御性地使用任何UI小部件,这让你面临空指针异常的风险。例如,我会将txtName =(EditText)findViewById(R.id.inputName);等代码移到onCreate()方法中。 - rcbevans
5个回答

7

IDE是指集成开发环境。

  String name = txtName.getText().toString();
  String price = txtPrice.getText().toString();
  String description = txtDesc.getText().toString();

读取这些值不应该是一个问题,但为了消除这个警告/错误,您可以将其移入 onClick 并通过 execute() 传递这些值。例如:

btnSave.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View arg0) {
        String name = txtName.getText().toString();
        String price = txtPrice.getText().toString();
        String description = txtDesc.getText().toString();
        new SaveProductDetails().execute(name, price, description);
    }
});

当调用doInBackground时,您可以通过前面的参数String... args读取这些后台数据。三个点的结构表示可变参数,可变参数可以像数组一样使用[]符号访问。在这个例子中,

args[0]包含name的值,args[1]包含price的值,args[2]包含description的值。


先生,我已经按照您在答案中建议的做了,但是当我检查logcat时仍然存在问题,错误是onClick和String...args,您能帮我解决吗? - DayllCasquejo
强制关闭错误,我现在可以运行应用程序,但当我进入添加部分或编辑部分时,检查Logcat时会出现这些错误。 位于**com.example.casquejo.loginadmin.NewProductActivity$CreateNewProduct.onPreExecute(NewProductActivity.java:67)**。 - DayllCasquejo
我遇到的错误是“强制关闭”,整个应用程序可以运行,但当我尝试添加或编辑时,应用程序会强制关闭,但我输入的数据已被输入到数据库中。 当我检查LogCat时,发现了一些错误。 我不知道如何在这里放置图片,所以不知道如何向您展示我的错误。 - DayllCasquejo
复制您的NewProductActivity.java文件,编辑您的问题并粘贴它。 - Blackbelt
我已经编辑过了,您能否请检查一下?谢谢您。 - DayllCasquejo
显示剩余8条评论

5

您正在由AsyncTask生成的后台线程中调用getText()

首先获取文本,然后调用异步任务。以下是一个示例:

new SaveProductDetails()
    .execute(txtName.getText().toString(), 
        txtPrice.getText().toString(), 
        txtDesc.getText().toString());

SaveProductDetailsdoInBackground方法内部:

String name = args[0],
       price = args[1],
       description = args[2];

非常感谢! 感谢你们所有人的帮助,我现在明白了。 我确实无法在doInBackground中使用它,谢谢。 - DayllCasquejo

2
在异步任务中,doInBackground(...) 方法在后台(非UI)线程中运行。正如所示的错误提示,您不允许从后台线程与UI元素进行交互。
您可以选择将参数传递到后台线程中,就像其他答案中建议的那样,或者修改您的异步任务,使得UI字符串值在 onPreExecute() 方法中被读取,该方法在UI线程上执行(就像 onPostExecute() 方法一样)。
class SaveProductDetails extends AsyncTask<String, String, String> {

private String name, price, description;

@Override
protected void onPreExecute() {
    super.onPreExecute();
    pDialog = new ProgressDialog(EditProductActivity.this);
    pDialog.setMessage("Saving product ...");
    pDialog.setIndeterminate(false);
    pDialog.setCancelable(true);
    pDialog.show();

    name = txtName.getText().toString();
    price = txtPrice.getText().toString();
    description = txtDesc.getText().toString();
}

protected String doInBackground(String... args) {
    //... Use as you would before

我建议您查看这篇博客文章(例如此文),以了解更多关于AsyncTasks的内容,包括它们的工作原理、如何使用它们以及哪个方法在哪个线程上运行等详细信息。请注意保留HTML标签。

非常感谢!感谢大家的帮助,我现在明白了。我真的不能在doInBackground中使用它,谢谢。 - DayllCasquejo
如果其中一个答案对您有帮助,请不要忘记使用答案左侧的箭头进行点赞,并标记一个答案为被接受和正确的,以帮助其他人在未来找到这个问题 :) - rcbevans
哎呀!哈哈,所有的答案都帮了我,我想给它们点赞并接受它们。但是为了未来的读者,所有的答案都是正确的,这些人真的帮了我很多 :) - DayllCasquejo

0

在执行方法中,您不需要传递值。 将名称、价格和描述变量设置为全局变量。 要在按钮单击时从EditTest获取值,请按以下方式编写代码:

每次需要单击按钮才能获取JSON数据。

btnSave.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View arg0) {
        name = txtName.getText().toString();
        price = txtPrice.getText().toString();
        description = txtDesc.getText().toString();
        new CreateNewProduct().execute();
    }
});

现在名称、价格和描述都有其值,无论您输入什么。

现在您的CreateNewProduct类看起来像这样:

class CreateNewProduct extends AsyncTask<String, String, String> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        pDialog = new ProgressDialog(NewProductActivity.this);
        pDialog.setMessage("Creating Product..");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(true);
        pDialog.show();

    }

    protected String doInBackground(String... args) {

        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("name", name));
        params.add(new BasicNameValuePair("price", price));
        params.add(new BasicNameValuePair("description", description));

        JSONObject json = jsonParser.makeHttpRequest(url_create_product,
                "POST", params);

        Log.d("Create Response", json.toString());

        try {
            int success = json.getInt(TAG_SUCCESS);

            if (success == 1) {
                Intent i = new Intent(getApplicationContext(), AllProductsActivity.class);
                startActivity(i);
                finish();
            }
            else {

            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return null;
    }

    protected void onPostExecute(String file_url) {
        pDialog.dismiss();
    }

}


0
你可以阅读从工作线程使用变量。 在您的问题中,您尝试从后台线程访问TextView文本。 为了保持一致,您不应该这样做,因为可能存在主线程(UI线程)同时设置TextView的可能性。为避免这种情况,您可以执行以下操作:
class SaveProductDetails extends AsyncTask<String, String, String>{
      //create constructor and pass values of text view in it
      String textViewValue1;
      public SaveProductDetails (String textViewValue1 ){
           this.textViewValue1=textViewValue1
      }

     //other code below
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接