我在运行RssReader的Android项目时出错。

代码:

URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();

它显示以下错误:

android.os.NetworkOnMainThreadException

如何解决此问题?


当前回答

RxAndroid是解决这个问题的另一个更好的选择,它让我们省去了创建线程然后在Android UI线程上发布结果的麻烦。

我们只需要指定需要在哪些线程上执行任务,并且所有事情都在内部处理。

Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() {

  @Override
  public List<String> call() {
    return mRestClient.getFavoriteMusicShows();
  }

});

mMusicShowSubscription = musicShowsObservable
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Observer<List<String>>() {

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }

    @Override
    public void onNext(List<String> musicShows) {
        listMusicShows(musicShows);
    }
});

通过指定(Schedulers.io()),RxAndroid将在不同的线程上运行getFavoriteMusicShows()。通过使用AndroidSchedulers.mainThread(),我们希望在UI线程上观察这个Observable,也就是说,我们希望我们的onNext()回调在UI线程中被调用。

其他回答

您不能在蜂巢上的UI线程上执行网络I/O。从技术上讲,这在早期版本的Android上是可能的,但这是一个非常糟糕的想法,因为它会导致你的应用停止响应,并可能导致操作系统因你的应用行为不端而杀死你的应用。您需要运行后台进程或使用AsyncTask在后台线程上执行网络事务。

Android开发者网站上有一篇关于“无痛苦线程”的文章,这是一篇很好的介绍,它将为您提供比这里实际提供的答案更深入的答案。

我将返回值的网络访问函数转换为挂起函数,如下所示:


suspend fun isInternetReachable(): Boolean {
  ...
  ...
  return result
}

然后,我修改了使用函数的位置,以适应以下情况:

...
Globalscope.async{
  ...
  result = isInternetReachable()
  ...
}
...

这仅适用于针对蜂巢SDK或更高版本的应用程序。针对早期SDK版本的应用程序可以在其主事件循环线程上进行网络连接。

错误是SDK警告!

谷歌不推荐Android 11中的Android AsyncTask API。

即使在主活动之外创建线程类,只要在main中调用它,也会得到相同的错误。调用必须在一个可运行的线程内,但如果您需要一些异步代码在后台执行或稍后在这里执行,您可以查看Kotlin和Java的一些替代方案:

*https://stackoverflow.com/questions/58767733/android-asynctask-api-deprecating-in-android-11-what-are-the-alternatives*

特别适合我的是mayank1513对上面链接中可运行线程的Java8实现的回答。代码如下:

new Thread(() -> {
        // do background stuff here
        runOnUiThread(()->{
            // OnPostExecute stuff here
        });
    }).start();

但是,您可以先在代码的某个部分定义线程,然后在其他地方启动它,如下所示:

线程定义

Thread thread = new Thread(() -> {
            // do background stuff here
            runOnUiThread(()->{
                // OnPostExecute stuff here
            });
        });

线程调用

thread.start();

我希望这可以让人省去看到不推荐的AsyncTask时的头疼。

使用以下代码执行繁重的任务。

// Your package here


import java.util.List;
import org.apache.http.NameValuePair;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.view.View.OnSystemUiVisibilityChangeListener;

public class AsyncRequest extends AsyncTask<String, Integer, String> {

    Context context;
    ProgressDialog pDialog;

    // Three Constructors
    public AsyncRequest(Activity a, String m, List<NameValuePair> p) {
        context = a;
        method = m;
        parameters = p;
    }

    public AsyncRequest(Activity a) {
        this.caller = (OnAsyncRequestComplete) a;
        context = a;
    }

    public String doInBackground(String... urls) {

        //Perform your task here
        return result;
    }

    public void onPreExecute() {
        pDialog = new ProgressDialog(context);
        pDialog.setMessage("Please wait..");
        pDialog.setCancelable(false);
        pDialog.show();
    }

    public void onProgressUpdate(Integer... progress) {
        // You can implement some progressBar and update it in this record.
        //   setProgressPercent(progress[0]);
    }

    public void onPostExecute(String response) {
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
        // Get the result here
    }

    protected void onCancelled(String response) {

        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
    }
}