【问题标题】:Handler Thread - NetworkOnMainThread exception (android.os.handler)处理程序线程 - NetworkOnMainThread 异常 (android.os.handler)
【发布时间】:2016-04-24 22:24:55
【问题描述】:

这不是上面发布的问题的重复。当 HAndlerThread 启动一个 newThread 并且我得到一个 NetworkOnMainThread 异常时,我认为这是一个奇怪的错误。解决正常的 NetworkOnMainThread 异常是全新的问题。请取消标记。

大家好,首先我正在练习 HandlerThread 的。我已经使用 Fragment 启动了一个处理线程并向线程发送消息。在 HandlerThread 的句柄消息中,我使用网络调用来下载图像。然后我使用 MainThread 的处理程序将下载的图像发布到 UI 线程。但是当运行这个应用程序时,我得到一个 NetworkOnMainThread 异常。下面是片段和处理程序线程的代码。

片段:

public class LoaderFragment extends Fragment {
Button loaderButton;
LoaderThread loadThread;
Handler loaderThreadHandler;
Handler mainHandler;
String[] queArray;


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mainHandler = new Handler(Looper.getMainLooper());
    loadThread = new LoaderThread("LoaderThread",mainHandler);
     queArray = new String[]{"http://www.paynekillers.com/downloads/screens/17_screen05.jpg",
            "http://www.hdwallpapers.in/walls/jacob_frye_assassins_creed_syndicate-wide.jpg",
    "http://www.percivalconstantine.com/wp-content/uploads/2015/03/Tomb-Raider-Lara-Croft-Summit-2013.jpg",
    "http://orig00.deviantart.net/f904/f/2014/155/e/a/batman_arkham_knight_hd_wallpaper_1_by_rajivcr7-d7l19pt.jpg",
    "http://static5.gamespot.com/uploads/original/536/5360430/2753139-15774024117_9026e0a43c_o.jpg"};
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View loaderView = inflater.inflate(R.layout.loader_layout, container, false);
    this.loaderButton = (Button) loaderView.findViewById(R.id.proc_button);
    return loaderView;
}

@Override
public void onResume() {
    super.onResume();



    loaderButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            loadThread.start();
            loaderThreadHandler = loadThread.requestHandler();
            Message msg = Message.obtain(loaderThreadHandler, 1, queArray);
            msg.sendToTarget();
        }
    });
}

@Override
public void onDestroy() {
    super.onDestroy();
    loadThread.quit();
} }

处理线程:

public class LoaderThread extends HandlerThread implements Handler.Callback{
Handler imageHandler;
Handler loaderHandler;
Map<Integer,String> queMap;
HandlerActivity activity;


public LoaderThread(String name, Handler handler) {
    super(name);
    imageHandler= handler;
    Log.d("inside : ", name);
}

Handler requestHandler(){
    loaderHandler = new Handler(this.getLooper(),this);
    return loaderHandler;
}


@Override
public boolean handleMessage(Message msg) {
    String[] que = (String[]) msg.obj;
    queMap = new HashMap<Integer,String>();
    if(que != null){Log.d("",que[3]);}
    for (int i=0;i<=4; i++){
        String url = que[i];

        queMap.put(i,url);
        Log.d("1",url);
    }
    Log.d("THR : ", String.valueOf(Thread.currentThread()));
    Random rand = new Random();
   String link = queMap.get(rand.nextInt(5));

    loadImages(link);
    return true;
}

void loadImages(final String url){
    Log.d("inside Load :","Inside Load");
    try {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        final Bitmap image = BitmapFactory.decodeStream(connection.getInputStream());
        imageHandler.post(new Runnable() {
            @Override
            public void run() {
                activity.loadImage(image, url);
            }
        });

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

Activity的loadImage方法:

public void loadImage(Bitmap image, String url) {
    Bitmap img = image;
    String link = url;
    ft = fmg.beginTransaction();
    ft.add(R.id.frag_container,imgFrag,"ImageFragment");
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    ft.show(imgFrag);
    ft.commit();
    imgFrag.loadImg(img,link);

}

此外,当我使用日志检查我在哪个线程(在 HandlerThread 的 handleMessage 中)时,我会收到一条日志消息,就像我在 Main 中一样。这怎么可能?我做错了吗?

【问题讨论】:

  • 查看here,了解如何使用HandlerThread
  • But I am trying to create an entirely new HandlerThread 我在做什么???你为什么要扩展HandlerThread?这是什么原因?
  • 在 cmets 中的讨论变得非常困难。如果您有答案,请发布答案,或在此处要求澄清信息。如果您需要进行更广泛的讨论,请使用我们的聊天室之一。
  • 评论不用于扩展讨论;这个对话是moved to chat
  • 能否请您也发布异常日志?我尝试了您的代码,但无法模拟该问题。谢谢

标签: android multithreading android-fragments android-handler android-handlerthread


【解决方案1】:

LoaderFragment 的更改来自

mainHandler = new Handler(Looper.getMainLooper());
loadThread = new LoaderThread("LoaderThread",mainHandler);

loadThread = new LoaderThread("LoaderThread");

然后在LoaderThread

public LoaderThread(String name, Handler handler) {
     super(name);         
     Log.d("inside : ", name);
}
 @Override
 protected void onLooperPrepared() {
     imageHandler = new Handler(getLooper(), this);
  }

您收到错误是因为您将 UI 线程 Looper 传递给 LoaderThread,您在 requestHandler() 中再次传递该 LoaderThread 以进行网络访问。因此,您的网络操作由 UI Thread Looper 出列。

【讨论】:

  • 感谢您的回复。我认为这可以解决问题。但我想做的是在 2 个线程之间传递消息。现在想想我有一个 Thread1 和 Thread 2。我创建了一个处理程序并在每个线程中准备了它。我可以将线程 1 处理程序引用传递给线程 2,线程 2 也是如此。现在如果我从thread1向thread2发送消息会发生什么?消息会从第一个线程处理吗?
  • 如果你说我将 UI looper 传递给第二个线程并在请求 looper 中再次获取它。如果我将 thread1 处理程序发送到 thread2,也会发生同样的情况。如果我请求 Thread2 looper,我会得到第一个线程的 looper 吗?
  • 我在您的示例LoaderThread loadThread 中只看到一个线程,但要回答您的问题,您有一个目标和您的Message 的来源,您需要为此提供相应的处理程序(想想他们作为 _id_s) 并且当 dequeu 消息时,您可以通过更改源和目​​标在另一个线程 looper 上发布消息。要设置它们,您可以在创建 thread2 并从 thread2 传递消息时传递 thread1 处理程序的引用。当线程 1 读取消息时,它可以存储源处理程序
  • 对于您的任务,您甚至可以使用相同的线程并使用Message what 参数来编码所需的操作。您发布一条消息以下载 (what=100) 图像,并在完成此消息之前在同一线程上发布一条新消息以显示图像 (what==101)。
  • "要设置它们,您可以在创建线程 2 并从线程 2 传递消息时传递对线程 1 处理程序的引用。当线程 1 读取消息时,它可以存储源处理程序"。这就是我在之前的编码中实际所做的。我只是将主线程的处理程序传递到 HandlerThread 的构造函数中的 HandlerThread 中。但是你说这样做会绑定到主线程的looper。
【解决方案2】:

我遇到了同样的问题。然后我将我的HandlerThread's 处理程序设置在名为onLooperPrepared() 的重写方法中,并且不再收到该异常,因此您应该尝试在您的LoaderThread 类中执行类似的操作:

@Override
protected void onLooperPrepared() {
    super.onLooperPrepared();
    loaderHandler = new Handler(this.getLooper(),this);
}

onLooperPrepared 是,根据documentation,我引用:如果需要在 Looper 循环之前执行某些设置,可以显式重写的回调方法。请在此处说明这是否适合您。

【讨论】:

  • 你不需要它:你可以在你start()ed你的HandlerThread之后打电话给getLooper()
  • 我应该从哪里打电话给getLooper()
  • 在您想要的任何地方,但在致电start() 之后,请参阅here
  • @DevetiPutnik 实际上 HandlerThreads 的工作是简化上述步骤。它实际上处理了弯针的准备和附件。
【解决方案3】:

对于 Android 中的任何网络操作,建议在 AsyncTask 中编写该代码。

【讨论】:

  • @zIronManBox 我已经做了很多网络和 AsyncTask 的例子是最好的......!!
  • @AjajPatel 带有执行器的异步任务比普通的异步任务要好
  • 最好将其作为后台服务,甚至在不同的进程上使用?
猜你喜欢
  • 2013-12-02
  • 2011-04-07
  • 2013-09-10
  • 2013-10-06
  • 2014-03-23
  • 2014-08-07
  • 1970-01-01
  • 1970-01-01
  • 2012-03-04
相关资源
最近更新 更多